Extrusion of perimeters

This commit is contained in:
Alessandro Ranellucci 2011-09-02 21:10:20 +02:00
parent 55a523e1fa
commit febd655e22
11 changed files with 206 additions and 5 deletions

View File

@ -34,7 +34,7 @@ layers and representing internally the following features:
This kind of abstraction will allow to implement particular logic and allow the This kind of abstraction will allow to implement particular logic and allow the
user to specify custom options. user to specify custom options.
I need to implement algorithms to produce perimeter outlines and surface fill. I need to implement algorithms to produce surface fill, while perimeter is done.
Future goals include support material, options to control bridges, skirt, cool. Future goals include support material, options to control bridges, skirt, cool.

View File

@ -5,6 +5,7 @@ use warnings;
use Slic3r::Layer; use Slic3r::Layer;
use Slic3r::Line; use Slic3r::Line;
use Slic3r::Perimeter;
use Slic3r::Point; use Slic3r::Point;
use Slic3r::Polyline; use Slic3r::Polyline;
use Slic3r::Polyline::Closed; use Slic3r::Polyline::Closed;
@ -14,5 +15,7 @@ use Slic3r::Surface;
our $layer_height = 0.4; our $layer_height = 0.4;
our $resolution = 0.1; our $resolution = 0.1;
our $perimeter_offsets = 3;
our $flow_width = 0.4; # make sure this is > $resolution
1; 1;

View File

@ -32,6 +32,12 @@ has 'surfaces' => (
default => sub { [] }, default => sub { [] },
); );
has 'perimeters' => (
is => 'rw',
isa => 'ArrayRef[Slic3r::Polyline]',
default => sub { [] },
);
sub z { sub z {
my $self = shift; my $self = shift;
return $self->id * $Slic3r::layer_height / $Slic3r::resolution; return $self->id * $Slic3r::layer_height / $Slic3r::resolution;
@ -313,7 +319,7 @@ sub merge_contiguous_surfaces {
); );
printf " merging into new surface %s\n", $new_surface->id; printf " merging into new surface %s\n", $new_surface->id;
push @{ $self->surfaces }, $surface; push @{ $self->surfaces }, $new_surface;
$self->remove_surface($_) for ($surface, $neighbor_surface); $self->remove_surface($_) for ($surface, $neighbor_surface);
} }

View File

@ -1,6 +1,7 @@
package Slic3r::Line; package Slic3r::Line;
use Moose; use Moose;
use Moose::Util::TypeConstraints;
use Scalar::Util qw(weaken); use Scalar::Util qw(weaken);
has 'a' => ( has 'a' => (
@ -21,6 +22,11 @@ has 'polyline' => (
weak_ref => 1, weak_ref => 1,
); );
has 'solid_side' => (
is => 'rw',
isa => enum([qw(left right)]), # going from a to b
);
sub BUILD { sub BUILD {
my $self = shift; my $self = shift;
@ -45,6 +51,12 @@ sub coincides_with {
|| ($self->a->coincides_with($line->b) && $self->b->coincides_with($line->a)); || ($self->a->coincides_with($line->b) && $self->b->coincides_with($line->a));
} }
sub has_endpoint {
my $self = shift;
my ($point) = @_;#printf " %s has endpoint %s: %s\n", $self->id, $point->id, ($point eq $self->a || $point eq $self->b);
return $point->coincides_with($self->a) || $point->coincides_with($self->b);
}
sub slope { sub slope {
my $self = shift; my $self = shift;
return undef if $self->b->x == $self->a->x; # line is vertical return undef if $self->b->x == $self->a->x; # line is vertical

104
lib/Slic3r/Perimeter.pm Normal file
View File

@ -0,0 +1,104 @@
package Slic3r::Perimeter;
use Moose;
use Math::Geometry::Planar;
*Math::Geometry::Planar::OffsetPolygon = *Math::Geometry::Planar::Offset::OffsetPolygon;
sub make_perimeter {
my $self = shift;
my ($layer) = @_;
printf "Making perimeter for layer %d:\n", $layer->id;
# skip entire section if no perimeters are requested
return unless $Slic3r::perimeter_offsets > 0;
my (@perimeters, %contours, %holes) = ();
foreach my $surface (@{ $layer->surfaces }) {
$contours{$surface} = [];
$holes{$surface} = [];
# first perimeter
{
my $polygon = $surface->mgp_polygon;
my ($contour_p, @holes_p) = @{ $polygon->polygons };
push @{ $contours{$surface} }, $contour_p;
push @{ $holes{$surface} }, @holes_p;
push @perimeters, $polygon;
}
# create other offsets
for (my $loop = 1; $loop < $Slic3r::perimeter_offsets; $loop++) {
my ($contour_p, @holes_p) = map $self->_mgp_from_points_ref($_), @{ $perimeters[-1]->polygons };
# generate offsets
my $contour_offsets = $contour_p->offset_polygon($Slic3r::flow_width / $Slic3r::resolution);
my @hole_offsets = map @$_, map $_->offset_polygon(- $Slic3r::flow_width / $Slic3r::resolution), @holes_p;
# now we subtract perimeter offsets from the contour offset polygon
# this will generate a single polygon with correct holes and also
# will take care of collisions between contour offset and holes
foreach my $contour_points (@$contour_offsets) {
my $tmp = $self->_mgp_from_points_ref($contour_points)->convert2gpc;
foreach my $hole_points (@hole_offsets) {
$hole_points = $self->_mgp_from_points_ref($hole_points)->convert2gpc;
$tmp = GpcClip('DIFFERENCE', $tmp, $hole_points);
}
my ($result) = Gpc2Polygons($tmp);
# now we've got $result, which is a Math::Geometry::Planar
# representing the inner surface including hole perimeters
my $result_polylines = $result->polygons;
($contour_p, @holes_p) = @$result_polylines;
push @{ $contours{$surface} }, $contour_p;
push @{ $holes{$surface} }, @holes_p;
push @perimeters, $result;
}
}
}
# 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 $p (map @$_, values %holes) {
push @{ $layer->perimeters }, Slic3r::Polyline->new_from_points(@{ $p->points });
}
# 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;
# TODO: the initial $points->[0] should be 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 @path_points, @$points, $points->[0];
}
push @{ $layer->perimeters }, Slic3r::Polyline->new_from_points(reverse @path_points);
}
}
sub _mgp_from_points_ref {
my $self = shift;
my ($points) = @_;
my $p = Math::Geometry::Planar->new;
$p->points($points);
return $p;
}
sub _mgp_from_polygons_ref {
my $self = shift;
my ($polygons) = @_;
my $p = Math::Geometry::Planar->new;
$p->polygons($polygons);
return $p;
}
1;

View File

@ -35,6 +35,8 @@ sub coincides_with {
my $self = shift; my $self = shift;
my ($point) = @_; my ($point) = @_;
$point = Slic3r::Point->new(x => $point->[0], y => $point->[1]) #==
if ref $point eq 'ARRAY';
return $self->x == $point->x && $self->y == $point->y; #= return $self->x == $point->x && $self->y == $point->y; #=
} }

