From 5bfe19a8b9d10fea39532c458de79ddf9b2967a0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 9 Apr 2012 11:04:32 +0200 Subject: [PATCH] Huge speed Boost (pun intended). Also fixes a problem where infill was escaping perimeters sometimes (#305). --- Build.PL | 3 +- lib/Slic3r/ExPolygon.pm | 34 ++++++---------------- lib/Slic3r/Fill/Rectilinear.pm | 19 +++++++------ lib/Slic3r/Line.pm | 6 ++++ lib/Slic3r/Polyline.pm | 52 +++++++--------------------------- t/polyclip.t | 6 ++-- 6 files changed, 42 insertions(+), 78 deletions(-) diff --git a/Build.PL b/Build.PL index d4a265ede..961239562 100644 --- a/Build.PL +++ b/Build.PL @@ -2,11 +2,12 @@ use Module::Build; my $build = Module::Build->new( module_name => 'Slic3r', - dist_abstract => 'STL-to-GCODE translator', + dist_abstract => 'G-code generator for 3D printers', dist_author => 'Alessandro Ranellucci ', dist_version => '0.1', license => 'perl', requires => { + 'Boost::Geometry::Utils' => '0', 'File::Basename' => '0', 'Getopt::Long' => '0', 'Math::Clipper' => '1.05', diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index a7b636fca..1c014eb05 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -4,6 +4,7 @@ use warnings; # an ExPolygon is a polygon with holes +use Boost::Geometry::Utils; use Math::Geometry::Voronoi; use Slic3r::Geometry qw(X Y A B point_in_polygon same_line); use Slic3r::Geometry::Clipper qw(union_ex JT_MITER); @@ -53,6 +54,11 @@ sub clipper_expolygon { }; } +sub boost_polygon { + my $self = shift; + return Boost::Geometry::Utils::polygon(@$self); +} + sub offset { my $self = shift; my ($distance, $scale, $joinType, $miterLimit) = @_; @@ -131,32 +137,10 @@ sub clip_line { my $self = shift; my ($line) = @_; # line must be a Slic3r::Line object - my @intersections = grep $_, map $_->intersection($line, 1), map $_->lines, @$self; - my @dir = ( - $line->[B][X] <=> $line->[A][X], - $line->[B][Y] <=> $line->[A][Y], + return Boost::Geometry::Utils::polygon_linestring_intersection( + $self->boost_polygon, + $line->boost_linestring, ); - - @intersections = sort { - (($a->[X] <=> $b->[X]) == $dir[X]) && (($a->[Y] <=> $b->[Y]) == $dir[Y]) ? 1 : -1 - } @intersections, @$line; - - shift @intersections if $intersections[0]->coincides_with($intersections[1]); - pop @intersections if $intersections[-1]->coincides_with($intersections[-2]); - - shift @intersections - if !$self->encloses_point($intersections[0]) - && !$self->point_on_segment($intersections[0]); - - my @lines = (); - while (@intersections) { - # skip tangent points - my @points = splice @intersections, 0, 2; - next if !$points[1]; - next if $points[0]->coincides_with($points[1]); - push @lines, [ @points ]; - } - return [@lines]; } sub simplify { diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index 2d589a67d..5e71bf067 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -34,23 +34,26 @@ sub fill_surface { my $overlap_distance = scale $Slic3r::flow_width * 0.4; - my @paths = (); my $x = $bounding_box->[X1]; my $is_line_pattern = $self->isa('Slic3r::Fill::Line'); + my @vertical_lines = (); for (my $i = 0; $x <= $bounding_box->[X2] + scale epsilon; $i++) { my $vertical_line = Slic3r::Line->new([$x, $bounding_box->[Y2]], [$x, $bounding_box->[Y1]]); if ($is_line_pattern && $i % 2) { $vertical_line->[A][X] += $line_oscillation; $vertical_line->[B][X] -= $line_oscillation; } - my @clipped_lines = @{ $expolygon->clip_line($vertical_line) }; - for (@clipped_lines) { - $_->[0][Y] += $overlap_distance; - $_->[-1][Y] -= $overlap_distance; - } - push @paths, @clipped_lines; + push @vertical_lines, $vertical_line; $x += $distance_between_lines; } + my @paths = @{ Boost::Geometry::Utils::polygon_linestring_intersection( + $expolygon->boost_polygon, + Boost::Geometry::Utils::linestring(@vertical_lines), + ) }; + for (@paths) { + $_->[0][Y] += $overlap_distance; + $_->[-1][Y] -= $overlap_distance; + } # connect lines { @@ -75,7 +78,7 @@ sub fill_surface { # TODO: we should also check that both points are on a fill_boundary to avoid # connecting paths on the boundaries of internal regions if ($can_connect->(@distance, $paths[-1][-1], $path->points->[0]) - && $expolygon_off->encloses_line([ $paths[-1][-1], $path->points->[0] ])) { + && $expolygon_off->encloses_line(Slic3r::Line->new($paths[-1][-1], $path->points->[0]))) { push @{$paths[-1]}, @{$path->points}; next; } diff --git a/lib/Slic3r/Line.pm b/lib/Slic3r/Line.pm index d636f4203..806b096ca 100644 --- a/lib/Slic3r/Line.pm +++ b/lib/Slic3r/Line.pm @@ -2,6 +2,7 @@ package Slic3r::Line; use strict; use warnings; +use Boost::Geometry::Utils; use Slic3r::Geometry qw(A B X Y); sub new { @@ -31,6 +32,11 @@ sub coordinates { return ($self->a->coordinates, $self->b->coordinates); } +sub boost_linestring { + my $self = shift; + return Boost::Geometry::Utils::linestring($self); +} + sub coincides_with { my $self = shift; my ($line) = @_; diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 7c416e782..ce8abd241 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -40,6 +40,11 @@ sub lines { return @lines; } +sub boost_linestring { + my $self = shift; + return Boost::Geometry::Utils::linestring($self); +} + sub merge_continuous_lines { my $self = shift; @@ -95,47 +100,12 @@ sub clip_with_expolygon { my $self = shift; my ($expolygon) = @_; - #printf "Clipping polyline of %d points to expolygon of %d polygons and %d points\n", - # scalar(@$self), scalar(@$expolygon), scalar(map @$_, @$expolygon); - - my @polylines = (); - my $current_polyline = []; - foreach my $line ($self->lines) { - my ($first_line, @other_lines) = @{ $expolygon->clip_line($line) }; - next unless $first_line; - - if (!@$current_polyline) { - push @$current_polyline, @$first_line; - } elsif ($first_line->[A]->coincides_with($current_polyline->[-1])) { - push @$current_polyline, $first_line->[B]; - } else { - push @polylines, $current_polyline; - $current_polyline = [ @$first_line ]; - } - - foreach my $other_line (@other_lines) { - if (@$current_polyline) { - push @polylines, $current_polyline; - $current_polyline = []; - } - push @polylines, [ @$other_line ]; - } - } - if (@$current_polyline) { - push @polylines, $current_polyline; - } - - if (@polylines > 1 && same_point($polylines[-1][-1], $polylines[0][0])) { - if (scalar(@{$polylines[-1]}) == 2) { - unshift @{$polylines[0]}, $polylines[-1][0]; - pop @polylines; - } else { - push @{$polylines[-1]}, $polylines[0][-1]; - shift @polylines; - } - } - - return map Slic3r::Polyline->new($_), @polylines; + my $result = Boost::Geometry::Utils::polygon_linestring_intersection( + $expolygon->boost_polygon, + $self->boost_linestring, + ); + bless $_, 'Slic3r::Polyline' for @$result; + return @$result; } sub bounding_box { diff --git a/t/polyclip.t b/t/polyclip.t index 12352bf4e..285ef45be 100644 --- a/t/polyclip.t +++ b/t/polyclip.t @@ -140,12 +140,12 @@ is_deeply $intersection, [ [12, 12], [18, 16] ], 'internal lines are preserved'; is is_counter_clockwise($small_circle), 0, "hole is clockwise"; my $expolygon = Slic3r::ExPolygon->new($large_circle, $small_circle); - $line = Slic3r::Line->new([152.741724,288.086671142818], [152.741724,34.166466971035]); + $line = Slic3r::Line->new([152.742,288.086671142818], [152.742,34.166466971035]); my $intersections = $expolygon->clip_line($line); is_deeply $intersections, [ - [ [152.741724, 288.086671142818], [152.741724, 215.178806915206], ], - [ [152.741724, 108.087543109156], [152.741724, 35.166466971035] ], + [ [152.742, 288.087], [152.742, 215.179], ], + [ [152.742, 108.088], [152.742, 35.1665] ], ], 'line is clipped to square with hole'; }