diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp index c2c19046e..3d5710d29 100644 --- a/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -179,8 +179,8 @@ struct SegmentIntersection // Kept grouped with other booleans for smaller memory footprint. LinkType prev_on_contour_type { LinkType::Horizontal }; LinkType next_on_contour_type { LinkType::Horizontal }; - LinkQuality prev_on_contour_quality { true }; - LinkQuality next_on_contour_quality { true }; + LinkQuality prev_on_contour_quality { LinkQuality::Valid }; + LinkQuality next_on_contour_quality { LinkQuality::Valid }; // Was this segment along the y axis consumed? // Up means up along the vertical segment. bool consumed_vertical_up { false }; @@ -237,36 +237,34 @@ struct SegmentIntersection int left_vertical(Direction dir) const { return (dir == Direction::Up ? this->has_left_vertical_up() : this->has_left_vertical_down()) ? this->prev_on_contour : -1; } int left_vertical() const { return this->has_left_vertical() ? this->prev_on_contour : -1; } int left_vertical_outside() const { return this->is_low() ? this->left_vertical_down() : this->left_vertical_up(); } - int right_vertical_up() const { return this->has_right_vertical_up() ? this->prev_on_contour : -1; } - int right_vertical_down() const { return this->has_right_vertical_down() ? this->prev_on_contour : -1; } + int right_vertical_up() const { return this->has_right_vertical_up() ? this->next_on_contour : -1; } + int right_vertical_down() const { return this->has_right_vertical_down() ? this->next_on_contour : -1; } int right_vertical(Direction dir) const { return (dir == Direction::Up ? this->has_right_vertical_up() : this->has_right_vertical_down()) ? this->next_on_contour : -1; } - int right_vertical() const { return this->has_right_vertical() ? this->prev_on_contour : -1; } + int right_vertical() const { return this->has_right_vertical() ? this->next_on_contour : -1; } int right_vertical_outside() const { return this->is_low() ? this->right_vertical_down() : this->right_vertical_up(); } int vertical_up(Side side) const { return side == Side::Left ? this->left_vertical_up() : this->right_vertical_up(); } int vertical_down(Side side) const { return side == Side::Left ? this->left_vertical_down() : this->right_vertical_down(); } int vertical_outside(Side side) const { return side == Side::Left ? this->left_vertical_outside() : this->right_vertical_outside(); } + // Returns -1 if there is no link up. int vertical_up() const { - assert(! this->has_left_vertical_up() || ! this->has_right_vertical_up()); return this->has_left_vertical_up() ? this->left_vertical_up() : this->right_vertical_up(); } LinkQuality vertical_up_quality() const { - assert(! this->has_left_vertical_up() || ! this->has_right_vertical_up()); + assert(this->has_left_vertical_up() != this->has_right_vertical_up()); return this->has_left_vertical_up() ? this->prev_on_contour_quality : this->next_on_contour_quality; } + // Returns -1 if there is no link down. int vertical_down() const { - assert(! this->has_left_vertical_down() || ! this->has_right_vertical_down()); +// assert(! this->has_left_vertical_down() || ! this->has_right_vertical_down()); return this->has_left_vertical_down() ? this->left_vertical_down() : this->right_vertical_down(); } LinkQuality vertical_down_quality() const { - assert(! this->has_left_vertical_down() || ! this->has_right_vertical_down()); + assert(this->has_left_vertical_down() != this->has_right_vertical_down()); return this->has_left_vertical_down() ? this->prev_on_contour_quality : this->next_on_contour_quality; } int vertical_outside() const { return this->is_low() ? this->vertical_down() : this->vertical_up(); } -// int next_up() const { return this->prev_on_contour_vertical ? -1 : this->prev_on_contour; } -// int next_right() const { return this->next_on_contour_vertical ? -1 : this->next_on_contour; } - // Compare two y intersection points given by rational numbers. // Note that the rational number is given as pos_p/pos_q, where pos_p is int64 and pos_q is uint32. // This function calculates pos_p * other.pos_q < other.pos_p * pos_q as a 48bit number. @@ -489,32 +487,32 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical size_t iIntersection, SegmentIntersection::Side side) { - const SegmentedIntersectionLine &il_this = segs[iVerticalLine]; - const SegmentIntersection &itsct_this = il_this.intersections[iIntersection]; - if (itsct_this.has_vertical(side)) + const SegmentedIntersectionLine &vline_this = segs[iVerticalLine]; + const SegmentIntersection &it_this = vline_this.intersections[iIntersection]; + if (it_this.has_vertical(side)) // Not the first intersection along the contor. This intersection point // has been preceded by an intersection point along the vertical line. return INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; - int iIntersectionOther = itsct_this.horizontal(side); + int iIntersectionOther = it_this.horizontal(side); if (iIntersectionOther == -1) return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED; assert(side == SegmentIntersection::Side::Right ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0)); - const SegmentedIntersectionLine &il_other = segs[side == SegmentIntersection::Side::Right ? (iVerticalLine+1) : (iVerticalLine-1)]; - const SegmentIntersection &itsct_other = il_other.intersections[iIntersectionOther]; - assert(itsct_other.is_inner()); + const SegmentedIntersectionLine &vline_other = segs[side == SegmentIntersection::Side::Right ? (iVerticalLine + 1) : (iVerticalLine - 1)]; + const SegmentIntersection &it_other = vline_other.intersections[iIntersectionOther]; + assert(it_other.is_inner()); assert(iIntersectionOther > 0); - assert(iIntersectionOther + 1 < il_other.intersections.size()); + assert(iIntersectionOther + 1 < vline_other.intersections.size()); // Is iIntersectionOther at the boundary of a vertical segment? - const SegmentIntersection &itsct_other2 = il_other.intersections[itsct_other.is_low() ? iIntersectionOther - 1 : iIntersectionOther + 1]; - if (itsct_other2.is_inner()) + const SegmentIntersection &it_other2 = vline_other.intersections[it_other.is_low() ? iIntersectionOther - 1 : iIntersectionOther + 1]; + if (it_other2.is_inner()) // Cannot follow a perimeter segment into the middle of another vertical segment. // Only perimeter segments connecting to the end of a vertical segment are followed. return INTERSECTION_TYPE_OTHER_VLINE_INNER; - assert(itsct_other.is_low() == itsct_other2.is_low()); - if (side == SegmentIntersection::Side::Right ? itsct_this.consumed_perimeter_right : itsct_other.consumed_perimeter_right) + assert(it_other.is_low() == it_other2.is_low()); + if (side == SegmentIntersection::Side::Right ? it_this.consumed_perimeter_right : it_other.consumed_perimeter_right) // This perimeter segment was already consumed. return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED; - if (itsct_other.is_low() ? itsct_other.consumed_vertical_up : il_other.intersections[iIntersectionOther-1].consumed_vertical_up) + if (it_other.is_low() ? it_other.consumed_vertical_up : vline_other.intersections[iIntersectionOther - 1].consumed_vertical_up) // This vertical segment was already consumed. return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED; return INTERSECTION_TYPE_OTHER_VLINE_OK; @@ -555,23 +553,23 @@ static inline coordf_t measure_perimeter_prev_next_segment_length( return coordf_t(-1); } - const SegmentedIntersectionLine &il = segs[iVerticalLine]; - const SegmentIntersection &itsct = il.intersections[iIntersection]; - const SegmentedIntersectionLine &il2 = segs[iVerticalLineOther]; - const SegmentIntersection &itsct2 = il2.intersections[iIntersection2]; - assert(itsct.iContour == itsct2.iContour); - const Polygon &poly = poly_with_offset.contour(itsct.iContour); -// const bool ccw = poly_with_offset.is_contour_ccw(il.iContour); - assert(itsct.type == itsct2.type); - assert(itsct.iContour == itsct2.iContour); - assert(itsct.is_inner()); - const bool forward = itsct.is_low() == dir_is_next; + const SegmentedIntersectionLine &vline = segs[iVerticalLine]; + const SegmentIntersection &it = vline.intersections[iIntersection]; + const SegmentedIntersectionLine &vline2 = segs[iVerticalLineOther]; + const SegmentIntersection &it2 = vline2.intersections[iIntersection2]; + assert(it.iContour == it2.iContour); + const Polygon &poly = poly_with_offset.contour(it.iContour); +// const bool ccw = poly_with_offset.is_contour_ccw(vline.iContour); + assert(it.type == it2.type); + assert(it.iContour == it2.iContour); + assert(it.is_inner()); + const bool forward = it.is_low() == dir_is_next; - Point p1(il.pos, itsct.pos()); - Point p2(il2.pos, itsct2.pos()); + Point p1(vline.pos, it.pos()); + Point p2(vline2.pos, it2.pos()); return forward ? - segment_length(poly, itsct .iSegment, p1, itsct2.iSegment, p2) : - segment_length(poly, itsct2.iSegment, p2, itsct .iSegment, p1); + segment_length(poly, it .iSegment, p1, it2.iSegment, p2) : + segment_length(poly, it2.iSegment, p2, it .iSegment, p1); } static inline coordf_t measure_perimeter_prev_segment_length( @@ -736,10 +734,10 @@ static inline float measure_outer_contour_slab( distance_of_segmens(poly, iSegBelow, itsct.iSegment, true); int d_up = (iAbove == -1) ? std::numeric_limits::max() : distance_of_segmens(poly, iSegAbove, itsct.iSegment, true); - if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up)) + if (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up)) // The vertical crossing comes eralier than the prev crossing. // Disable the perimeter going back. - intrsctn_type_prev = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; + intrsection_type_prev = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; if (d_up > std::min(d_horiz, d_down)) // The horizontal crossing comes earlier than the vertical crossing. vert_seg_dir_valid_mask &= ~DIR_BACKWARD; @@ -918,7 +916,7 @@ static std::vector slice_region_by_vertical_lines(con } // Verify the segments. If something is wrong, give up. -#define ASSERT_THROW(CONDITION) do { assert(CONDITION); throw InfillFailedException(); } while (0) +#define ASSERT_THROW(CONDITION) do { assert(CONDITION); if (! (CONDITION)) throw InfillFailedException(); } while (0) for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) { SegmentedIntersectionLine &sil = segs[i_seg]; // The intersection points have to be even. @@ -952,18 +950,19 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset const SegmentedIntersectionLine *il_prev = i_vline > 0 ? &segs[i_vline - 1] : nullptr; const SegmentedIntersectionLine *il_next = i_vline + 1 < segs.size() ? &segs[i_vline + 1] : nullptr; - for (size_t i_intersection = 0; i_intersection + 1 < il.intersections.size(); ++ i_intersection) { + for (int i_intersection = 0; i_intersection < il.intersections.size(); ++ i_intersection) { SegmentIntersection &itsct = il.intersections[i_intersection]; const Polygon &poly = poly_with_offset.contour(itsct.iContour); // 1) Find possible connection points on the previous / next vertical line. - // Find an intersection point on iVerticalLineOther, intersecting iInnerContour - // at the same orientation as iIntersection, and being closest to iIntersection + // Find an intersection point on il_prev, intersecting i_intersection + // at the same orientation as i_intersection, and being closest to i_intersection // in the number of contour segments, when following the direction of the contour. + //FIXME this has O(n) time complexity. Likely an O(log(n)) scheme is possible. int iprev = -1; if (il_prev) { int dmin = std::numeric_limits::max(); - for (size_t i = 0; i < il_prev->intersections.size(); ++ i) { + for (int i = 0; i < il_prev->intersections.size(); ++ i) { const SegmentIntersection &itsct2 = il_prev->intersections[i]; if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) { // The intersection points lie on the same contour and have the same orientation. @@ -976,10 +975,11 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset } } } + // The same for il_next. int inext = -1; if (il_next) { int dmin = std::numeric_limits::max(); - for (size_t i = 0; i < il_next->intersections.size(); ++ i) { + for (int i = 0; i < il_next->intersections.size(); ++ i) { const SegmentIntersection &itsct2 = il_next->intersections[i]; if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) { // The intersection points lie on the same contour and have the same orientation. @@ -996,14 +996,14 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset // 2) Find possible connection points on the same vertical line. int iabove = -1; // Does the perimeter intersect the current vertical line above intrsctn? - for (size_t i = i_intersection + 1; i + 1 < il.intersections.size(); ++ i) + for (int i = i_intersection + 1; i < il.intersections.size(); ++ i) if (il.intersections[i].iContour == itsct.iContour) { iabove = i; break; } // Does the perimeter intersect the current vertical line below intrsctn? int ibelow = -1; - for (size_t i = i_intersection - 1; i > 0; -- i) + for (int i = i_intersection - 1; i >= 0; -- i) if (il.intersections[i].iContour == itsct.iContour) { ibelow = i; break; @@ -1019,7 +1019,7 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset int d_down = (ibelow == -1) ? std::numeric_limits::max() : distance_of_segmens(poly, il.intersections[ibelow].iSegment, itsct.iSegment, forward); int d_up = (iabove == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, il.intersections[ibelow].iSegment, itsct.iSegment, forward); + distance_of_segmens(poly, il.intersections[iabove].iSegment, itsct.iSegment, forward); if (d_horiz < std::min(d_down, d_up)) { itsct.prev_on_contour = iprev; itsct.prev_on_contour_type = SegmentIntersection::LinkType::Horizontal; @@ -1061,7 +1061,7 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset // Such intersection shall always exist. static const SegmentIntersection& end_of_vertical_run_raw(const SegmentIntersection &start) { - assert(start.type != SegmentIntersection::INNER_LOW); + assert(start.type == SegmentIntersection::INNER_LOW); // Step back to the beginning of the vertical segment to mark it as consumed. auto *it = &start; do { @@ -1083,7 +1083,7 @@ static SegmentIntersection& end_of_vertical_run_raw(SegmentIntersection &start) // Such intersection shall always exist. static const SegmentIntersection& end_of_vertical_run(const SegmentedIntersectionLine &il, const SegmentIntersection &start) { - assert(start.type != SegmentIntersection::INNER_LOW); + assert(start.type == SegmentIntersection::INNER_LOW); const SegmentIntersection *end = &end_of_vertical_run_raw(start); assert(end->type == SegmentIntersection::INNER_HIGH); for (;;) { @@ -1263,17 +1263,17 @@ static void traverse_graph_generate_polylines( { // For each outer only chords, measure their maximum distance to the bow of the outer contour. // Mark an outer only chord as consumed, if the distance is low. - for (size_t i_vline = 0; i_vline < segs.size(); ++i_vline) { - SegmentedIntersectionLine& seg = segs[i_vline]; - for (size_t i_intersection = 0; i_intersection + 1 < seg.intersections.size(); ++i_intersection) { - if (seg.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW && - seg.intersections[i_intersection + 1].type == SegmentIntersection::OUTER_HIGH) { + for (int i_vline = 0; i_vline < segs.size(); ++ i_vline) { + SegmentedIntersectionLine &vline = segs[i_vline]; + for (int i_intersection = 0; i_intersection + 1 < vline.intersections.size(); ++ i_intersection) { + if (vline.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW && + vline.intersections[i_intersection + 1].type == SegmentIntersection::OUTER_HIGH) { bool consumed = false; // if (params.full_infill()) { // measure_outer_contour_slab(poly_with_offset, segs, i_vline, i_ntersection); // } else consumed = true; - seg.intersections[i_intersection].consumed_vertical_up = consumed; + vline.intersections[i_intersection].consumed_vertical_up = consumed; } } } @@ -1283,34 +1283,34 @@ static void traverse_graph_generate_polylines( // Naively one would expect to achieve best results by chaining the paths by the shortest distance, // but that procedure does not create the longest continuous paths. // A simple "sweep left to right" procedure achieves better results. - size_t i_vline = 0; - size_t i_intersection = size_t(-1); + int i_vline = 0; + int i_intersection = -1; // Follow the line, connect the lines into a graph. // Until no new line could be added to the output path: Point pointLast; - Polyline* polyline_current = NULL; - if (!polylines_out.empty()) + Polyline* polyline_current = nullptr; + if (! polylines_out.empty()) pointLast = polylines_out.back().points.back(); for (;;) { - if (i_intersection == size_t(-1)) { + if (i_intersection == -1) { // The path has been interrupted. Find a next starting point, closest to the previous extruder position. coordf_t dist2min = std::numeric_limits().max(); - for (size_t i_vline2 = 0; i_vline2 < segs.size(); ++i_vline2) { - const SegmentedIntersectionLine& seg = segs[i_vline2]; - if (!seg.intersections.empty()) { - assert(seg.intersections.size() > 1); + for (int i_vline2 = 0; i_vline2 < segs.size(); ++ i_vline2) { + const SegmentedIntersectionLine &vline = segs[i_vline2]; + if (! vline.intersections.empty()) { + assert(vline.intersections.size() > 1); // Even number of intersections with the loops. - assert((seg.intersections.size() & 1) == 0); - assert(seg.intersections.front().type == SegmentIntersection::OUTER_LOW); - for (size_t i = 0; i < seg.intersections.size(); ++i) { - const SegmentIntersection& intrsctn = seg.intersections[i]; + assert((vline.intersections.size() & 1) == 0); + assert(vline.intersections.front().type == SegmentIntersection::OUTER_LOW); + for (int i = 0; i < vline.intersections.size(); ++ i) { + const SegmentIntersection& intrsctn = vline.intersections[i]; if (intrsctn.is_outer()) { assert(intrsctn.is_low() || i > 0); bool consumed = intrsctn.is_low() ? intrsctn.consumed_vertical_up : - seg.intersections[i - 1].consumed_vertical_up; - if (!consumed) { - coordf_t dist2 = sqr(coordf_t(pointLast(0) - seg.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos())); + vline.intersections[i - 1].consumed_vertical_up; + if (! consumed) { + coordf_t dist2 = sqr(coordf_t(pointLast(0) - vline.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos())); if (dist2 < dist2min) { dist2min = dist2; i_vline = i_vline2; @@ -1326,7 +1326,7 @@ static void traverse_graph_generate_polylines( } } } - if (i_intersection == size_t(-1)) + if (i_intersection == -1) // We are finished. break; found: @@ -1339,220 +1339,196 @@ static void traverse_graph_generate_polylines( } // From the initial point (i_vline, i_intersection), follow a path. - SegmentedIntersectionLine& seg = segs[i_vline]; - SegmentIntersection* intrsctn = &seg.intersections[i_intersection]; - bool going_up = intrsctn->is_low(); - bool try_connect = false; + SegmentedIntersectionLine &vline = segs[i_vline]; + SegmentIntersection *it = &vline.intersections[i_intersection]; + bool going_up = it->is_low(); + bool try_connect = false; if (going_up) { - assert(!intrsctn->consumed_vertical_up); - assert(i_intersection + 1 < seg.intersections.size()); + assert(! it->consumed_vertical_up); + assert(i_intersection + 1 < vline.intersections.size()); // Step back to the beginning of the vertical segment to mark it as consumed. - if (intrsctn->is_inner()) { + if (it->is_inner()) { assert(i_intersection > 0); - --intrsctn; - --i_intersection; + -- it; + -- i_intersection; } // Consume the complete vertical segment up to the outer contour. do { - intrsctn->consumed_vertical_up = true; - ++intrsctn; - ++i_intersection; - assert(i_intersection < seg.intersections.size()); - } while (intrsctn->type != SegmentIntersection::OUTER_HIGH); - if ((intrsctn - 1)->is_inner()) { + it->consumed_vertical_up = true; + ++ it; + ++ i_intersection; + assert(i_intersection < vline.intersections.size()); + } while (it->type != SegmentIntersection::OUTER_HIGH); + if ((it - 1)->is_inner()) { // Step back. - --intrsctn; - --i_intersection; - assert(intrsctn->type == SegmentIntersection::INNER_HIGH); + -- it; + -- i_intersection; + assert(it->type == SegmentIntersection::INNER_HIGH); try_connect = true; } } else { // Going down. - assert(intrsctn->is_high()); + assert(it->is_high()); assert(i_intersection > 0); - assert(!(intrsctn - 1)->consumed_vertical_up); + assert(!(it - 1)->consumed_vertical_up); // Consume the complete vertical segment up to the outer contour. - if (intrsctn->is_inner()) - intrsctn->consumed_vertical_up = true; + if (it->is_inner()) + it->consumed_vertical_up = true; do { assert(i_intersection > 0); - --intrsctn; - --i_intersection; - intrsctn->consumed_vertical_up = true; - } while (intrsctn->type != SegmentIntersection::OUTER_LOW); - if ((intrsctn + 1)->is_inner()) { + -- it; + -- i_intersection; + it->consumed_vertical_up = true; + } while (it->type != SegmentIntersection::OUTER_LOW); + if ((it + 1)->is_inner()) { // Step back. - ++intrsctn; - ++i_intersection; - assert(intrsctn->type == SegmentIntersection::INNER_LOW); + ++ it; + ++ i_intersection; + assert(it->type == SegmentIntersection::INNER_LOW); try_connect = true; } } if (try_connect) { // Decide, whether to finish the segment, or whether to follow the perimeter. - // 1) Find possible connection points on the previous / next vertical line. - IntersectionTypeOtherVLine intrsctn_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection); + IntersectionTypeOtherVLine intrsection_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection); IntersectionTypeOtherVLine intrsctn_type_next = intersection_type_on_next_vertical_line(segs, i_vline, i_intersection); // Try to connect to a previous or next vertical line, making a zig-zag pattern. - if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) { - int iPrev = intrsctn->left_horizontal(); - int iNext = intrsctn->right_horizontal(); - coordf_t distPrev = (intrsctn_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : - measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, i_intersection, iPrev); - coordf_t distNext = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : - measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, i_intersection, iNext); + if (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) { + // A horizontal connection along the perimeter line exists. + int i_prev = it->left_horizontal(); + int i_next = it->right_horizontal(); + coordf_t dist_prev = (intrsection_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : + measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_prev); + coordf_t dist_next = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : + measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_next); // Take the shorter path. //FIXME this may not be always the best strategy to take the shortest connection line now. - bool take_next = (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ? - (distNext < distPrev) : + bool take_next = (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ? + (dist_next < dist_prev) : intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK; - assert(intrsctn->is_inner()); - bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? distNext : distPrev) > link_max_length); + assert(it->is_inner()); + bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? dist_next : dist_prev) > link_max_length); if (skip) { +#if 1 // Just skip the connecting contour and start a new path. goto dont_connect; - polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); - polylines_out.push_back(Polyline()); +#else + polyline_current->points.emplace_back(vline.pos, it->pos()); + polylines_out.emplace_back(); polyline_current = &polylines_out.back(); const SegmentedIntersectionLine& il2 = segs[take_next ? (i_vline + 1) : (i_vline - 1)]; - polyline_current->points.push_back(Point(il2.pos, il2.intersections[take_next ? iNext : iPrev].pos())); + polyline_current->points.emplace_back(il2.pos, il2.intersections[take_next ? i_next : i_prev].pos()); +#endif } else { - polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); - emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, take_next ? iNext : iPrev, *polyline_current, take_next); + polyline_current->points.emplace_back(vline.pos, it->pos()); + emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, it->iContour, i_intersection, take_next ? i_next : i_prev, *polyline_current, take_next); } // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. - if (iPrev != -1) - segs[i_vline - 1].intersections[iPrev].consumed_perimeter_right = true; - if (iNext != -1) - intrsctn->consumed_perimeter_right = true; + if (i_prev != -1) + segs[i_vline - 1].intersections[i_prev].consumed_perimeter_right = true; + if (i_next != -1) + it->consumed_perimeter_right = true; //FIXME consume the left / right connecting segments at the other end of this line? Currently it is not critical because a perimeter segment is not followed if the vertical segment at the other side has already been consumed. // Advance to the neighbor line. if (take_next) { - ++i_vline; - i_intersection = iNext; + ++ i_vline; + i_intersection = i_next; } else { - --i_vline; - i_intersection = iPrev; + -- i_vline; + i_intersection = i_prev; } continue; } // 5) Try to connect to a previous or next point on the same vertical line. - if (int inext = intrsctn->vertical_outside(); inext != -1) { + if (int inext = it->vertical_outside(); inext != -1) { bool valid = true; // Verify, that there is no intersection with the inner contour up to the end of the contour segment. // Verify, that the successive segment has not been consumed yet. if (going_up) { - if (seg.intersections[inext].consumed_vertical_up) + if (vline.intersections[inext].consumed_vertical_up) valid = false; else { - for (int i = (int)i_intersection + 1; i < inext && valid; ++i) - if (seg.intersections[i].is_inner()) + for (int i = i_intersection + 1; i < inext && valid; ++ i) + if (vline.intersections[i].is_inner()) valid = false; } } else { - if (seg.intersections[inext - 1].consumed_vertical_up) + if (vline.intersections[inext - 1].consumed_vertical_up) valid = false; else { - for (int i = inext + 1; i < (int)i_intersection && valid; ++i) - if (seg.intersections[i].is_inner()) + for (int i = inext + 1; i < i_intersection && valid; ++ i) + if (vline.intersections[i].is_inner()) valid = false; } } if (valid) { - const Polygon& poly = poly_with_offset.contour(intrsctn->iContour); - assert(intrsctn->iContour == seg.intersections[inext].iContour); - int iSegNext = seg.intersections[inext].iSegment; + const Polygon &poly = poly_with_offset.contour(it->iContour); + assert(it->iContour == vline.intersections[inext].iContour); // Skip this perimeter line? bool skip = params.dont_connect; - bool dir_forward = intrsctn->has_right_vertical_outside(); + bool dir_forward = it->has_right_vertical_outside(); if (! skip && link_max_length > 0) { coordf_t link_length = measure_perimeter_segment_on_vertical_line_length( poly_with_offset, segs, i_vline, i_intersection, inext, dir_forward); skip = link_length > link_max_length; } - polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); + polyline_current->points.emplace_back(vline.pos, it->pos()); if (skip) { // Just skip the connecting contour and start a new path. - polylines_out.push_back(Polyline()); + polylines_out.emplace_back(); polyline_current = &polylines_out.back(); - polyline_current->points.push_back(Point(seg.pos, seg.intersections[inext].pos())); + polyline_current->points.emplace_back(vline.pos, vline.intersections[inext].pos()); } else { // Consume the connecting contour and the next segment. - emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, inext, *polyline_current, dir_forward); + emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, i_intersection, inext, *polyline_current, dir_forward); } // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. // If there are any outer intersection points skipped (bypassed) by the contour, // mark them as processed. - if (going_up) { - for (int i = (int)i_intersection; i < inext; ++i) - seg.intersections[i].consumed_vertical_up = true; - } else { - for (int i = inext; i < (int)i_intersection; ++i) - seg.intersections[i].consumed_vertical_up = true; - } - // seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true; - intrsctn->consumed_perimeter_right = true; - i_intersection = inext; if (going_up) - ++intrsctn; + for (int i = i_intersection; i < inext; ++ i) + vline.intersections[i].consumed_vertical_up = true; else - --intrsctn; - intrsctn->consumed_perimeter_right = true; + for (int i = inext; i < i_intersection; ++ i) + vline.intersections[i].consumed_vertical_up = true; + // seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true; + it->consumed_perimeter_right = true; + (going_up ? ++ it : -- it)->consumed_perimeter_right = true; + i_intersection = inext; continue; } } + dont_connect: // No way to continue the current polyline. Take the rest of the line up to the outer contour. // This will finish the polyline, starting another polyline at a new point. - if (going_up) - ++intrsctn; - else - --intrsctn; + going_up ? ++ it : -- it; } // Finish the current vertical line, // reset the current vertical line to pick a new starting point in the next round. - assert(intrsctn->is_outer()); - assert(intrsctn->is_high() == going_up); - pointLast = Point(seg.pos, intrsctn->pos()); - polyline_current->points.push_back(pointLast); + assert(it->is_outer()); + assert(it->is_high() == going_up); + pointLast = Point(vline.pos, it->pos()); + polyline_current->points.emplace_back(pointLast); // Handle duplicate points and zero length segments. polyline_current->remove_duplicate_points(); - assert(!polyline_current->has_duplicate_points()); + assert(! polyline_current->has_duplicate_points()); // Handle nearly zero length edges. if (polyline_current->points.size() <= 1 || (polyline_current->points.size() == 2 && std::abs(polyline_current->points.front()(0) - polyline_current->points.back()(0)) < SCALED_EPSILON && std::abs(polyline_current->points.front()(1) - polyline_current->points.back()(1)) < SCALED_EPSILON)) polylines_out.pop_back(); - intrsctn = NULL; - i_intersection = -1; - polyline_current = NULL; + it = nullptr; + i_intersection = -1; + polyline_current = nullptr; } } -struct MonotonousRegion; - -struct NextMonotonousRegion -{ - MonotonousRegion *region; - struct Path { - float length { 0 }; // Length of the link to the next region. - float visibility { 0 }; // 1 / length. Which length, just to the next region, or including the path accross the region? - float pheromone { 0 }; // <0, 1> - }; - enum Index : int { - LowLow, - LowHigh, - HighLow, - HighHigh - }; - Path paths[4]; -}; - struct MonotonousRegion { struct Boundary { @@ -1565,20 +1541,28 @@ struct MonotonousRegion Boundary right; // Length when starting at left.low - double len1; + float len1 { 0.f }; // Length when starting at left.high - double len2; + float len2 { 0.f }; // If true, then when starting at left.low, then ending at right.high and vice versa. // If false, then ending at the same side as starting. - bool flips; + bool flips { false }; + bool length(bool region_flipped) const { return region_flipped ? len2 : len1; } int left_intersection_point(bool region_flipped) const { return region_flipped ? left.high : left.low; } int right_intersection_point(bool region_flipped) const { return (region_flipped == flips) ? right.low : right.high; } // Left regions are used to track whether all regions left to this one have already been printed. boost::container::small_vector left_neighbors; // Right regions are held to pick a next region to be extruded using the "Ant colony" heuristics. - boost::container::small_vector right_neighbors; + boost::container::small_vector right_neighbors; +}; + +struct AntPath +{ + float length { -1. }; // Length of the link to the next region. + float visibility { -1. }; // 1 / length. Which length, just to the next region, or including the path accross the region? + float pheromone { 0 }; // <0, 1> }; struct MonotonousRegionLink @@ -1587,10 +1571,65 @@ struct MonotonousRegionLink bool flipped; // Distance of right side of this region to left side of the next region, if the "flipped" flag of this region and the next region // is applied as defined. - NextMonotonousRegion::Path *next; + AntPath *next; // Distance of right side of this region to left side of the next region, if the "flipped" flag of this region and the next region // is applied in reverse order as if the zig-zags were flipped. - NextMonotonousRegion::Path *next_flipped; + AntPath *next_flipped; +}; + +class AntPathMatrix +{ +public: + AntPathMatrix(const std::vector ®ions, const ExPolygonWithOffset &poly_with_offset, const std::vector &segs) : + m_regions(regions), + m_poly_with_offset(poly_with_offset), + m_segs(segs), + // From end of one region to the start of another region, both flipped or not flipped. + m_matrix(regions.size() * regions.size() * 4) {} + + AntPath& operator()(const MonotonousRegion ®ion_from, bool flipped_from, const MonotonousRegion ®ion_to, bool flipped_to) + { + int row = 2 * int(®ion_from - m_regions.data()) + flipped_from; + int col = 2 * int(®ion_to - m_regions.data()) + flipped_to; + AntPath &path = m_matrix[row * m_regions.size() * 2 + col]; + if (path.length == -1.) { + // This path is accessed for the first time. Update the length and cost. + int i_from = region_from.right_intersection_point(flipped_from); + int i_to = region_to.left_intersection_point(flipped_to); + const SegmentedIntersectionLine &vline_from = m_segs[region_from.right.vline]; + const SegmentedIntersectionLine &vline_to = m_segs[region_to.left.vline]; + if (region_from.right.vline + 1 == region_from.left.vline) { + int i_right = vline_from.intersections[i_from].right_horizontal(); + if (i_right == i_to && vline_from.intersections[i_from].next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) { + // Measure length along the contour. + path.length = measure_perimeter_next_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to); + } + } + if (path.length == -1.) { + // Just apply the Eucledian distance of the end points. + path.length = Vec2f(vline_to.pos - vline_from.pos, vline_to.intersections[i_to].pos() - vline_from.intersections[i_from].pos()).norm(); + } + path.visibility = 1. / (path.length + EPSILON); + } + return path; + } + + AntPath& operator()(const MonotonousRegionLink ®ion_from, const MonotonousRegion ®ion_to, bool flipped_to) + { return (*this)(*region_from.region, region_from.flipped, region_to, flipped_to); } + AntPath& operator()(const MonotonousRegion ®ion_from, bool flipped_from, const MonotonousRegionLink ®ion_to) + { return (*this)(region_from, flipped_from, *region_to.region, region_to.flipped); } + AntPath& operator()(const MonotonousRegionLink ®ion_from, const MonotonousRegionLink ®ion_to) + { return (*this)(*region_from.region, region_from.flipped, *region_to.region, region_to.flipped); } + +private: + // Source regions, used for addressing and updating m_matrix. + const std::vector &m_regions; + // To calculate the intersection points and contour lengths. + const ExPolygonWithOffset &m_poly_with_offset; + const std::vector &m_segs; + // From end of one region to the start of another region, both flipped or not flipped. + //FIXME one may possibly use sparse representation of the matrix. + std::vector m_matrix; }; static const SegmentIntersection& vertical_run_bottom(const SegmentedIntersectionLine &vline, const SegmentIntersection &start) @@ -1601,10 +1640,15 @@ static const SegmentIntersection& vertical_run_bottom(const SegmentedIntersectio for (;;) { while (it->type != SegmentIntersection::INNER_LOW) -- it; - int down = it->vertical_down(); - if (down == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid) - break; - it = &vline.intersections[down]; + if ((it - 1)->type == SegmentIntersection::INNER_HIGH) + -- it; + else { + int down = it->vertical_down(); + if (down == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid) + break; + it = &vline.intersections[down]; + assert(it->type == SegmentIntersection::INNER_HIGH); + } } return *it; } @@ -1621,10 +1665,15 @@ static const SegmentIntersection& vertical_run_top(const SegmentedIntersectionLi for (;;) { while (it->type != SegmentIntersection::INNER_HIGH) ++ it; - int up = it->vertical_up(); - if (up == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid) - break; - it = &vline.intersections[up]; + if ((it + 1)->type == SegmentIntersection::INNER_LOW) + ++ it; + else { + int up = it->vertical_up(); + if (up == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid) + break; + it = &vline.intersections[up]; + assert(it->type == SegmentIntersection::INNER_LOW); + } } return *it; } @@ -1719,41 +1768,53 @@ static std::vector generate_montonous_regions(std::vector monotonous_regions; - for (size_t i_vline_seed = 0; i_vline_seed < segs.size(); ++ i_vline_seed) { + for (int i_vline_seed = 0; i_vline_seed < segs.size(); ++ i_vline_seed) { SegmentedIntersectionLine &vline_seed = segs[i_vline_seed]; - for (size_t i_intersection_seed = 1; i_intersection_seed + 1 < vline_seed.intersections.size(); ) { + for (int i_intersection_seed = 1; i_intersection_seed + 1 < vline_seed.intersections.size(); ) { while (i_intersection_seed + 1 < vline_seed.intersections.size() && vline_seed.intersections[i_intersection_seed].type != SegmentIntersection::INNER_LOW) ++ i_intersection_seed; SegmentIntersection *start = &vline_seed.intersections[i_intersection_seed]; - SegmentIntersection *end = &end_of_vertical_run_raw(*start); + SegmentIntersection *end = &end_of_vertical_run(vline_seed, *start); if (! start->consumed_vertical_up) { // Draw a new monotonous region starting with this segment. // while there is only a single right neighbor - start->consumed_vertical_up = true; - size_t i_vline = i_vline_seed; + int i_vline = i_vline_seed; std::pair left(start, end); MonotonousRegion region; region.left.vline = i_vline; - region.left.low = left.first - vline_seed.intersections.data(); - region.left.high = left.second - vline_seed.intersections.data(); + region.left.low = int(left.first - vline_seed.intersections.data()); + region.left.high = int(left.second - vline_seed.intersections.data()); region.right = region.left; + start->consumed_vertical_up = true; + int num_lines = 1; while (++ i_vline < segs.size()) { SegmentedIntersectionLine &vline_left = segs[i_vline - 1]; SegmentedIntersectionLine &vline_right = segs[i_vline]; - std::pair right = right_overlap(left, vline_right); - std::pair right_left = left_overlap(right, vline_left); + std::pair right = right_overlap(left, vline_right); + if (right.first == nullptr) + // No neighbor at the right side of the current segment. + break; + SegmentIntersection* right_top_first = &vertical_run_top(vline_right, *right.first); + if (right_top_first != right.second) + // This segment overlaps with multiple segments at its right side. + break; + std::pair right_left = left_overlap(right, vline_left); if (left != right_left) - // Left & right draws don't overlap exclusively. + // Left & right draws don't overlap exclusively, right neighbor segment overlaps with multiple segments at its left. break; region.right.vline = i_vline; - region.right.low = right.first - vline_right.intersections.data(); - region.right.high = right.second - vline_right.intersections.data(); + region.right.low = int(right.first - vline_right.intersections.data()); + region.right.high = int(right.second - vline_right.intersections.data()); right.first->consumed_vertical_up = true; + ++ num_lines; left = right; } + // Even number of lines makes the infill zig-zag to exit on the other side of the region than where it starts. + region.flips = (num_lines & 1) != 0; + monotonous_regions.emplace_back(region); } - i_intersection_seed = end - vline_seed.intersections.data() + 1; + i_intersection_seed = int(end - vline_seed.intersections.data()) + 1; } } @@ -1781,38 +1842,41 @@ static void connect_monotonous_regions(std::vector ®ions, s for (MonotonousRegion ®ion : regions) { if (region.left.vline > 0) { auto &vline = segs[region.left.vline]; - auto begin = &vline.intersections[region.left.low]; - auto end = &vline.intersections[region.left.high]; - for (;;) { - MapType key(begin, nullptr); - auto it = std::lower_bound(map_intersection_to_region_end.begin(), map_intersection_to_region_end.end(), key); - assert(it != map_intersection_to_region_end.end() && it->first == key.first); - NextMonotonousRegion next_region{ ®ion }; - it->second->right_neighbors.emplace_back(next_region); - SegmentIntersection *next = &vertical_run_top(vline, *begin); - if (next == end) - break; - while (next->type != SegmentIntersection::INNER_LOW) - ++ next; - begin = next; - } + auto &vline_left = segs[region.left.vline - 1]; + auto[lbegin, lend] = left_overlap(vline.intersections[region.left.low], vline.intersections[region.left.high], vline_left); + if (lbegin != nullptr) { + for (;;) { + MapType key(lbegin, nullptr); + auto it = std::lower_bound(map_intersection_to_region_end.begin(), map_intersection_to_region_end.end(), key); + assert(it != map_intersection_to_region_end.end() && it->first == key.first); + it->second->right_neighbors.emplace_back(®ion); + SegmentIntersection *lnext = &vertical_run_top(vline_left, *lbegin); + if (lnext == lend) + break; + while (lnext->type != SegmentIntersection::INNER_LOW) + ++ lnext; + lbegin = lnext; + } + } } if (region.right.vline + 1 < segs.size()) { auto &vline = segs[region.right.vline]; - auto begin = &vline.intersections[region.right.low]; - auto end = &vline.intersections[region.right.high]; - for (;;) { - MapType key(begin, nullptr); - auto it = std::lower_bound(map_intersection_to_region_start.begin(), map_intersection_to_region_start.end(), key); - assert(it != map_intersection_to_region_start.end() && it->first == key.first); - it->second->left_neighbors.emplace_back(®ion); - SegmentIntersection *next = &vertical_run_top(vline, *begin); - if (next == end) - break; - while (next->type != SegmentIntersection::INNER_LOW) - ++ next; - begin = next; - } + auto &vline_right = segs[region.right.vline + 1]; + auto [rbegin, rend] = right_overlap(vline.intersections[region.right.low], vline.intersections[region.right.high], vline_right); + if (rbegin != nullptr) { + for (;;) { + MapType key(rbegin, nullptr); + auto it = std::lower_bound(map_intersection_to_region_start.begin(), map_intersection_to_region_start.end(), key); + assert(it != map_intersection_to_region_start.end() && it->first == key.first); + it->second->left_neighbors.emplace_back(®ion); + SegmentIntersection *rnext = &vertical_run_top(vline_right, *rbegin); + if (rnext == rend) + break; + while (rnext->type != SegmentIntersection::INNER_LOW) + ++ rnext; + rbegin = rnext; + } + } } } } @@ -1821,7 +1885,7 @@ static void connect_monotonous_regions(std::vector ®ions, s // https://www.chalmers.se/en/departments/math/research/research-groups/optimization/OptimizationMasterTheses/MScThesis-RaadSalman-final.pdf // Algorithm 6.1 Lexicographic Path Preserving 3-opt // Optimize path while maintaining the ordering constraints. -void monotonous_3_opt(std::vector &path, std::vector &segs) +void monotonous_3_opt(std::vector &path, const std::vector &segs) { // When doing the 3-opt path preserving flips, one has to fulfill two constraints: // @@ -1842,19 +1906,19 @@ void monotonous_3_opt(std::vector &path, std::vector chain_monotonous_regions( - std::vector ®ions, std::vector &segs, std::mt19937_64 &rng) + std::vector ®ions, const ExPolygonWithOffset &poly_with_offset, const std::vector &segs, std::mt19937_64 &rng) { // Start point of a region (left) given the direction of the initial infill line. auto region_start_point = [&segs](const MonotonousRegion ®ion, bool dir) { - SegmentedIntersectionLine &vline = segs[region.left.vline]; - SegmentIntersection &ipt = vline.intersections[dir ? region.left.high : region.left.low]; + const SegmentedIntersectionLine &vline = segs[region.left.vline]; + const SegmentIntersection &ipt = vline.intersections[dir ? region.left.high : region.left.low]; return Vec2f(float(vline.pos), float(ipt.pos())); }; // End point of a region (right) given the direction of the initial infill line and whether the monotonous run contains // even or odd number of vertical lines. auto region_end_point = [&segs](const MonotonousRegion ®ion, bool dir) { - SegmentedIntersectionLine &vline = segs[region.right.vline]; - SegmentIntersection &ipt = vline.intersections[(dir == region.flips) ? region.right.low : region.right.high]; + const SegmentedIntersectionLine &vline = segs[region.right.vline]; + const SegmentIntersection &ipt = vline.intersections[(dir == region.flips) ? region.right.low : region.right.high]; return Vec2f(float(vline.pos), float(ipt.pos())); }; @@ -1867,7 +1931,7 @@ static std::vector chain_monotonous_regions( if (region.left_neighbors.empty()) queue.emplace_back(®ion); else - left_neighbors_unprocessed[®ion - regions.data()] = region.left_neighbors.size(); + left_neighbors_unprocessed[®ion - regions.data()] = int(region.left_neighbors.size()); // Make copy of structures that need to be initialized at each ant iteration. auto left_neighbors_unprocessed_initial = left_neighbors_unprocessed; auto queue_initial = queue; @@ -1878,14 +1942,16 @@ static std::vector chain_monotonous_regions( float best_path_length = std::numeric_limits::max(); struct NextCandidate { - NextMonotonousRegion *region; - NextMonotonousRegion::Path *link; - NextMonotonousRegion::Path *link_flipped; - float cost; - bool dir; + MonotonousRegion *region; + AntPath *link; + AntPath *link_flipped; + float cost; + bool dir; }; std::vector next_candidates; + AntPathMatrix path_matrix(regions, poly_with_offset, segs); + // How many times to repeat the ant simulation. constexpr int num_runs = 10; // With how many ants each of the run will be performed? @@ -1900,10 +1966,10 @@ static std::vector chain_monotonous_regions( constexpr float pheromone_alpha = 1.f; // pheromone exponent constexpr float pheromone_beta = 2.f; // attractiveness weighted towards edge length // Cost of traversing a link between two monotonous regions. - auto path_cost = [pheromone_alpha, pheromone_beta](NextMonotonousRegion::Path &path) { + auto path_cost = [pheromone_alpha, pheromone_beta](AntPath &path) { return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta); }; - for (int run = 0; run < num_runs; ++ run) + for (int run = 0; run < num_runs; ++ run) { for (int ant = 0; ant < num_ants; ++ ant) { @@ -1911,92 +1977,111 @@ static std::vector chain_monotonous_regions( path.clear(); queue = queue_initial; left_neighbors_unprocessed = left_neighbors_unprocessed_initial; - while (! queue.empty()) { - // Sort the queue by distance to the last point. - // Take a candidate based on shortest distance? or ant colony? - if (path.empty()) { - // Pick randomly the first from the queue at random orientation. - int first_idx = std::uniform_int_distribution<>(0, int(queue.size()) - 1)(rng); - path.emplace_back(MonotonousRegionLink{ queue[first_idx], rng() > rng.max() / 2 }); - *(queue.begin() + first_idx) = std::move(queue.back()); - queue.pop_back(); + // Pick randomly the first from the queue at random orientation. + int first_idx = std::uniform_int_distribution<>(0, int(queue.size()) - 1)(rng); + path.emplace_back(MonotonousRegionLink{ queue[first_idx], rng() > rng.max() / 2 }); + *(queue.begin() + first_idx) = std::move(queue.back()); + queue.pop_back(); + assert(left_neighbors_unprocessed[path.back().region - regions.data()] == 0); + + while (! queue.empty() || ! path.back().region->right_neighbors.empty()) { + // Chain. + MonotonousRegion ®ion = *path.back().region; + bool dir = path.back().flipped; + Vec2f end_pt = region_end_point(region, dir); + // Sort by distance to pt. + next_candidates.clear(); + next_candidates.reserve(region.right_neighbors.size() * 2); + for (MonotonousRegion *next : region.right_neighbors) { + int &unprocessed = left_neighbors_unprocessed[next - regions.data()]; + assert(unprocessed > 0); + if (-- unprocessed == 0) { + // Dependencies of the successive blocks are satisfied. + AntPath &path1 = path_matrix(region, dir, *next, false); + AntPath &path1_flipped = path_matrix(region, ! dir, *next, true); + AntPath &path2 = path_matrix(region, dir, *next, true); + AntPath &path2_flipped = path_matrix(region, ! dir, *next, false); + next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_cost(path1), false }); + next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_cost(path2), true }); + } + } + size_t num_direct_neighbors = next_candidates.size(); + //FIXME add the queue items to the candidates? These are valid moves as well. + if (num_direct_neighbors == 0) { + // Add the queue candidates. + for (MonotonousRegion *next : queue) { + AntPath &path1 = path_matrix(region, dir, *next, false); + AntPath &path1_flipped = path_matrix(region, ! dir, *next, true); + AntPath &path2 = path_matrix(region, dir, *next, true); + AntPath &path2_flipped = path_matrix(region, ! dir, *next, false); + next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_cost(path1), false }); + next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_cost(path2), true }); + } + } + float dice = float(rng()) / float(rng.max()); + std::vector::iterator take_path; + if (dice < probability_take_best) { + // Take the lowest cost path. + take_path = std::min_element(next_candidates.begin(), next_candidates.end(), [](auto &l, auto &r){ return l.cost < r.cost; }); } else { - // Pick the closest neighbor from the queue? - } - -- left_neighbors_unprocessed[path.back().region - regions.data()]; - while (! path.back().region->right_neighbors.empty()) { - // Chain. - MonotonousRegion ®ion = *path.back().region; - bool dir = path.back().flipped; - Vec2f end_pt = region_end_point(region, dir); - // Sort by distance to pt. - next_candidates.reserve(region.right_neighbors.size() * 2); - for (NextMonotonousRegion &next : region.right_neighbors) { - int unprocessed = left_neighbors_unprocessed[next.region - regions.data()]; - assert(unprocessed > 0); - if (unprocessed == 1) { - // Dependencies of the successive blocks are satisfied. - bool flip = dir == region.flips; - auto path_cost = [pheromone_alpha, pheromone_beta](NextMonotonousRegion::Path& path) { - return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta); - }; - NextMonotonousRegion::Path &path_low = next.paths[flip ? NextMonotonousRegion::HighLow : NextMonotonousRegion::LowLow]; - NextMonotonousRegion::Path &path_low_flipped = next.paths[flip ? NextMonotonousRegion::LowHigh : NextMonotonousRegion::HighHigh]; - NextMonotonousRegion::Path &path_high = next.paths[flip ? NextMonotonousRegion::HighHigh : NextMonotonousRegion::LowHigh]; - NextMonotonousRegion::Path &path_high_flipped = next.paths[flip ? NextMonotonousRegion::LowLow : NextMonotonousRegion::HighLow]; - next_candidates.emplace_back(NextCandidate{ &next, &path_low, &path_low_flipped, path_cost(path_low), false }); - next_candidates.emplace_back(NextCandidate{ &next, &path_high, &path_high_flipped, path_cost(path_high), true }); - } - } - //std::sort(next_candidates.begin(), next_candidates.end(), [](const auto &l, const auto &r) { l.dist < r.dist; }); - float dice = float(rng()) / float(rng.max()); - std::vector::iterator take_path; - if (dice < probability_take_best) { - // Take the lowest cost path. - take_path = std::min_element(next_candidates.begin(), next_candidates.end(), [](auto &l, auto &r){ return l.cost < r.cost; }); - } else { - // Take the path based on the cost. - // Calculate the total cost. - float total_cost = std::accumulate(next_candidates.begin(), next_candidates.end(), 0.f, [](const float l, const NextCandidate& r) { return l + r.cost; }); - // Take a random path based on the cost. - float cost_threshold = floor(float(rng()) * total_cost / float(rng.max())); - take_path = next_candidates.end(); - -- take_path; - for (auto it = next_candidates.begin(); it < next_candidates.end(); ++ it) - if (cost_threshold -= it->cost <= 0.) { - take_path = it; - break; - } - } - // Extend the path. - NextMonotonousRegion &next_region = *take_path->region; - bool next_dir = take_path->dir; - path.back().next = take_path->link; - path.back().next_flipped = take_path->link_flipped; - path.emplace_back(MonotonousRegionLink{ next_region.region, next_dir }); - // Decrease the number of next block dependencies. - -- left_neighbors_unprocessed[next_region.region - regions.data()]; - // Update pheromones along this link. - take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit; + // Take the path based on the cost. + // Calculate the total cost. + float total_cost = std::accumulate(next_candidates.begin(), next_candidates.end(), 0.f, [](const float l, const NextCandidate& r) { return l + r.cost; }); + // Take a random path based on the cost. + float cost_threshold = floor(float(rng()) * total_cost / float(rng.max())); + take_path = next_candidates.end(); + -- take_path; + for (auto it = next_candidates.begin(); it < next_candidates.end(); ++ it) + if (cost_threshold -= it->cost <= 0.) { + take_path = it; + break; + } } + // Move the other right neighbors with satisified constraints to the queue. + bool direct_neighbor_taken = take_path - next_candidates.begin() < num_direct_neighbors; + for (std::vector::iterator it_next_candidate = next_candidates.begin(); it_next_candidate != next_candidates.begin() + num_direct_neighbors; ++ it_next_candidate) + if ((queue.empty() || it_next_candidate->region != queue.back()) && it_next_candidate->region != take_path->region) + queue.emplace_back(it_next_candidate->region); + if (take_path - next_candidates.begin() >= num_direct_neighbors) { + // Remove the selected path from the queue. + auto it = std::find(queue.begin(), queue.end(), take_path->region); + *it = queue.back(); + queue.pop_back(); + } + // Extend the path. + MonotonousRegion *next_region = take_path->region; + bool next_dir = take_path->dir; + path.back().next = take_path->link; + path.back().next_flipped = take_path->link_flipped; + path.emplace_back(MonotonousRegionLink{ next_region, next_dir }); + // Update pheromones along this link. + take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit; } // Perform 3-opt local optimization of the path. monotonous_3_opt(path, segs); // Measure path length. - float path_length = std::accumulate(path.begin(), path.end(), 0.f, [](const float l, const MonotonousRegionLink& r) { return l + r.next->length; }); + assert(! path.empty()); + float path_length = std::accumulate(path.begin(), path.end() - 1, + path.back().region->length(path.back().flipped), + [&path_matrix](const float l, const MonotonousRegionLink &r) { + const MonotonousRegionLink &next = *(&r + 1); + return l + r.region->length(r.flipped) + path_matrix(*r.region, r.flipped, *next.region, next.flipped).length; + }); // Save the shortest path. if (path_length < best_path_length) { best_path_length = path_length; - std::swap(best_path_length, path_length); + std::swap(best_path, path); } } // Reinforce the path feromones with the best path. - float total_cost = best_path_length; - for (MonotonousRegionLink &link : path) + float total_cost = best_path_length + EPSILON; + for (size_t i = 0; i + 1 < path.size(); ++ i) { + MonotonousRegionLink &link = path[i]; link.next->pheromone = (1.f - pheromone_evaporation) * link.next->pheromone + pheromone_evaporation / total_cost; + } } return best_path; @@ -2055,76 +2140,76 @@ static void polylines_from_paths(const std::vector &path, } for (;;) { - const SegmentedIntersectionLine &seg = segs[i_vline]; - const SegmentIntersection *intrsctn = &seg.intersections[i_intersection]; - const bool going_up = intrsctn->is_low(); + const SegmentedIntersectionLine &vline = segs[i_vline]; + const SegmentIntersection *it = &vline.intersections[i_intersection]; + const bool going_up = it->is_low(); if (polyline == nullptr) { polylines_out.emplace_back(); polyline = &polylines_out.back(); // Extend the infill line up to the outer contour. - polyline->points.emplace_back(seg.pos, (intrsctn + (going_up ? - 1 : 1))->pos()); + polyline->points.emplace_back(vline.pos, (it + (going_up ? - 1 : 1))->pos()); } else - polyline->points.emplace_back(seg.pos, intrsctn->pos()); + polyline->points.emplace_back(vline.pos, it->pos()); - int iright = intrsctn->right_horizontal(); + int iright = it->right_horizontal(); if (going_up) { // Consume the complete vertical segment up to the inner contour. for (;;) { do { - ++ intrsctn; - iright = std::max(iright, intrsctn->right_horizontal()); - } while (intrsctn->type != SegmentIntersection::INNER_HIGH); - polyline->points.emplace_back(seg.pos, intrsctn->pos()); - int inext = intrsctn->vertical_up(); + ++ it; + iright = std::max(iright, it->right_horizontal()); + } while (it->type != SegmentIntersection::INNER_HIGH); + polyline->points.emplace_back(vline.pos, it->pos()); + int inext = it->vertical_up(); if (inext == -1) break; - const Polygon &poly = poly_with_offset.contour(intrsctn->iContour); - assert(intrsctn->iContour == seg.intersections[inext].iContour); - emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, inext, *polyline, intrsctn->has_right_vertical_up()); - intrsctn = seg.intersections.data() + inext; + const Polygon &poly = poly_with_offset.contour(it->iContour); + assert(it->iContour == vline.intersections[inext].iContour); + emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, it - vline.intersections.data(), inext, *polyline, it->has_left_vertical_up()); + it = vline.intersections.data() + inext; } } else { // Going down. - assert(intrsctn->is_high()); + assert(it->is_high()); assert(i_intersection > 0); for (;;) { do { - -- intrsctn; - if (int iright_new = intrsctn->right_horizontal(); iright_new != -1) + -- it; + if (int iright_new = it->right_horizontal(); iright_new != -1) iright = iright_new; - } while (intrsctn->type != SegmentIntersection::INNER_LOW); - polyline->points.emplace_back(seg.pos, intrsctn->pos()); - int inext = intrsctn->vertical_down(); + } while (it->type != SegmentIntersection::INNER_LOW); + polyline->points.emplace_back(vline.pos, it->pos()); + int inext = it->vertical_down(); if (inext == -1) break; - const Polygon &poly = poly_with_offset.contour(intrsctn->iContour); - assert(intrsctn->iContour == seg.intersections[inext].iContour); - emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, intrsctn - seg.intersections.data(), inext, *polyline, intrsctn->has_right_vertical_down()); - intrsctn = seg.intersections.data() + inext; + const Polygon &poly = poly_with_offset.contour(it->iContour); + assert(it->iContour == vline.intersections[inext].iContour); + emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, it - vline.intersections.data(), inext, *polyline, it->has_right_vertical_down()); + it = vline.intersections.data() + inext; } } if (i_vline == region.right.vline) break; - int inext = intrsctn->right_horizontal(); - if (inext != -1 && intrsctn->next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) { + int inext = it->right_horizontal(); + if (inext != -1 && it->next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) { // Emit a horizontal connection contour. - emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, intrsctn - seg.intersections.data(), inext, *polyline, true); + emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, it->iContour, it - vline.intersections.data(), inext, *polyline, true); i_intersection = inext; } else { // Finish the current vertical line, - going_up ? ++ intrsctn : -- intrsctn; - assert(intrsctn->is_outer()); - assert(intrsctn->is_high() == going_up); - polyline->points.back() = Point(seg.pos, intrsctn->pos()); + going_up ? ++ it : -- it; + assert(it->is_outer()); + assert(it->is_high() == going_up); + polyline->points.back() = Point(vline.pos, it->pos()); finish_polyline(); if (inext == -1) { // Find the end of the next overlapping vertical segment. const SegmentedIntersectionLine &vline_right = segs[i_vline + 1]; const SegmentIntersection *right = going_up ? &vertical_run_top(vline_right, vline_right.intersections[iright]) : &vertical_run_bottom(vline_right, vline_right.intersections[iright]); - i_intersection = right - vline_right.intersections.data(); + i_intersection = int(right - vline_right.intersections.data()); } else i_intersection = inext; } @@ -2132,6 +2217,18 @@ static void polylines_from_paths(const std::vector &path, ++ i_vline; } } + + if (polyline != nullptr) { + // Finish the current vertical line, + const MonotonousRegion ®ion = *path.back().region; + const SegmentedIntersectionLine &vline = segs[region.right.vline]; + const SegmentIntersection *ip = &vline.intersections[region.right_intersection_point(path.back().flipped)]; + assert(ip->is_inner()); + ip->is_low() ? -- ip : ++ ip; + assert(ip->is_outer()); + polyline->points.back() = Point(vline.pos, ip->pos()); + finish_polyline(); + } } bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out) @@ -2224,13 +2321,16 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP svg.Close(); #endif /* SLIC3R_DEBUG */ + //FIXME this is a hack to get the monotonous infill rolling. We likely want a smarter switch, likely based on user decison. bool monotonous_infill = params.density > 0.99; if (monotonous_infill) { std::vector regions = generate_montonous_regions(segs); connect_monotonous_regions(regions, segs); - std::mt19937_64 rng; - std::vector path = chain_monotonous_regions(regions, segs, rng); - polylines_from_paths(path, poly_with_offset, segs, polylines_out); + if (! regions.empty()) { + std::mt19937_64 rng; + std::vector path = chain_monotonous_regions(regions, poly_with_offset, segs, rng); + polylines_from_paths(path, poly_with_offset, segs, polylines_out); + } } else traverse_graph_generate_polylines(poly_with_offset, params, this->link_max_length, segs, polylines_out); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 01a804ee7..b5890f578 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3439,10 +3439,13 @@ void GCode::ObjectByExtruder::Island::Region::append(const Type type, const Extr // First we append the entities, there are eec->entities.size() of them: size_t old_size = perimeters_or_infills->size(); - size_t new_size = old_size + eec->entities.size(); + size_t new_size = old_size + (eec->can_reverse() ? eec->entities.size() : 1); perimeters_or_infills->reserve(new_size); - for (auto* ee : eec->entities) - perimeters_or_infills->emplace_back(ee); + if (eec->can_reverse()) { + for (auto* ee : eec->entities) + perimeters_or_infills->emplace_back(ee); + } else + perimeters_or_infills->emplace_back(const_cast(eec)); if (copies_extruder != nullptr) { // Don't reallocate overrides if not needed.