View File

@ -26,7 +26,7 @@ sub BUILD {
sub id { sub id {
my $self = shift; my $self = shift;
return join '-', map($_->id, $self->points); return join '-', map($_->id, $self->ordered_points(1));
} }
sub new_from_points { sub new_from_points {
@ -61,4 +61,44 @@ sub points {
return values %points; return values %points;
} }
sub ordered_points {
my $self = shift;
my ($as_objects) = @_;
my $points = [];
#printf "\n\n==> Number of lines: %d\n", scalar @{ $self->lines };
my @lines = @{ $self->lines };
while (@lines && @$points < @{ $self->lines }) {
#printf "\nNumber of points: %d\n", scalar @{ $points };
my @temp = @lines;
@lines = ();
foreach my $line (@temp) {
#printf "Line: %s\n", $line->id;
my $point;
if (!@$points) {
# make sure we start from a point not connected to another segment if any
push @$points, sort { @{$a->lines} <=> @{$b->lines} } $line->points;
next;
} elsif ($line->has_endpoint($points->[-1])) {
$point = +(grep $points->[-1] ne $_, $line->points)[0];
}
if (!$point) {
#printf " no point found, retrying\n";
push @lines, $line;
next;
}
#printf " adding point %s\n", $point->id;
push @$points, $point;
}
}
pop @$points
if $self->isa('Slic3r::Polyline::Closed') && $points->[0]->coincides_with($points->[-1]);
return @$points if $as_objects;
$points = [ map [ $_->x, $_->y ], @$points ]; #]
return $points;
}
1; 1;

View File

@ -58,4 +58,15 @@ sub encloses_point {
return $side; return $side;
} }
sub mgp_polygon {
my $self = shift;
# we need a list of ordered points
my $points = $self->ordered_points;
my $p = Math::Geometry::Planar->new;
$p->points($points);
return $p;
}
1; 1;

View File

@ -26,4 +26,16 @@ sub layer {
return $self->layers->[$layer_id]; return $self->layers->[$layer_id];
} }
sub extrude_perimeters {
my $self = shift;
my $perimeter_extruder = Slic3r::Perimeter->new;
foreach my $layer (@{ $self->layers }) {
$perimeter_extruder->make_perimeter($layer);
printf " generated paths: %s\n",
join ' ', map $_->id, @{ $layer->perimeters };
}
}
1; 1;

View File

@ -1,6 +1,7 @@
package Slic3r::Surface; package Slic3r::Surface;
use Moose; use Moose;
use Math::Geometry::Planar;
use Moose::Util::TypeConstraints; use Moose::Util::TypeConstraints;
has 'contour' => ( has 'contour' => (
@ -55,4 +56,12 @@ sub encloses_point {
return 1; return 1;
} }
sub mgp_polygon {
my $self = shift;
my $p = Math::Geometry::Planar->new;
$p->polygons([ map $_->points, $self->contour->mgp_polygon, map($_->mgp_polygon, @{ $self->holes }) ]);
return $p;
}
1; 1;

View File

@ -14,6 +14,8 @@ use XXX;
my $stl_parser = Slic3r::STL->new; my $stl_parser = Slic3r::STL->new;
my $print = $stl_parser->parse_file("testcube20mm.stl"); my $print = $stl_parser->parse_file("testcube20mm.stl");
$print->extrude_perimeters;
#XXX $print; #XXX $print;
__END__ __END__