Browse Source

Huge speed Boost (pun intended). Also fixes a problem where infill was escaping perimeters sometimes (#305).

Alessandro Ranellucci 13 years ago
parent
commit
5bfe19a8b9
6 changed files with 42 additions and 78 deletions
  1. 2 1
      Build.PL
  2. 9 25
      lib/Slic3r/ExPolygon.pm
  3. 11 8
      lib/Slic3r/Fill/Rectilinear.pm
  4. 6 0
      lib/Slic3r/Line.pm
  5. 11 41
      lib/Slic3r/Polyline.pm
  6. 3 3
      t/polyclip.t

+ 2 - 1
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 <aar@cpan.org>',
     dist_version    => '0.1',
     license         => 'perl',
     requires        => {
+        'Boost::Geometry::Utils'    => '0',
         'File::Basename'            => '0',
         'Getopt::Long'              => '0',
         'Math::Clipper'             => '1.05',

+ 9 - 25
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 {

+ 11 - 8
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;
                 }

+ 6 - 0
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) = @_;

+ 11 - 41
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 {

+ 3 - 3
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';
 }