diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 6aa15a2ff..8aba26fcb 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -7,7 +7,7 @@ use warnings; use Boost::Geometry::Utils; use List::Util qw(first); use Math::Geometry::Voronoi; -use Slic3r::Geometry qw(X Y A B point_in_polygon same_line epsilon); +use Slic3r::Geometry qw(X Y A B point_in_polygon same_line epsilon scaled_epsilon); use Slic3r::Geometry::Clipper qw(union_ex); sub wkt { @@ -111,7 +111,14 @@ sub _medial_axis_clip { my $grow = sub { my ($line, $distance) = @_; - my ($a, $b) = @$line; + + my $line_clone = $line->clone; + $line_clone->clip_start(scaled_epsilon); + return () if !$line_clone->is_valid; + $line_clone->clip_end(scaled_epsilon); + return () if !$line_clone->is_valid; + + my ($a, $b) = @$line_clone; my $dx = $a->x - $b->x; my $dy = $a->y - $b->y; #- my $dist = sqrt($dx*$dx + $dy*$dy); @@ -130,9 +137,18 @@ sub _medial_axis_clip { foreach my $polygon (@$self) { my @polylines = (); foreach my $line (@{$polygon->lines}) { + # remove the areas that are already covered from this line my $clipped = Boost::Geometry::Utils::multi_linestring_multi_polygon_difference([$line->pp], [ map $_->pp, @{union_ex($covered)} ]); + + # skip very short segments/dots @$clipped = grep $_->length > $width/10, map Slic3r::Polyline->new(@$_), @$clipped; + + # grow the remaining lines and add them to the covered areas push @$covered, map $grow->($_, $width*1.1), @$clipped; + + # if the first remaining segment is connected to the last polyline, append it + # to that -- NOTE: this assumes that multi_linestring_multi_polygon_difference() + # preserved the orientation of the input linestring if (@polylines && @$clipped && $clipped->[0]->first_point->distance_to($polylines[-1]->last_point) <= $width/10) { $polylines[-1]->append_polyline(shift @$clipped); } @@ -140,7 +156,7 @@ sub _medial_axis_clip { } foreach my $polyline (@polylines) { - next if $polyline->length <= $width/10; + # if this polyline looks like a closed loop, return it as a polygon if ($polyline->first_point->coincides_with($polyline->last_point)) { next if @$polyline == 2; $polyline->pop_back; diff --git a/xs/src/MultiPoint.cpp b/xs/src/MultiPoint.cpp index 8e24c9941..dab41877b 100644 --- a/xs/src/MultiPoint.cpp +++ b/xs/src/MultiPoint.cpp @@ -49,6 +49,12 @@ MultiPoint::length() const return len; } +bool +MultiPoint::is_valid() const +{ + return this->points.size() >= 2; +} + #ifdef SLIC3RXS void MultiPoint::from_SV(SV* poly_sv) diff --git a/xs/src/MultiPoint.hpp b/xs/src/MultiPoint.hpp index 2473551fd..a1ad19c95 100644 --- a/xs/src/MultiPoint.hpp +++ b/xs/src/MultiPoint.hpp @@ -20,6 +20,7 @@ class MultiPoint virtual Point* last_point() const = 0; virtual Lines lines() const = 0; double length() const; + bool is_valid() const; #ifdef SLIC3RXS void from_SV(SV* poly_sv); diff --git a/xs/src/Polyline.cpp b/xs/src/Polyline.cpp index 7f7c36f12..0f8dd851b 100644 --- a/xs/src/Polyline.cpp +++ b/xs/src/Polyline.cpp @@ -40,6 +40,15 @@ Polyline::clip_end(double distance) } } +// removes the given distance from the start of the polyline +void +Polyline::clip_start(double distance) +{ + this->reverse(); + this->clip_end(distance); + if (this->points.size() >= 2) this->reverse(); +} + #ifdef SLIC3RXS SV* Polyline::to_SV_ref() diff --git a/xs/src/Polyline.hpp b/xs/src/Polyline.hpp index 2636dc406..8983cfef4 100644 --- a/xs/src/Polyline.hpp +++ b/xs/src/Polyline.hpp @@ -11,6 +11,7 @@ class Polyline : public MultiPoint { Point* last_point() const; Lines lines() const; void clip_end(double distance); + void clip_start(double distance); #ifdef SLIC3RXS SV* to_SV_ref(); diff --git a/xs/xsp/Polyline.xsp b/xs/xsp/Polyline.xsp index 9566cb3cd..12cc17f03 100644 --- a/xs/xsp/Polyline.xsp +++ b/xs/xsp/Polyline.xsp @@ -24,7 +24,9 @@ Point* last_point() %code{% const char* CLASS = "Slic3r::Point"; RETVAL = THIS->last_point(); %}; double length(); + bool is_valid(); void clip_end(double distance); + void clip_start(double distance); %{ Polyline*