mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-12 18:49:05 +08:00
Incomplete work - still wrong order
This commit is contained in:
parent
df62c25c8f
commit
67b24efd49
@ -146,7 +146,7 @@ sub simplify_polygons {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub traverse_pt {
|
sub traverse_pt {
|
||||||
my ($polynodes) = @_;
|
my ($polynodes, $min_depth, $max_depth) = @_;
|
||||||
|
|
||||||
# use a nearest neighbor search to order these children
|
# use a nearest neighbor search to order these children
|
||||||
# TODO: supply second argument to chained_path_items() too?
|
# TODO: supply second argument to chained_path_items() too?
|
||||||
@ -156,8 +156,14 @@ sub traverse_pt {
|
|||||||
|
|
||||||
my @polygons = ();
|
my @polygons = ();
|
||||||
foreach my $polynode (@$polynodes) {
|
foreach my $polynode (@$polynodes) {
|
||||||
push @polygons, traverse_pt($polynode->{children});
|
# traverse the next depth
|
||||||
|
push @polygons, traverse_pt(
|
||||||
|
$polynode->{children},
|
||||||
|
(defined $min_depth ? $min_depth-1 : undef),
|
||||||
|
(defined $max_depth ? $max_depth-1 : undef),
|
||||||
|
) if !defined $max_depth || $max_depth >= 1;
|
||||||
push @polygons, $polynode->{outer} // [ reverse @{$polynode->{hole}} ]
|
push @polygons, $polynode->{outer} // [ reverse @{$polynode->{hole}} ]
|
||||||
|
if !defined $min_depth || $min_depth <= 0;
|
||||||
}
|
}
|
||||||
return @polygons;
|
return @polygons;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use List::Util qw(sum first);
|
|||||||
use Slic3r::ExtrusionPath ':roles';
|
use Slic3r::ExtrusionPath ':roles';
|
||||||
use Slic3r::Geometry qw(PI X1 X2 Y1 Y2 A B scale chained_path_items points_coincide);
|
use Slic3r::Geometry qw(PI X1 X2 Y1 Y2 A B scale chained_path_items points_coincide);
|
||||||
use Slic3r::Geometry::Clipper qw(safety_offset union_ex diff_ex intersection_ex
|
use Slic3r::Geometry::Clipper qw(safety_offset union_ex diff_ex intersection_ex
|
||||||
offset offset2_ex);
|
offset offset2_ex PFT_EVENODD union_pt traverse_pt);
|
||||||
use Slic3r::Surface ':types';
|
use Slic3r::Surface ':types';
|
||||||
|
|
||||||
has 'layer' => (
|
has 'layer' => (
|
||||||
@ -161,37 +161,22 @@ sub make_perimeters {
|
|||||||
|
|
||||||
my $perimeter_spacing = $self->perimeter_flow->scaled_spacing;
|
my $perimeter_spacing = $self->perimeter_flow->scaled_spacing;
|
||||||
my $infill_spacing = $self->solid_infill_flow->scaled_spacing;
|
my $infill_spacing = $self->solid_infill_flow->scaled_spacing;
|
||||||
my $gap_area_threshold = $self->perimeter_flow->scaled_width ** 2;
|
my $gap_area_threshold = $self->perimeter_flow->scaled_width ** 2;
|
||||||
|
|
||||||
# this array will hold one arrayref per original surface (island);
|
|
||||||
# each item of this arrayref is an arrayref representing a depth (from outer
|
|
||||||
# perimeters to inner); each item of this arrayref is an ExPolygon:
|
|
||||||
# @perimeters = (
|
|
||||||
# [ # first island
|
|
||||||
# [ Slic3r::ExPolygon, Slic3r::ExPolygon... ], #depth 0: outer loop
|
|
||||||
# [ Slic3r::ExPolygon, Slic3r::ExPolygon... ], #depth 1: inner loop
|
|
||||||
# ],
|
|
||||||
# [ # second island
|
|
||||||
# ...
|
|
||||||
# ]
|
|
||||||
# )
|
|
||||||
my @perimeters = (); # one item per depth; each item
|
|
||||||
|
|
||||||
# organize islands using a nearest-neighbor search
|
|
||||||
my @surfaces = @{chained_path_items([
|
|
||||||
map [ $_->contour->[0], $_ ], @{$self->slices},
|
|
||||||
])};
|
|
||||||
|
|
||||||
$self->perimeters([]);
|
$self->perimeters([]);
|
||||||
$self->fill_surfaces([]);
|
$self->fill_surfaces([]);
|
||||||
$self->thin_fills([]);
|
$self->thin_fills([]);
|
||||||
|
|
||||||
|
my @contours = (); # array of Polygons with ccw orientation
|
||||||
|
my @holes = (); # array of Polygons with cw orientation
|
||||||
|
my @gaps = (); # array of ExPolygons
|
||||||
|
|
||||||
# for each island:
|
# for each island:
|
||||||
foreach my $surface (@surfaces) {
|
foreach my $surface (@{$self->slices}) {
|
||||||
my @last_offsets = ($surface->expolygon);
|
|
||||||
|
|
||||||
# experimental hole compensation (see ArcCompensation in the RepRap wiki)
|
# experimental hole compensation (see ArcCompensation in the RepRap wiki)
|
||||||
if (0) {
|
if (0) {
|
||||||
|
my @last_offsets = (); # dumb instantiation
|
||||||
foreach my $hole ($last_offsets[0]->holes) {
|
foreach my $hole ($last_offsets[0]->holes) {
|
||||||
my $circumference = abs($hole->length);
|
my $circumference = abs($hole->length);
|
||||||
next unless $circumference <= &Slic3r::SMALL_PERIMETER_LENGTH;
|
next unless $circumference <= &Slic3r::SMALL_PERIMETER_LENGTH;
|
||||||
@ -213,42 +198,40 @@ sub make_perimeters {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my @gaps = ();
|
# detect how many perimeters must be generated for this island
|
||||||
|
|
||||||
# generate perimeters inwards (loop 0 is the external one)
|
|
||||||
my $loop_number = $Slic3r::Config->perimeters + ($surface->extra_perimeters || 0);
|
my $loop_number = $Slic3r::Config->perimeters + ($surface->extra_perimeters || 0);
|
||||||
push @perimeters, [] if $loop_number > 0;
|
|
||||||
|
|
||||||
# do one more loop (<= instead of <) so that we can detect gaps even after the desired
|
# generate loops
|
||||||
# number of perimeters has been generated
|
# (one more than necessary so that we can detect gaps even after the desired
|
||||||
for (my $loop = 0; $loop <= $loop_number; $loop++) {
|
# number of perimeters has been generated)
|
||||||
my $spacing = $perimeter_spacing;
|
my @last = @{$surface->expolygon};
|
||||||
$spacing /= 2 if $loop == 0;
|
for my $i (0 .. $loop_number) {
|
||||||
|
# external loop only needs half inset distance
|
||||||
|
my $spacing = ($i == 0)
|
||||||
|
? $perimeter_spacing / 2
|
||||||
|
: $perimeter_spacing;
|
||||||
|
|
||||||
# offsetting a polygon can result in one or many offset polygons
|
my @offsets = offset2_ex(\@last, -1.5*$spacing, +0.5*$spacing);
|
||||||
my @new_offsets = offset2_ex([ map @$_, @last_offsets ], -1.5*$spacing, +0.5*$spacing);
|
my @contours_offsets = map $_->contour, @offsets;
|
||||||
|
my @holes_offsets = map $_->holes, @offsets;
|
||||||
|
@offsets = (@contours_offsets, @holes_offsets); # turn @offsets from ExPolygons to Polygons
|
||||||
|
|
||||||
# where the above check collapses the expolygon, then there's no room for an inner loop
|
# where offset2() collapses the expolygon, then there's no room for an inner loop
|
||||||
# and we can extract the gap for later processing
|
# and we can extract the gap for later processing
|
||||||
{
|
{
|
||||||
my $diff = diff_ex(
|
my $diff = diff_ex(
|
||||||
[ offset([ map @$_, @last_offsets ], -0.5*$spacing) ],
|
[ offset(\@last, -0.5*$spacing) ],
|
||||||
# +2 on the offset here makes sure that Clipper float truncation
|
# +2 on the offset here makes sure that Clipper float truncation
|
||||||
# won't shrink the clip polygon to be smaller than intended.
|
# won't shrink the clip polygon to be smaller than intended.
|
||||||
[ offset([ map @$_, @new_offsets ], +0.5*$spacing + 2) ],
|
[ offset(\@offsets, +0.5*$spacing + 2) ],
|
||||||
);
|
);
|
||||||
push @gaps, grep $_->area >= $gap_area_threshold, @$diff;
|
push @gaps, grep $_->area >= $gap_area_threshold, @$diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
last if !@new_offsets || $loop == $loop_number;
|
last if !@offsets || $i == $loop_number;
|
||||||
@last_offsets = @new_offsets;
|
push @contours, @contours_offsets;
|
||||||
|
push @holes, @holes_offsets;
|
||||||
# sort loops before storing them
|
@last = @offsets;
|
||||||
@last_offsets = @{chained_path_items([
|
|
||||||
map [ $_->contour->[0], $_ ], @last_offsets,
|
|
||||||
])};
|
|
||||||
|
|
||||||
push @{ $perimeters[-1] }, [@last_offsets];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# create one more offset to be used as boundary for fill
|
# create one more offset to be used as boundary for fill
|
||||||
@ -259,7 +242,7 @@ sub make_perimeters {
|
|||||||
push @{ $self->fill_surfaces },
|
push @{ $self->fill_surfaces },
|
||||||
map $_->simplify(&Slic3r::SCALED_RESOLUTION),
|
map $_->simplify(&Slic3r::SCALED_RESOLUTION),
|
||||||
offset2_ex(
|
offset2_ex(
|
||||||
[ map @$_, @last_offsets ],
|
\@last,
|
||||||
-($perimeter_spacing/2 + $infill_spacing),
|
-($perimeter_spacing/2 + $infill_spacing),
|
||||||
+$infill_spacing,
|
+$infill_spacing,
|
||||||
);
|
);
|
||||||
@ -344,99 +327,43 @@ sub make_perimeters {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# process one island (original surface) at time
|
# TODO: can these be removed?
|
||||||
# islands are already sorted with a nearest-neighbor search
|
@contours = grep $_->is_printable($self->perimeter_flow->scaled_width), @contours;
|
||||||
foreach my $island (@perimeters) {
|
@holes = grep $_->is_printable($self->perimeter_flow->scaled_width), @holes;
|
||||||
# do holes starting from innermost one
|
|
||||||
my @holes = ();
|
|
||||||
my %is_external = ();
|
|
||||||
|
|
||||||
# each item of @$island contains the expolygons having the same depth;
|
|
||||||
# for each depth we build an arrayref containing all the holes
|
|
||||||
my @hole_depths = map [ map $_->holes, @$_ ], @$island;
|
|
||||||
|
|
||||||
# organize the outermost hole loops using a nearest-neighbor search
|
|
||||||
@{$hole_depths[0]} = @{chained_path_items([
|
|
||||||
map [ $_->[0], $_ ], @{$hole_depths[0]},
|
|
||||||
])};
|
|
||||||
|
|
||||||
# loop while we have spare holes
|
|
||||||
CYCLE: while (map @$_, @hole_depths) {
|
|
||||||
# remove first depth container if it contains no holes anymore
|
|
||||||
shift @hole_depths while !@{$hole_depths[0]};
|
|
||||||
|
|
||||||
# take first available hole
|
|
||||||
push @holes, shift @{$hole_depths[0]};
|
|
||||||
$is_external{$#holes} = 1;
|
|
||||||
|
|
||||||
my $current_depth = 0;
|
|
||||||
while (1) {
|
|
||||||
$current_depth++;
|
|
||||||
|
|
||||||
# look for the hole containing this one if any
|
|
||||||
next CYCLE if !$hole_depths[$current_depth];
|
|
||||||
my $parent_hole;
|
|
||||||
for (@{$hole_depths[$current_depth]}) {
|
|
||||||
if ($_->encloses_point($holes[-1]->[0])) {
|
|
||||||
$parent_hole = $_;
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
next CYCLE if !$parent_hole;
|
|
||||||
|
|
||||||
# look for other holes contained in such parent
|
|
||||||
for (@{$hole_depths[$current_depth-1]}) {
|
|
||||||
if ($parent_hole->encloses_point($_->[0])) {
|
|
||||||
# we have a sibling, so let's move onto next iteration
|
|
||||||
next CYCLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
push @holes, $parent_hole;
|
|
||||||
@{$hole_depths[$current_depth]} = grep $_ ne $parent_hole, @{$hole_depths[$current_depth]};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# first do holes
|
|
||||||
$self->_add_perimeter($holes[$_], $is_external{$_} ? EXTR_ROLE_EXTERNAL_PERIMETER : undef)
|
|
||||||
for reverse 0 .. $#holes;
|
|
||||||
|
|
||||||
# then do contours according to the user settings
|
|
||||||
my @contour_order = 0 .. $#$island;
|
|
||||||
@contour_order = reverse @contour_order if !$Slic3r::Config->external_perimeters_first;
|
|
||||||
for my $depth (@contour_order) {
|
|
||||||
my $role = $depth == $#$island ? EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER
|
|
||||||
: $depth == 0 ? EXTR_ROLE_EXTERNAL_PERIMETER
|
|
||||||
: EXTR_ROLE_PERIMETER;
|
|
||||||
$self->_add_perimeter($_, $role) for map $_->contour, @{$island->[$depth]};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# if brim will be printed, reverse the order of perimeters so that
|
# find nesting hierarchies separately for contours and holes
|
||||||
# we continue inwards after having finished the brim
|
my $contours_pt = union_pt(\@contours, PFT_EVENODD);
|
||||||
if ($self->layer->id == 0 && $Slic3r::Config->brim_width > 0) {
|
my $holes_pt = union_pt(\@holes, PFT_EVENODD);
|
||||||
@{$self->perimeters} = reverse @{$self->perimeters};
|
|
||||||
}
|
|
||||||
|
|
||||||
# add thin walls as perimeters
|
# find external perimeters
|
||||||
push @{ $self->perimeters }, Slic3r::ExtrusionPath::Collection->new(paths => [
|
my $other_contours_pt = [ ];
|
||||||
map {
|
|
||||||
Slic3r::ExtrusionPath->pack(
|
# external contours are root items of $contours_pt
|
||||||
polyline => ($_->isa('Slic3r::Polygon') ? $_->split_at_first_point : $_),
|
# internal contours are the ones next to external
|
||||||
role => EXTR_ROLE_EXTERNAL_PERIMETER,
|
my @external_contours = map $self->_perimeter($_, EXTR_ROLE_EXTERNAL_PERIMETER), traverse_pt($contours_pt, 0, 0);
|
||||||
flow_spacing => $self->perimeter_flow->spacing,
|
my @internal_contours = map $self->_perimeter($_, EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER), traverse_pt($contours_pt, 1, 1);
|
||||||
);
|
my @other_contours = map $self->_perimeter($_), traverse_pt($contours_pt, 2);
|
||||||
} @{ $self->thin_walls }
|
my @external_holes = map $self->_perimeter($_, EXTR_ROLE_EXTERNAL_PERIMETER), traverse_pt($holes_pt, 0, 0);
|
||||||
])->chained_path;
|
my @other_holes = map $self->_perimeter($_), traverse_pt($holes_pt, 1);
|
||||||
|
|
||||||
|
my @loops = (
|
||||||
|
@other_holes,
|
||||||
|
@external_holes,
|
||||||
|
@other_contours,
|
||||||
|
@internal_contours,
|
||||||
|
@external_contours,
|
||||||
|
);
|
||||||
|
@loops = reverse @loops if $Slic3r::Config->external_perimeters_first;
|
||||||
|
|
||||||
|
push @{ $self->perimeters }, @loops;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _add_perimeter {
|
sub _perimeter {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($polygon, $role) = @_;
|
my ($polygon, $role) = @_;
|
||||||
|
|
||||||
return unless $polygon->is_printable($self->perimeter_flow->scaled_width);
|
return Slic3r::ExtrusionLoop->pack(
|
||||||
push @{ $self->perimeters }, Slic3r::ExtrusionLoop->pack(
|
polygon => Slic3r::Polygon->new($polygon),
|
||||||
polygon => $polygon,
|
|
||||||
role => ($role // EXTR_ROLE_PERIMETER),
|
role => ($role // EXTR_ROLE_PERIMETER),
|
||||||
flow_spacing => $self->perimeter_flow->spacing,
|
flow_spacing => $self->perimeter_flow->spacing,
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user