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
|
||||
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.
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
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 ($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; #=
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user