diff --git a/t/thin.t b/t/thin.t index d091117a2..fcab28470 100644 --- a/t/thin.t +++ b/t/thin.t @@ -1,4 +1,4 @@ -use Test::More tests => 28; +use Test::More tests => 29; use strict; use warnings; @@ -102,7 +102,9 @@ if (0) { is scalar(@$res), 1, 'medial axis of a semicircumference is a single line'; # check whether turns are all CCW or all CW - my @lines = @{$res->[0]->lines}; + my @alllines = @{$res->[0]->lines}; + # remove lines taht are near the end. + my @lines = grep($_->a->y >= 1578184 || $_->b->y >= 1578184, @alllines); my @angles = map { $lines[$_-1]->ccw($lines[$_]->b) } 1..$#lines; ok !!(none { $_ < 0 } @angles) || (none { $_ > 0 } @angles), 'all medial axis segments of a semicircumference have the same orientation'; @@ -113,16 +115,25 @@ if (0) { [4.3, 4], [4.3, 0], [4,0], [4,4], [0,4], [0,4.5], [4,4.5], [4,10], [4.3,10], [4.3, 4.5], [6, 4.5], [6,10], [6.2,10], [6.2,4.5], [10,4.5], [10,4], [6.2,4], [6.2,0], [6, 0], [6, 4], )); + $expolygon->contour->make_counter_clockwise(); my $res = $expolygon->medial_axis(scale 0.55, scale 0.25); is scalar(@$res), 2, 'medial axis of a (bit too narrow) french cross is two lines'; ok unscale($res->[0]->length) >= (9.9) - epsilon, 'medial axis has reasonable length'; ok unscale($res->[1]->length) >= (9.9) - epsilon, 'medial axis has reasonable length'; + my @lines1 = @{$res->[0]->lines}; + my @angles1 = map { $lines1[$_-1]->ccw($lines1[$_]->b) } 1..$#lines1; + my @lines2 = @{$res->[1]->lines}; + my @angles2 = map { $lines2[$_-1]->ccw($lines2[$_]->b) } 1..$#lines2; + my @angles = (@angles1, @angles2); + ok !!(none { $_ != 0 } @angles), + 'medial axis of a (bit too narrow) french cross is two lines has only strait lines'; } { my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new_scale( [0.86526705,1.4509841], [0.57696039,1.8637021], [0.4502297,2.5569978], [0.45626199,3.2965596], [1.1218851,3.3049455], [0.96681072,2.8243202], [0.86328971,2.2056997], [0.85367905,1.7790778], )); + $expolygon->contour->make_counter_clockwise(); my $res = $expolygon->medial_axis(scale 1, scale 0.25); is scalar(@$res), 1, 'medial axis of a (bit too narrow) french cross is two lines'; ok unscale($res->[0]->length) >= (1.4) - epsilon, 'medial axis has reasonable length'; diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp index 82541545d..ffcdab1b6 100644 --- a/xs/src/libslic3r/ExPolygon.cpp +++ b/xs/src/libslic3r/ExPolygon.cpp @@ -237,11 +237,7 @@ ExPolygon::remove_point_too_near(const coord_t tolerance) { void ExPolygon::medial_axis(const ExPolygon &bounds, double max_width, double min_width, ThickPolylines* polylines, double height) const { - ExPolygon simplifiedBounds = bounds; - simplifiedBounds.remove_point_too_near(SCALED_RESOLUTION); - ExPolygon simplifiedPolygon = *this; - simplifiedPolygon.remove_point_too_near(SCALED_RESOLUTION); - Slic3r::MedialAxis ma(simplifiedPolygon, simplifiedBounds, max_width, min_width, height); + Slic3r::MedialAxis ma(*this, bounds, max_width, min_width, height); ma.build(polylines); } diff --git a/xs/src/libslic3r/MedialAxis.cpp b/xs/src/libslic3r/MedialAxis.cpp index 4fbbf5344..7be8c0958 100644 --- a/xs/src/libslic3r/MedialAxis.cpp +++ b/xs/src/libslic3r/MedialAxis.cpp @@ -359,21 +359,21 @@ add_point_same_percent(ThickPolyline* pattern, ThickPolyline* to_modify) /// return 1 for an angle of 90° and 0 for an angle of 0° or 180° double get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t min_dist_between_point) { - double nearestDist = point.distance_to(contour.contour.points.front()); - Point nearest = contour.contour.points.front(); + double nearest_dist = point.distance_to(contour.contour.points.front()); + Point point_nearest = contour.contour.points.front(); size_t id_nearest = 0; - double nearDist = nearestDist; - Point near = nearest; + double near_dist = nearest_dist; + Point point_near = point_nearest; size_t id_near = 0; for (size_t id_point = 1; id_point < contour.contour.points.size(); ++id_point) { - if (nearestDist > point.distance_to(contour.contour.points[id_point])) { - //update near + if (nearest_dist > point.distance_to(contour.contour.points[id_point])) { + //update point_near id_near = id_nearest; - near = nearest; - nearDist = nearestDist; + point_near = point_nearest; + near_dist = nearest_dist; //update nearest - nearestDist = point.distance_to(contour.contour.points[id_point]); - nearest = contour.contour.points[id_point]; + nearest_dist = point.distance_to(contour.contour.points[id_point]); + point_nearest = contour.contour.points[id_point]; id_nearest = id_point; } } @@ -381,7 +381,7 @@ get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t mi size_t id_before = id_nearest == 0 ? contour.contour.points.size() - 1 : id_nearest - 1; Point point_before = id_nearest == 0 ? contour.contour.points.back() : contour.contour.points[id_nearest - 1]; //Search one point far enough to be relevant - while (nearest.distance_to(point_before) < min_dist_between_point) { + while (point_nearest.distance_to(point_before) < min_dist_between_point) { point_before = id_before == 0 ? contour.contour.points.back() : contour.contour.points[id_before - 1]; id_before = id_before == 0 ? contour.contour.points.size() - 1 : id_before - 1; //don't loop @@ -394,7 +394,7 @@ get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t mi size_t id_after = id_nearest == contour.contour.points.size() - 1 ? 0 : id_nearest + 1; Point point_after = id_nearest == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_nearest + 1]; //Search one point far enough to be relevant - while (nearest.distance_to(point_after) < min_dist_between_point) { + while (point_nearest.distance_to(point_after) < min_dist_between_point) { point_after = id_after == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_after + 1]; id_after = id_after == contour.contour.points.size() - 1 ? 0 : id_after + 1; //don't loop @@ -405,15 +405,15 @@ get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t mi } } //compute angle - angle = nearest.ccw_angle(point_before, point_after); + angle = point_nearest.ccw_angle(point_before, point_after); if (angle >= PI) angle = 2 * PI - angle; // smaller angle //compute the diff from 90° angle = abs(angle - PI / 2); - if (near.coincides_with(nearest) && max(nearestDist, nearDist) + SCALED_EPSILON < nearest.distance_to(near)) { + if (point_near.coincides_with(point_nearest) && max(nearest_dist, near_dist) + SCALED_EPSILON < point_nearest.distance_to(point_near)) { //not only nearest Point point_before = id_near == 0 ? contour.contour.points.back() : contour.contour.points[id_near - 1]; Point point_after = id_near == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_near + 1]; - double angle2 = min(nearest.ccw_angle(point_before, point_after), nearest.ccw_angle(point_after, point_before)); + double angle2 = min(point_nearest.ccw_angle(point_before, point_after), point_nearest.ccw_angle(point_after, point_before)); angle2 = abs(angle - PI / 2); angle = (angle + angle2) / 2; } @@ -613,8 +613,12 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con // polyline, after we extend the start point it will be caught by the intersection() // call, so we keep the inner point until we perform the second intersection() as well if (polyline.endpoints.second && !bounds.has_boundary_point(polyline.points.back())) { - Line line(*(polyline.points.end() - 2), polyline.points.back()); - + size_t first_idx = polyline.points.size() - 2; + Line line(*(polyline.points.begin() + first_idx), polyline.points.back()); + while (line.length() < SCALED_RESOLUTION && first_idx>0) { + first_idx--; + line.a = *(polyline.points.begin() + first_idx); + } // prevent the line from touching on the other side, otherwise intersection() might return that solution if (polyline.points.size() == 2) line.a = line.midpoint(); @@ -623,16 +627,39 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con if (this->expolygon.contour.has_boundary_point(polyline.points.back())) { new_back = polyline.points.back(); } else { + //TODO: verify also for holes. (void)this->expolygon.contour.first_intersection(line, &new_back); // safety check if no intersection - if (new_back.x == 0 && new_back.y == 0) return; + if (new_back.x == 0 && new_back.y == 0) { + if (!this->expolygon.contains(line.b)) { + //it's outside!!! + std::cout << "Error, a line is formed that start in a polygon, end outside of it can don't cross it!\n"; + } + new_back = line.b; + } polyline.points.push_back(new_back); polyline.width.push_back(polyline.width.back()); } Point new_bound; + //TODO: verify also for holes. (void)bounds.contour.first_intersection(line, &new_bound); // safety check if no intersection - if (new_bound.x == 0 && new_bound.y == 0) return; + if (new_bound.x == 0 && new_bound.y == 0) { + if (line.b.coincides_with_epsilon(polyline.points.back())) { + return; + } + //check if we don't over-shoot inside us + bool is_in_anchor = false; + for (const ExPolygon& a : anchors) { + if (a.contains(line.b)) { + is_in_anchor = true; + break; + } + } + if (!is_in_anchor) std::cout << "not in anchor:\n"; + if (!is_in_anchor) return; + new_bound = line.b; + } /* if (new_bound.coincides_with_epsilon(new_back)) { return; }*/ @@ -765,7 +792,7 @@ MedialAxis::main_fusion(ThickPolylines& pp) coord_t biggest_main_branch_length = 0; for (size_t k = 0; k < pp.size(); ++k) { //std::cout << "try to find main : " << k << " ? " << i << " " << j << " "; - if (k == i | k == j) continue; + if (k == i || k == j) continue; ThickPolyline& main = pp[k]; if (polyline.first_point().coincides_with(main.last_point())) { main.reverse(); @@ -993,19 +1020,22 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp) while (polyline.points.size() > 1 && polyline.width.front() < this->min_width && polyline.endpoints.first) { //try to split if possible if (polyline.width[1] > min_width) { - double percent_can_keep = (min_width - polyline.width[0]) / (polyline.width[1] - polyline.width[0]); - if (polyline.points.front().distance_to(polyline.points[1]) * percent_can_keep > this->max_width / 2 - && polyline.points.front().distance_to(polyline.points[1])* (1 - percent_can_keep) > this->max_width / 2) { + double percent_can_keep = 1 - (min_width - polyline.width[0]) / (polyline.width[1] - polyline.width[0]); + if (polyline.points.front().distance_to(polyline.points[1]) * percent_can_keep > SCALED_RESOLUTION) { //Can split => move the first point and assign a new weight. //the update of endpoints wil be performed in concatThickPolylines polyline.points.front().x = polyline.points.front().x + - (coord_t)((polyline.points[1].x - polyline.points.front().x) * percent_can_keep); + (coord_t)((polyline.points[1].x - polyline.points.front().x) * (1 - percent_can_keep)); polyline.points.front().y = polyline.points.front().y + - (coord_t)((polyline.points[1].y - polyline.points.front().y) * percent_can_keep); + (coord_t)((polyline.points[1].y - polyline.points.front().y) * (1 - percent_can_keep)); polyline.width.front() = min_width; - changes = true; - break; + } else { + /// almost 0-length, Remove + polyline.points.erase(polyline.points.begin()); + polyline.width.erase(polyline.width.begin()); } + changes = true; + break; } polyline.points.erase(polyline.points.begin()); polyline.width.erase(polyline.width.begin()); @@ -1014,25 +1044,29 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp) while (polyline.points.size() > 1 && polyline.width.back() < this->min_width && polyline.endpoints.second) { //try to split if possible if (polyline.width[polyline.points.size() - 2] > min_width) { - double percent_can_keep = (min_width - polyline.width.back()) / (polyline.width[polyline.points.size() - 2] - polyline.width.back()); - if (polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * percent_can_keep > this->max_width / 2 - && polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * (1 - percent_can_keep) > this->max_width / 2) { + double percent_can_keep = 1 - (min_width - polyline.width.back()) / (polyline.width[polyline.points.size() - 2] - polyline.width.back()); + if (polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * percent_can_keep > SCALED_RESOLUTION) { //Can split => move the first point and assign a new weight. //the update of endpoints wil be performed in concatThickPolylines polyline.points.back().x = polyline.points.back().x + - (coord_t)((polyline.points[polyline.points.size() - 2].x - polyline.points.back().x) * percent_can_keep); + (coord_t)((polyline.points[polyline.points.size() - 2].x - polyline.points.back().x) * (1 - percent_can_keep)); polyline.points.back().y = polyline.points.back().y + - (coord_t)((polyline.points[polyline.points.size() - 2].y - polyline.points.back().y) * percent_can_keep); + (coord_t)((polyline.points[polyline.points.size() - 2].y - polyline.points.back().y) * (1 - percent_can_keep)); polyline.width.back() = min_width; - changes = true; - break; + } else { + /// almost 0-length, Remove + polyline.points.erase(polyline.points.end() - 1); + polyline.width.erase(polyline.width.end() - 1); } + changes = true; + break; } polyline.points.erase(polyline.points.end() - 1); polyline.width.erase(polyline.width.end() - 1); changes = true; } - if (polyline.points.size() < 2) { + //remove points and bits that comes from a "main line" + if (polyline.points.size() < 2 || (changes && polyline.length() < max_width && polyline.points.size() ==2)) { //remove self if too small pp.erase(pp.begin() + i); --i; @@ -1056,7 +1090,6 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp) Optimisation of the old algorithm : now we select the most "strait line" choice when we merge with an other line at a point with more than two meet. */ - bool changes = false; for (size_t i = 0; i < pp.size(); ++i) { ThickPolyline& polyline = pp[i]; if (polyline.endpoints.first && polyline.endpoints.second) continue; // optimization @@ -1066,8 +1099,11 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp) size_t best_idx = 0; // find another polyline starting here - for (size_t j = i + 1; j < pp.size(); ++j) { + for (size_t j = 0; j < pp.size(); ++j) { + if (j == i) continue; ThickPolyline& other = pp[j]; + if (other.endpoints.first && other.endpoints.second) continue; + if (polyline.last_point().coincides_with(other.last_point())) { other.reverse(); } else if (polyline.first_point().coincides_with(other.last_point())) { @@ -1091,16 +1127,14 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp) } } if (best_candidate != nullptr) { - polyline.points.insert(polyline.points.end(), best_candidate->points.begin() + 1, best_candidate->points.end()); polyline.width.insert(polyline.width.end(), best_candidate->width.begin() + 1, best_candidate->width.end()); polyline.endpoints.second = best_candidate->endpoints.second; assert(polyline.width.size() == polyline.points.size()); - changes = true; + if (best_idx < i) i--; pp.erase(pp.begin() + best_idx); } } - if (changes) concatThickPolylines(pp); } void @@ -1276,15 +1310,35 @@ void MedialAxis::build(ThickPolylines* polylines_out) { this->id++; - + //std::cout << layerid << "\n"; + //{ + // stringstream stri; + // stri << "medial_axis_0_enter_" << id << ".svg"; + // SVG svg(stri.str()); + // svg.draw(this->surface); + // svg.Close(); + //} this->expolygon = simplify_polygon_frontier(); + //{ + // stringstream stri; + // stri << "medial_axis_0.5_simplified_" << id << ".svg"; + // SVG svg(stri.str()); + // svg.draw(bounds); + // svg.draw(this->expolygon); + // svg.Close(); + //} + //safety check + if (this->expolygon.area() < this->min_width * this->min_width) this->expolygon = this->surface; + if (this->expolygon.area() < this->min_width * this->min_width) return; - + //std::cout << "simplify_polygon_frontier\n"; // compute the Voronoi diagram and extract medial axis polylines ThickPolylines pp; this->polyline_from_voronoi(this->expolygon.lines(), &pp); + concatThickPolylines(pp); + //std::cout << "concatThickPolylines\n"; //{ // stringstream stri; // stri << "medial_axis_1_voronoi_" << id << ".svg"; @@ -1313,7 +1367,6 @@ MedialAxis::build(ThickPolylines* polylines_out) // svg.Close(); //} - concatThickPolylines(pp); // Aligned fusion: Fusion the bits at the end of lines by "increasing thickness" // For that, we have to find other lines, @@ -1334,6 +1387,16 @@ MedialAxis::build(ThickPolylines* polylines_out) //fusion right-angle corners. fusion_corners(pp); + if (do_not_overextrude) { + const ExPolygons anchors = offset2_ex(diff_ex(this->bounds, this->expolygon), -SCALED_RESOLUTION, SCALED_RESOLUTION); + for (size_t i = 0; i < pp.size(); ++i) { + ThickPolyline& polyline = pp[i]; + extends_line(polyline, anchors, min_width); + polyline.reverse(); + extends_line(polyline, anchors, min_width); + } + } + //reduce extrusion when it's too thin to be printable remove_too_thin_extrusion(pp); //{ @@ -1359,12 +1422,14 @@ MedialAxis::build(ThickPolylines* polylines_out) // Loop through all returned polylines in order to extend their endpoints to the // expolygon boundaries - const ExPolygons anchors = offset2_ex(diff_ex(this->bounds, this->expolygon), -SCALED_RESOLUTION, SCALED_RESOLUTION); - for (size_t i = 0; i < pp.size(); ++i) { - ThickPolyline& polyline = pp[i]; - extends_line(polyline, anchors, min_width); - polyline.reverse(); - extends_line(polyline, anchors, min_width); + if (!do_not_overextrude) { + const ExPolygons anchors = offset2_ex(diff_ex(this->bounds, this->expolygon), -SCALED_RESOLUTION, SCALED_RESOLUTION); + for (size_t i = 0; i < pp.size(); ++i) { + ThickPolyline& polyline = pp[i]; + extends_line(polyline, anchors, min_width); + polyline.reverse(); + extends_line(polyline, anchors, min_width); + } } //{ // stringstream stri; diff --git a/xs/src/libslic3r/MedialAxis.hpp b/xs/src/libslic3r/MedialAxis.hpp index c65cbdb84..560e2bf03 100644 --- a/xs/src/libslic3r/MedialAxis.hpp +++ b/xs/src/libslic3r/MedialAxis.hpp @@ -22,6 +22,7 @@ namespace Slic3r { const double max_width; const double min_width; const double height; + bool do_not_overextrude = true; MedialAxis(const ExPolygon &_expolygon, const ExPolygon &_bounds, const double _max_width, const double _min_width, const double _height) : surface(_expolygon), bounds(_bounds), max_width(_max_width), min_width(_min_width), height(_height) { }; diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index 5b50b9dad..e1cbfb63b 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -249,16 +249,18 @@ void PerimeterGenerator::process() // detect edge case where a curve can be split in multiple small chunks. ExPolygons no_thin_onion = offset_ex(last, -(float)(ext_perimeter_width / 2)); - if (no_thin_onion.size()>0 && next_onion.size() > 3 * no_thin_onion.size()) { + float div = 2; + while (no_thin_onion.size() > 0 && next_onion.size() > no_thin_onion.size() && no_thin_onion.size() + next_onion.size() > 3) { + div += 0.5; //use a sightly smaller spacing to try to drastically improve the split ExPolygons next_onion_secondTry = offset2_ex( last, - -(float)(ext_perimeter_width / 2 + ext_min_spacing / 2.5 - 1), - +(float)(ext_min_spacing / 2.5 - 1)); - if (abs(((int32_t)next_onion.size()) - ((int32_t)no_thin_onion.size())) > - 2*abs(((int32_t)next_onion_secondTry.size()) - ((int32_t)no_thin_onion.size()))) { + -(float)(ext_perimeter_width / 2 + ext_min_spacing / div - 1), + +(float)(ext_min_spacing / div - 1)); + if (next_onion.size() > next_onion_secondTry.size()) { next_onion = next_onion_secondTry; } + if (div > 3) break; } // the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width @@ -269,27 +271,32 @@ void PerimeterGenerator::process() // medial axis requires non-overlapping geometry ExPolygons thin_zones = diff_ex(last, no_thin_zone, true); //don't use offset2_ex, because we don't want to merge the zones that have been separated. - ExPolygons expp = offset_ex(thin_zones, (float)(-min_width / 2)); + //a very little bit of overlap can be created here with other thin polygons, but it's more useful than worisome. + ExPolygons half_thins = offset_ex(thin_zones, (float)(-min_width / 2)); + //simplify them + for (ExPolygon &half_thin : half_thins) { + half_thin.remove_point_too_near(SCALED_RESOLUTION); + } //we push the bits removed and put them into what we will use as our anchor - if (expp.size() > 0) { - no_thin_zone = diff_ex(last, offset_ex(expp, (float)(min_width / 2)), true); + if (half_thins.size() > 0) { + no_thin_zone = diff_ex(last, offset_ex(half_thins, (float)(min_width / 2) - SCALED_EPSILON), true); } // compute a bit of overlap to anchor thin walls inside the print. - for (ExPolygon &ex : expp) { + for (ExPolygon &half_thin : half_thins) { //growing back the polygon - //a very little bit of overlap can be created here with other thin polygons, but it's more useful than worisome. - ex.remove_point_too_near(SCALED_RESOLUTION); - ExPolygons ex_bigger = offset_ex(ex, (float)(min_width / 2)); - if (ex_bigger.size() != 1) continue; // impossible error, growing a single polygon can't create multiple or 0. - ExPolygons anchor = intersection_ex(offset_ex(ex, (float)(min_width / 2) + + ExPolygons thin = offset_ex(half_thin, (float)(min_width / 2)); + if (thin.size() != 1) continue; // impossible error, growing a single polygon can't create multiple or 0. + ExPolygons anchor = intersection_ex(offset_ex(half_thin, (float)(min_width / 2) + (float)(ext_perimeter_width / 2), jtSquare), no_thin_zone, true); - ExPolygons bounds = union_ex(ex_bigger, anchor, true); + ExPolygons bounds = union_ex(thin, anchor, true); for (ExPolygon &bound : bounds) { - if (!intersection_ex(ex_bigger[0], bound).empty()) { + if (!intersection_ex(thin[0], bound).empty()) { //be sure it's not too small to extrude reliably - if (ex_bigger[0].area() > min_width*(ext_perimeter_width + ext_perimeter_spacing2)) { + thin[0].remove_point_too_near(SCALED_RESOLUTION); + if (thin[0].area() > min_width*(ext_perimeter_width + ext_perimeter_spacing2)) { + bound.remove_point_too_near(SCALED_RESOLUTION); // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop - ex_bigger[0].medial_axis(bound, ext_perimeter_width + ext_perimeter_spacing2, min_width, + thin[0].medial_axis(bound, ext_perimeter_width + ext_perimeter_spacing2, min_width, &thin_walls, this->layer_height); } break; diff --git a/xs/src/libslic3r/Polyline.cpp b/xs/src/libslic3r/Polyline.cpp index 53f59c026..346924406 100644 --- a/xs/src/libslic3r/Polyline.cpp +++ b/xs/src/libslic3r/Polyline.cpp @@ -296,6 +296,11 @@ void concatThickPolylines(ThickPolylines& pp) { //concat polyline if only 2 polyline at a point for (size_t i = 0; i < pp.size(); ++i) { ThickPolyline *polyline = &pp[i]; + if (polyline->first_point().coincides_with(polyline->last_point())) { + polyline->endpoints.first = false; + polyline->endpoints.second = false; + continue; + } size_t id_candidate_first_point = -1; size_t id_candidate_last_point = -1; @@ -305,77 +310,78 @@ void concatThickPolylines(ThickPolylines& pp) { for (size_t j = 0; j < pp.size(); ++j) { if (j == i) continue; ThickPolyline *other = &pp[j]; + if (other->first_point().coincides_with(other->last_point())) continue; if (polyline->last_point().coincides_with(other->last_point())) { - other->reverse(); id_candidate_last_point = j; nbCandidate_last_point++; - } else if (polyline->first_point().coincides_with(other->last_point())) { - id_candidate_first_point = j; - nbCandidate_first_point++; - } else if (polyline->first_point().coincides_with(other->first_point())) { - id_candidate_first_point = j; - nbCandidate_first_point++; - other->reverse(); - } else if (polyline->last_point().coincides_with(other->first_point())) { + } + if (polyline->last_point().coincides_with(other->first_point())) { id_candidate_last_point = j; nbCandidate_last_point++; - } else { - continue; + } + if (polyline->first_point().coincides_with(other->last_point())) { + id_candidate_first_point = j; + nbCandidate_first_point++; + } + if (polyline->first_point().coincides_with(other->first_point())) { + id_candidate_first_point = j; + nbCandidate_first_point++; } } if (id_candidate_last_point == id_candidate_first_point && nbCandidate_first_point == 1 && nbCandidate_last_point == 1) { + if (polyline->first_point().coincides_with(pp[id_candidate_first_point].first_point())) pp[id_candidate_first_point].reverse(); // it's a trap! it's a loop! - if (pp[id_candidate_first_point].points.size() > 2) { - polyline->points.insert(polyline->points.begin(), pp[id_candidate_first_point].points.begin() + 1, pp[id_candidate_first_point].points.end() - 1); - polyline->width.insert(polyline->width.begin(), pp[id_candidate_first_point].width.begin() + 1, pp[id_candidate_first_point].width.end() - 1); - } + polyline->points.insert(polyline->points.end(), pp[id_candidate_first_point].points.begin() + 1, pp[id_candidate_first_point].points.end()); + polyline->width.insert(polyline->width.end(), pp[id_candidate_first_point].width.begin() + 1, pp[id_candidate_first_point].width.end()); pp.erase(pp.begin() + id_candidate_first_point); changes = true; polyline->endpoints.first = false; polyline->endpoints.second = false; - continue; - } + } else { - if (nbCandidate_first_point == 1) { - //concat at front - polyline->width[0] = std::max(polyline->width.front(), pp[id_candidate_first_point].width.back()); - polyline->points.insert(polyline->points.begin(), pp[id_candidate_first_point].points.begin(), pp[id_candidate_first_point].points.end() - 1); - polyline->width.insert(polyline->width.begin(), pp[id_candidate_first_point].width.begin(), pp[id_candidate_first_point].width.end() - 1); - polyline->endpoints.first = pp[id_candidate_first_point].endpoints.first; - pp.erase(pp.begin() + id_candidate_first_point); - changes = true; - if (id_candidate_first_point < i) { - i--; - polyline = &pp[i]; + if (nbCandidate_first_point == 1) { + if (polyline->first_point().coincides_with(pp[id_candidate_first_point].first_point())) pp[id_candidate_first_point].reverse(); + //concat at front + polyline->width[0] = std::max(polyline->width.front(), pp[id_candidate_first_point].width.back()); + polyline->points.insert(polyline->points.begin(), pp[id_candidate_first_point].points.begin(), pp[id_candidate_first_point].points.end() - 1); + polyline->width.insert(polyline->width.begin(), pp[id_candidate_first_point].width.begin(), pp[id_candidate_first_point].width.end() - 1); + polyline->endpoints.first = pp[id_candidate_first_point].endpoints.first; + pp.erase(pp.begin() + id_candidate_first_point); + changes = true; + if (id_candidate_first_point < i) { + i--; + polyline = &pp[i]; + } + if (id_candidate_last_point > id_candidate_first_point) { + id_candidate_last_point--; + } + } else if (nbCandidate_first_point == 0) { + //update endpoint + polyline->endpoints.first = true; } - if (id_candidate_last_point > id_candidate_first_point) { - id_candidate_last_point--; + if (nbCandidate_last_point == 1) { + if (polyline->last_point().coincides_with(pp[id_candidate_last_point].last_point())) pp[id_candidate_last_point].reverse(); + //concat at back + polyline->width[polyline->width.size() - 1] = std::max(polyline->width.back(), pp[id_candidate_last_point].width.front()); + polyline->points.insert(polyline->points.end(), pp[id_candidate_last_point].points.begin() + 1, pp[id_candidate_last_point].points.end()); + polyline->width.insert(polyline->width.end(), pp[id_candidate_last_point].width.begin() + 1, pp[id_candidate_last_point].width.end()); + polyline->endpoints.second = pp[id_candidate_last_point].endpoints.second; + pp.erase(pp.begin() + id_candidate_last_point); + changes = true; + if (id_candidate_last_point < i) { + i--; + polyline = &pp[i]; + } + } else if (nbCandidate_last_point == 0) { + //update endpoint + polyline->endpoints.second = true; } - } else if (nbCandidate_first_point == 0 && !polyline->endpoints.first && !polyline->first_point().coincides_with(polyline->last_point())) { - //update endpoint - polyline->endpoints.first = true; - } - if (nbCandidate_last_point == 1) { - //concat at back - polyline->width[polyline->width.size() - 1] = std::max(polyline->width.back(), pp[id_candidate_last_point].width.front()); - polyline->points.insert(polyline->points.end(), pp[id_candidate_last_point].points.begin() + 1, pp[id_candidate_last_point].points.end()); - polyline->width.insert(polyline->width.end(), pp[id_candidate_last_point].width.begin() + 1, pp[id_candidate_last_point].width.end()); - polyline->endpoints.second = pp[id_candidate_last_point].endpoints.second; - pp.erase(pp.begin() + id_candidate_last_point); - changes = true; - if (id_candidate_last_point < i) { - i--; - polyline = &pp[i]; - } - } else if (nbCandidate_last_point == 0 && !polyline->endpoints.second && !polyline->first_point().coincides_with(polyline->last_point())) { - //update endpoint - polyline->endpoints.second = true; - } - if (polyline->last_point().coincides_with(polyline->first_point())) { - //the concat has created a loop : update endpoints - polyline->endpoints.first = false; - polyline->endpoints.second = false; + if (polyline->last_point().coincides_with(polyline->first_point())) { + //the concat has created a loop : update endpoints + polyline->endpoints.first = false; + polyline->endpoints.second = false; + } } } }