From 3025c7767538cd671acbf81ce5e5f673a9a98e32 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 21 Nov 2013 14:15:38 +0100 Subject: [PATCH] Use Clipper for line clipping --- lib/Slic3r/ExPolygon.pm | 8 +-- lib/Slic3r/Fill/Honeycomb.pm | 22 ++++--- lib/Slic3r/Fill/Rectilinear.pm | 11 ++-- lib/Slic3r/GCode/MotionPlanner.pm | 6 +- lib/Slic3r/Geometry.pm | 96 +------------------------------ lib/Slic3r/Geometry/Clipper.pm | 2 +- lib/Slic3r/Layer/Region.pm | 6 +- t/polyclip.t | 78 +++++++++---------------- xs/src/ClipperUtils.cpp | 96 +++++++++++++++++++++++-------- xs/src/ClipperUtils.hpp | 20 +++++-- xs/src/Polygon.cpp | 10 ++-- xs/src/PolylineCollection.cpp | 11 ++++ xs/src/PolylineCollection.hpp | 1 + xs/src/clipper.hpp | 2 +- xs/t/11_clipper.t | 20 ++++++- xs/xsp/Clipper.xsp | 18 ++++++ xs/xsp/Line.xsp | 2 + xs/xsp/PolylineCollection.xsp | 2 + xs/xsp/my.map | 1 + xs/xsp/typemap.xspt | 1 + 20 files changed, 202 insertions(+), 211 deletions(-) diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index b72b93542..584136a10 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -8,7 +8,7 @@ use Boost::Geometry::Utils; use List::Util qw(first); use Math::Geometry::Voronoi; use Slic3r::Geometry qw(X Y A B point_in_polygon epsilon scaled_epsilon); -use Slic3r::Geometry::Clipper qw(union_ex); +use Slic3r::Geometry::Clipper qw(union_ex diff_pl); sub wkt { my $self = shift; @@ -77,7 +77,7 @@ sub clip_line { return [ map Slic3r::Line->new(@$_), - @{Boost::Geometry::Utils::polygon_multi_linestring_intersection($self->pp, [$line->pp])} + @{Slic3r::Geometry::Clipper::intersection_pl([ Slic3r::Polyline->new(@$line) ], \@$self)} ]; } @@ -138,10 +138,10 @@ sub _medial_axis_clip { 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)} ]); + my $clipped = diff_pl([$line->as_polyline], $covered); # skip very short segments/dots - @$clipped = grep $_->length > $width/10, map Slic3r::Polyline->new(@$_), @$clipped; + @$clipped = grep $_->length > $width/10, @$clipped; # grow the remaining lines and add them to the covered areas push @$covered, map $grow->($_, $width*1.1), @$clipped; diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm index eabbf861f..ceb398b5d 100644 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -6,7 +6,7 @@ extends 'Slic3r::Fill::Base'; has 'cache' => (is => 'rw', default => sub {{}}); use Slic3r::Geometry qw(PI X Y MIN MAX scale scaled_epsilon); -use Slic3r::Geometry::Clipper qw(intersection); +use Slic3r::Geometry::Clipper qw(intersection intersection_pl); sub angles () { [0, PI/3, PI/3*2] } @@ -88,17 +88,16 @@ sub fill_surface { # consider polygons as polylines without re-appending the initial point: # this cuts the last segment on purpose, so that the jump to the next # path is more straight - @paths = map Slic3r::Polyline->new(@$_), - @{ Boost::Geometry::Utils::polygon_multi_linestring_intersection( - $surface->expolygon->pp, - [ map $_->pp, @polygons ], - ) }; + @paths = @{intersection_pl( + [ map Slic3r::Polyline->new(@$_), @polygons ], + [ @{$surface->expolygon} ], + )}; # connect paths { my $collection = Slic3r::Polyline::Collection->new(@paths); @paths = (); - foreach my $path (@{$collection->chained_path(0)}) { + foreach my $path (@{$collection->chained_path_from($collection->leftmost_point, 0)}) { if (@paths) { # distance between first point of this path and last point of last path my $distance = $paths[-1]->last_point->distance_to($path->first_point); @@ -115,11 +114,10 @@ sub fill_surface { } # clip paths again to prevent connection segments from crossing the expolygon boundaries - @paths = map Slic3r::Polyline->new(@$_), - @{ Boost::Geometry::Utils::multi_polygon_multi_linestring_intersection( - [ map $_->pp, @{$surface->expolygon->offset_ex(scaled_epsilon)} ], - [ map $_->pp, @paths ], - ) } if @paths; # this temporary check is a workaround for the multilinestring bug in B::G::U + @paths = @{intersection_pl( + \@paths, + [ @{$surface->expolygon->offset_ex(scaled_epsilon)} ], + )}; } return { flow_spacing => $params{flow_spacing} }, @paths; diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index dd39b3040..78e1bc985 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -6,6 +6,7 @@ extends 'Slic3r::Fill::Base'; has 'cache' => (is => 'rw', default => sub {{}}); use Slic3r::Geometry qw(A B X Y MIN scale unscale scaled_epsilon); +use Slic3r::Geometry::Clipper qw(intersection_pl offset); sub fill_surface { my $self = shift; @@ -47,7 +48,7 @@ sub fill_surface { $vertical_line->[A][X] += $line_oscillation; $vertical_line->[B][X] -= $line_oscillation; } - push @vertical_lines, $vertical_line; + push @vertical_lines, Slic3r::Polyline->new(@$vertical_line); $i++; $x += $line_spacing; } @@ -57,11 +58,7 @@ sub fill_surface { # the minimum offset for preventing edge lines from being clipped is scaled_epsilon; # however we use a larger offset to support expolygons with slightly skewed sides and # not perfectly straight - my @polylines = map Slic3r::Polyline->new(@$_), - @{ Boost::Geometry::Utils::multi_polygon_multi_linestring_intersection( - [ map $_->pp, @{$expolygon->offset_ex($line_spacing*0.05)} ], - [ @vertical_lines ], - ) }; + my @polylines = @{intersection_pl(\@vertical_lines, $expolygon->offset($line_spacing*0.05))}; # connect lines unless ($params{dont_connect}) { @@ -78,7 +75,7 @@ sub fill_surface { } : sub { $_[X] <= $diagonal_distance && $_[Y] <= $diagonal_distance }; - foreach my $polyline (@{$collection->chained_path(0)}) { + foreach my $polyline (@{$collection->chained_path_from($collection->leftmost_point, 0)}) { if (@polylines) { my $first_point = $polyline->first_point; my $last_point = $polylines[-1]->last_point; diff --git a/lib/Slic3r/GCode/MotionPlanner.pm b/lib/Slic3r/GCode/MotionPlanner.pm index 68c386aa9..53201d055 100644 --- a/lib/Slic3r/GCode/MotionPlanner.pm +++ b/lib/Slic3r/GCode/MotionPlanner.pm @@ -14,7 +14,7 @@ has '_tolerance' => (is => 'lazy'); use List::Util qw(first); use Slic3r::Geometry qw(A B scale epsilon); -use Slic3r::Geometry::Clipper qw(diff_ex offset); +use Slic3r::Geometry::Clipper qw(diff_ex offset intersection_pl); # clearance (in mm) from the perimeters has '_inner_margin' => (is => 'ro', default => sub { scale 0.5 }); @@ -91,7 +91,7 @@ sub BUILD { for my $m (0 .. $#{$outer[$i]}) { for my $n (0 .. $#{$outer[$j]}) { my $line = Slic3r::Line->new($outer[$i][$m], $outer[$j][$n]); - if (!@{Boost::Geometry::Utils::multi_polygon_multi_linestring_intersection([ map $_->pp, @outer_ex ], [$line->pp])}) { + if (!@{intersection_pl([$line->as_polyline], [ map @$_, @outer_ex ])}) { # this line does not cross any polygon my $dist = $line->length; $edges->{$outer[$i][$m]}{$outer[$j][$n]} = $dist; @@ -112,7 +112,7 @@ sub BUILD { for my $m (0 .. $#{$inner[$i]}) { for my $n (0 .. $#{$inner[$j]}) { my $line = Slic3r::Line->new($inner[$i][$m], $inner[$j][$n]); - if (!@{Boost::Geometry::Utils::multi_polygon_multi_linestring_intersection([ map $_->pp, @inner_ex ], [$line->pp])}) { + if (!@{intersection_pl([$line->as_polyline], [ map @$_, @inner_ex ])}) { # this line does not cross any polygon my $dist = $line->length * CROSSING_FACTOR; $edges->{$inner[$i][$m]}{$inner[$j][$n]} = $dist; diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index c236b68ac..6c7283492 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -11,9 +11,9 @@ our @EXPORT_OK = qw( point_in_polygon point_in_segment segment_in_segment point_is_on_left_of_segment polyline_lines polygon_lines point_along_segment polygon_segment_having_point polygon_has_subsegment - polygon_has_vertex can_connect_points deg2rad rad2deg - rotate_points move_points clip_segment_polygon - sum_vectors multiply_vector subtract_vectors dot perp polygon_points_visibility + deg2rad rad2deg + rotate_points move_points + dot perp polygon_points_visibility line_intersection bounding_box bounding_box_intersect angle3points three_points_aligned line_direction polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices @@ -229,14 +229,6 @@ sub polygon_has_subsegment { return 0; } -sub polygon_has_vertex { - my ($polygon, $point) = @_; - foreach my $p (@$polygon) { - return 1 if points_coincide($p, $point); - } - return 0; -} - # polygon must be simple (non complex) and ccw sub polygon_is_convex { my ($points) = @_; @@ -247,29 +239,6 @@ sub polygon_is_convex { return 1; } -sub can_connect_points { - my ($p1, $p2, $polygons) = @_; - - # check that the two points are visible from each other - return 0 if grep !polygon_points_visibility($_, $p1, $p2), @$polygons; - - # get segment where $p1 lies - my $p1_segment; - for (@$polygons) { - $p1_segment = polygon_segment_having_point($_, $p1); - last if $p1_segment; - } - - # defensive programming, this shouldn't happen - if (!$p1_segment) { - die sprintf "Point %f,%f wasn't found in polygon contour or holes!", @$p1; - } - - # check whether $p2 is internal or external (internal = on the left) - return point_is_on_left_of_segment($p2, $p1_segment) - || point_in_segment($p2, $p1_segment); -} - sub deg2rad { my ($degrees) = @_; return PI() * $degrees / 180; @@ -315,65 +284,6 @@ sub move_points_3D { ], @points; } -# implementation of Liang-Barsky algorithm -# polygon must be convex and ccw -sub clip_segment_polygon { - my ($line, $polygon) = @_; - - if (@$line == 1) { - # the segment is a point, check for inclusion - return point_in_polygon($line, $polygon); - } - - my @V = (@$polygon, $polygon->[0]); - my $tE = 0; # the maximum entering segment parameter - my $tL = 1; # the minimum entering segment parameter - my $dS = subtract_vectors($line->[B], $line->[A]); # the segment direction vector - - for (my $i = 0; $i < $#V; $i++) { # process polygon edge V[i]V[Vi+1] - my $e = subtract_vectors($V[$i+1], $V[$i]); - my $N = perp($e, subtract_vectors($line->[A], $V[$i])); - my $D = -perp($e, $dS); - if (abs($D) < epsilon) { # $line is nearly parallel to this edge - ($N < 0) ? return : next; # P0 outside this edge ? $line is outside : $line cannot cross edge, thus ignoring - } - - my $t = $N / $D; - if ($D < 0) { # $line is entering across this edge - if ($t > $tE) { # new max $tE - $tE = $t; - return if $tE > $tL; # $line enters after leaving polygon? - } - } else { # $line is leaving across this edge - if ($t < $tL) { # new min $tL - $tL = $t; - return if $tL < $tE; # $line leaves before entering polygon? - } - } - } - - # $tE <= $tL implies that there is a valid intersection subsegment - return [ - sum_vectors($line->[A], multiply_vector($dS, $tE)), # = P(tE) = point where S enters polygon - sum_vectors($line->[A], multiply_vector($dS, $tL)), # = P(tE) = point where S enters polygon - ]; -} - -sub sum_vectors { - my ($v1, $v2) = @_; - return [ $v1->[X] + $v2->[X], $v1->[Y] + $v2->[Y] ]; -} - -sub multiply_vector { - my ($line, $scalar) = @_; - return [ $line->[X] * $scalar, $line->[Y] * $scalar ]; -} - -sub subtract_vectors { - my ($line2, $line1) = @_; - return [ $line2->[X] - $line1->[X], $line2->[Y] - $line1->[Y] ]; -} - sub normal { my ($line1, $line2) = @_; diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index 9e23b6c79..4aa0b0752 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -7,7 +7,7 @@ our @ISA = qw(Exporter); our @EXPORT_OK = qw(offset offset_ex diff_ex diff union_ex intersection_ex xor_ex JT_ROUND JT_MITER JT_SQUARE is_counter_clockwise union_pt offset2 offset2_ex traverse_pt - intersection union CLIPPER_OFFSET_SCALE); + intersection intersection_pl diff_pl union CLIPPER_OFFSET_SCALE); use Slic3r::Geometry qw(scale); diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index a495d2464..34c5bd61a 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -562,12 +562,10 @@ sub _detect_bridge_direction { my @lines = (); for (my $x = $bounding_box->x_min; $x <= $bounding_box->x_max; $x += $line_increment) { - push @lines, [ [$x, $bounding_box->y_min], [$x, $bounding_box->y_max] ]; + push @lines, Slic3r::Polyline->new([$x, $bounding_box->y_min], [$x, $bounding_box->y_max]); } - # TODO: use a multi_polygon_multi_linestring_intersection() call - my @clipped_lines = map Slic3r::Line->new(@$_), - map @{ Boost::Geometry::Utils::polygon_multi_linestring_intersection($_->pp, \@lines) }, @$inset; + my @clipped_lines = map Slic3r::Line->new(@$_), @{ intersection_pl(\@lines, [ map @$_, @$inset ]) }; # remove any line not having both endpoints within anchors @clipped_lines = grep { diff --git a/t/polyclip.t b/t/polyclip.t index 548b6ba65..772a9eebc 100644 --- a/t/polyclip.t +++ b/t/polyclip.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 24; +plan tests => 23; BEGIN { use FindBin; @@ -31,24 +31,6 @@ my $square = Slic3r::Polygon->new( # ccw my $line = Slic3r::Line->new([50, 150], [300, 150]); -my $intersection = Slic3r::Geometry::clip_segment_polygon($line, $square); -is_deeply $intersection, [ [100, 150], [200, 150] ], 'line is clipped to square'; - -#========================================================== - -$intersection = Slic3r::Geometry::clip_segment_polygon(Slic3r::Line->new([0, 150], [80, 150]), $square); -is $intersection, undef, 'external lines are ignored 1'; - -#========================================================== - -$intersection = Slic3r::Geometry::clip_segment_polygon(Slic3r::Line->new([300, 150], [400, 150]), $square); -is $intersection, undef, 'external lines are ignored 2'; - -#========================================================== - -$intersection = Slic3r::Geometry::clip_segment_polygon(Slic3r::Line->new([120, 120], [180, 160]), $square); -is_deeply $intersection, [ [120, 120], [180, 160] ], 'internal lines are preserved'; - #========================================================== { @@ -64,42 +46,38 @@ is_deeply $intersection, [ [120, 120], [180, 160] ], 'internal lines are preserv is $expolygon->encloses_point(Slic3r::Point->new(140, 150)), 1, 'point on hole contour is recognized'; is $expolygon->encloses_point(Slic3r::Point->new(140, 140)), 1, 'point on hole corner is recognized'; { - my $intersections = $expolygon->clip_line(Slic3r::Line->new([150,180], [150,150])); - is_deeply [ map $_->pp, @$intersections ], [ - [ [150, 180], [150, 160] ], - ], 'line is clipped to square with hole'; + my $intersection = $expolygon->clip_line(Slic3r::Line->new([150,180], [150,150])); + is $intersection->[0]->length, Slic3r::Line->new([150, 180], [150, 160])->length, + 'line is clipped to square with hole'; } { - my $intersections = $expolygon->clip_line(Slic3r::Line->new([150,150], [150,120])); - is_deeply [ map $_->pp, @$intersections ], [ - [ [150, 140], [150, 120] ], - ], 'line is clipped to square with hole'; + my $intersection = $expolygon->clip_line(Slic3r::Line->new([150,150], [150,120])); + is $intersection->[0]->length, Slic3r::Line->new([150, 140], [150, 120])->length, + 'line is clipped to square with hole'; } { - my $intersections = $expolygon->clip_line(Slic3r::Line->new([120,180], [180,180])); - is_deeply [ map $_->pp, @$intersections ], [ - [ [120,180], [180,180] ], - ], 'line is clipped to square with hole'; + my $intersection = $expolygon->clip_line(Slic3r::Line->new([120,180], [180,180])); + is $intersection->[0]->length, Slic3r::Line->new([120,180], [180,180])->length, + 'line is clipped to square with hole'; } { - my $intersections = $expolygon->clip_line($line); - is_deeply [ map $_->pp, @$intersections ], [ - [ [100, 150], [140, 150] ], - [ [160, 150], [200, 150] ], - ], 'line is clipped to square with hole'; + my $intersection = $expolygon->clip_line($line); + is $intersection->[0]->length, Slic3r::Line->new([100, 150], [140, 150])->length, + 'line is clipped to square with hole'; + is $intersection->[1]->length, Slic3r::Line->new([160, 150], [200, 150])->length, + 'line is clipped to square with hole'; } { - my $intersections = $expolygon->clip_line(Slic3r::Line->new(reverse @$line)); - is_deeply [ map $_->pp, @$intersections ], [ - [ [200, 150], [160, 150] ], - [ [140, 150], [100, 150] ], - ], 'reverse line is clipped to square with hole'; + my $intersection = $expolygon->clip_line(Slic3r::Line->new(reverse @$line)); + is $intersection->[0]->length, Slic3r::Line->new([200, 150], [160, 150])->length, + 'reverse line is clipped to square with hole'; + is $intersection->[1]->length, Slic3r::Line->new([140, 150], [100, 150])->length, + 'reverse line is clipped to square with hole'; } { - my $intersections = $expolygon->clip_line(Slic3r::Line->new([100,180], [200,180])); - is_deeply [ map $_->pp, @$intersections ], [ - [ [100, 180], [200, 180] ], - ], 'tangent line is clipped to square with hole'; + my $intersection = $expolygon->clip_line(Slic3r::Line->new([100,180], [200,180])); + is $intersection->[0]->length, Slic3r::Line->new([100,180], [200,180])->length, + 'tangent line is clipped to square with hole'; } { my $polyline = Slic3r::Polyline->new([50, 180], [250, 180], [250, 150], [150, 150], [150, 120], [120, 120], [120, 50]); @@ -141,11 +119,11 @@ is_deeply $intersection, [ [120, 120], [180, 160] ], 'internal lines are preserv my $expolygon = Slic3r::ExPolygon->new($large_circle, $small_circle); $line = Slic3r::Line->new_scale([152.742,288.086671142818], [152.742,34.166466971035]); - my $intersections = $expolygon->clip_line($line); - is_deeply [ map $_->pp, @$intersections ], [ - [ [152742000, 288086661], [152742000, 215178843], ], - [ [152742000, 108087507], [152742000, 35166477] ], - ], 'line is clipped to square with hole'; + my $intersection = $expolygon->clip_line($line); + is $intersection->[0]->length, Slic3r::Line->new([152742000, 288086661], [152742000, 215178843])->length, + 'line is clipped to square with hole'; + is $intersection->[1]->length, Slic3r::Line->new([152742000, 108087507], [152742000, 35166477])->length, + 'line is clipped to square with hole'; } #========================================================== diff --git a/xs/src/ClipperUtils.cpp b/xs/src/ClipperUtils.cpp index 6df7213df..e074a3b79 100644 --- a/xs/src/ClipperUtils.cpp +++ b/xs/src/ClipperUtils.cpp @@ -8,11 +8,11 @@ void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPoly { size_t cnt = expolygons.size(); expolygons.resize(cnt + 1); - ClipperPolygon_to_Slic3rPolygon(polynode.Contour, expolygons[cnt].contour); + ClipperPath_to_Slic3rMultiPoint(polynode.Contour, expolygons[cnt].contour); expolygons[cnt].holes.resize(polynode.ChildCount()); for (int i = 0; i < polynode.ChildCount(); ++i) { - ClipperPolygon_to_Slic3rPolygon(polynode.Childs[i]->Contour, expolygons[cnt].holes[i]); + ClipperPath_to_Slic3rMultiPoint(polynode.Childs[i]->Contour, expolygons[cnt].holes[i]); //Add outer polygons contained by (nested within) holes ... for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++j) AddOuterPolyNodeToExPolygons(*polynode.Childs[i]->Childs[j], expolygons); @@ -27,8 +27,9 @@ void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& ex } //----------------------------------------------------------- +template void -ClipperPolygon_to_Slic3rPolygon(const ClipperLib::Path &input, Slic3r::Polygon &output) +ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T &output) { output.points.clear(); for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) { @@ -36,19 +37,20 @@ ClipperPolygon_to_Slic3rPolygon(const ClipperLib::Path &input, Slic3r::Polygon & } } +template void -ClipperPolygons_to_Slic3rPolygons(const ClipperLib::Paths &input, Slic3r::Polygons &output) +ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T &output) { output.clear(); for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) { - Slic3r::Polygon p; - ClipperPolygon_to_Slic3rPolygon(*it, p); + typename T::value_type p; + ClipperPath_to_Slic3rMultiPoint(*it, p); output.push_back(p); } } void -ClipperPolygons_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons &output) +ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons &output) { // init Clipper ClipperLib::Clipper clipper; @@ -67,7 +69,7 @@ ClipperPolygons_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPo } void -Slic3rPolygon_to_ClipperPolygon(const Slic3r::MultiPoint &input, ClipperLib::Path &output) +Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path &output) { output.clear(); for (Slic3r::Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit) { @@ -77,12 +79,12 @@ Slic3rPolygon_to_ClipperPolygon(const Slic3r::MultiPoint &input, ClipperLib::Pat template void -Slic3rPolygons_to_ClipperPolygons(const T &input, ClipperLib::Paths &output) +Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths &output) { output.clear(); for (typename T::const_iterator it = input.begin(); it != input.end(); ++it) { ClipperLib::Path p; - Slic3rPolygon_to_ClipperPolygon(*it, p); + Slic3rMultiPoint_to_ClipperPath(*it, p); output.push_back(p); } } @@ -104,7 +106,7 @@ offset(Slic3r::Polygons &polygons, ClipperLib::Paths &retval, const float delta, { // read input ClipperLib::Paths* input = new ClipperLib::Paths(); - Slic3rPolygons_to_ClipperPolygons(polygons, *input); + Slic3rMultiPoints_to_ClipperPaths(polygons, *input); // scale input scaleClipperPolygons(*input, scale); @@ -126,7 +128,7 @@ offset(Slic3r::Polygons &polygons, Slic3r::Polygons &retval, const float delta, offset(polygons, *output, delta, scale, joinType, miterLimit); // convert into ExPolygons - ClipperPolygons_to_Slic3rPolygons(*output, retval); + ClipperPaths_to_Slic3rMultiPoints(*output, retval); delete output; } @@ -136,7 +138,7 @@ offset(Slic3r::Polylines &polylines, ClipperLib::Paths &retval, const float delt { // read input ClipperLib::Paths* input = new ClipperLib::Paths(); - Slic3rPolygons_to_ClipperPolygons(polylines, *input); + Slic3rMultiPoints_to_ClipperPaths(polylines, *input); // scale input scaleClipperPolygons(*input, scale); @@ -158,7 +160,7 @@ offset(Slic3r::Polylines &polylines, Slic3r::Polygons &retval, const float delta offset(polylines, *output, delta, scale, joinType, miterLimit); // convert into ExPolygons - ClipperPolygons_to_Slic3rPolygons(*output, retval); + ClipperPaths_to_Slic3rMultiPoints(*output, retval); delete output; } @@ -171,7 +173,7 @@ offset_ex(Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float de offset(polygons, *output, delta, scale, joinType, miterLimit); // convert into ExPolygons - ClipperPolygons_to_Slic3rExPolygons(*output, retval); + ClipperPaths_to_Slic3rExPolygons(*output, retval); delete output; } @@ -181,7 +183,7 @@ offset2(Slic3r::Polygons &polygons, ClipperLib::Paths &retval, const float delta { // read input ClipperLib::Paths* input = new ClipperLib::Paths(); - Slic3rPolygons_to_ClipperPolygons(polygons, *input); + Slic3rMultiPoints_to_ClipperPaths(polygons, *input); // scale input scaleClipperPolygons(*input, scale); @@ -208,7 +210,7 @@ offset2(Slic3r::Polygons &polygons, Slic3r::Polygons &retval, const float delta1 offset2(polygons, *output, delta1, delta2, scale, joinType, miterLimit); // convert into ExPolygons - ClipperPolygons_to_Slic3rPolygons(*output, retval); + ClipperPaths_to_Slic3rMultiPoints(*output, retval); delete output; } @@ -221,7 +223,7 @@ offset2_ex(Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float d offset2(polygons, *output, delta1, delta2, scale, joinType, miterLimit); // convert into ExPolygons - ClipperPolygons_to_Slic3rExPolygons(*output, retval); + ClipperPaths_to_Slic3rExPolygons(*output, retval); delete output; } @@ -232,8 +234,8 @@ void _clipper_do(const ClipperLib::ClipType clipType, Slic3r::Polygons &subject, // read input ClipperLib::Paths* input_subject = new ClipperLib::Paths(); ClipperLib::Paths* input_clip = new ClipperLib::Paths(); - Slic3rPolygons_to_ClipperPolygons(subject, *input_subject); - Slic3rPolygons_to_ClipperPolygons(clip, *input_clip); + Slic3rMultiPoints_to_ClipperPaths(subject, *input_subject); + Slic3rMultiPoints_to_ClipperPaths(clip, *input_clip); // perform safety offset if (safety_offset_) { @@ -258,6 +260,29 @@ void _clipper_do(const ClipperLib::ClipType clipType, Slic3r::Polygons &subject, clipper.Execute(clipType, retval, fillType, fillType); } +void _clipper_do(const ClipperLib::ClipType clipType, Slic3r::Polylines &subject, + Slic3r::Polygons &clip, ClipperLib::PolyTree &retval, const ClipperLib::PolyFillType fillType) +{ + // read input + ClipperLib::Paths* input_subject = new ClipperLib::Paths(); + ClipperLib::Paths* input_clip = new ClipperLib::Paths(); + Slic3rMultiPoints_to_ClipperPaths(subject, *input_subject); + Slic3rMultiPoints_to_ClipperPaths(clip, *input_clip); + + // init Clipper + ClipperLib::Clipper clipper; + clipper.Clear(); + + // add polygons + clipper.AddPaths(*input_subject, ClipperLib::ptSubject, false); + delete input_subject; + clipper.AddPaths(*input_clip, ClipperLib::ptClip, true); + delete input_clip; + + // perform operation + clipper.Execute(clipType, retval, fillType, fillType); +} + void _clipper(ClipperLib::ClipType clipType, Slic3r::Polygons &subject, Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_) { @@ -266,7 +291,7 @@ void _clipper(ClipperLib::ClipType clipType, Slic3r::Polygons &subject, _clipper_do(clipType, subject, clip, *output, ClipperLib::pftNonZero, safety_offset_); // convert into Polygons - ClipperPolygons_to_Slic3rPolygons(*output, retval); + ClipperPaths_to_Slic3rMultiPoints(*output, retval); delete output; } @@ -282,6 +307,19 @@ void _clipper(ClipperLib::ClipType clipType, Slic3r::Polygons &subject, delete polytree; } +void _clipper(ClipperLib::ClipType clipType, Slic3r::Polylines &subject, + Slic3r::Polygons &clip, Slic3r::Polylines &retval) +{ + // perform operation + ClipperLib::PolyTree polytree; + _clipper_do(clipType, subject, clip, polytree, ClipperLib::pftNonZero); + + // convert into Polygons + ClipperLib::Paths output; + ClipperLib::PolyTreeToPaths(polytree, output); + ClipperPaths_to_Slic3rMultiPoints(output, retval); +} + template void diff(Slic3r::Polygons &subject, Slic3r::Polygons &clip, T &retval, bool safety_offset_) { @@ -290,6 +328,11 @@ void diff(Slic3r::Polygons &subject, Slic3r::Polygons &clip, T &retval, bool saf template void diff(Slic3r::Polygons &subject, Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_); template void diff(Slic3r::Polygons &subject, Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_); +void diff(Slic3r::Polylines &subject, Slic3r::Polygons &clip, Slic3r::Polylines &retval) +{ + _clipper(ClipperLib::ctDifference, subject, clip, retval); +} + template void intersection(Slic3r::Polygons &subject, Slic3r::Polygons &clip, T &retval, bool safety_offset_) { @@ -298,6 +341,11 @@ void intersection(Slic3r::Polygons &subject, Slic3r::Polygons &clip, T &retval, template void intersection(Slic3r::Polygons &subject, Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_); template void intersection(Slic3r::Polygons &subject, Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_); +void intersection(Slic3r::Polylines &subject, Slic3r::Polygons &clip, Slic3r::Polylines &retval) +{ + _clipper(ClipperLib::ctIntersection, subject, clip, retval); +} + void xor_ex(Slic3r::Polygons &subject, Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_) { @@ -323,14 +371,14 @@ void simplify_polygons(Slic3r::Polygons &subject, Slic3r::Polygons &retval) { // convert into Clipper polygons ClipperLib::Paths* input_subject = new ClipperLib::Paths(); - Slic3rPolygons_to_ClipperPolygons(subject, *input_subject); + Slic3rMultiPoints_to_ClipperPaths(subject, *input_subject); ClipperLib::Paths* output = new ClipperLib::Paths(); ClipperLib::SimplifyPolygons(*input_subject, *output, ClipperLib::pftNonZero); delete input_subject; // convert into Slic3r polygons - ClipperPolygons_to_Slic3rPolygons(*output, retval); + ClipperPaths_to_Slic3rMultiPoints(*output, retval); delete output; } @@ -371,7 +419,7 @@ polynode2perl(const ClipperLib::PolyNode& node) { HV* hv = newHV(); Slic3r::Polygon p; - ClipperPolygon_to_Slic3rPolygon(node.Contour, p); + ClipperPath_to_Slic3rMultiPoint(node.Contour, p); if (node.IsHole()) { (void)hv_stores( hv, "hole", p.to_SV_clone_ref() ); } else { diff --git a/xs/src/ClipperUtils.hpp b/xs/src/ClipperUtils.hpp index e6fdeb81d..6759262c3 100644 --- a/xs/src/ClipperUtils.hpp +++ b/xs/src/ClipperUtils.hpp @@ -21,12 +21,14 @@ void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPoly void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons); //----------------------------------------------------------- -void Slic3rPolygon_to_ClipperPolygon(const Slic3r::MultiPoint &input, ClipperLib::Path &output); +void Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path &output); template -void Slic3rPolygons_to_ClipperPolygons(const T &input, ClipperLib::Paths &output); -void ClipperPolygon_to_Slic3rPolygon(const ClipperLib::Path &input, Slic3r::Polygon &output); -void ClipperPolygons_to_Slic3rPolygons(const ClipperLib::Paths &input, Slic3r::Polygons &output); -void ClipperPolygons_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons &output); +void Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths &output); +template +void ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T &output); +template +void ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T &output); +void ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons &output); void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale); @@ -63,17 +65,25 @@ void offset2_ex(Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const fl template void _clipper_do(ClipperLib::ClipType clipType, Slic3r::Polygons &subject, Slic3r::Polygons &clip, T &retval, bool safety_offset_); +void _clipper_do(ClipperLib::ClipType clipType, Slic3r::Polylines &subject, + Slic3r::Polygons &clip, ClipperLib::Paths &retval); void _clipper(ClipperLib::ClipType clipType, Slic3r::Polygons &subject, Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_); void _clipper(ClipperLib::ClipType clipType, Slic3r::Polygons &subject, Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_); +void _clipper(ClipperLib::ClipType clipType, Slic3r::Polylines &subject, + Slic3r::Polygons &clip, Slic3r::Polylines &retval); template void diff(Slic3r::Polygons &subject, Slic3r::Polygons &clip, T &retval, bool safety_offset_); +void diff(Slic3r::Polylines &subject, Slic3r::Polygons &clip, Slic3r::Polylines &retval); + template void intersection(Slic3r::Polygons &subject, Slic3r::Polygons &clip, T &retval, bool safety_offset_); +void intersection(Slic3r::Polylines &subject, Slic3r::Polygons &clip, Slic3r::Polylines &retval); + void xor_ex(Slic3r::Polygons &subject, Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_ = false); diff --git a/xs/src/Polygon.cpp b/xs/src/Polygon.cpp index 109f06d14..5ebd00cb3 100644 --- a/xs/src/Polygon.cpp +++ b/xs/src/Polygon.cpp @@ -66,18 +66,16 @@ double Polygon::area() const { ClipperLib::Path p; - Slic3rPolygon_to_ClipperPolygon(*this, p); + Slic3rMultiPoint_to_ClipperPath(*this, p); return ClipperLib::Area(p); } bool Polygon::is_counter_clockwise() const { - ClipperLib::Path* p = new ClipperLib::Path(); - Slic3rPolygon_to_ClipperPolygon(*this, *p); - bool orientation = ClipperLib::Orientation(*p); - delete p; - return orientation; + ClipperLib::Path p; + Slic3rMultiPoint_to_ClipperPath(*this, p); + return ClipperLib::Orientation(p); } bool diff --git a/xs/src/PolylineCollection.cpp b/xs/src/PolylineCollection.cpp index 34eebf580..6cd9175c3 100644 --- a/xs/src/PolylineCollection.cpp +++ b/xs/src/PolylineCollection.cpp @@ -41,4 +41,15 @@ PolylineCollection::chained_path_from(const Point* start_near, bool no_reverse) return retval; } +Point* +PolylineCollection::leftmost_point() const +{ + const Point* p = NULL; + for (Polylines::const_iterator it = this->polylines.begin(); it != this->polylines.end(); ++it) { + if (p == NULL || it->points.front().x < p->x) + p = &(it->points.front()); + } + return new Point (*p); +} + } diff --git a/xs/src/PolylineCollection.hpp b/xs/src/PolylineCollection.hpp index 96afb0ec5..2528d0d9f 100644 --- a/xs/src/PolylineCollection.hpp +++ b/xs/src/PolylineCollection.hpp @@ -12,6 +12,7 @@ class PolylineCollection Polylines polylines; PolylineCollection* chained_path(bool no_reverse) const; PolylineCollection* chained_path_from(const Point* start_near, bool no_reverse) const; + Point* leftmost_point() const; }; } diff --git a/xs/src/clipper.hpp b/xs/src/clipper.hpp index be304503d..eea704b9f 100755 --- a/xs/src/clipper.hpp +++ b/xs/src/clipper.hpp @@ -44,7 +44,7 @@ //#define use_xyz //use_lines: Enables line clipping. Adds a very minor cost to performance. -//#define use_lines +#define use_lines //When enabled, code developed with earlier versions of Clipper //(ie prior to ver 6) should compile without changes. diff --git a/xs/t/11_clipper.t b/xs/t/11_clipper.t index df9f8fc4f..cf2c7844d 100644 --- a/xs/t/11_clipper.t +++ b/xs/t/11_clipper.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 5; +use Test::More tests => 11; my $square = [ # ccw [200, 100], @@ -88,4 +88,22 @@ my $expolygon = Slic3r::ExPolygon->new($square, $hole_in_square); is $result->[0]->area, $expolygon->area, 'diff_ex'; } +{ + my $polyline = Slic3r::Polyline->new([50,150], [300,150]); + { + my $result = Slic3r::Geometry::Clipper::intersection_pl([$polyline], [$square, $hole_in_square]); + is scalar(@$result), 2, 'intersection_pl - correct number of result lines'; + # results are in no particular order + is scalar(grep $_->length == 40, @$result), 2, 'intersection_pl - result lines have correct length'; + } + { + my $result = Slic3r::Geometry::Clipper::diff_pl([$polyline], [$square, $hole_in_square]); + is scalar(@$result), 3, 'diff_pl - correct number of result lines'; + # results are in no particular order + is scalar(grep $_->length == 50, @$result), 1, 'diff_pl - the left result line has correct length'; + is scalar(grep $_->length == 100, @$result), 1, 'diff_pl - two right result line has correct length'; + is scalar(grep $_->length == 20, @$result), 1, 'diff_pl - the central result line has correct length'; + } +} + __END__ diff --git a/xs/xsp/Clipper.xsp b/xs/xsp/Clipper.xsp index 2e63afbb9..129f83517 100644 --- a/xs/xsp/Clipper.xsp +++ b/xs/xsp/Clipper.xsp @@ -91,6 +91,15 @@ diff_ex(subject, clip, safety_offset = false) OUTPUT: RETVAL +Polylines +diff_pl(subject, clip) + Polylines subject + Polygons clip + CODE: + diff(subject, clip, RETVAL); + OUTPUT: + RETVAL + Polygons intersection(subject, clip, safety_offset = false) Polygons subject @@ -111,6 +120,15 @@ intersection_ex(subject, clip, safety_offset = false) OUTPUT: RETVAL +Polylines +intersection_pl(subject, clip) + Polylines subject + Polygons clip + CODE: + intersection(subject, clip, RETVAL); + OUTPUT: + RETVAL + ExPolygons xor_ex(subject, clip, safety_offset = false) Polygons subject diff --git a/xs/xsp/Line.xsp b/xs/xsp/Line.xsp index 57ff19a39..28b60e056 100644 --- a/xs/xsp/Line.xsp +++ b/xs/xsp/Line.xsp @@ -25,6 +25,8 @@ %code{% const char* CLASS = "Slic3r::Point"; RETVAL = THIS->midpoint(); %}; Point* point_at(double distance) %code{% const char* CLASS = "Slic3r::Point"; RETVAL = THIS->point_at(distance); %}; + Polyline* as_polyline() + %code{% const char* CLASS = "Slic3r::Polyline"; RETVAL = new Polyline(); RETVAL->points.push_back(THIS->a); RETVAL->points.push_back(THIS->b); %}; %{ Line* diff --git a/xs/xsp/PolylineCollection.xsp b/xs/xsp/PolylineCollection.xsp index 7256e83fc..d93dbc7b1 100644 --- a/xs/xsp/PolylineCollection.xsp +++ b/xs/xsp/PolylineCollection.xsp @@ -17,6 +17,8 @@ %code{% const char* CLASS = "Slic3r::Polyline::Collection"; RETVAL = THIS->chained_path_from(start_near, no_reverse); %}; int count() %code{% RETVAL = THIS->polylines.size(); %}; + Point* leftmost_point() + %code{% const char* CLASS = "Slic3r::Point"; RETVAL = THIS->leftmost_point(); %}; %{ PolylineCollection* diff --git a/xs/xsp/my.map b/xs/xsp/my.map index e0a4cb9d6..dbcdbe956 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -22,6 +22,7 @@ ClipperLib::PolyFillType T_UV Points T_ARRAYREF Lines T_ARRAYREF Polygons T_ARRAYREF +Polylines T_ARRAYREF ExPolygons T_ARRAYREF # we return these types whenever we want the items to be returned diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 2927404dd..d20d5789d 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -15,6 +15,7 @@ %typemap{Points}; %typemap{Lines}; %typemap{Polygons}; +%typemap{Polylines}; %typemap{ExPolygons}; %typemap{Polygons*}; %typemap{TriangleMeshPtrs};