From 6dc42ee902ec630ef21c1f50c96b726a8b56105b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 19 Mar 2016 15:33:58 +0100 Subject: [PATCH] Variable-width gap fill. Yay! #2960 --- xs/src/libslic3r/ExPolygon.cpp | 80 +++++--- xs/src/libslic3r/ExPolygon.hpp | 1 + xs/src/libslic3r/ExtrusionEntity.cpp | 2 +- xs/src/libslic3r/ExtrusionEntity.hpp | 4 + .../libslic3r/ExtrusionEntityCollection.cpp | 14 ++ .../libslic3r/ExtrusionEntityCollection.hpp | 2 + xs/src/libslic3r/Geometry.cpp | 100 ++++++---- xs/src/libslic3r/Geometry.hpp | 13 +- xs/src/libslic3r/Line.cpp | 49 +++++ xs/src/libslic3r/Line.hpp | 14 ++ xs/src/libslic3r/MultiPoint.cpp | 10 + xs/src/libslic3r/MultiPoint.hpp | 1 + xs/src/libslic3r/PerimeterGenerator.cpp | 188 +++++++++++------- xs/src/libslic3r/PerimeterGenerator.hpp | 13 +- xs/src/libslic3r/Polygon.hpp | 2 +- xs/src/libslic3r/Polyline.cpp | 27 ++- xs/src/libslic3r/Polyline.hpp | 13 +- xs/src/libslic3r/SVG.cpp | 11 +- xs/src/libslic3r/SVG.hpp | 1 + 19 files changed, 379 insertions(+), 166 deletions(-) diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp index 83f6665bb..04ba1023a 100644 --- a/xs/src/libslic3r/ExPolygon.cpp +++ b/xs/src/libslic3r/ExPolygon.cpp @@ -6,7 +6,6 @@ #include "ClipperUtils.hpp" #include "polypartition.h" #include "poly2tri/poly2tri.h" - #include #include @@ -171,10 +170,11 @@ ExPolygon::simplify(double tolerance, ExPolygons* expolygons) const } void -ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) const +ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polylines) const { // init helper object Slic3r::Geometry::MedialAxis ma(max_width, min_width); + ma.expolygon = this; // populate list of segments for the Voronoi diagram ma.lines = this->contour.lines(); @@ -184,41 +184,71 @@ ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) } // compute the Voronoi diagram - Polylines pp; + ThickPolylines pp; ma.build(&pp); - // clip segments to our expolygon area - // (do this before extending endpoints as external segments coule be extended into - // expolygon, this leaving wrong things inside) - pp = intersection(pp, *this); + /* + SVG svg("medial_axis.svg"); + svg.draw(*this); + svg.draw(pp); + svg.Close(); + */ - // extend initial and final segments of each polyline (they will be clipped) - // unless they represent closed loops - for (Polylines::iterator polyline = pp.begin(); polyline != pp.end(); ++polyline) { - if (polyline->points.front().distance_to(polyline->points.back()) < min_width) continue; - // TODO: we should *not* extend endpoints where other polylines start/end - // (such as T joints, which are returned as three polylines by MedialAxis) - polyline->extend_start(max_width); - polyline->extend_end(max_width); - } - - // clip again after extending endpoints to prevent them from exceeding the expolygon boundaries - pp = intersection(pp, *this); - - // remove too short polylines - // (we can't do this check before endpoints extension and clipping because we don't - // know how long will the endpoints be extended since it depends on polygon thickness - // which is variable - extension will be <= max_width/2 on each side) for (size_t i = 0; i < pp.size(); ++i) { - if (pp[i].length() < max_width) { + ThickPolyline& polyline = pp[i]; + + // extend initial and final segments of each polyline if they're actual endpoints + /* 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 */ + Point new_front = polyline.points.front(); + Point new_back = polyline.points.back(); + if (polyline.endpoints.front() && !this->has_boundary_point(new_front)) { + Line line(polyline.points.front(), polyline.points[1]); + + // prevent the line from touching on the other side, otherwise intersection() might return that solution + if (polyline.points.size() == 2) line.b = line.midpoint(); + + line.extend_start(max_width); + (void)this->contour.intersection(line, &new_front); + } + if (polyline.endpoints.back() && !this->has_boundary_point(new_back)) { + Line line( + *(polyline.points.end() - 2), + polyline.points.back() + ); + + // prevent the line from touching on the other side, otherwise intersection() might return that solution + if (polyline.points.size() == 2) line.a = line.midpoint(); + line.extend_end(max_width); + + (void)this->contour.intersection(line, &new_back); + } + polyline.points.front() = new_front; + polyline.points.back() = new_back; + + /* remove too short polylines + (we can't do this check before endpoints extension and clipping because we don't + know how long will the endpoints be extended since it depends on polygon thickness + which is variable - extension will be <= max_width/2 on each side) */ + if (polyline.length() < max_width) { pp.erase(pp.begin() + i); --i; + continue; } } polylines->insert(polylines->end(), pp.begin(), pp.end()); } +void +ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) const +{ + ThickPolylines tp; + this->medial_axis(max_width, min_width, &tp); + polylines->insert(polylines->end(), tp.begin(), tp.end()); +} + void ExPolygon::get_trapezoids(Polygons* polygons) const { diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp index 5fe5df256..f23178cc0 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -32,6 +32,7 @@ class ExPolygon Polygons simplify_p(double tolerance) const; ExPolygons simplify(double tolerance) const; void simplify(double tolerance, ExPolygons* expolygons) const; + void medial_axis(double max_width, double min_width, ThickPolylines* polylines) 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/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp index 54ca91e4a..5d0e5502a 100644 --- a/xs/src/libslic3r/ExtrusionEntity.cpp +++ b/xs/src/libslic3r/ExtrusionEntity.cpp @@ -114,7 +114,7 @@ Polygons ExtrusionPath::grow() const { Polygons pp; - offset(this->polyline, &pp, +this->width/2); + offset(this->polyline, &pp, +scale_(this->width/2)); return pp; } diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index 98fbdc92f..2cba689a5 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -104,6 +104,10 @@ class ExtrusionLoop : public ExtrusionEntity ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : role(role) {}; ExtrusionLoop(const ExtrusionPaths &paths, ExtrusionLoopRole role = elrDefault) : paths(paths), role(role) {}; + ExtrusionLoop(const ExtrusionPath &path, ExtrusionLoopRole role = elrDefault) + : role(role) { + this->paths.push_back(path); + }; bool is_loop() const { return true; }; diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp index dc9b722d3..31aa3d4c8 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp @@ -101,6 +101,20 @@ ExtrusionEntityCollection::append(const ExtrusionPaths &paths) this->append(*path); } +void +ExtrusionEntityCollection::replace(size_t i, const ExtrusionEntity &entity) +{ + delete this->entities[i]; + this->entities[i] = entity.clone(); +} + +void +ExtrusionEntityCollection::remove(size_t i) +{ + delete this->entities[i]; + this->entities.erase(this->entities.begin() + i); +} + ExtrusionEntityCollection ExtrusionEntityCollection::chained_path(bool no_reverse, std::vector* orig_indices) const { diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp index 8ee1b3864..7e44ccd18 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp @@ -36,6 +36,8 @@ class ExtrusionEntityCollection : public ExtrusionEntity void append(const ExtrusionEntity &entity); void append(const ExtrusionEntitiesPtr &entities); void append(const ExtrusionPaths &paths); + void replace(size_t i, const ExtrusionEntity &entity); + void remove(size_t i); ExtrusionEntityCollection chained_path(bool no_reverse = false, std::vector* orig_indices = NULL) const; void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, std::vector* orig_indices = NULL) const; void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, std::vector* orig_indices = NULL) const; diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp index dcac40ad4..890272eb5 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/xs/src/libslic3r/Geometry.cpp @@ -5,12 +5,13 @@ #include "PolylineCollection.hpp" #include "clipper.hpp" #include +#include #include #include #include #include +#include #include -#include #ifdef SLIC3R_DEBUG #include "SVG.hpp" @@ -289,19 +290,8 @@ arrange(size_t total_parts, Pointf part, coordf_t dist, const BoundingBoxf* bb) return positions; } -Line -MedialAxis::edge_to_line(const VD::edge_type &edge) const -{ - Line line; - line.a.x = edge.vertex0()->x(); - line.a.y = edge.vertex0()->y(); - line.b.x = edge.vertex1()->x(); - line.b.y = edge.vertex1()->y(); - return line; -} - void -MedialAxis::build(Polylines* polylines) +MedialAxis::build(ThickPolylines* polylines) { /* // build bounding box (we use it for clipping infinite segments) @@ -317,7 +307,7 @@ MedialAxis::build(Polylines* polylines) for (VD::const_edge_iterator edge = this->vd.edges().begin(); edge != this->vd.edges().end(); ++edge) { if (edge->is_infinite()) continue; - Polyline polyline; + ThickPolyline polyline; polyline.points.push_back(Point( edge->vertex0()->x(), edge->vertex0()->y() )); polyline.points.push_back(Point( edge->vertex1()->x(), edge->vertex1()->y() )); polylines->push_back(polyline); @@ -373,7 +363,7 @@ MedialAxis::build(Polylines* polylines) assert(vertex_edges[v].size() == 1); edge_t* edge = *vertex_edges[v].begin(); - if (!this->is_valid_edge(*edge)) { + if (!this->validate_edge(*edge)) { // if edge is not valid, erase it and its twin from edge list (void)this->edges.erase(edge); (void)this->edges.erase(edge->twin()); @@ -400,22 +390,36 @@ MedialAxis::build(Polylines* polylines) edge_t &edge = **this->edges.begin(); // start a polyline - Polyline polyline; + 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[&edge].first); + polyline.width.push_back(this->thickness[&edge].second); // remove this edge and its twin from the available edges (void)this->edges.erase(&edge); (void)this->edges.erase(edge.twin()); // get next points - this->process_edge_neighbors(edge, &polyline.points); + this->process_edge_neighbors(edge, &polyline.points, &polyline.width, &polyline.endpoints); // get previous points { Points pp; - this->process_edge_neighbors(*edge.twin(), &pp); + std::vector width; + std::vector endpoints; + this->process_edge_neighbors(*edge.twin(), &pp, &width, &endpoints); polyline.points.insert(polyline.points.begin(), pp.rbegin(), pp.rend()); + polyline.width.insert(polyline.width.begin(), width.rbegin(), width.rend()); + polyline.endpoints.insert(polyline.endpoints.begin(), endpoints.rbegin(), endpoints.rend()); + } + + assert(polyline.width.size() == polyline.points.size()*2 - 2); + assert(polyline.endpoints.size() == polyline.points.size()); + + if (polyline.first_point().coincides_with(polyline.last_point())) { + polyline.endpoints.front() = false; + polyline.endpoints.back() = false; } // append polyline to result @@ -424,7 +428,16 @@ MedialAxis::build(Polylines* polylines) } void -MedialAxis::process_edge_neighbors(const VD::edge_type& edge, Points* points) +MedialAxis::build(Polylines* polylines) +{ + ThickPolylines tp; + this->build(&tp); + polylines->insert(polylines->end(), tp.begin(), tp.end()); +} + +void +MedialAxis::process_edge_neighbors(const VD::edge_type& edge, Points* points, + std::vector* width, std::vector* endpoints) { // Since rot_next() works on the edge starting point but we want // to find neighbors on the ending point, we just swap edge with @@ -439,18 +452,26 @@ MedialAxis::process_edge_neighbors(const VD::edge_type& edge, Points* points) // if we have a single neighbor then we can continue recursively if (neighbors.size() == 1) { + endpoints->push_back(false); const VD::edge_type& neighbor = *neighbors.front(); points->push_back(Point( neighbor.vertex1()->x(), neighbor.vertex1()->y() )); + width->push_back(this->thickness[&neighbor].first); + width->push_back(this->thickness[&neighbor].second); (void)this->edges.erase(&neighbor); (void)this->edges.erase(neighbor.twin()); - this->process_edge_neighbors(neighbor, points); + this->process_edge_neighbors(neighbor, points, width, endpoints); + } else if (neighbors.size() == 0) { + endpoints->push_back(true); + } else { + // T-shaped or star-shaped joint + endpoints->push_back(false); } } bool -MedialAxis::is_valid_edge(const VD::edge_type& edge) const +MedialAxis::validate_edge(const VD::edge_type& edge) { - /* If the cells sharing this edge have a common vertex, we're not interested + /* If the cells sharing this edge have a common vertex, we're not (probably) interested in this edge. Why? Because it means that the edge lies on the bisector of two contiguous input lines and it was included in the Voronoi graph because it's the locus of centers of circles tangent to both vertices. Due to the @@ -481,24 +502,20 @@ MedialAxis::is_valid_edge(const VD::edge_type& edge) const // and we might need to skip the edge since it's not really part of // our skeleton - // get perpendicular distance of each edge vertex to the segment(s) - double dist0 = segment1.a.distance_to(segment2.b); - double dist1 = segment1.b.distance_to(segment2.a); + /* Calculate perpendicular distance. We consider segment2 instead of segment1 + because our Voronoi edge is part of a CCW sequence going around its Voronoi cell + (segment). + This means that such segment is on the left on our edge, and goes backwards. + So we use the cell of the twin edge, which is located on the right of our edge + and goes in the same direction as it. This way we can map dist0 and dist1 + correctly. */ + const Line line( + Point( edge.vertex0()->x(), edge.vertex0()->y() ), + Point( edge.vertex1()->x(), edge.vertex1()->y() ) + ); + coordf_t dist0 = segment2.a.perp_distance_to(line)*2; + coordf_t dist1 = segment2.b.perp_distance_to(line)*2; - /* - Line line = this->edge_to_line(edge); - double diff = fabs(dist1 - dist0); - double dist_between_segments1 = segment1.a.distance_to(segment2); - double dist_between_segments2 = segment1.b.distance_to(segment2); - printf("w = %f/%f, dist0 = %f, dist1 = %f, diff = %f, seg1len = %f, seg2len = %f, edgelen = %f, s2s = %f / %f\n", - unscale(this->max_width), unscale(this->min_width), - unscale(dist0), unscale(dist1), unscale(diff), - unscale(segment1.length()), unscale(segment2.length()), - unscale(line.length()), - unscale(dist_between_segments1), unscale(dist_between_segments2) - ); - */ - // if this edge is the centerline for a very thin area, we might want to skip it // in case the area is too thin if (dist0 < this->min_width && dist1 < this->min_width) { @@ -506,6 +523,11 @@ MedialAxis::is_valid_edge(const VD::edge_type& edge) const return false; } + if (this->expolygon != NULL && !this->expolygon->contains(line)) + return false; + + this->thickness[&edge] = std::make_pair(dist0, dist1); + return true; } diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp index f64871d2d..6d9a63b73 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/xs/src/libslic3r/Geometry.hpp @@ -3,6 +3,7 @@ #include "libslic3r.h" #include "BoundingBox.hpp" +#include "ExPolygon.hpp" #include "Polygon.hpp" #include "Polyline.hpp" @@ -43,18 +44,22 @@ class MedialAxis { public: Points points; Lines lines; + const ExPolygon* expolygon; double max_width; double min_width; - MedialAxis(double _max_width, double _min_width) : max_width(_max_width), min_width(_min_width) {}; + MedialAxis(double _max_width, double _min_width) + : max_width(_max_width), min_width(_min_width), expolygon(NULL) {}; + void build(ThickPolylines* polylines); void build(Polylines* polylines); private: typedef voronoi_diagram VD; VD vd; std::set edges; - Line edge_to_line(const VD::edge_type &edge) const; - void process_edge_neighbors(const voronoi_diagram::edge_type& edge, Points* points); - bool is_valid_edge(const voronoi_diagram::edge_type& edge) const; + std::map > thickness; + void process_edge_neighbors(const voronoi_diagram::edge_type& edge, + Points* points, std::vector* width, std::vector* endpoints); + bool validate_edge(const voronoi_diagram::edge_type& edge); const Line& retrieve_segment(const voronoi_diagram::cell_type& cell) const; }; diff --git a/xs/src/libslic3r/Line.cpp b/xs/src/libslic3r/Line.cpp index f3b8ff43c..1940402ed 100644 --- a/xs/src/libslic3r/Line.cpp +++ b/xs/src/libslic3r/Line.cpp @@ -163,6 +163,55 @@ Line::normal() const return Vector((this->b.y - this->a.y), -(this->b.x - this->a.x)); } +void +Line::extend_end(double distance) +{ + // relocate last point by extending the segment by the specified length + Line line = *this; + line.reverse(); + this->b = line.point_at(-distance); +} + +void +Line::extend_start(double distance) +{ + // relocate first point by extending the first segment by the specified length + this->a = this->point_at(-distance); +} + +bool +Line::intersection(const Line& line, Point* intersection) const +{ + double denom = ((line.b.y - line.a.y)*(this->b.x - this->a.x)) - + ((line.b.x - line.a.x)*(this->b.y - this->a.y)); + + double nume_a = ((line.b.x - line.a.x)*(this->a.y - line.a.y)) - + ((line.b.y - line.a.y)*(this->a.x - line.a.x)); + + double nume_b = ((this->b.x - this->a.x)*(this->a.y - line.a.y)) - + ((this->b.y - this->a.y)*(this->a.x - line.a.x)); + + if (fabs(denom) < EPSILON) { + if (fabs(nume_a) < EPSILON && fabs(nume_b) < EPSILON) { + return false; // coincident + } + return false; // parallel + } + + double ua = nume_a / denom; + double ub = nume_b / denom; + + if (ua >= 0 && ua <= 1.0f && ub >= 0 && ub <= 1.0f) + { + // Get the intersection point. + intersection->x = this->a.x + ua*(this->b.x - this->a.x); + intersection->y = this->a.y + ua*(this->b.y - this->a.y); + return true; + } + + return false; // not intersecting +} + Pointf3 Linef3::intersect_plane(double z) const { diff --git a/xs/src/libslic3r/Line.hpp b/xs/src/libslic3r/Line.hpp index c4438792a..ae1f4cb7d 100644 --- a/xs/src/libslic3r/Line.hpp +++ b/xs/src/libslic3r/Line.hpp @@ -9,7 +9,9 @@ namespace Slic3r { class Line; class Linef3; class Polyline; +class ThickLine; typedef std::vector Lines; +typedef std::vector ThickLines; class Line { @@ -39,6 +41,18 @@ class Line double direction() const; Vector vector() const; Vector normal() const; + void extend_end(double distance); + void extend_start(double distance); + bool intersection(const Line& line, Point* intersection) const; +}; + +class ThickLine : public Line +{ + public: + coordf_t a_width, b_width; + + ThickLine() : a_width(0), b_width(0) {}; + ThickLine(Point _a, Point _b) : a_width(0), b_width(0), Line(_a, _b) {}; }; class Linef diff --git a/xs/src/libslic3r/MultiPoint.cpp b/xs/src/libslic3r/MultiPoint.cpp index 0e4ede761..ff2771cff 100644 --- a/xs/src/libslic3r/MultiPoint.cpp +++ b/xs/src/libslic3r/MultiPoint.cpp @@ -118,6 +118,16 @@ MultiPoint::append(const Points::const_iterator &begin, const Points::const_iter this->points.insert(this->points.end(), begin, end); } +bool +MultiPoint::intersection(const Line& line, Point* intersection) const +{ + Lines lines = this->lines(); + for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) { + if (it->intersection(line, intersection)) return true; + } + return false; +} + Points MultiPoint::_douglas_peucker(const Points &points, const double tolerance) { diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp index 053709bbd..18d67a9e4 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/xs/src/libslic3r/MultiPoint.hpp @@ -36,6 +36,7 @@ class MultiPoint void append(const Point &point); void append(const Points &points); void append(const Points::const_iterator &begin, const Points::const_iterator &end); + bool intersection(const Line& line, Point* intersection) const; static Points _douglas_peucker(const Points &points, const double tolerance); }; diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index c51163c1e..af12161df 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -1,6 +1,7 @@ #include "PerimeterGenerator.hpp" #include "ClipperUtils.hpp" #include "ExtrusionEntityCollection.hpp" +#include namespace Slic3r { @@ -91,7 +92,7 @@ PerimeterGenerator::process() // the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width // (actually, something larger than that still may exist due to mitering or other causes) - coord_t min_width = ext_pwidth / 2; + coord_t min_width = scale_(this->ext_perimeter_flow.nozzle_diameter / 3); ExPolygons expp = offset2_ex(diffpp, -min_width/2, +min_width/2); // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop @@ -252,43 +253,37 @@ PerimeterGenerator::process() // fill gaps if (!gaps.empty()) { /* - if (false) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output( - "gaps.svg", - expolygons => union_ex(\@gaps), - ); - } + SVG svg("gaps.svg"); + svg.draw(union_ex(gaps)); + svg.Close(); */ - // where $pwidth < thickness < 2*$pspacing, infill with width = 2*$pwidth - // where 0.1*$pwidth < thickness < $pwidth, infill with width = 1*$pwidth - std::vector gap_sizes; - gap_sizes.push_back(PerimeterGeneratorGapSize(pwidth, 2*pspacing, 2*pwidth)); - gap_sizes.push_back(PerimeterGeneratorGapSize(0.1*pwidth, pwidth, 1*pwidth)); + // collapse + double min = 0.1*pwidth * (1 - INSET_OVERLAP_TOLERANCE); + double max = 2*pspacing; + ExPolygons gaps_ex = diff_ex( + offset2(gaps, -min/2, +min/2), + offset2(gaps, -max/2, +max/2), + true + ); - for (std::vector::const_iterator gap_size = gap_sizes.begin(); - gap_size != gap_sizes.end(); ++gap_size) { - ExtrusionEntityCollection gap_fill = this->_fill_gaps(gap_size->min, - gap_size->max, unscale(gap_size->width), gaps); - this->gap_fill->append(gap_fill.entities); + ThickPolylines polylines; + for (ExPolygons::const_iterator ex = gaps_ex.begin(); ex != gaps_ex.end(); ++ex) + ex->medial_axis(max, min/2, &polylines); + + if (!polylines.empty()) { + ExtrusionEntityCollection gap_fill = this->_variable_width(polylines, + erGapFill, this->solid_infill_flow); - // Make sure we don't infill narrow parts that are already gap-filled - // (we only consider this surface's gaps to reduce the diff() complexity). - // Growing actual extrusions ensures that gaps not filled by medial axis - // are not subtracted from fill surfaces (they might be too short gaps - // that medial axis skips but infill might join with other infill regions - // and use zigzag). - coord_t dist = gap_size->width/2; - Polygons filled; - for (ExtrusionEntitiesPtr::const_iterator it = gap_fill.entities.begin(); - it != gap_fill.entities.end(); ++it) { - Polygons f; - offset((*it)->as_polyline(), &f, dist); - filled.insert(filled.end(), f.begin(), f.end()); - } - last = diff(last, filled); - gaps = diff(gaps, filled); // prevent more gap fill here + this->gap_fill->append(gap_fill.entities); + + /* Make sure we don't infill narrow parts that are already gap-filled + (we only consider this surface's gaps to reduce the diff() complexity). + Growing actual extrusions ensures that gaps not filled by medial axis + are not subtracted from fill surfaces (they might be too short gaps + that medial axis skips but infill might join with other infill regions + and use zigzag). */ + last = diff(last, gap_fill.grow()); } } @@ -454,51 +449,92 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops, } ExtrusionEntityCollection -PerimeterGenerator::_fill_gaps(double min, double max, double w, - const Polygons &gaps) const +PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const { + const double tolerance = scale_(0.1); + ExtrusionEntityCollection coll; - - min *= (1 - INSET_OVERLAP_TOLERANCE); - - ExPolygons curr = diff_ex( - offset2(gaps, -min/2, +min/2), - offset2(gaps, -max/2, +max/2), - true - ); - - Polylines polylines; - for (ExPolygons::const_iterator ex = curr.begin(); ex != curr.end(); ++ex) - ex->medial_axis(max, min/2, &polylines); - if (polylines.empty()) - return coll; - - #ifdef SLIC3R_DEBUG - if (!curr.empty()) - printf(" %zu gaps filled with extrusion width = %f\n", curr.size(), w); - #endif - - //my $flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL, 0, $w); - Flow flow( - w, this->layer_height, this->solid_infill_flow.nozzle_diameter - ); - - double mm3_per_mm = flow.mm3_per_mm(); - - for (Polylines::const_iterator p = polylines.begin(); p != polylines.end(); ++p) { - ExtrusionPath path(erGapFill); - path.polyline = *p; - path.mm3_per_mm = mm3_per_mm; - path.width = flow.width; - path.height = this->layer_height; + for (ThickPolylines::const_iterator p = polylines.begin(); p != polylines.end(); ++p) { + ExtrusionPaths paths; + ExtrusionPath path(role); + ThickLines lines = p->thicklines(); + for (size_t i = 0; i < lines.size(); ++i) { + const ThickLine& line = lines[i]; + const double thickness_delta = fabs(line.a_width - line.b_width); + if (thickness_delta > tolerance) { + const unsigned short segments = ceil(thickness_delta / tolerance); + const coordf_t line_len = line.length(); + const coordf_t seg_len = line_len / segments; + Points pp; + std::vector width; + { + pp.push_back(line.a); + width.push_back(line.a_width); + for (size_t j = 1; j < segments; ++j) { + pp.push_back(line.point_at(j*seg_len)); + + coordf_t w = line.a_width + (j*seg_len) * (line.b_width-line.a_width) / line_len; + width.push_back(w); + width.push_back(w); + } + pp.push_back(line.b); + width.push_back(line.b_width); + + assert(pp.size() == segments + 1); + assert(width.size() == segments*2); + } + + // delete this line and insert new ones + lines.erase(lines.begin() + i); + for (size_t j = 0; j < segments; ++j) { + ThickLine new_line(pp[j], pp[j+1]); + new_line.a_width = width[2*j]; + new_line.b_width = width[2*j+1]; + lines.insert(lines.begin() + i + j, new_line); + } + + --i; + continue; + } + + const double w = fmax(line.a_width, line.b_width); + + if (path.polyline.points.empty()) { + path.polyline.append(line.a); + path.polyline.append(line.b); + + flow.width = unscale(w); + #ifdef SLIC3R_DEBUG + printf(" filling %f gap\n", flow.width); + #endif + path.mm3_per_mm = flow.mm3_per_mm(); + path.width = flow.width; + path.height = flow.height; + } else if (fabs(flow.width - w) <= tolerance) { + // the width difference between this line and the current flow width is + // within the accepted tolerance + + path.polyline.append(line.b); + } else { + // we need to initialize a new line + paths.push_back(path); + path = ExtrusionPath(role); + --i; + } + } + if (!path.polyline.points.empty()) + paths.push_back(path); - if (p->is_valid() && p->first_point().coincides_with(p->last_point())) { - // since medial_axis() now returns only Polyline objects, detect loops here - ExtrusionLoop loop; - loop.paths.push_back(path); - coll.append(loop); - } else { - coll.append(path); + // loop through generated paths + for (ExtrusionPaths::const_iterator p = paths.begin(); p != paths.end(); ++p) { + if (p->polyline.is_valid()) { + if (p->first_point().coincides_with(p->last_point())) { + // since medial_axis() now returns only Polyline objects, detect loops here + coll.append(ExtrusionLoop(*p)); + } else { + coll.append(*p); + } + } } } diff --git a/xs/src/libslic3r/PerimeterGenerator.hpp b/xs/src/libslic3r/PerimeterGenerator.hpp index b25a996a0..fe1a0aa15 100644 --- a/xs/src/libslic3r/PerimeterGenerator.hpp +++ b/xs/src/libslic3r/PerimeterGenerator.hpp @@ -66,17 +66,8 @@ class PerimeterGenerator { ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops, Polylines &thin_walls) const; - ExtrusionEntityCollection _fill_gaps(double min, double max, double w, - const Polygons &gaps) const; -}; - -class PerimeterGeneratorGapSize { - public: - coord_t min; - coord_t max; - coord_t width; - PerimeterGeneratorGapSize(coord_t min, coord_t max, coord_t width) - : min(min), max(max), width(width) {}; + ExtrusionEntityCollection _variable_width + (const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const; }; } diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp index ff8ec0d80..ccde4a740 100644 --- a/xs/src/libslic3r/Polygon.hpp +++ b/xs/src/libslic3r/Polygon.hpp @@ -23,7 +23,7 @@ class Polygon : public MultiPoint { Polygon() {}; explicit Polygon(const Points &points): MultiPoint(points) {}; Point last_point() const; - Lines lines() const; + virtual Lines lines() const; Polyline split_at_vertex(const Point &point) const; Polyline split_at_index(int index) const; Polyline split_at_first_point() const; diff --git a/xs/src/libslic3r/Polyline.cpp b/xs/src/libslic3r/Polyline.cpp index 7bfc79425..5de9b6268 100644 --- a/xs/src/libslic3r/Polyline.cpp +++ b/xs/src/libslic3r/Polyline.cpp @@ -83,17 +83,18 @@ void Polyline::extend_end(double distance) { // relocate last point by extending the last segment by the specified length - Line line(this->points[ this->points.size()-2 ], this->points.back()); - this->points.pop_back(); - this->points.push_back(line.point_at(line.length() + distance)); + Line line( + this->points.back(), + *(this->points.end() - 2) + ); + this->points.back() = line.point_at(-distance); } void Polyline::extend_start(double distance) { // relocate first point by extending the first segment by the specified length - Line line(this->points[1], this->points.front()); - this->points[0] = line.point_at(line.length() + distance); + this->points.front() = Line(this->points.front(), this->points[1]).point_at(-distance); } /* this method returns a collection of points picked on the polygon contour @@ -218,4 +219,20 @@ Polyline::wkt() const return wkt.str(); } +ThickLines +ThickPolyline::thicklines() const +{ + ThickLines lines; + if (this->points.size() >= 2) { + lines.reserve(this->points.size() - 1); + for (size_t i = 0; i < this->points.size()-1; ++i) { + ThickLine line(this->points[i], this->points[i+1]); + line.a_width = this->width[2*i]; + line.b_width = this->width[2*i+1]; + lines.push_back(line); + } + } + return lines; +} + } diff --git a/xs/src/libslic3r/Polyline.hpp b/xs/src/libslic3r/Polyline.hpp index ef327cbb3..1c50c50d7 100644 --- a/xs/src/libslic3r/Polyline.hpp +++ b/xs/src/libslic3r/Polyline.hpp @@ -5,12 +5,14 @@ #include "Line.hpp" #include "MultiPoint.hpp" #include +#include namespace Slic3r { -class ExPolygon; class Polyline; +class ThickPolyline; typedef std::vector Polylines; +typedef std::vector ThickPolylines; class Polyline : public MultiPoint { public: @@ -18,7 +20,7 @@ class Polyline : public MultiPoint { operator Line() const; Point last_point() const; Point leftmost_point() const; - Lines lines() const; + virtual Lines lines() const; void clip_end(double distance); void clip_start(double distance); void extend_end(double distance); @@ -31,6 +33,13 @@ class Polyline : public MultiPoint { std::string wkt() const; }; +class ThickPolyline : public Polyline { + public: + std::vector width; + std::vector endpoints; + ThickLines thicklines() const; +}; + } #endif diff --git a/xs/src/libslic3r/SVG.cpp b/xs/src/libslic3r/SVG.cpp index eacbe2e63..af367ae76 100644 --- a/xs/src/libslic3r/SVG.cpp +++ b/xs/src/libslic3r/SVG.cpp @@ -6,7 +6,7 @@ namespace Slic3r { SVG::SVG(const char* filename) - : arrows(true), fill("grey"), stroke("black"), filename(filename) + : arrows(false), fill("grey"), stroke("black"), filename(filename) { this->f = fopen(filename, "w"); fprintf(this->f, @@ -90,7 +90,14 @@ void SVG::draw(const Polylines &polylines, std::string stroke) { for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) - this->draw(*it, fill); + this->draw(*it, stroke); +} + +void +SVG::draw(const ThickPolylines &polylines, std::string stroke) +{ + for (ThickPolylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) + this->draw((Polyline)*it, stroke); } void diff --git a/xs/src/libslic3r/SVG.hpp b/xs/src/libslic3r/SVG.hpp index def1f13f0..f0ae06d04 100644 --- a/xs/src/libslic3r/SVG.hpp +++ b/xs/src/libslic3r/SVG.hpp @@ -24,6 +24,7 @@ class SVG void draw(const Polygons &polygons, std::string fill = "grey"); void draw(const Polyline &polyline, std::string stroke = "black"); void draw(const Polylines &polylines, std::string stroke = "black"); + void draw(const ThickPolylines &polylines, std::string stroke = "black"); void draw(const Point &point, std::string fill = "black", unsigned int radius = 3); void draw(const Points &points, std::string fill = "black", unsigned int radius = 3); void Close();