Extrusion of perimeters
This commit is contained in:
parent
55a523e1fa
commit
febd655e22
@ -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.
|
||||||
|
|
||||||
|
@ -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;
|
||||||
@ -12,7 +13,9 @@ use Slic3r::Print;
|
|||||||
use Slic3r::STL;
|
use Slic3r::STL;
|
||||||
use Slic3r::Surface;
|
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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
104
lib/Slic3r/Perimeter.pm
Normal 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;
|
@ -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; #=
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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__
|
||||||
|
Loading…
Reference in New Issue
Block a user