diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp index 21d89e777..27b19daf8 100644 --- a/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -1775,13 +1775,18 @@ FillRectilinear2WGapFill::fill_surface_extrusion(const Surface *surface, const F // remove areas for gapfill // factor=0.5 : remove area smaller than a spacing. factor=1 : max spacing for the gapfill (but not the width) - float factor = 0.9f; - ExPolygons rectilinear_areas = offset2_ex(ExPolygons{ surface->expolygon }, -params.flow->scaled_spacing() * factor, params.flow->scaled_spacing() * factor); + //choose between 2 to avoid dotted line effect. + float factor1 = 0.99f; + float factor2 = 0.7f; + ExPolygons rectilinear_areas1 = offset2_ex(ExPolygons{ surface->expolygon }, -params.flow->scaled_spacing() * factor1, params.flow->scaled_spacing() * factor1); + ExPolygons rectilinear_areas2 = offset2_ex(ExPolygons{ surface->expolygon }, -params.flow->scaled_spacing() * factor2, params.flow->scaled_spacing() * factor2); + std::cout << "FillRectilinear2WGapFill use " << (rectilinear_areas1.size() <= rectilinear_areas2.size() + 1 ? "1" : "2") << "\n"; + ExPolygons &rectilinear_areas = rectilinear_areas1.size() <= rectilinear_areas2.size() + 1 ? rectilinear_areas1 : rectilinear_areas2; ExPolygons gapfill_areas = diff_ex(ExPolygons{ surface->expolygon }, rectilinear_areas); double rec_area = 0; for (ExPolygon &p : rectilinear_areas)rec_area += p.area(); double gf_area = 0; - for (ExPolygon &p : gapfill_areas)gf_area += p.area(); + for (ExPolygon &p : gapfill_areas) gf_area += p.area(); //std::cout << unscaled(unscaled(surface->expolygon.area())) << " = " << unscaled(unscaled(rec_area)) << " + " << unscaled(unscaled(gf_area)) << "\n"; // rectilinear @@ -1851,21 +1856,20 @@ FillRectilinear2WGapFill::fill_surface_extrusion(const Surface *surface, const F //gapfill if (gapfill_areas.size() > 0) { ThickPolylines polylines_gapfill; - double min = 0.2 * params.flow->scaled_width() * (1 - INSET_OVERLAP_TOLERANCE); + double min = 0.4 * scale_(params.flow->nozzle_diameter) * (1 - INSET_OVERLAP_TOLERANCE); double max = 2. * params.flow->scaled_width(); // collapse - double min_offset = 0.1 * params.flow->scaled_width() * (1 - INSET_OVERLAP_TOLERANCE); //be sure we don't gapfill where the perimeters are already touching each other (negative spacing). min = std::max(min, double(Flow::new_from_spacing(EPSILON, params.flow->nozzle_diameter , params.flow->height, false).scaled_width())); - double max_offset = 2. * params.flow->scaled_spacing(); - ExPolygons gapfill_areas_collapsed = diff_ex( - offset2_ex(gapfill_areas, double(-min_offset / 2), double(+min_offset / 2)), - offset2_ex(gapfill_areas, double(-max_offset / 2), double(+max_offset / 2)), - true); + //ExPolygons gapfill_areas_collapsed = diff_ex( + // offset2_ex(gapfill_areas, double(-min / 2), double(+min / 2)), + // offset2_ex(gapfill_areas, double(-max / 2), double(+max / 2)), + // true); + ExPolygons gapfill_areas_collapsed = offset2_ex(gapfill_areas, double(-min / 2), double(+min / 2)); for (const ExPolygon &ex : gapfill_areas_collapsed) { //remove too small gaps that are too hard to fill. //ie one that are smaller than an extrusion with width of min and a length of max. - if (ex.area() > min * max) { + if (ex.area() > scale_(params.flow->nozzle_diameter)*scale_(params.flow->nozzle_diameter) * 2) { MedialAxis{ ex, params.flow->scaled_width() * 2, params.flow->scaled_width() / 5, coord_t(params.flow->height) }.build(polylines_gapfill); } } diff --git a/src/libslic3r/MedialAxis.cpp b/src/libslic3r/MedialAxis.cpp index 5a89d5e22..abaf59bdb 100644 --- a/src/libslic3r/MedialAxis.cpp +++ b/src/libslic3r/MedialAxis.cpp @@ -29,28 +29,30 @@ MedialAxis::polyline_from_voronoi(const Lines& voronoi_edges, ThickPolylines* po this->lines = voronoi_edges; construct_voronoi(lines.begin(), lines.end(), &this->vd); - /* + typedef const VD::edge_type edge_t; + // DEBUG: dump all Voronoi edges - { + /*{ for (VD::const_edge_iterator edge = this->vd.edges().begin(); edge != this->vd.edges().end(); ++edge) { if (edge->is_infinite()) continue; - + const edge_t* edgeptr = &*edge; ThickPolyline polyline; polyline.points.push_back(Point( edge->vertex0()->x(), edge->vertex0()->y() )); polyline.points.push_back(Point( edge->vertex1()->x(), edge->vertex1()->y() )); + polyline.width.push_back(this->thickness[edgeptr].first); + polyline.width.push_back(this->thickness[edgeptr].second); polylines->push_back(polyline); } return; - } - */ + }*/ + - typedef const VD::edge_type edge_t; // collect valid edges (i.e. prune those not belonging to MAT) // note: this keeps twins, so it inserts twice the number of the valid edges this->valid_edges.clear(); { - std::set seen_edges; + std::set seen_edges; for (VD::const_edge_iterator edge = this->vd.edges().begin(); edge != this->vd.edges().end(); ++edge) { // if we only process segments representing closed loops, none if the // infinite edges (if any) would be part of our MAT anyway @@ -71,6 +73,18 @@ MedialAxis::polyline_from_voronoi(const Lines& voronoi_edges, ThickPolylines* po // iterate through the valid edges to build polylines while (!this->edges.empty()) { const edge_t* edge = *this->edges.begin(); + if (this->thickness[edge].first > this->max_width*1.001) { + //std::cerr << "Error, edge.first has a thickness of " << unscaled(this->thickness[edge].first) << " > " << unscaled(this->max_width) << "\n"; + //(void)this->edges.erase(edge); + //(void)this->edges.erase(edge->twin()); + //continue; + } + if (this->thickness[edge].second > this->max_width*1.001) { + //std::cerr << "Error, edge.second has a thickness of " << unscaled(this->thickness[edge].second) << " > " << unscaled(this->max_width) << "\n"; + //(void)this->edges.erase(edge); + //(void)this->edges.erase(edge->twin()); + //continue; + } // start a polyline ThickPolyline polyline; @@ -97,7 +111,7 @@ MedialAxis::polyline_from_voronoi(const Lines& voronoi_edges, ThickPolylines* po assert(polyline.width.size() == polyline.points.size()); - // prevent loop endpoints from being extended + // if loop, set endpoints to false if (polyline.first_point().coincides_with(polyline.last_point())) { polyline.endpoints.first = false; polyline.endpoints.second = false; @@ -446,7 +460,7 @@ MedialAxis::fusion_curve(ThickPolylines &pp) for (size_t i = 0; i < pp.size(); ++i) { ThickPolyline& polyline = pp[i]; // only consider 2-point polyline with endpoint - if (polyline.points.size() != 2) continue; + //if (polyline.points.size() != 2) continue; // too restrictive. if (polyline.endpoints.first) polyline.reverse(); else if (!polyline.endpoints.second) continue; if (polyline.width.back() > EPSILON) continue; @@ -496,6 +510,10 @@ MedialAxis::fusion_curve(ThickPolylines &pp) sum_dot += dot_temp; } } + sum_dot = abs(sum_dot); + std::cout << " with mindot= " << min_dot << "< 0.5" << " ; with sum_dot= " << sum_dot << "< 0.2" << " ; with crosspoint.size= " << crosspoint.size() << " ; with coeff_contour_angle= " << coeff_contour_angle << " 0.2> " << (1 - (coeff_contour_angle / (PI / 2))) + << " ; length= " << unscaled(polyline.length())<<" >? 1.42*width= "<< polyline.width.front()<<"->"<< polyline.width.back() << "\n"; + //only consider very shallow angle for contour if (mindot > 0.15 && (1 - (coeff_contour_angle / (PI / 2))) > 0.2) continue; @@ -504,6 +522,8 @@ MedialAxis::fusion_curve(ThickPolylines &pp) if (crosspoint.size() != 2) continue; if (sum_dot > 0.2) continue; if (min_dot > 0.5) continue; + //don't remove useful bits. TODO: use the mindot to know by how much to multiply (1 when 90°, 1.42 when 45+, 1 when 0°) + if (polyline.length() > polyline.width.front()*1.42) continue; //don't pull, it distords the line if there are too many points. //// pull it a bit, depends on my size, the dot?, and the coeff at my 0-end (~14% for a square, almost 0 for a gentle curve) @@ -534,6 +554,8 @@ MedialAxis::fusion_curve(ThickPolylines &pp) concatThickPolylines(pp); ///reorder, in case of change std::sort(pp.begin(), pp.end(), [](const ThickPolyline & a, const ThickPolyline & b) { return a.length() < b.length(); }); + //have to redo it to remove multi-branch bits. + fusion_curve(pp); } } @@ -622,8 +644,14 @@ MedialAxis::extends_line_both_side(ThickPolylines& pp) { for (size_t i = 0; i < pp.size(); ++i) { ThickPolyline& polyline = pp[i]; this->extends_line(polyline, anchors, this->min_width); - polyline.reverse(); - this->extends_line(polyline, anchors, this->min_width); + if (!polyline.points.empty()) { + polyline.reverse(); + this->extends_line(polyline, anchors, this->min_width); + } + if (polyline.points.empty()) { + pp.erase(pp.begin() + i); + --i; + } } } @@ -742,9 +770,8 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con l2.extend_end(max_width); (void)bounds->contour.first_intersection(l2, &new_bound); } - if (new_bound.coincides_with_epsilon(new_back)) { + if (new_bound.coincides_with_epsilon(new_back)) return; - } polyline.points.push_back(new_bound); //polyline.width.push_back(join_width); //it thickens the line a bit too early, imo @@ -1303,6 +1330,22 @@ MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_siz } } +void +MedialAxis::check_width(ThickPolylines& pp, double max_width, std::string msg) +{ + //remove empty polyline + int nb = 0; + for (size_t i = 0; i < pp.size(); ++i) { + for (size_t j = 0; j < pp[i].width.size(); ++j) { + if (pp[i].width[j] > max_width * 1.01) { + std::cout << "Error " << msg << " width " << unscaled(pp[i].width[j]) << "(" << i << ":" << j << ") > " << unscaled(max_width) << "\n"; + nb++; + } + } + } + if (nb > 0) std::cout << "== nbBig = " << nb << " ==\n"; +} + void MedialAxis::ensure_not_overextrude(ThickPolylines& pp) { @@ -1494,6 +1537,27 @@ MedialAxis::build(ThickPolylines &polylines_out) // compute the Voronoi diagram and extract medial axis polylines ThickPolylines pp; this->polyline_from_voronoi(this->expolygon.lines(), &pp); + + //sanity check, as the voronoi can return (abeit very rarely) randomly high values. + for (ThickPolyline &tp : pp) { + for (int i = 0; i < tp.width.size(); i++) { + if (tp.width[i] > this->max_width) { + tp.width[i] = this->max_width; + } + } + } + //std::cout << "polyline_from_voronoi\n"; + //{ + // std::stringstream stri; + // stri << "medial_axis_1_voronoi_" << id << ".svg"; + // SVG svg(stri.str()); + // //svg.draw(bounds); + // svg.draw(this->expolygon); + // svg.draw(pp); + // svg.Close(); + //} + + //check_width(pp, this->max_width, "polyline_from_voronoi"); concatThickPolylines(pp); @@ -1522,6 +1586,7 @@ MedialAxis::build(ThickPolylines &polylines_out) // std::cout << "\n"; //} + // "remove" the little paths that are at the outside of a curve. fusion_curve(pp); //{ // std::stringstream stri; diff --git a/src/libslic3r/MedialAxis.hpp b/src/libslic3r/MedialAxis.hpp index b68423eca..c09cc970c 100644 --- a/src/libslic3r/MedialAxis.hpp +++ b/src/libslic3r/MedialAxis.hpp @@ -110,6 +110,8 @@ class MedialAxis { void grow_to_nozzle_diameter(ThickPolylines& pp, const ExPolygons& anchors); /// taper the ends of polylines (don't activate that for gapfill) void taper_ends(ThickPolylines& pp); + //cleaning method + void check_width(ThickPolylines& pp, double max_width, std::string msg); }; /// create a ExtrusionEntityCollection from ThickPolylines, discretizing the variable width into little sections (of 4*SCALED_RESOLUTION length) where needed. diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 424f0c0d4..960df5ebd 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -279,7 +279,6 @@ 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())) { id_candidate_last_point = j; nbCandidate_last_point++; diff --git a/src/test/libslic3r/test_thin.cpp b/src/test/libslic3r/test_thin.cpp index 5b7a6257d..7a9cf8c39 100644 --- a/src/test/libslic3r/test_thin.cpp +++ b/src/test/libslic3r/test_thin.cpp @@ -86,30 +86,37 @@ SCENARIO("thin walls: ") } } } - + //TODO: compare with mainline slic3r - GIVEN("semicicumference"){ + GIVEN("semicicumference") { ExPolygon expolygon; expolygon.contour = Slic3r::Polygon{ Points{ - Point{ 1185881, 829367 }, Point{ 1421988, 1578184 }, Point{ 1722442, 2303558 }, Point{ 2084981, 2999998 }, Point{ 2506843, 3662186 }, Point{ 2984809, 4285086 }, Point{ 3515250, 4863959 }, Point{ 4094122, 5394400 }, Point{ 4717018, 5872368 }, Point{ 5379210, 6294226 }, Point{ 6075653, 6656769 }, Point{ 6801033, 6957229 }, Point{ 7549842, 7193328 }, Point{ 8316383, 7363266 }, Point{ 9094809, 7465751 }, Point{ 9879211, 7500000 }, Point{ 10663611, 7465750 }, Point{ 11442038, 7363265 }, Point{ 12208580, 7193327 }, Point{ 12957389, 6957228 }, Point{ 13682769, 6656768 }, Point{ 14379209, 6294227 }, Point{ 15041405, 5872366 }, Point{ 15664297, 5394401 }, Point{ 16243171, 4863960 }, Point{ 16758641, 4301424 }, Point{ 17251579, 3662185 }, Point{ 17673439, 3000000 }, Point{ 18035980, 2303556 }, Point{ 18336441, 1578177 }, Point{ 18572539, 829368 }, Point{ 18750748, 0 }, Point{ 19758422, 0 }, Point{ 19727293, 236479 }, Point{ 19538467, 1088188 }, Point{ 19276136, 1920196 }, Point{ 18942292, 2726179 }, Point{ 18539460, 3499999 }, Point{ 18070731, 4235755 }, Point{ 17539650, 4927877 }, Point{ 16950279, 5571067 }, Point{ 16307090, 6160437 }, Point{ 15614974, 6691519 }, Point{ 14879209, 7160248 }, Point{ 14105392, 7563079 }, Point{ 13299407, 7896927 }, Point{ 12467399, 8159255 }, Point{ 11615691, 8348082 }, Point{ 10750769, 8461952 }, Point{ 9879211, 8500000 }, Point{ 9007652, 8461952 }, Point{ 8142729, 8348082 }, Point{ 7291022, 8159255 }, Point{ 6459015, 7896927 }, Point{ 5653029, 7563079 }, Point{ 4879210, 7160247 }, Point{ 4143447, 6691519 }, Point{ 3451331, 6160437 }, Point{ 2808141, 5571066 }, Point{ 2218773, 4927878 }, Point{ 1687689, 4235755 }, Point{ 1218962, 3499999 }, Point{ 827499, 2748020 }, Point{ 482284, 1920196 }, Point{ 219954, 1088186 }, Point{ 31126, 236479 }, Point{ 0, 0 }, Point{ 1005754, 0 } + Point{ 1185881, 829367 }, Point{ 1421988, 1578184 }, Point{ 1722442, 2303558 }, Point{ 2084981, 2999998 }, Point{ 2506843, 3662186 }, Point{ 2984809, 4285086 }, Point{ 3515250, 4863959 }, Point{ 4094122, 5394400 }, Point{ 4717018, 5872368 }, + Point{ 5379210, 6294226 }, Point{ 6075653, 6656769 }, Point{ 6801033, 6957229 }, Point{ 7549842, 7193328 }, Point{ 8316383, 7363266 }, Point{ 9094809, 7465751 }, Point{ 9879211, 7500000 }, Point{ 10663611, 7465750 }, Point{ 11442038, 7363265 }, + Point{ 12208580, 7193327 }, Point{ 12957389, 6957228 }, Point{ 13682769, 6656768 }, Point{ 14379209, 6294227 }, Point{ 15041405, 5872366 }, Point{ 15664297, 5394401 }, Point{ 16243171, 4863960 }, Point{ 16758641, 4301424 }, Point{ 17251579, 3662185 }, + Point{ 17673439, 3000000 }, Point{ 18035980, 2303556 }, Point{ 18336441, 1578177 }, Point{ 18572539, 829368 }, Point{ 18750748, 0 }, Point{ 19758422, 0 }, Point{ 19727293, 236479 }, Point{ 19538467, 1088188 }, Point{ 19276136, 1920196 }, + Point{ 18942292, 2726179 }, Point{ 18539460, 3499999 }, Point{ 18070731, 4235755 }, Point{ 17539650, 4927877 }, Point{ 16950279, 5571067 }, Point{ 16307090, 6160437 }, Point{ 15614974, 6691519 }, Point{ 14879209, 7160248 }, Point{ 14105392, 7563079 }, + Point{ 13299407, 7896927 }, Point{ 12467399, 8159255 }, Point{ 11615691, 8348082 }, Point{ 10750769, 8461952 }, Point{ 9879211, 8500000 }, Point{ 9007652, 8461952 }, Point{ 8142729, 8348082 }, Point{ 7291022, 8159255 }, Point{ 6459015, 7896927 }, + Point{ 5653029, 7563079 }, Point{ 4879210, 7160247 }, Point{ 4143447, 6691519 }, Point{ 3451331, 6160437 }, Point{ 2808141, 5571066 }, Point{ 2218773, 4927878 }, Point{ 1687689, 4235755 }, Point{ 1218962, 3499999 }, Point{ 827499, 2748020 }, + Point{ 482284, 1920196 }, Point{ 219954, 1088186 }, Point{ 31126, 236479 }, Point{ 0, 0 }, Point{ 1005754, 0 } } }; - WHEN("creating the medial axis"){ + WHEN("creating the medial axis") { Polylines res; expolygon.medial_axis(scale_(1.324888), scale_(0.25), &res); - THEN("medial axis of a semicircumference is a single line"){ + THEN("medial axis of a semicircumference is a single line") { REQUIRE(res.size() == 1); } - THEN("all medial axis segments of a semicircumference have the same orientation (but the 2 end points)"){ + THEN("all medial axis segments of a semicircumference have the same orientation (but the 2 end points)") { Lines lines = res[0].lines(); double min_angle = 1, max_angle = -1; //std::cout << "first angle=" << lines[0].ccw(lines[1].b) << "\n"; - for (int idx = 1; idx < lines.size()-1; idx++){ + for (int idx = 1; idx < lines.size() - 1; idx++) { double angle = lines[idx - 1].ccw(lines[idx].b); if (std::abs(angle) - EPSILON < 0) angle = 0; //if (angle < 0) std::cout << unscale_(lines[idx - 1].a.x()) << ":" << unscale_(lines[idx - 1].a.y()) << " -> " << unscale_(lines[idx - 1].b.x()) << ":" << unscale_(lines[idx - 1].b.y()) << " -> " << unscale_(lines[idx].b.x()) << ":" << unscale_(lines[idx].b.y()) << "\n"; - std::cout << "angle=" << 180*lines[idx].a.ccw_angle(lines[idx-1].a, lines[idx].b)/PI << "\n"; + std::cout << "angle=" << 180 * lines[idx].a.ccw_angle(lines[idx - 1].a, lines[idx].b) / PI << "\n"; min_angle = std::min(min_angle, angle); max_angle = std::max(max_angle, angle); } @@ -122,6 +129,28 @@ SCENARIO("thin walls: ") } } } + + + GIVEN("round with large and very small distance between points"){ + ExPolygon expolygon; + expolygon.contour = Slic3r::Polygon{ Points{ + Point::new_scale(15.181601,-2.389639), Point::new_scale(15.112616,-1.320034), Point::new_scale(14.024491,-0.644338), Point::new_scale(13.978982,-0.624495), Point::new_scale(9.993299,0.855584), Point::new_scale(9.941970,0.871195), Point::new_scale(5.796743,1.872643), + Point::new_scale(5.743826,1.882168), Point::new_scale(1.509170,2.386464), Point::new_scale(1.455460,2.389639), Point::new_scale(-2.809359,2.389639), Point::new_scale(-2.862805,2.386464), Point::new_scale(-7.097726,1.882168), Point::new_scale(-7.150378,1.872643), Point::new_scale(-11.286344,0.873576), + Point::new_scale(-11.335028,0.858759), Point::new_scale(-14.348632,-0.237938), Point::new_scale(-14.360538,-0.242436), Point::new_scale(-15.181601,-0.737570), Point::new_scale(-15.171309,-2.388509) + } }; + expolygon.holes.push_back(Slic3r::Polygon{ Points{ + Point::new_scale( -11.023311,-1.034226 ), Point::new_scale( -6.920984,-0.042941 ), Point::new_scale( -2.768613,0.463207 ), Point::new_scale( 1.414714,0.463207 ), Point::new_scale( 5.567085,-0.042941 ), Point::new_scale( 9.627910,-1.047563 ) + } }); + + WHEN("creating the medial axis"){ + Polylines res; + expolygon.medial_axis(scale_(2.5), scale_(0.5), &res); + + THEN("medial axis of it is two line"){ + REQUIRE(res.size() == 2); + } + } + } GIVEN("french cross") {