Refactoring: split support material code into several methods
This commit is contained in:
parent
e6efda4ba4
commit
bdf825d078
@ -17,11 +17,52 @@ sub flow {
|
|||||||
sub generate {
|
sub generate {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
my $flow = $self->flow;
|
# Determine the top surfaces of the support, defined as:
|
||||||
|
# contact = overhangs - margin
|
||||||
|
# This method is responsible for identifying what contact surfaces
|
||||||
|
# should the support material expose to the object in order to guarantee
|
||||||
|
# that it will be effective, regardless of how it's built below.
|
||||||
|
my ($contact, $overhang) = $self->contact_area;
|
||||||
|
|
||||||
|
# Determine the top surfaces of the object. We need these to determine
|
||||||
|
# the layer heights of support material and to clip support to the object
|
||||||
|
# silhouette.
|
||||||
|
my ($top) = $self->object_top($contact);
|
||||||
|
|
||||||
|
# We now know the upper and lower boundaries for our support material object
|
||||||
|
# (@$contact_z and @$top_z), so we can generate intermediate layers.
|
||||||
|
my ($support_z) = $self->support_layers_z([ sort keys %$contact ], [ sort keys %$top ]);
|
||||||
|
|
||||||
|
# If we wanted to apply some special logic to the first support layers lying on
|
||||||
|
# object's top surfaces this is the place to detect them
|
||||||
|
|
||||||
|
# Propagate contact layers downwards to generate interface layers
|
||||||
|
my ($interface) = $self->generate_interface_layers($support_z, $contact, $top);
|
||||||
|
|
||||||
|
# Propagate contact layers and interface layers downwards to generate
|
||||||
|
# the main support layers.
|
||||||
|
my ($base) = $self->generate_base_layers($support_z, $contact, $interface, $top);
|
||||||
|
|
||||||
|
# Install support layers into object.
|
||||||
|
push @{$self->object->support_layers}, map Slic3r::Layer::Support->new(
|
||||||
|
object => $self->object,
|
||||||
|
id => $_,
|
||||||
|
height => ($_ == 0) ? $support_z->[$_] : ($support_z->[$_] - $support_z->[$_-1]),
|
||||||
|
print_z => $support_z->[$_],
|
||||||
|
slice_z => -1,
|
||||||
|
slices => [],
|
||||||
|
), 0 .. $#$support_z;
|
||||||
|
|
||||||
|
# Generate the actual toolpaths and save them into each layer.
|
||||||
|
$self->generate_toolpaths($overhang, $contact, $interface, $base);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub contact_area {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
# how much we extend support around the actual contact area
|
# how much we extend support around the actual contact area
|
||||||
#my $margin = $flow->scaled_width / 2;
|
#my $margin = $flow->scaled_width / 2;
|
||||||
my $margin = scale 3;
|
my $margin = scale 3;
|
||||||
|
|
||||||
# increment used to reach $margin in steps to avoid trespassing thin objects
|
# increment used to reach $margin in steps to avoid trespassing thin objects
|
||||||
my $margin_step = $margin/3;
|
my $margin_step = $margin/3;
|
||||||
@ -33,12 +74,6 @@ sub generate {
|
|||||||
Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad);
|
Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad);
|
||||||
}
|
}
|
||||||
|
|
||||||
# shape of contact area
|
|
||||||
my $contact_loops = 1;
|
|
||||||
my $circle_radius = 1.5 * $flow->scaled_width;
|
|
||||||
my $circle_distance = 3 * $circle_radius;
|
|
||||||
my $circle = Slic3r::Polygon->new(map [ $circle_radius * cos $_, $circle_radius * sin $_ ], (5*PI/3, 4*PI/3, PI, 2*PI/3, PI/3, 0));
|
|
||||||
|
|
||||||
# determine contact areas
|
# determine contact areas
|
||||||
my %contact = (); # contact_z => [ polygons ]
|
my %contact = (); # contact_z => [ polygons ]
|
||||||
my %overhang = (); # contact_z => [ expolygons ] - this stores the actual overhang supported by each contact layer
|
my %overhang = (); # contact_z => [ expolygons ] - this stores the actual overhang supported by each contact layer
|
||||||
@ -131,7 +166,14 @@ sub generate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
my @contact_z = sort keys %contact;
|
|
||||||
|
return (\%contact, \%overhang);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub object_top {
|
||||||
|
my ($self, $contact) = @_;
|
||||||
|
|
||||||
|
my $flow = $self->flow;
|
||||||
|
|
||||||
# find object top surfaces
|
# find object top surfaces
|
||||||
# we'll use them to clip our support and detect where does it stick
|
# we'll use them to clip our support and detect where does it stick
|
||||||
@ -144,10 +186,10 @@ sub generate {
|
|||||||
# first add all the 'new' contact areas to the current projection
|
# first add all the 'new' contact areas to the current projection
|
||||||
# ('new' means all the areas that are lower than the last top layer
|
# ('new' means all the areas that are lower than the last top layer
|
||||||
# we considered)
|
# we considered)
|
||||||
my $min_top = min(keys %top) // max(keys %contact);
|
my $min_top = min(keys %top) // max(keys %$contact);
|
||||||
# use <= instead of just < because otherwise we'd ignore any contact regions
|
# use <= instead of just < because otherwise we'd ignore any contact regions
|
||||||
# having the same Z of top layers
|
# having the same Z of top layers
|
||||||
push @$projection, map @{$contact{$_}}, grep { $_ > $layer->print_z && $_ <= $min_top } keys %contact;
|
push @$projection, map @{$contact->{$_}}, grep { $_ > $layer->print_z && $_ <= $min_top } keys %$contact;
|
||||||
|
|
||||||
# now find whether any projection falls onto this top surface
|
# now find whether any projection falls onto this top surface
|
||||||
my $touching = intersection($projection, [ map $_->p, @top ]);
|
my $touching = intersection($projection, [ map $_->p, @top ]);
|
||||||
@ -164,25 +206,67 @@ sub generate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
my @top_z = sort keys %top;
|
|
||||||
|
|
||||||
# we now know the upper and lower boundaries for our support material object
|
return \%top;
|
||||||
# (@contact_z and @top_z), so we can generate intermediate layers
|
}
|
||||||
my @support_layers = $self->_compute_support_layers(\@contact_z, \@top_z);
|
|
||||||
|
sub support_layers_z {
|
||||||
|
my ($self, $contact_z, $top_z) = @_;
|
||||||
|
|
||||||
# if we wanted to apply some special logic to the first support layers lying on
|
my $flow = $self->flow;
|
||||||
# object's top surfaces this is the place to detect them
|
|
||||||
|
# quick table to check whether a given Z is a top surface
|
||||||
|
my %top = map { $_ => 1 } @$top_z;
|
||||||
|
|
||||||
|
# determine layer height for any non-contact layer
|
||||||
|
# we use max() to prevent many ultra-thin layers to be inserted in case
|
||||||
|
# layer_height > nozzle_diameter * 0.75
|
||||||
|
my $support_material_height = max($self->object->config->layer_height, $flow->nozzle_diameter * 0.75);
|
||||||
|
|
||||||
|
my @z = sort { $a <=> $b } @$contact_z, @$top_z,
|
||||||
|
(map { $_ + $flow->nozzle_diameter } @$top_z);
|
||||||
|
|
||||||
|
# enforce first layer height
|
||||||
|
my $first_layer_height = $self->object->config->get_value('first_layer_height');
|
||||||
|
shift @z while @z && $z[0] <= $first_layer_height;
|
||||||
|
unshift @z, $first_layer_height;
|
||||||
|
|
||||||
|
for (my $i = $#z; $i >= 0; $i--) {
|
||||||
|
my $target_height = $support_material_height;
|
||||||
|
if ($i > 0 && $top{ $z[$i-1] }) {
|
||||||
|
$target_height = $flow->nozzle_diameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
# enforce first layer height
|
||||||
|
if (($i == 0 && $z[$i] > $target_height + $first_layer_height)
|
||||||
|
|| ($z[$i] - $z[$i-1] > $target_height + Slic3r::Geometry::epsilon)) {
|
||||||
|
splice @z, $i, 0, ($z[$i] - $target_height);
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# remove duplicates and make sure all 0.x values have the leading 0
|
||||||
|
{
|
||||||
|
my %sl = map { 1 * $_ => 1 } @z;
|
||||||
|
@z = sort { $a <=> $b } keys %sl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return \@z;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub generate_interface_layers {
|
||||||
|
my ($self, $support_z, $contact, $top) = @_;
|
||||||
|
|
||||||
# let's now generate interface layers below contact areas
|
# let's now generate interface layers below contact areas
|
||||||
my %interface = (); # layer_id => [ polygons ]
|
my %interface = (); # layer_id => [ polygons ]
|
||||||
my $interface_layers = $self->object->config->support_material_interface_layers;
|
my $interface_layers = $self->object->config->support_material_interface_layers;
|
||||||
for my $layer_id (0 .. $#support_layers) {
|
for my $layer_id (0 .. $#$support_z) {
|
||||||
my $z = $support_layers[$layer_id];
|
my $z = $support_z->[$layer_id];
|
||||||
my $this = $contact{$z} // next;
|
my $this = $contact->{$z} // next;
|
||||||
|
|
||||||
# count contact layer as interface layer
|
# count contact layer as interface layer
|
||||||
for (my $i = $layer_id-1; $i >= 0 && $i > $layer_id-$interface_layers; $i--) {
|
for (my $i = $layer_id-1; $i >= 0 && $i > $layer_id-$interface_layers; $i--) {
|
||||||
$z = $support_layers[$i];
|
$z = $support_z->[$i];
|
||||||
# Compute interface area on this layer as diff of upper contact area
|
# Compute interface area on this layer as diff of upper contact area
|
||||||
# (or upper interface area) and layer slices.
|
# (or upper interface area) and layer slices.
|
||||||
# This diff is responsible of the contact between support material and
|
# This diff is responsible of the contact between support material and
|
||||||
@ -194,43 +278,54 @@ sub generate {
|
|||||||
@{ $interface{$i} || [] }, # interface regions already applied to this layer
|
@{ $interface{$i} || [] }, # interface regions already applied to this layer
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@{ $top{$z} || [] }, # top slices on this layer
|
@{ $top->{$z} || [] }, # top slices on this layer
|
||||||
@{ $contact{$z} || [] }, # contact regions on this layer
|
@{ $contact->{$z} || [] }, # contact regions on this layer
|
||||||
],
|
],
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return \%interface;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub generate_base_layers {
|
||||||
|
my ($self, $support_z, $contact, $interface, $top) = @_;
|
||||||
|
|
||||||
# let's now generate support layers under interface layers
|
# let's now generate support layers under interface layers
|
||||||
my %support = (); # layer_id => [ polygons ]
|
my $base = {}; # layer_id => [ polygons ]
|
||||||
{
|
{
|
||||||
for my $i (reverse 0 .. $#support_layers-1) {
|
for my $i (reverse 0 .. $#$support_z-1) {
|
||||||
my $z = $support_layers[$i];
|
my $z = $support_z->[$i];
|
||||||
$support{$i} = diff(
|
$base->{$i} = diff(
|
||||||
[
|
[
|
||||||
@{ $support{$i+1} || [] }, # support regions on upper layer
|
@{ $base->{$i+1} || [] }, # support regions on upper layer
|
||||||
@{ $interface{$i+1} || [] }, # interface regions on upper layer
|
@{ $interface->{$i+1} || [] }, # interface regions on upper layer
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@{ $top{$z} || [] }, # top slices on this layer
|
@{ $top->{$z} || [] }, # top slices on this layer
|
||||||
@{ $interface{$i} || [] }, # interface regions on this layer
|
@{ $interface->{$i} || [] }, # interface regions on this layer
|
||||||
@{ $contact{$z} || [] }, # contact regions on this layer
|
@{ $contact->{$z} || [] }, # contact regions on this layer
|
||||||
],
|
],
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
push @{$self->object->support_layers}, map Slic3r::Layer::Support->new(
|
return $base;
|
||||||
object => $self->object,
|
}
|
||||||
id => $_,
|
|
||||||
height => ($_ == 0) ? $support_layers[$_] : ($support_layers[$_] - $support_layers[$_-1]),
|
|
||||||
print_z => $support_layers[$_],
|
|
||||||
slice_z => -1,
|
|
||||||
slices => [],
|
|
||||||
), 0 .. $#support_layers;
|
|
||||||
|
|
||||||
|
sub generate_toolpaths {
|
||||||
|
my ($self, $overhang, $contact, $interface, $base) = @_;
|
||||||
|
|
||||||
|
my $flow = $self->flow;
|
||||||
|
|
||||||
|
# shape of contact area
|
||||||
|
my $contact_loops = 1;
|
||||||
|
my $circle_radius = 1.5 * $flow->scaled_width;
|
||||||
|
my $circle_distance = 3 * $circle_radius;
|
||||||
|
my $circle = Slic3r::Polygon->new(map [ $circle_radius * cos $_, $circle_radius * sin $_ ], (5*PI/3, 4*PI/3, PI, 2*PI/3, PI/3, 0));
|
||||||
|
|
||||||
Slic3r::debugf "Generating patterns\n";
|
Slic3r::debugf "Generating patterns\n";
|
||||||
|
|
||||||
# prepare fillers
|
# prepare fillers
|
||||||
@ -255,22 +350,23 @@ sub generate {
|
|||||||
my $process_layer = sub {
|
my $process_layer = sub {
|
||||||
my ($layer_id) = @_;
|
my ($layer_id) = @_;
|
||||||
my $layer = $self->object->support_layers->[$layer_id];
|
my $layer = $self->object->support_layers->[$layer_id];
|
||||||
|
my $z = $layer->print_z;
|
||||||
|
|
||||||
my $overhang = $overhang{$support_layers[$layer_id]} || [];
|
my $overhang = $overhang->{$z} || [];
|
||||||
my $contact = $contact{$support_layers[$layer_id]} || [];
|
my $contact = $contact->{$z} || [];
|
||||||
my $interface = $interface{$layer_id} || [];
|
my $interface = $interface->{$layer_id} || [];
|
||||||
my $support = $support{$layer_id} || [];
|
my $base = $base->{$layer_id} || [];
|
||||||
|
|
||||||
if (0) {
|
if (0) {
|
||||||
require "Slic3r/SVG.pm";
|
require "Slic3r/SVG.pm";
|
||||||
Slic3r::SVG::output("layer_" . $support_layers[$layer_id] . ".svg",
|
Slic3r::SVG::output("layer_" . $z . ".svg",
|
||||||
red_expolygons => union_ex($contact),
|
red_expolygons => union_ex($contact),
|
||||||
green_expolygons => union_ex($interface),
|
green_expolygons => union_ex($interface),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
# islands
|
# islands
|
||||||
$layer->support_islands->append(@{union_ex([ @$interface, @$support, @$contact ])});
|
$layer->support_islands->append(@{union_ex([ @$interface, @$base, @$contact ])});
|
||||||
|
|
||||||
# contact
|
# contact
|
||||||
my $contact_infill = [];
|
my $contact_infill = [];
|
||||||
@ -325,11 +421,11 @@ sub generate {
|
|||||||
# steal some space from support
|
# steal some space from support
|
||||||
$interface = intersection(
|
$interface = intersection(
|
||||||
offset([ @$interface, @$contact_infill ], scale 3),
|
offset([ @$interface, @$contact_infill ], scale 3),
|
||||||
[ @$interface, @$support, @$contact_infill ],
|
[ @$interface, @$base, @$contact_infill ],
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
$support = diff(
|
$base = diff(
|
||||||
$support,
|
$base,
|
||||||
$interface,
|
$interface,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -354,14 +450,14 @@ sub generate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# support or flange
|
# support or flange
|
||||||
if (@$support) {
|
if (@$base) {
|
||||||
my $filler = $fillers{support};
|
my $filler = $fillers{support};
|
||||||
$filler->angle($angles[ ($layer_id) % @angles ]);
|
$filler->angle($angles[ ($layer_id) % @angles ]);
|
||||||
my $density = $support_density;
|
my $density = $support_density;
|
||||||
my $flow_spacing = $flow->spacing;
|
my $flow_spacing = $flow->spacing;
|
||||||
|
|
||||||
# TODO: use offset2_ex()
|
# TODO: use offset2_ex()
|
||||||
my $to_infill = union_ex($support, 1);
|
my $to_infill = union_ex($base, 1);
|
||||||
my @paths = ();
|
my @paths = ();
|
||||||
|
|
||||||
# base flange
|
# base flange
|
||||||
@ -406,7 +502,7 @@ sub generate {
|
|||||||
|
|
||||||
if (0) {
|
if (0) {
|
||||||
require "Slic3r/SVG.pm";
|
require "Slic3r/SVG.pm";
|
||||||
Slic3r::SVG::output("islands_" . $support_layers[$layer_id] . ".svg",
|
Slic3r::SVG::output("islands_" . $z . ".svg",
|
||||||
red_expolygons => union_ex($contact),
|
red_expolygons => union_ex($contact),
|
||||||
green_expolygons => union_ex($interface),
|
green_expolygons => union_ex($interface),
|
||||||
green_polylines => [ map $_->unpack->polyline, @{$layer->support_contact_fills} ],
|
green_polylines => [ map $_->unpack->polyline, @{$layer->support_contact_fills} ],
|
||||||
@ -429,48 +525,4 @@ sub generate {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _compute_support_layers {
|
|
||||||
my ($self, $contact_z, $top_z) = @_;
|
|
||||||
|
|
||||||
my $flow = $self->flow;
|
|
||||||
|
|
||||||
# quick table to check whether a given Z is a top surface
|
|
||||||
my %top = map { $_ => 1 } @$top_z;
|
|
||||||
|
|
||||||
# determine layer height for any non-contact layer
|
|
||||||
# we use max() to prevent many ultra-thin layers to be inserted in case
|
|
||||||
# layer_height > nozzle_diameter * 0.75
|
|
||||||
my $support_material_height = max($self->object->config->layer_height, $flow->nozzle_diameter * 0.75);
|
|
||||||
|
|
||||||
my @support_layers = sort { $a <=> $b } @$contact_z, @$top_z,
|
|
||||||
(map { $_ + $flow->nozzle_diameter } @$top_z);
|
|
||||||
|
|
||||||
# enforce first layer height
|
|
||||||
my $first_layer_height = $self->object->config->get_value('first_layer_height');
|
|
||||||
shift @support_layers while @support_layers && $support_layers[0] <= $first_layer_height;
|
|
||||||
unshift @support_layers, $first_layer_height;
|
|
||||||
|
|
||||||
for (my $i = $#support_layers; $i >= 0; $i--) {
|
|
||||||
my $target_height = $support_material_height;
|
|
||||||
if ($i > 0 && $top{ $support_layers[$i-1] }) {
|
|
||||||
$target_height = $flow->nozzle_diameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
# enforce first layer height
|
|
||||||
if (($i == 0 && $support_layers[$i] > $target_height + $first_layer_height)
|
|
||||||
|| ($support_layers[$i] - $support_layers[$i-1] > $target_height + Slic3r::Geometry::epsilon)) {
|
|
||||||
splice @support_layers, $i, 0, ($support_layers[$i] - $target_height);
|
|
||||||
$i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# remove duplicates and make sure all 0.x values have the leading 0
|
|
||||||
{
|
|
||||||
my %sl = map { 1 * $_ => 1 } @support_layers;
|
|
||||||
@support_layers = sort { $a <=> $b } keys %sl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return @support_layers;
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
Loading…
Reference in New Issue
Block a user