2012-09-23 00:52:31 +00:00
|
|
|
|
package Slic3r::Layer::Region;
|
2014-06-12 07:23:10 +00:00
|
|
|
|
use strict;
|
|
|
|
|
use warnings;
|
2012-09-22 17:04:36 +00:00
|
|
|
|
|
|
|
|
|
use Slic3r::ExtrusionPath ':roles';
|
2013-12-30 17:28:41 +00:00
|
|
|
|
use Slic3r::Flow ':roles';
|
2015-01-07 15:04:53 +00:00
|
|
|
|
use Slic3r::Geometry qw(scale);
|
|
|
|
|
use Slic3r::Geometry::Clipper qw(diff_ex intersection_ex
|
|
|
|
|
);
|
2012-09-22 17:04:36 +00:00
|
|
|
|
use Slic3r::Surface ':types';
|
|
|
|
|
|
|
|
|
|
|
2014-05-06 08:07:18 +00:00
|
|
|
|
# TODO: lazy
|
|
|
|
|
sub infill_area_threshold {
|
2013-02-18 10:52:47 +00:00
|
|
|
|
my $self = shift;
|
2013-12-30 17:28:41 +00:00
|
|
|
|
return $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing ** 2;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-06 08:07:18 +00:00
|
|
|
|
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; }
|
|
|
|
|
|
2012-09-22 17:04:36 +00:00
|
|
|
|
sub make_perimeters {
|
2014-07-12 13:28:21 +00:00
|
|
|
|
my ($self, $slices, $fill_surfaces) = @_;
|
2012-09-22 17:04:36 +00:00
|
|
|
|
|
2013-07-18 20:29:12 +00:00
|
|
|
|
$self->perimeters->clear;
|
|
|
|
|
$self->thin_fills->clear;
|
2012-09-22 17:04:36 +00:00
|
|
|
|
|
2015-01-07 15:04:53 +00:00
|
|
|
|
my $generator = Slic3r::Layer::PerimeterGenerator->new(
|
|
|
|
|
# input:
|
|
|
|
|
config => $self->config,
|
2015-01-19 08:52:24 +00:00
|
|
|
|
object_config => $self->layer->object->config,
|
2015-01-07 15:04:53 +00:00
|
|
|
|
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,
|
2014-04-14 23:41:40 +00:00
|
|
|
|
);
|
2015-01-07 15:04:53 +00:00
|
|
|
|
$generator->process;
|
2013-05-13 11:07:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-09-22 17:04:36 +00:00
|
|
|
|
sub prepare_fill_surfaces {
|
|
|
|
|
my $self = shift;
|
|
|
|
|
|
2014-06-11 15:51:18 +00:00
|
|
|
|
# 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.
|
|
|
|
|
|
2012-09-22 17:04:36 +00:00
|
|
|
|
# if no solid layers are requested, turn top/bottom surfaces to internal
|
2013-08-25 12:37:50 +00:00
|
|
|
|
if ($self->config->top_solid_layers == 0) {
|
2013-09-06 16:36:38 +00:00
|
|
|
|
$_->surface_type(S_TYPE_INTERNAL) for @{$self->fill_surfaces->filter_by_type(S_TYPE_TOP)};
|
2012-11-16 11:37:47 +00:00
|
|
|
|
}
|
2013-08-25 12:37:50 +00:00
|
|
|
|
if ($self->config->bottom_solid_layers == 0) {
|
2014-03-25 00:11:28 +00:00
|
|
|
|
$_->surface_type(S_TYPE_INTERNAL)
|
|
|
|
|
for @{$self->fill_surfaces->filter_by_type(S_TYPE_BOTTOM)}, @{$self->fill_surfaces->filter_by_type(S_TYPE_BOTTOMBRIDGE)};
|
2012-09-22 17:04:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-03-07 14:47:32 +00:00
|
|
|
|
# turn too small internal regions into solid regions according to the user setting
|
2013-08-25 17:59:42 +00:00
|
|
|
|
if ($self->config->fill_density > 0) {
|
2013-08-25 12:37:50 +00:00
|
|
|
|
my $min_area = scale scale $self->config->solid_infill_below_area; # scaling an area requires two calls!
|
2013-09-06 16:36:38 +00:00
|
|
|
|
$_->surface_type(S_TYPE_INTERNALSOLID)
|
|
|
|
|
for grep { $_->area <= $min_area } @{$self->fill_surfaces->filter_by_type(S_TYPE_INTERNAL)};
|
2012-09-22 17:04:36 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-07 14:47:32 +00:00
|
|
|
|
sub process_external_surfaces {
|
2013-11-11 19:37:06 +00:00
|
|
|
|
my ($self, $lower_layer) = @_;
|
2012-09-22 17:04:36 +00:00
|
|
|
|
|
2013-09-06 16:36:38 +00:00
|
|
|
|
my @surfaces = @{$self->fill_surfaces};
|
2013-08-13 07:45:33 +00:00
|
|
|
|
my $margin = scale &Slic3r::EXTERNAL_INFILL_MARGIN;
|
2013-07-26 10:31:25 +00:00
|
|
|
|
|
|
|
|
|
my @bottom = ();
|
2014-03-25 00:11:28 +00:00
|
|
|
|
foreach my $surface (grep $_->is_bottom, @surfaces) {
|
2013-08-08 00:10:34 +00:00
|
|
|
|
my $grown = $surface->expolygon->offset_ex(+$margin);
|
2013-04-29 13:53:15 +00:00
|
|
|
|
|
2013-07-26 10:31:25 +00:00
|
|
|
|
# detect bridge direction before merging grown surfaces otherwise adjacent bridges
|
|
|
|
|
# would get merged into a single one while they need different directions
|
2013-07-29 09:54:32 +00:00
|
|
|
|
# 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
|
2014-04-07 21:18:11 +00:00
|
|
|
|
my $angle;
|
|
|
|
|
if ($lower_layer) {
|
2014-11-15 21:41:22 +00:00
|
|
|
|
my $bridge_detector = Slic3r::BridgeDetector->new(
|
|
|
|
|
$surface->expolygon,
|
|
|
|
|
$lower_layer->slices,
|
|
|
|
|
$self->flow(FLOW_ROLE_INFILL, $self->height, 1)->scaled_width,
|
2014-04-07 21:18:11 +00:00
|
|
|
|
);
|
|
|
|
|
Slic3r::debugf "Processing bridge at layer %d:\n", $self->id;
|
2014-11-15 21:41:22 +00:00
|
|
|
|
$bridge_detector->detect_angle;
|
|
|
|
|
$angle = $bridge_detector->angle;
|
2014-04-26 14:07:43 +00:00
|
|
|
|
|
|
|
|
|
if (defined $angle && $self->object->config->support_material) {
|
2014-12-17 00:21:12 +00:00
|
|
|
|
$self->bridged->append(Slic3r::ExPolygon->new($_))
|
|
|
|
|
for @{ $bridge_detector->coverage_by_angle($angle) };
|
2014-11-09 15:23:50 +00:00
|
|
|
|
$self->unsupported_bridge_edges->append($_) for @{ $bridge_detector->unsupported_edges };
|
2014-04-26 14:07:43 +00:00
|
|
|
|
}
|
2014-04-07 21:18:11 +00:00
|
|
|
|
}
|
2012-09-22 17:04:36 +00:00
|
|
|
|
|
2013-08-08 00:10:34 +00:00
|
|
|
|
push @bottom, map $surface->clone(expolygon => $_, bridge_angle => $angle), @$grown;
|
2013-07-26 10:31:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my @top = ();
|
2013-09-06 16:36:38 +00:00
|
|
|
|
foreach my $surface (grep $_->surface_type == S_TYPE_TOP, @surfaces) {
|
2013-03-07 14:47:32 +00:00
|
|
|
|
# give priority to bottom surfaces
|
2013-07-26 10:31:25 +00:00
|
|
|
|
my $grown = diff_ex(
|
2013-08-08 00:10:34 +00:00
|
|
|
|
$surface->expolygon->offset(+$margin),
|
2013-07-26 10:31:25 +00:00
|
|
|
|
[ 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
|
2013-11-11 19:37:06 +00:00
|
|
|
|
my @fill_boundaries = $self->config->fill_density > 0
|
2013-09-06 16:36:38 +00:00
|
|
|
|
? @surfaces
|
|
|
|
|
: grep $_->surface_type != S_TYPE_INTERNAL, @surfaces;
|
2013-07-26 10:31:25 +00:00
|
|
|
|
|
|
|
|
|
# intersect the grown surfaces with the actual fill boundaries
|
|
|
|
|
my @new_surfaces = ();
|
2013-11-23 17:15:59 +00:00
|
|
|
|
foreach my $group (@{Slic3r::Surface::Collection->new(@top, @bottom)->group}) {
|
2013-07-26 10:31:25 +00:00
|
|
|
|
push @new_surfaces,
|
|
|
|
|
map $group->[0]->clone(expolygon => $_),
|
|
|
|
|
@{intersection_ex(
|
2013-03-07 14:47:32 +00:00
|
|
|
|
[ map $_->p, @$group ],
|
2013-07-26 10:31:25 +00:00
|
|
|
|
[ map $_->p, @fill_boundaries ],
|
|
|
|
|
1, # to ensure adjacent expolygons are unified
|
2013-03-07 14:47:32 +00:00
|
|
|
|
)};
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-26 10:31:25 +00:00
|
|
|
|
# subtract the new top surfaces from the other non-top surfaces and re-add them
|
2014-03-25 00:11:28 +00:00
|
|
|
|
my @other = grep $_->surface_type != S_TYPE_TOP && !$_->is_bottom, @surfaces;
|
2013-11-23 17:15:59 +00:00
|
|
|
|
foreach my $group (@{Slic3r::Surface::Collection->new(@other)->group}) {
|
2013-07-26 10:31:25 +00:00
|
|
|
|
push @new_surfaces, map $group->[0]->clone(expolygon => $_), @{diff_ex(
|
|
|
|
|
[ map $_->p, @$group ],
|
|
|
|
|
[ map $_->p, @new_surfaces ],
|
|
|
|
|
)};
|
|
|
|
|
}
|
2013-09-06 16:36:38 +00:00
|
|
|
|
$self->fill_surfaces->clear;
|
2014-11-09 15:23:50 +00:00
|
|
|
|
$self->fill_surfaces->append($_) for @new_surfaces;
|
2013-04-18 16:43:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-09-22 17:04:36 +00:00
|
|
|
|
1;
|