Optimization of travel paths for perimeters

This commit is contained in:
Alessandro Ranellucci 2011-09-25 23:15:45 +02:00
parent 03341f3485
commit 0cd10441a1
7 changed files with 113 additions and 46 deletions

View File

@ -8,6 +8,7 @@ sub debugf {
printf @_ if $debug;
}
use Slic3r::ExtrusionLoop;
use Slic3r::ExtrusionPath;
use Slic3r::Fill;
use Slic3r::Geometry;

View File

@ -0,0 +1,31 @@
package Slic3r::ExtrusionLoop;
use Moo;
use XXX;
extends 'Slic3r::Polyline::Closed';
sub split_at {
my $self = shift;
my ($point) = @_;
$point = Slic3r::Point->cast($point);
# find index of point
my $i = -1;
for (my $n = 0; $n <= $#{$self->points}; $n++) {
if ($point->id eq $self->points->[$n]->id) {
$i = $n;
last;
}
}
die "Point not found" if $i == -1;
my @new_points = ();
push @new_points, @{$self->points}[$i .. $#{$self->points}];
push @new_points, @{$self->points}[0 .. $i];
return Slic3r::ExtrusionPath->new(points => [@new_points]);
}
1;

View File

@ -3,4 +3,23 @@ use Moo;
extends 'Slic3r::Polyline';
sub clip_end {
my $self = shift;
my ($distance) = @_;
while ($distance > 0) {
my $last_point = pop @{$self->points};
my $last_segment_length = $last_point->distance_to($self->points->[-1]);
if ($last_segment_length <= $distance) {
$distance -= $last_segment_length;
next;
}
my $new_point = Slic3r::Geometry::point_along_segment($last_point->p, $self->points->[-1]->p, $distance);
push @{$self->points}, Slic3r::Point->cast($new_point);
$distance = 0;
}
}
1;

View File

@ -115,4 +115,33 @@ sub polygon_lines {
return @lines;
}
sub nearest_point {
my ($point, $points) = @_;
my ($nearest_point, $distance);
foreach my $p (@$points) {
my $d = distance_between_points($point, $p);
if (!defined $distance || $d < $distance) {
$nearest_point = $p;
$distance = $d;
}
}
return $nearest_point;
}
sub point_along_segment {
my ($p1, $p2, $distance) = @_;
my $point = [ @$p1 ];
my $line_length = sqrt( (($p2->[X] - $p1->[X])**2) + (($p2->[Y] - $p1->[Y])**2) );
for (X, Y) {
if ($p1->[$_] != $p2->[$_]) {
$point->[$_] = $p1->[$_] + ($p2->[$_] - $p1->[$_]) * $distance / $line_length;
}
}
return $point;
}
1;

View File

@ -58,40 +58,18 @@ sub make_perimeter {
), $self->offset_polygon($perimeters[-1]),
}
# generate paths for holes
# generate paths for holes:
# we start from innermost loops (that is, external ones), do them
# for all holes, than go on with inner loop and do that for all
# holes and so on
foreach my $hole (map @$_, values %holes) {
my @points = @$hole;
push @points, [ @{$points[0]} ];
# to avoid blobs, the first point is replaced by the point of
# the segment which is $Slic3r::flow_width / $Slic3r::resolution
# away from it to avoid the extruder to get two times there
$points[0] = $self->_get_point_along_line($points[0], $points[1],
$Slic3r::flow_width / $Slic3r::resolution);
push @{ $layer->perimeters }, Slic3r::ExtrusionPath->cast([@points]);
}
# generate paths for contours
# holes and so on;
# then we generate paths for contours:
# this time we do something different: we do contour loops for one
# shape (that is, one original surface) at a time: we start from the
# innermost loop (that is, internal one), then without interrupting
# our path we go onto the outer loop and continue; this should ensure
# good surface quality
foreach my $polylines (values %contours) {
my @path_points = ();
foreach my $p (map $self->_mgp_from_points_ref($_), @$polylines) {
my $points = $p->points;
# to avoid blobs, the first point is replaced by the point of
# the segment which is $Slic3r::flow_width / $Slic3r::resolution
# away from it to avoid the extruder to get two times there
push @$points, [ @{$points->[0]} ];
$points->[0] = $self->_get_point_along_line($points->[0], $points->[1],
$Slic3r::flow_width / $Slic3r::resolution);
push @path_points, @$points;
}
push @{ $layer->perimeters }, Slic3r::ExtrusionPath->cast([ reverse @path_points ]);
foreach my $p (map @$_, values %holes, values %contours) {
push @{ $layer->perimeters }, Slic3r::ExtrusionLoop->cast($p);
}
# generate skirt on bottom layer
@ -158,20 +136,4 @@ sub _mgp_from_polygons_ref {
return $p;
}
sub _get_point_along_line {
my $self = shift;
my ($p1, $p2, $distance) = @_;
my $point = [ @$p1 ];
my $line_length = sqrt( (($p2->[X] - $p1->[X])**2) + (($p2->[Y] - $p1->[Y])**2) );
for (X, Y) {
if ($p1->[$_] != $p2->[$_]) {
$point->[$_] = $p1->[$_] + ($p2->[$_] - $p1->[$_]) * $distance / $line_length;
}
}
return $point;
}
1;

View File

@ -18,11 +18,11 @@ sub id {
}
sub cast {
my $self = shift;
my $class = shift;
my ($points) = @_;
@$points = map { ref $_ eq 'ARRAY' ? Slic3r::Point->cast($_) : $_ } @$points;
return __PACKAGE__->new(points => $points);
return $class->new(points => $points);
}
sub lines {
@ -82,4 +82,15 @@ sub make_clockwise {
$self->reverse_points if $self->is_counter_clockwise;
}
sub nearest_point_to {
my $self = shift;
my ($point) = @_;
# get point as arrayref
$point = ref $point eq 'ARRAY' ? $point : $point->p;
$point = Slic3r::Geometry::nearest_point($point, $self->p);
return Slic3r::Point->cast($point);
}
1;

View File

@ -183,6 +183,7 @@ sub export_gcode {
# make up a subroutine to generate G1 commands
my $extrusion_distance = 0;
my $last_pos; # on XY plane
my $G1 = sub {
my ($point, $z, $e, $comment) = @_;
printf $fh "G1";
@ -191,6 +192,7 @@ sub export_gcode {
printf $fh " X%.${dec}f Y%.${dec}f",
($point->x * $Slic3r::resolution) + $shift[X],
($point->y * $Slic3r::resolution) + $shift[Y]; #**
$last_pos = $point->p;
}
if ($z) {
printf $fh " Z%.${dec}f", $z;
@ -269,7 +271,19 @@ sub export_gcode {
$Extrude->($_, 'skirt') for @{ $layer->skirts };
# extrude perimeters
$Extrude->($_, 'perimeter') for @{ $layer->perimeters };
for my $loop (@{ $layer->perimeters }) {
# find the point of the loop that is closest to the current extruder position
my $start_at = $last_pos ? $loop->nearest_point_to($last_pos) : $loop->points->[0];
# split the loop at the starting point and make a path
my $extrusion_path = $loop->split_at($start_at);
# clip the path to avoid the extruder to get exactly on the first point of the loop
$extrusion_path->clip_end($Slic3r::flow_width / $Slic3r::resolution);
# extrude along the path
$Extrude->($extrusion_path, 'perimeter')
}
# extrude fills
$Extrude->($_, 'fill') for @{ $layer->fills };