PrusaSlicer-NonPlainar/lib/Slic3r/Layer/Region.pm

156 lines
6.0 KiB
Perl
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package Slic3r::Layer::Region;
use strict;
use warnings;
use Slic3r::ExtrusionPath ':roles';
use Slic3r::Flow ':roles';
use Slic3r::Geometry qw(scale);
use Slic3r::Geometry::Clipper qw(diff_ex intersection_ex
);
use Slic3r::Surface ':types';
# TODO: lazy
sub infill_area_threshold {
my $self = shift;
return $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing ** 2;
}
sub id { return $_[0]->layer->id; }
sub slice_z { return $_[0]->layer->slice_z; }
sub print_z { return $_[0]->layer->print_z; }
sub height { return $_[0]->layer->height; }
sub object { return $_[0]->layer->object; }
sub print { return $_[0]->layer->print; }
sub config { return $_[0]->region->config; }
sub make_perimeters {
my ($self, $slices, $fill_surfaces) = @_;
$self->perimeters->clear;
$self->thin_fills->clear;
my $generator = Slic3r::Layer::PerimeterGenerator->new(
# input:
config => $self->config,
object_config => $self->layer->object->config,
print_config => $self->layer->print->config,
layer_height => $self->height,
layer_id => $self->layer->id,
slices => $slices,
lower_slices => defined($self->layer->lower_layer) ? $self->layer->lower_layer->slices : undef,
perimeter_flow => $self->flow(FLOW_ROLE_PERIMETER),
ext_perimeter_flow => $self->flow(FLOW_ROLE_EXTERNAL_PERIMETER),
overhang_flow => $self->region->flow(FLOW_ROLE_PERIMETER, -1, 1, 0, -1, $self->layer->object),
solid_infill_flow => $self->flow(FLOW_ROLE_SOLID_INFILL),
# output:
loops => $self->perimeters,
gap_fill => $self->thin_fills,
fill_surfaces => $fill_surfaces,
);
$generator->process;
}
sub prepare_fill_surfaces {
my $self = shift;
# Note: in order to make the psPrepareInfill step idempotent, we should never
# alter fill_surfaces boundaries on which our idempotency relies since that's
# the only meaningful information returned by psPerimeters.
# if no solid layers are requested, turn top/bottom surfaces to internal
if ($self->config->top_solid_layers == 0) {
$_->surface_type(S_TYPE_INTERNAL) for @{$self->fill_surfaces->filter_by_type(S_TYPE_TOP)};
}
if ($self->config->bottom_solid_layers == 0) {
$_->surface_type(S_TYPE_INTERNAL)
for @{$self->fill_surfaces->filter_by_type(S_TYPE_BOTTOM)}, @{$self->fill_surfaces->filter_by_type(S_TYPE_BOTTOMBRIDGE)};
}
# turn too small internal regions into solid regions according to the user setting
if ($self->config->fill_density > 0) {
my $min_area = scale scale $self->config->solid_infill_below_area; # scaling an area requires two calls!
$_->surface_type(S_TYPE_INTERNALSOLID)
for grep { $_->area <= $min_area } @{$self->fill_surfaces->filter_by_type(S_TYPE_INTERNAL)};
}
}
sub process_external_surfaces {
my ($self, $lower_layer) = @_;
my @surfaces = @{$self->fill_surfaces};
my $margin = scale &Slic3r::EXTERNAL_INFILL_MARGIN;
my @bottom = ();
foreach my $surface (grep $_->is_bottom, @surfaces) {
my $grown = $surface->expolygon->offset_ex(+$margin);
# detect bridge direction before merging grown surfaces otherwise adjacent bridges
# would get merged into a single one while they need different directions
# also, supply the original expolygon instead of the grown one, because in case
# of very thin (but still working) anchors, the grown expolygon would go beyond them
my $angle;
if ($lower_layer) {
my $bridge_detector = Slic3r::BridgeDetector->new(
$surface->expolygon,
$lower_layer->slices,
$self->flow(FLOW_ROLE_INFILL, $self->height, 1)->scaled_width,
);
Slic3r::debugf "Processing bridge at layer %d:\n", $self->id;
$bridge_detector->detect_angle;
$angle = $bridge_detector->angle;
if (defined $angle && $self->object->config->support_material) {
$self->bridged->append(Slic3r::ExPolygon->new($_))
for @{ $bridge_detector->coverage_by_angle($angle) };
$self->unsupported_bridge_edges->append($_) for @{ $bridge_detector->unsupported_edges };
}
}
push @bottom, map $surface->clone(expolygon => $_, bridge_angle => $angle), @$grown;
}
my @top = ();
foreach my $surface (grep $_->surface_type == S_TYPE_TOP, @surfaces) {
# give priority to bottom surfaces
my $grown = diff_ex(
$surface->expolygon->offset(+$margin),
[ map $_->p, @bottom ],
);
push @top, map $surface->clone(expolygon => $_), @$grown;
}
# if we're slicing with no infill, we can't extend external surfaces
# over non-existent infill
my @fill_boundaries = $self->config->fill_density > 0
? @surfaces
: grep $_->surface_type != S_TYPE_INTERNAL, @surfaces;
# intersect the grown surfaces with the actual fill boundaries
my @new_surfaces = ();
foreach my $group (@{Slic3r::Surface::Collection->new(@top, @bottom)->group}) {
push @new_surfaces,
map $group->[0]->clone(expolygon => $_),
@{intersection_ex(
[ map $_->p, @$group ],
[ map $_->p, @fill_boundaries ],
1, # to ensure adjacent expolygons are unified
)};
}
# subtract the new top surfaces from the other non-top surfaces and re-add them
my @other = grep $_->surface_type != S_TYPE_TOP && !$_->is_bottom, @surfaces;
foreach my $group (@{Slic3r::Surface::Collection->new(@other)->group}) {
push @new_surfaces, map $group->[0]->clone(expolygon => $_), @{diff_ex(
[ map $_->p, @$group ],
[ map $_->p, @new_surfaces ],
)};
}
$self->fill_surfaces->clear;
$self->fill_surfaces->append($_) for @new_surfaces;
}
1;