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
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.

View File

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

View File

@ -32,6 +32,12 @@ has 'surfaces' => (
default => sub { [] },
);
has 'perimeters' => (
is => 'rw',
isa => 'ArrayRef[Slic3r::Polyline]',
default => sub { [] },
);
sub z {
my $self = shift;
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;
push @{ $self->surfaces }, $surface;
push @{ $self->surfaces }, $new_surface;
$self->remove_surface($_) for ($surface, $neighbor_surface);
}

View File

@ -1,6 +1,7 @@
package Slic3r::Line;
use Moose;
use Moose::Util::TypeConstraints;
use Scalar::Util qw(weaken);
has 'a' => (
@ -21,6 +22,11 @@ has 'polyline' => (
weak_ref => 1,
);
has 'solid_side' => (
is => 'rw',
isa => enum([qw(left right)]), # going from a to b
);
sub BUILD {
my $self = shift;
@ -45,6 +51,12 @@ sub coincides_with {
|| ($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 {
my $self = shift;
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 ($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; #=
}

View File

@ -26,7 +26,7 @@ sub BUILD {
sub id {
my $self = shift;
return join '-', map($_->id, $self->points);
return join '-', map($_->id, $self->ordered_points(1));
}
sub new_from_points {
@ -61,4 +61,44 @@ sub 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;

View File

@ -58,4 +58,15 @@ sub encloses_point {
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;

View File

@ -26,4 +26,16 @@ sub layer {
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;

View File

@ -1,6 +1,7 @@
package Slic3r::Surface;
use Moose;
use Math::Geometry::Planar;
use Moose::Util::TypeConstraints;
has 'contour' => (
@ -55,4 +56,12 @@ sub encloses_point {
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;

View File

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