From 26d17e03ee293bc6a4f137e99634ae72c2cb04e8 Mon Sep 17 00:00:00 2001 From: supermerill Date: Sat, 8 Jun 2019 15:04:27 +0200 Subject: [PATCH] change elepehant foot direction (again) use reference in medial axis add comments in medial axis --- src/libslic3r/ExPolygon.cpp | 8 +- src/libslic3r/ExPolygon.hpp | 1 - src/libslic3r/Fill/FillConcentric.cpp | 2 +- src/libslic3r/Fill/FillRectilinear2.cpp | 5 +- src/libslic3r/MedialAxis.cpp | 130 +++++++++++++----------- src/libslic3r/MedialAxis.hpp | 92 ++++++++++++----- src/libslic3r/PerimeterGenerator.cpp | 12 ++- src/libslic3r/Polygon.cpp | 2 +- src/libslic3r/Polygon.hpp | 2 +- src/libslic3r/PrintObject.cpp | 33 +++--- src/libslic3r/TriangleMesh.cpp | 2 +- src/test/libslic3r/test_model.cpp | 15 +++ 12 files changed, 189 insertions(+), 115 deletions(-) diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index dca992b6c..ad4948eba 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -219,17 +219,11 @@ 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 { - Slic3r::MedialAxis ma(*this, bounds, (coord_t)max_width, (coord_t)min_width, height); - ma.build(polylines); -} - void ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) const { ThickPolylines tp; - this->medial_axis(*this, max_width, min_width, &tp, max_width/2.0); + MedialAxis{ *this, coord_t(max_width), coord_t(min_width), coord_t(max_width / 2.0) }.build(tp); polylines->insert(polylines->end(), tp.begin(), tp.end()); } diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index c5d15a5fe..dce032cf7 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -57,7 +57,6 @@ public: ExPolygons simplify(double tolerance) const; void simplify(double tolerance, ExPolygons* expolygons) const; void remove_point_too_near(const coord_t tolerance); - void medial_axis(const ExPolygon &bounds, double max_width, double min_width, ThickPolylines* polylines, double height) const; void medial_axis(double max_width, double min_width, Polylines* polylines) const; // void get_trapezoids(Polygons* polygons) const; // void get_trapezoids(Polygons* polygons, double angle) const; diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index 27147f746..a5fb06951 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -138,7 +138,7 @@ void FillConcentricWGapFill::fill_surface_extrusion(const Surface *surface, cons //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) { - ex.medial_axis(ex, max, min, &polylines, params.flow->height); + MedialAxis{ ex, coord_t(max), coord_t(min), coord_t(params.flow->height) }.build(polylines); } } if (!polylines.empty() && good_role != erBridgeInfill) { diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp index 21976ff5d..67d187b0f 100644 --- a/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -1774,7 +1774,8 @@ FillRectilinear2WGapFill::fill_surface_extrusion(const Surface *surface, const F ExtrusionRole good_role = getRoleFromSurfaceType(params, surface); // remove areas for gapfill - ExPolygons rectilinear_areas = offset2_ex(ExPolygons{ surface->expolygon }, -params.flow->scaled_spacing() * 0.8f, params.flow->scaled_spacing() * 0.8f); + float factor = 1; // 0.8f; + ExPolygons rectilinear_areas = offset2_ex(ExPolygons{ surface->expolygon }, -params.flow->scaled_spacing() * factor, params.flow->scaled_spacing() * factor); ExPolygons gapfill_areas = diff_ex(ExPolygons{ surface->expolygon }, rectilinear_areas); double rec_area = 0; for (ExPolygon &p : rectilinear_areas)rec_area += p.area(); @@ -1854,7 +1855,7 @@ FillRectilinear2WGapFill::fill_surface_extrusion(const Surface *surface, const F //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) { - ex.medial_axis(ex, params.flow->scaled_width()*2, params.flow->scaled_width()/5, &polylines_gapfill, params.flow->height); + MedialAxis{ ex, params.flow->scaled_width() * 2, params.flow->scaled_width() / 5, coord_t(params.flow->height) }.build(polylines_gapfill); } } if (!polylines_gapfill.empty() && good_role != erBridgeInfill) { diff --git a/src/libslic3r/MedialAxis.cpp b/src/libslic3r/MedialAxis.cpp index 4c50c82da..4620e8881 100644 --- a/src/libslic3r/MedialAxis.cpp +++ b/src/libslic3r/MedialAxis.cpp @@ -1,3 +1,4 @@ +#include "MedialAxis.hpp" #include "BoundingBox.hpp" #include "ExPolygon.hpp" #include "Geometry.hpp" @@ -12,15 +13,13 @@ #include namespace Slic3r { - -int MedialAxis::id = 0; void -MedialAxis::build(Polylines* polylines) +MedialAxis::build(Polylines &polylines) { ThickPolylines tp; - this->build(&tp); - polylines->insert(polylines->end(), tp.begin(), tp.end()); + this->build(tp); + polylines.insert(polylines.end(), tp.begin(), tp.end()); } void @@ -353,7 +352,9 @@ add_point_same_percent(ThickPolyline* pattern, ThickPolyline* to_modify) coordf_t new_width = to_modify->width[idx_other - 1] * (1 - percent_dist); new_width += to_modify->width[idx_other] * (percent_dist); to_modify->width.insert(to_modify->width.begin() + idx_other, new_width); - to_modify->points.insert(to_modify->points.begin() + idx_other, to_modify->points[idx_other - 1].interpolate(percent_dist, to_modify->points[idx_other])); + to_modify->points.insert( + to_modify->points.begin() + idx_other, + to_modify->points[idx_other - 1].interpolate(percent_dist, to_modify->points[idx_other])); } } } @@ -455,7 +456,7 @@ MedialAxis::fusion_curve(ThickPolylines &pp) size_t closest_point_idx = this->expolygon.contour.closest_point_index(polyline.points.back()); - //check the 0-wodth point is on the contour. + //check the 0-width point is on the contour. if (closest_point_idx == (size_t)-1) continue; size_t prev_idx = closest_point_idx == 0 ? this->expolygon.contour.points.size() - 1 : closest_point_idx - 1; @@ -616,7 +617,7 @@ MedialAxis::fusion_corners(ThickPolylines &pp) void MedialAxis::extends_line_both_side(ThickPolylines& pp) { - const ExPolygons anchors = offset2_ex(diff_ex(this->bounds, this->expolygon), double(-SCALED_RESOLUTION), double(SCALED_RESOLUTION)); + const ExPolygons anchors = offset2_ex(diff_ex(*this->bounds, this->expolygon), double(-SCALED_RESOLUTION), double(SCALED_RESOLUTION)); for (size_t i = 0; i < pp.size(); ++i) { ThickPolyline& polyline = pp[i]; this->extends_line(polyline, anchors, this->min_width); @@ -632,7 +633,7 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con // We assign new endpoints to temporary variables because in case of a single-line // 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())) { + if (polyline.endpoints.second && !bounds->has_boundary_point(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) { @@ -674,10 +675,10 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con polyline.width.push_back(polyline.width.back()); } Point new_bound; - bool finded = bounds.contour.first_intersection(line, &new_bound); + bool finded = bounds->contour.first_intersection(line, &new_bound); //verify also for holes. Point new_bound_temp; - for (Polygon hole : bounds.holes) { + for (Polygon hole : bounds->holes) { if (hole.first_intersection(line, &new_bound_temp)) { if (!finded || line.a.distance_to(new_bound_temp) < line.a.distance_to(new_bound)) { finded = true; @@ -687,9 +688,8 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con } // safety check if no intersection if (!finded) { - if (line.b.coincides_with_epsilon(polyline.points.back())) { + 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) { @@ -724,7 +724,7 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con p_obj.y() /= 2; Line l2 = Line(new_back, p_obj); l2.extend_end(max_width); - (void)bounds.contour.first_intersection(l2, &new_bound); + (void)bounds->contour.first_intersection(l2, &new_bound); } if (new_bound.coincides_with_epsilon(new_back)) { return; @@ -983,6 +983,8 @@ MedialAxis::main_fusion(ThickPolylines& pp) coord_t max_width_from_main = std::sqrt(main_branch_width*main_branch_width + main_branch_dist*main_branch_dist); if (find_main_branch && polyline.width[idx_point] > max_width_from_main) polyline.width[idx_point] = max_width_from_main; + if (find_main_branch && polyline.width[idx_point] > pp[biggest_main_branch_id].width.front() * 1.1) + polyline.width[idx_point] = pp[biggest_main_branch_id].width.front() * 1.1; //std::cout << "main fusion, max dist : " << max_width_from_main << "\n"; ++idx_point; @@ -1025,7 +1027,7 @@ MedialAxis::main_fusion(ThickPolylines& pp) } //remove points that are outside of the geometry for (size_t idx_point = 0; idx_point < polyline.points.size(); ++idx_point) { - if (!bounds.contains_b(polyline.points[idx_point])) { + if (!bounds->contains_b(polyline.points[idx_point])) { polyline.points.erase(polyline.points.begin() + idx_point); polyline.width.erase(polyline.width.begin() + idx_point); --idx_point; @@ -1071,7 +1073,7 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp) //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]) * (1-percent_can_keep) > SCALED_RESOLUTION) { + if (polyline.points.front().distance_to(polyline.points[1]) * (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() = polyline.points.front().interpolate(percent_can_keep, polyline.points[1]); @@ -1173,7 +1175,7 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp) best_dot = other_dot; } } - if (best_candidate != nullptr && best_candidate->size() > 1) { + if (best_candidate != nullptr && best_candidate->points.size() > 1) { if (polyline.last_point().coincides_with(best_candidate->last_point())) { best_candidate->reverse(); } else if (polyline.first_point().coincides_with(best_candidate->last_point())) { @@ -1302,12 +1304,12 @@ MedialAxis::ensure_not_overextrude(ThickPolylines& pp) // compute bounds volume double boundsVolume = 0; - boundsVolume += height*bounds.area(); + boundsVolume += height*bounds->area(); // add external "perimeter gap" - double perimeterRoundGap = bounds.contour.length() * height * (1 - 0.25*PI) * 0.5; + double perimeterRoundGap = bounds->contour.length() * height * (1 - 0.25*PI) * 0.5; // add holes "perimeter gaps" double holesGaps = 0; - for (const Polygon &hole : bounds.holes) { + for (const Polygon &hole : bounds->holes) { holesGaps += hole.length() * height * (1 - 0.25*PI) * 0.5; } boundsVolume += perimeterRoundGap + holesGaps; @@ -1323,59 +1325,66 @@ MedialAxis::ensure_not_overextrude(ThickPolylines& pp) } } -ExPolygon +void MedialAxis::simplify_polygon_frontier() { //simplify the boundary between us and the bounds. - ExPolygon simplified_poly = this->surface; - simplified_poly.contour.remove_colinear_points(SCALED_EPSILON); - for (Polygon &hole : simplified_poly.holes) - hole.remove_colinear_points(SCALED_EPSILON); //it will remove every point in the surface contour that aren't on the bounds contour - if (&this->surface != &this->bounds) { + this->expolygon = this->surface; + this->expolygon.contour.remove_collinear_points(SCALED_EPSILON); + for (Polygon &hole : this->expolygon.holes) + hole.remove_collinear_points(SCALED_EPSILON); + if (&this->surface != this->bounds) { bool need_intersect = false; - for (size_t i = 0; i < simplified_poly.contour.points.size(); i++) { - Point &p_check = simplified_poly.contour.points[i]; + for (size_t i = 0; i < this->expolygon.contour.points.size(); i++) { + Point &p_check = this->expolygon.contour.points[i]; //if (!find) { - if (!bounds.has_boundary_point(p_check)) { + if (!bounds->has_boundary_point(p_check)) { //check if we put it at a bound point instead of delete it - size_t prev_i = i == 0 ? simplified_poly.contour.points.size() - 1 : (i - 1); - size_t next_i = i == simplified_poly.contour.points.size() - 1 ? 0 : (i + 1); - const Point* closest = bounds.contour.closest_point(p_check); + size_t prev_i = i == 0 ? this->expolygon.contour.points.size() - 1 : (i - 1); + size_t next_i = i == this->expolygon.contour.points.size() - 1 ? 0 : (i + 1); + const Point* closest = bounds->contour.closest_point(p_check); if (closest != nullptr && closest->distance_to(p_check) + SCALED_EPSILON - < std::min(p_check.distance_to(simplified_poly.contour.points[prev_i]), p_check.distance_to(simplified_poly.contour.points[next_i])) / 2) { + < std::min(p_check.distance_to(this->expolygon.contour.points[prev_i]), p_check.distance_to(this->expolygon.contour.points[next_i])) / 2) { p_check.x() = closest->x(); p_check.y() = closest->y(); need_intersect = true; } else { - simplified_poly.contour.points.erase(simplified_poly.contour.points.begin() + i); + this->expolygon.contour.points.erase(this->expolygon.contour.points.begin() + i); i--; } } } if (need_intersect) { - ExPolygons simplified_polys = intersection_ex(simplified_poly, bounds); - if (simplified_polys.size() == 1) { - simplified_poly = simplified_polys[0]; + ExPolygons simplified_polygons = intersection_ex(this->expolygon, *bounds); + if (simplified_polygons.size() == 1) { + this->expolygon = simplified_polygons[0]; } else { - simplified_poly = this->surface; + //can't simplify that much, reuse the given one + this->expolygon = this->surface; + this->expolygon.contour.remove_collinear_points(SCALED_EPSILON); + for (Polygon &hole : this->expolygon.holes) + hole.remove_collinear_points(SCALED_EPSILON); } } } - if (!simplified_poly.contour.points.empty()) - simplified_poly.remove_point_too_near((coord_t)SCALED_RESOLUTION); - return simplified_poly; + if (!this->expolygon.contour.points.empty()) + this->expolygon.remove_point_too_near((coord_t)SCALED_RESOLUTION); } /// Grow the extrusion to at least nozzle_diameter*1.05 (lowest safe extrusion width) /// Do not grow points inside the anchor. void -MedialAxis::grow_to_nozzle_diameter(ThickPolylines& pp, const ExPolygons& anchors) { +MedialAxis::grow_to_nozzle_diameter(ThickPolylines& pp, const ExPolygons& anchors) +{ //compute the min width coord_t min_width = this->nozzle_diameter; - if (this->height > 0)min_width = Flow::new_from_spacing(float(unscale_(this->nozzle_diameter)), float(unscale_(this->nozzle_diameter)), float(unscale_(this->height)), false).scaled_width(); - //ensure the width is not lower than 0.4. + if (this->height > 0) min_width = Flow::new_from_spacing( + float(unscale_(this->nozzle_diameter)), + float(unscale_(this->nozzle_diameter)), + float(unscale_(this->height)), false).scaled_width(); + //ensure the width is not lower than min_width. for (ThickPolyline& polyline : pp) { for (int i = 0; i < polyline.points.size(); ++i) { bool is_anchored = false; @@ -1392,10 +1401,11 @@ MedialAxis::grow_to_nozzle_diameter(ThickPolylines& pp, const ExPolygons& anchor } void -MedialAxis::taper_ends(ThickPolylines& pp) { +MedialAxis::taper_ends(ThickPolylines& pp) +{ // minimum size of the taper: be sure to extrude at least the "round edges" of the extrusion (0-spacing extrusion). const coord_t min_size = std::max(this->nozzle_diameter * 0.1, this->height * (1. - 0.25 * PI)); - const coordf_t length = std::min(this->anchor_size, (this->nozzle_diameter - min_size) / 2); + const coordf_t length = std::min(this->taper_size, (this->nozzle_diameter - min_size) / 2); if (length <= SCALED_RESOLUTION) return; //ensure the width is not lower than min_size. for (ThickPolyline& polyline : pp) { @@ -1405,7 +1415,7 @@ MedialAxis::taper_ends(ThickPolylines& pp) { coord_t current_dist = min_size; coord_t last_dist = min_size; for (size_t i = 1; i length) { //create a new point if not near enough if (current_dist > length + SCALED_RESOLUTION) { @@ -1442,8 +1452,8 @@ MedialAxis::taper_ends(ThickPolylines& pp) { } void -MedialAxis::build(ThickPolylines* polylines_out) { - this->id++; +MedialAxis::build(ThickPolylines &polylines_out) +{ //std::cout << this->id << "\n"; //{ // std::stringstream stri; @@ -1452,7 +1462,7 @@ MedialAxis::build(ThickPolylines* polylines_out) { // svg.draw(this->surface); // svg.Close(); //} - this->expolygon = simplify_polygon_frontier(); + simplify_polygon_frontier(); //{ // std::stringstream stri; // stri << "medial_axis_0.5_simplified_" << id << ".svg"; @@ -1469,7 +1479,7 @@ MedialAxis::build(ThickPolylines* polylines_out) { // 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"; @@ -1612,7 +1622,9 @@ MedialAxis::build(ThickPolylines* polylines_out) { // svg.Close(); //} if (nozzle_diameter != min_width) { - grow_to_nozzle_diameter(pp, diff_ex(this->bounds, this->expolygon)); + grow_to_nozzle_diameter(pp, diff_ex(*this->bounds, this->expolygon)); + } + if(this->taper_size != 0){ taper_ends(pp); } //{ @@ -1633,29 +1645,30 @@ MedialAxis::build(ThickPolylines* polylines_out) { // std::cout << "\n"; //} - polylines_out->insert(polylines_out->end(), pp.begin(), pp.end()); + polylines_out.insert(polylines_out.end(), pp.begin(), pp.end()); } ExtrusionEntityCollection -thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) { +thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) +{ // this value determines granularity of adaptive width, as G-code does not allow // variable extrusion within a single move; this value shall only affect the amount // of segments, and any pruning shall be performed before we apply this tolerance const double tolerance = 4*SCALED_RESOLUTION;//scale_(0.05); - + ExtrusionEntityCollection coll; for (const ThickPolyline &p : polylines) { ExtrusionPaths paths; ExtrusionPath path(role); ThickLines lines = p.thicklines(); - + for (int i = 0; i < (int)lines.size(); ++i) { ThickLine& line = lines[i]; - + const coordf_t line_len = line.length(); if (line_len < SCALED_EPSILON) continue; - + assert(line.a_width >= 0); assert(line.b_width >= 0); double thickness_delta = fabs(line.a_width - line.b_width); @@ -1750,7 +1763,6 @@ thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow fl } } } - return coll; } diff --git a/src/libslic3r/MedialAxis.hpp b/src/libslic3r/MedialAxis.hpp index f4b66d42f..b68423eca 100644 --- a/src/libslic3r/MedialAxis.hpp +++ b/src/libslic3r/MedialAxis.hpp @@ -15,28 +15,57 @@ using boost::polygon::voronoi_diagram; namespace Slic3r { - class MedialAxis { +/// This class is used to create single-line extrusion pattern with variable width to cover a ExPolygon. +/// The ends can enter a boundary area if neded, and can have a taper at each end. +/// The constructor initialize the mandatory variable. +/// you must use the setter to add the opptional settings before calling build(). +class MedialAxis { public: - Lines lines; //lines is here only to avoid passing it in argument of many methods. Initialized in polyline_from_voronoi. - ExPolygon expolygon; - const ExPolygon& bounds; - const ExPolygon& surface; - const coord_t max_width; - const coord_t min_width; - const coord_t height; - coord_t nozzle_diameter; - coord_t anchor_size; - bool stop_at_min_width; - MedialAxis(const ExPolygon &_expolygon, const ExPolygon &_bounds, const coord_t _max_width, const coord_t _min_width, const coord_t _height) - : surface(_expolygon), bounds(_bounds), max_width(_max_width), min_width(_min_width), height(_height), - nozzle_diameter(_min_width), - anchor_size(0), - stop_at_min_width(true){}; - void build(ThickPolylines* polylines_out); - void build(Polylines* polylines); - + /// _expolygon: the polygon to fill + /// _max_width : maximum width of the extrusion. _expolygon shouldn't have a spot where a circle diameter is higher than that (or almost). + /// _min_width : minimum width of the extrusion, every spot where a circle diameter is lower than that will be ignored (unless it's the tip of the extrusion) + /// _height: height of the extrusion, used to compute the difference between width and spacing. + MedialAxis(const ExPolygon &_expolygon, const coord_t _max_width, const coord_t _min_width, const coord_t _height) + : surface(_expolygon), max_width(_max_width), min_width(_min_width), height(_height), + bounds(&_expolygon), nozzle_diameter(_min_width), taper_size(0), stop_at_min_width(true){}; + + /// create the polylines_out collection of variable-width polyline to extrude. + void build(ThickPolylines &polylines_out); + /// You shouldn't use this method as it doesn't give you the variable width. Can be useful for debugging. + void build(Polylines &polylines); + + /// optional parameter: anchor area in which the extrusion should extends into. Default : expolygon (no bound) + MedialAxis& use_bounds(const ExPolygon & _bounds) { this->bounds = &_bounds; return *this; } + /// optional parameter: the real minimum width : it will grow the width of every extrusion that has a width lower than that. Default : min_width (same min) + MedialAxis& use_min_real_width(const coord_t nozzle_diameter) { this->nozzle_diameter = nozzle_diameter; return *this; } + /// optional parameter: create a taper of this length at each end (inside a bound or not). Default : 0 (no taper) + MedialAxis& use_tapers(const coord_t taper_size) { this->taper_size = taper_size; return *this; } + /// optional parameter: if true, the entension inside the bounds can be cut if the width is too small. Default : true + MedialAxis& set_stop_at_min_width(const bool stop_at_min_width) { this->stop_at_min_width = stop_at_min_width; return *this; } + private: - static int id; + /// Cache value: lines is here only to avoid passing it in argument of many methods. Initialized in polyline_from_voronoi. + Lines lines; + + /// input polygon to fill + const ExPolygon& surface; + /// the copied expolygon from surface, it's modified in build() to simplify it. It's then used to create the voronoi diagram. + ExPolygon expolygon; + const ExPolygon* bounds; + /// maximum width of the extrusion. _expolygon shouldn't have a spot where a circle diameter is higher than that (or almost). + const coord_t max_width; + /// minimum width of the extrusion, every spot where a circle diameter is lower than that will be ignored (unless it's the tip of the extrusion) + const coord_t min_width; + /// height of the extrusion, used to compute the diufference between width and spacing. + const coord_t height; + /// Used to compute the real minimum width we can extrude. if != min_width, it activate grow_to_nozzle_diameter(). + coord_t nozzle_diameter; + /// if != , it activates taper_ends(). Can use nozzle_diameter. + coord_t taper_size; + //if true, remove_too_* can shorten the bits created by extends_line. + bool stop_at_min_width; + + //voronoi stuff class VD : public voronoi_diagram { public: typedef double coord_type; @@ -53,23 +82,38 @@ namespace Slic3r { const Point& retrieve_endpoint(const VD::cell_type* cell) const; void polyline_from_voronoi(const Lines& voronoi_edges, ThickPolylines* polylines_out); - ExPolygon simplify_polygon_frontier(); + // functions called by build: + + /// create a simplied version of surface, store it in expolygon + void simplify_polygon_frontier(); + /// fusion little polylines created (by voronoi) on the external side of a curve inside the main polyline. void fusion_curve(ThickPolylines &pp); + /// fusion polylines created by voronoi, where needed. void main_fusion(ThickPolylines& pp); + /// like fusion_curve but for sharp angles like a square corner. void fusion_corners(ThickPolylines &pp); + /// extends the polylines inside bounds, use extends_line on both end void extends_line_both_side(ThickPolylines& pp); + /// extends the polylines inside bounds (anchors) void extends_line(ThickPolyline& polyline, const ExPolygons& anchors, const coord_t join_width); + /// remove too thin bits at start & end of polylines void remove_too_thin_extrusion(ThickPolylines& pp); + /// instead of keeping polyline split at each corssing, we try to create long strait polylines that can cross each other. void concatenate_polylines_with_crossing(ThickPolylines& pp); + /// remove bits around points that are too thin (can be inside the polyline) void remove_too_thin_points(ThickPolylines& pp); + /// delete polylines that are too short void remove_too_short_polylines(ThickPolylines& pp, const coord_t min_size); + /// be sure we didn't try to push more plastic than the volume defined by surface * height can receive. If overextruded, reduce all widths by the correct %. void ensure_not_overextrude(ThickPolylines& pp); + /// if nozzle_diameter > min_width, grow bits that are < width(nozzle_diameter) to width(nozzle_diameter) (don't activate that for gapfill) 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); - }; - +}; + + /// create a ExtrusionEntityCollection from ThickPolylines, discretizing the variable width into little sections (of 4*SCALED_RESOLUTION length) where needed. ExtrusionEntityCollection thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow); - } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 400a78e1e..e4d266d3f 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -410,10 +410,12 @@ void PerimeterGenerator::process() if (thin[0].area() > min_width*(ext_perimeter_width + ext_perimeter_spacing2)) { bound.remove_point_too_near((coord_t)SCALED_RESOLUTION); // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop - Slic3r::MedialAxis ma(thin[0], bound, ext_perimeter_width + ext_perimeter_spacing2, min_width, this->layer_height); - ma.nozzle_diameter = (coord_t)scale_(this->ext_perimeter_flow.nozzle_diameter); - ma.anchor_size = overlap; - ma.build(&thin_walls); + Slic3r::MedialAxis ma{ thin[0], ext_perimeter_width + ext_perimeter_spacing2, + min_width, coord_t(this->layer_height) }; + ma.use_bounds(bound) + .use_min_real_width((coord_t)scale_(this->ext_perimeter_flow.nozzle_diameter)) + .use_tapers(overlap) + .build(thin_walls); } break; } @@ -601,7 +603,7 @@ void PerimeterGenerator::process() //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) { - ex.medial_axis(ex, max, min, &polylines, this->layer_height); + MedialAxis{ ex, coord_t(max), coord_t(min), coord_t(this->layer_height) }.build(polylines); } } if (!polylines.empty()) { diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 2fe3c4d18..febfde29a 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -316,7 +316,7 @@ Point Polygon::point_projection(const Point &point) const return proj; } -size_t Polygon::remove_colinear_points(coord_t max_offset){ +size_t Polygon::remove_collinear_points(coord_t max_offset){ size_t nb_del = 0; if (points.size() < 3) return 0; diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index c1004330d..ee504b258 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -61,7 +61,7 @@ public: Point point_projection(const Point &point) const; /// remove points that are (almost) on an existing line from previous & next point. /// return number of point removed - size_t remove_colinear_points(coord_t max_offset); + size_t remove_collinear_points(coord_t max_offset); }; extern BoundingBox get_extents(const Polygon &poly); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index bd2c452b8..7480e4ee4 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1984,13 +1984,20 @@ end: if (layer->m_regions.size() == 1) { // Optimized version for a single region layer. if (layer_id == 0) { - if (delta > elephant_foot_compensation) { - delta -= elephant_foot_compensation; - elephant_foot_compensation = 0.f; - } else if (delta > 0) - elephant_foot_compensation -= delta; + if (elephant_foot_compensation > 0) { + delta += elephant_foot_compensation; + elephant_foot_compensation = 0; + }else if (delta > 0) { + if (-elephant_foot_compensation < delta) { + delta += elephant_foot_compensation; + elephant_foot_compensation = 0; + } else { + elephant_foot_compensation += delta; + delta = 0; + } + } } - if (delta != 0.f || elephant_foot_compensation > 0.f) { + if (delta != 0.f || elephant_foot_compensation != 0.f) { // Single region, growing or shrinking. LayerRegion *layerm = layer->m_regions.front(); // Apply the XY compensation. @@ -1998,16 +2005,16 @@ end: to_expolygons(std::move(layerm->slices.surfaces)) : offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta); // Apply the elephant foot compensation. - if (elephant_foot_compensation > 0) { + if (elephant_foot_compensation != 0) { float elephant_foot_spacing = float(layerm->flow(frExternalPerimeter).scaled_elephant_foot_spacing()); float external_perimeter_nozzle = float(scale_(this->print()->config().nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder.value - 1))); // Apply the elephant foot compensation by steps of 1/10 nozzle diameter. float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle)); - size_t nsteps = size_t(steps); + size_t nsteps = size_t(std::abs(steps)); float step = elephant_foot_compensation / steps; for (size_t i = 0; i < nsteps; ++ i) { Polygons tmp = offset(expolygons, - step); - append(tmp, diff(to_polygons(expolygons), offset(offset_ex(expolygons, -elephant_foot_spacing - step), elephant_foot_spacing + step))); + append(tmp, diff(to_polygons(expolygons), offset(offset_ex(expolygons, -elephant_foot_spacing + step), elephant_foot_spacing - step))); expolygons = union_ex(tmp); } } @@ -2042,7 +2049,7 @@ end: for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) layer->m_regions[region_id]->trim_surfaces(trimming); } - if (elephant_foot_compensation > 0.f) { + if (elephant_foot_compensation != 0.f) { // Apply the elephant foot compensation. std::vector elephant_foot_spacing; elephant_foot_spacing.reserve(layer->m_regions.size()); @@ -2055,12 +2062,12 @@ end: external_perimeter_nozzle /= (float)layer->m_regions.size(); // Apply the elephant foot compensation by steps of 1/10 nozzle diameter. float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle)); - size_t nsteps = size_t(steps); + size_t nsteps = size_t(std::abs(steps)); float step = elephant_foot_compensation / steps; for (size_t i = 0; i < nsteps; ++ i) { Polygons trimming_polygons = offset(layer->merged(float(EPSILON)), double(- step - EPSILON)); for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) - layer->m_regions[region_id]->elephant_foot_compensation_step(elephant_foot_spacing[region_id] + step, trimming_polygons); + layer->m_regions[region_id]->elephant_foot_compensation_step(elephant_foot_spacing[region_id] - step, trimming_polygons); } } } @@ -2096,7 +2103,7 @@ void PrintObject::_offset_holes(double hole_delta, LayerRegion *layerm) { ok &= (hole.points.back().ccw_angle(*(hole.points.end() - 2), hole.points.front()) <= PI + 0.1); if (ok) { - for (Polygon newHole : offset(hole, -hole_delta)) { + for (Polygon newHole : offset(hole, hole_delta)) { //reverse because it's a hole, not an object newHole.make_clockwise(); new_ex_poly.holes.push_back(newHole); diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 766601a40..48224ce80 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1663,7 +1663,7 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic Polygons filered_polys = loops; if (this->model_precision > 0){ for (Polygon &hole : filered_polys){ - hole.remove_colinear_points(scale_(this->model_precision)); + hole.remove_collinear_points(scale_(this->model_precision)); } } diff --git a/src/test/libslic3r/test_model.cpp b/src/test/libslic3r/test_model.cpp index d876cb55c..01d0015a8 100644 --- a/src/test/libslic3r/test_model.cpp +++ b/src/test/libslic3r/test_model.cpp @@ -59,3 +59,18 @@ SCENARIO("Model construction") { } } + + +SCENARIO("xy compensations"){ + GIVEN(("A Square with a complex hole inside")){ + Polygon square/*new_scale*/{ std::vector{ + Point{ 100, 100 }, + Point{ 200, 100 }, + Point{ 200, 200 }, + Point{ 100, 200 }} }; + THEN("elephant and xy can compensate each other"){ + } + THEN("hole and xy can compensate each othere"){ + } + } +}