Fixed support unit tests and reduced dependency on $object

This commit is contained in:
Alessandro Ranellucci 2013-10-26 17:56:59 +02:00
parent bdf825d078
commit c08d4cc798
3 changed files with 57 additions and 58 deletions

View File

@ -773,7 +773,7 @@ sub generate_support_material {
return unless ($self->config->support_material || $self->config->raft_layers > 0) return unless ($self->config->support_material || $self->config->raft_layers > 0)
&& $self->layer_count >= 2; && $self->layer_count >= 2;
Slic3r::Print::SupportMaterial->new(object => $self)->generate; Slic3r::Print::SupportMaterial->new->generate($self);
} }
1; 1;

View File

@ -7,31 +7,34 @@ use Slic3r::Geometry qw(scale PI rad2deg deg2rad);
use Slic3r::Geometry::Clipper qw(offset diff union_ex intersection offset_ex offset2); use Slic3r::Geometry::Clipper qw(offset diff union_ex intersection offset_ex offset2);
use Slic3r::Surface ':types'; use Slic3r::Surface ':types';
has 'object' => (is => 'ro', required => 1); has 'config' => (is => 'rw', default => sub { Slic3r::Config->new_from_defaults });
has 'flow' => (is => 'rw');
sub flow {
my ($self) = @_;
return $self->object->print->support_material_flow;
}
sub generate { sub generate {
my $self = shift; my ($self, $object) = @_;
$self->flow($object->print->support_material_flow);
$self->config($object->config);
# Determine the top surfaces of the support, defined as: # Determine the top surfaces of the support, defined as:
# contact = overhangs - margin # contact = overhangs - margin
# This method is responsible for identifying what contact surfaces # This method is responsible for identifying what contact surfaces
# should the support material expose to the object in order to guarantee # should the support material expose to the object in order to guarantee
# that it will be effective, regardless of how it's built below. # that it will be effective, regardless of how it's built below.
my ($contact, $overhang) = $self->contact_area; my ($contact, $overhang) = $self->contact_area($object);
# Determine the top surfaces of the object. We need these to determine # 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 # the layer heights of support material and to clip support to the object
# silhouette. # silhouette.
my ($top) = $self->object_top($contact); my ($top) = $self->object_top($object, $contact);
# We now know the upper and lower boundaries for our support material object # We now know the upper and lower boundaries for our support material object
# (@$contact_z and @$top_z), so we can generate intermediate layers. # (@$contact_z and @$top_z), so we can generate intermediate layers.
my ($support_z) = $self->support_layers_z([ sort keys %$contact ], [ sort keys %$top ]); my ($support_z) = $self->support_layers_z(
[ sort keys %$contact ],
[ sort keys %$top ],
max(map $_->height, @{$object->layers})
);
# If we wanted to apply some special logic to the first support layers lying on # 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 # object's top surfaces this is the place to detect them
@ -44,8 +47,8 @@ sub generate {
my ($base) = $self->generate_base_layers($support_z, $contact, $interface, $top); my ($base) = $self->generate_base_layers($support_z, $contact, $interface, $top);
# Install support layers into object. # Install support layers into object.
push @{$self->object->support_layers}, map Slic3r::Layer::Support->new( push @{$object->support_layers}, map Slic3r::Layer::Support->new(
object => $self->object, object => $object,
id => $_, id => $_,
height => ($_ == 0) ? $support_z->[$_] : ($support_z->[$_] - $support_z->[$_-1]), height => ($_ == 0) ? $support_z->[$_] : ($support_z->[$_] - $support_z->[$_-1]),
print_z => $support_z->[$_], print_z => $support_z->[$_],
@ -54,11 +57,11 @@ sub generate {
), 0 .. $#$support_z; ), 0 .. $#$support_z;
# Generate the actual toolpaths and save them into each layer. # Generate the actual toolpaths and save them into each layer.
$self->generate_toolpaths($overhang, $contact, $interface, $base); $self->generate_toolpaths($object, $overhang, $contact, $interface, $base);
} }
sub contact_area { sub contact_area {
my ($self) = @_; my ($self, $object) = @_;
# 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;
@ -69,18 +72,18 @@ sub contact_area {
# if user specified a custom angle threshold, convert it to radians # if user specified a custom angle threshold, convert it to radians
my $threshold_rad; my $threshold_rad;
if ($self->object->config->support_material_threshold) { if ($self->config->support_material_threshold) {
$threshold_rad = deg2rad($self->object->config->support_material_threshold + 1); # +1 makes the threshold inclusive $threshold_rad = deg2rad($self->config->support_material_threshold + 1); # +1 makes the threshold inclusive
Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad); Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad);
} }
# 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
for my $layer_id (1 .. $#{$self->object->layers}) { for my $layer_id (1 .. $#{$object->layers}) {
last if $layer_id > $self->object->config->raft_layers && !$self->object->config->support_material; last if $layer_id > $self->config->raft_layers && !$self->config->support_material;
my $layer = $self->object->layers->[$layer_id]; my $layer = $object->layers->[$layer_id];
my $lower_layer = $self->object->layers->[$layer_id-1]; my $lower_layer = $object->layers->[$layer_id-1];
# detect overhangs and contact areas needed to support them # detect overhangs and contact areas needed to support them
my (@overhang, @contact) = (); my (@overhang, @contact) = ();
@ -90,8 +93,8 @@ sub contact_area {
# If a threshold angle was specified, use a different logic for detecting overhangs. # If a threshold angle was specified, use a different logic for detecting overhangs.
if (defined $threshold_rad if (defined $threshold_rad
|| $layer_id <= $self->object->config->support_material_enforce_layers || $layer_id <= $self->config->support_material_enforce_layers
|| $layer_id <= $self->object->config->raft_layers) { || $layer_id <= $self->config->raft_layers) {
my $d = defined $threshold_rad my $d = defined $threshold_rad
? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad)) ? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad))
: 0; : 0;
@ -171,16 +174,14 @@ sub contact_area {
} }
sub object_top { sub object_top {
my ($self, $contact) = @_; my ($self, $object, $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
my %top = (); # print_z => [ expolygons ] my %top = (); # print_z => [ expolygons ]
{ {
my $projection = []; my $projection = [];
foreach my $layer (reverse @{$self->object->layers}) { foreach my $layer (reverse @{$object->layers}) {
if (my @top = map @{$_->slices->filter_by_type(S_TYPE_TOP)}, @{$layer->regions}) { if (my @top = map @{$_->slices->filter_by_type(S_TYPE_TOP)}, @{$layer->regions}) {
# compute projection of the contact areas above this top layer # compute projection of the contact areas above this top layer
# first add all the 'new' contact areas to the current projection # first add all the 'new' contact areas to the current projection
@ -197,7 +198,7 @@ sub object_top {
# grow top surfaces so that interface and support generation are generated # grow top surfaces so that interface and support generation are generated
# with some spacing from object - it looks we don't need the actual # with some spacing from object - it looks we don't need the actual
# top shapes so this can be done here # top shapes so this can be done here
$top{ $layer->print_z } = offset($touching, $flow->scaled_spacing); $top{ $layer->print_z } = offset($touching, $self->flow->scaled_spacing);
} }
# remove the areas that touched from the projection that will continue on # remove the areas that touched from the projection that will continue on
@ -211,9 +212,7 @@ sub object_top {
} }
sub support_layers_z { sub support_layers_z {
my ($self, $contact_z, $top_z) = @_; my ($self, $contact_z, $top_z, $max_object_layer_height) = @_;
my $flow = $self->flow;
# quick table to check whether a given Z is a top surface # quick table to check whether a given Z is a top surface
my %top = map { $_ => 1 } @$top_z; my %top = map { $_ => 1 } @$top_z;
@ -221,20 +220,20 @@ sub support_layers_z {
# determine layer height for any non-contact layer # determine layer height for any non-contact layer
# we use max() to prevent many ultra-thin layers to be inserted in case # we use max() to prevent many ultra-thin layers to be inserted in case
# layer_height > nozzle_diameter * 0.75 # layer_height > nozzle_diameter * 0.75
my $support_material_height = max($self->object->config->layer_height, $flow->nozzle_diameter * 0.75); my $nozzle_diameter = $self->flow->nozzle_diameter;
my $support_material_height = max($max_object_layer_height, $nozzle_diameter * 0.75);
my @z = sort { $a <=> $b } @$contact_z, @$top_z, my @z = sort { $a <=> $b } @$contact_z, @$top_z, (map $_ + $nozzle_diameter, @$top_z);
(map { $_ + $flow->nozzle_diameter } @$top_z);
# enforce first layer height # enforce first layer height
my $first_layer_height = $self->object->config->get_value('first_layer_height'); my $first_layer_height = $self->config->get_value('first_layer_height');
shift @z while @z && $z[0] <= $first_layer_height; shift @z while @z && $z[0] <= $first_layer_height;
unshift @z, $first_layer_height; unshift @z, $first_layer_height;
for (my $i = $#z; $i >= 0; $i--) { for (my $i = $#z; $i >= 0; $i--) {
my $target_height = $support_material_height; my $target_height = $support_material_height;
if ($i > 0 && $top{ $z[$i-1] }) { if ($i > 0 && $top{ $z[$i-1] }) {
$target_height = $flow->nozzle_diameter; $target_height = $nozzle_diameter;
} }
# enforce first layer height # enforce first layer height
@ -259,7 +258,7 @@ sub generate_interface_layers {
# 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->config->support_material_interface_layers;
for my $layer_id (0 .. $#$support_z) { for my $layer_id (0 .. $#$support_z) {
my $z = $support_z->[$layer_id]; my $z = $support_z->[$layer_id];
my $this = $contact->{$z} // next; my $this = $contact->{$z} // next;
@ -316,7 +315,7 @@ sub generate_base_layers {
} }
sub generate_toolpaths { sub generate_toolpaths {
my ($self, $overhang, $contact, $interface, $base) = @_; my ($self, $object, $overhang, $contact, $interface, $base) = @_;
my $flow = $self->flow; my $flow = $self->flow;
@ -329,27 +328,27 @@ sub generate_toolpaths {
Slic3r::debugf "Generating patterns\n"; Slic3r::debugf "Generating patterns\n";
# prepare fillers # prepare fillers
my $pattern = $self->object->config->support_material_pattern; my $pattern = $self->config->support_material_pattern;
my @angles = ($self->object->config->support_material_angle); my @angles = ($self->config->support_material_angle);
if ($pattern eq 'rectilinear-grid') { if ($pattern eq 'rectilinear-grid') {
$pattern = 'rectilinear'; $pattern = 'rectilinear';
push @angles, $angles[0] + 90; push @angles, $angles[0] + 90;
} }
my %fillers = ( my %fillers = (
interface => $self->object->fill_maker->filler('rectilinear'), interface => $object->fill_maker->filler('rectilinear'),
support => $self->object->fill_maker->filler($pattern), support => $object->fill_maker->filler($pattern),
); );
my $interface_angle = $self->object->config->support_material_angle + 90; my $interface_angle = $self->config->support_material_angle + 90;
my $interface_spacing = $self->object->config->support_material_interface_spacing + $flow->spacing; my $interface_spacing = $self->config->support_material_interface_spacing + $flow->spacing;
my $interface_density = $interface_spacing == 0 ? 1 : $flow->spacing / $interface_spacing; my $interface_density = $interface_spacing == 0 ? 1 : $flow->spacing / $interface_spacing;
my $support_spacing = $self->object->config->support_material_spacing + $flow->spacing; my $support_spacing = $self->config->support_material_spacing + $flow->spacing;
my $support_density = $support_spacing == 0 ? 1 : $flow->spacing / $support_spacing; my $support_density = $support_spacing == 0 ? 1 : $flow->spacing / $support_spacing;
my $process_layer = sub { my $process_layer = sub {
my ($layer_id) = @_; my ($layer_id) = @_;
my $layer = $self->object->support_layers->[$layer_id]; my $layer = $object->support_layers->[$layer_id];
my $z = $layer->print_z; my $z = $layer->print_z;
my $overhang = $overhang->{$z} || []; my $overhang = $overhang->{$z} || [];
@ -463,9 +462,9 @@ sub generate_toolpaths {
# base flange # base flange
if ($layer_id == 0) { if ($layer_id == 0) {
$filler = $fillers{interface}; $filler = $fillers{interface};
$filler->angle($self->object->config->support_material_angle + 90); $filler->angle($self->config->support_material_angle + 90);
$density = 0.5; $density = 0.5;
$flow_spacing = $self->object->print->first_layer_support_material_flow->spacing; $flow_spacing = $object->print->first_layer_support_material_flow->spacing;
} else { } else {
# draw a perimeter all around support infill # draw a perimeter all around support infill
# TODO: use brim ordering algorithm # TODO: use brim ordering algorithm
@ -512,7 +511,7 @@ sub generate_toolpaths {
}; };
Slic3r::parallelize( Slic3r::parallelize(
items => [ 0 .. $#{$self->object->support_layers} ], items => [ 0 .. $#{$object->support_layers} ],
thread_cb => sub { thread_cb => sub {
my $q = shift; my $q = shift;
while (defined (my $layer_id = $q->dequeue)) { while (defined (my $layer_id = $q->dequeue)) {
@ -520,7 +519,7 @@ sub generate_toolpaths {
} }
}, },
no_threads_cb => sub { no_threads_cb => sub {
$process_layer->($_) for 0 .. $#{$self->object->support_layers}; $process_layer->($_) for 0 .. $#{$object->support_layers};
}, },
); );
} }

View File

@ -21,25 +21,25 @@ use Slic3r::Test;
my $print = Slic3r::Test::init_print('20mm_cube', config => $config); my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
$print->init_extruders; $print->init_extruders;
my $flow = $print->support_material_flow; my $flow = $print->support_material_flow;
my @support_layers = Slic3r::Print::SupportMaterial my $support_z = Slic3r::Print::SupportMaterial
->new(object => $print->objects->[0]) ->new(config => $config, flow => $flow)
->_compute_support_layers(\@contact_z, \@top_z); ->support_layers_z(\@contact_z, \@top_z, $config->layer_height);
is $support_layers[0], $config->first_layer_height, is $support_z->[0], $config->first_layer_height,
'first layer height is honored'; 'first layer height is honored';
is scalar(grep { $support_layers[$_]-$support_layers[$_-1] <= 0 } 1..$#support_layers), 0, is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0,
'no null or negative support layers'; 'no null or negative support layers';
is scalar(grep { $support_layers[$_]-$support_layers[$_-1] > $flow->nozzle_diameter + epsilon } 1..$#support_layers), 0, is scalar(grep { $support_z->[$_]-$support_z->[$_-1] > $flow->nozzle_diameter + epsilon } 1..$#$support_z), 0,
'no layers thicker than nozzle diameter'; 'no layers thicker than nozzle diameter';
my $wrong_top_spacing = 0; my $wrong_top_spacing = 0;
foreach my $top_z (@top_z) { foreach my $top_z (@top_z) {
# find layer index of this top surface # find layer index of this top surface
my $layer_id = first { abs($support_layers[$_] - $top_z) < epsilon } 0..$#support_layers; my $layer_id = first { abs($support_z->[$_] - $top_z) < epsilon } 0..$#$support_z;
# check that first support layer above this top surface is spaced with nozzle diameter # check that first support layer above this top surface is spaced with nozzle diameter
$wrong_top_spacing = 1 $wrong_top_spacing = 1
if ($support_layers[$layer_id+1] - $support_layers[$layer_id]) != $flow->nozzle_diameter; if ($support_z->[$layer_id+1] - $support_z->[$layer_id]) != $flow->nozzle_diameter;
} }
ok !$wrong_top_spacing, 'layers above top surfaces are spaced correctly'; ok !$wrong_top_spacing, 'layers above top surfaces are spaced correctly';
}; };