From 74f2f455547f17e69d391aa291922e87be3062a5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 23 Dec 2013 20:12:39 +0100 Subject: [PATCH] Fix regression preventing raft from being generated for the entire object footprint. #1614 #1567 --- lib/Slic3r/Print/SupportMaterial.pm | 121 +++++++++++++++------------- lib/Slic3r/Test.pm | 1 + t/support.t | 39 ++++++++- 3 files changed, 104 insertions(+), 57 deletions(-) diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 92ccb16e1..07f8624b1 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -81,6 +81,10 @@ sub contact_area { my %contact = (); # contact_z => [ polygons ] my %overhang = (); # contact_z => [ polygons ] - this stores the actual overhang supported by each contact layer for my $layer_id (0 .. $#{$object->layers}) { + # note $layer_id might != $layer->id when raft_layers > 0 + # so $layer_id == 0 means first object layer + # and $layer->id == 0 means first print layer (including raft) + if ($self->config->raft_layers == 0) { next if $layer_id == 0; } elsif (!$self->config->support_material) { @@ -89,69 +93,76 @@ sub contact_area { last if $layer_id > 0; } my $layer = $object->layers->[$layer_id]; - my $lower_layer = $object->layers->[$layer_id-1]; # detect overhangs and contact areas needed to support them my (@overhang, @contact) = (); - foreach my $layerm (@{$layer->regions}) { - my $fw = $layerm->perimeter_flow->scaled_width; - my $diff; + if ($layer_id == 0) { + # this is the first object layer, so we're here just to get the object + # footprint for the raft + push @overhang, map $_->clone, map @$_, @{$layer->slices}; + push @contact, @{offset(\@overhang, scale +MARGIN)}; + } else { + my $lower_layer = $object->layers->[$layer_id-1]; + foreach my $layerm (@{$layer->regions}) { + my $fw = $layerm->perimeter_flow->scaled_width; + my $diff; - # If a threshold angle was specified, use a different logic for detecting overhangs. - if (defined $threshold_rad - || $layer_id < $self->config->support_material_enforce_layers - || $self->config->raft_layers > 0) { - my $d = defined $threshold_rad - ? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad)) - : 0; + # If a threshold angle was specified, use a different logic for detecting overhangs. + if (defined $threshold_rad + || $layer_id < $self->config->support_material_enforce_layers + || $self->config->raft_layers > 0) { + my $d = defined $threshold_rad + ? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad)) + : 0; - $diff = diff( - offset([ map $_->p, @{$layerm->slices} ], -$d), - [ map @$_, @{$lower_layer->slices} ], - ); - - # only enforce spacing from the object ($fw/2) if the threshold angle - # is not too high: in that case, $d will be very small (as we need to catch - # very short overhangs), and such contact area would be eaten by the - # enforced spacing, resulting in high threshold angles to be almost ignored - $diff = diff( - offset($diff, $d - $fw/2), - [ map @$_, @{$lower_layer->slices} ], - ) if $d > $fw/2; - } else { - $diff = diff( - offset([ map $_->p, @{$layerm->slices} ], -$fw/2), - [ map @$_, @{$lower_layer->slices} ], - ); - - # collapse very tiny spots - $diff = offset2($diff, -$fw/10, +$fw/10); - - # $diff now contains the ring or stripe comprised between the boundary of - # lower slices and the centerline of the last perimeter in this overhanging layer. - # Void $diff means that there's no upper perimeter whose centerline is - # outside the lower slice boundary, thus no overhang - } - - # TODO: this is the place to remove bridged areas - - next if !@$diff; - push @overhang, @$diff; # NOTE: this is not the full overhang as it misses the outermost half of the perimeter width! - - # Let's define the required contact area by using a max gap of half the upper - # extrusion width and extending the area according to the configured margin. - # We increment the area in steps because we don't want our support to overflow - # on the other side of the object (if it's very thin). - { - my @slices_margin = @{offset([ map @$_, @{$lower_layer->slices} ], $fw/2)}; - for ($fw/2, map {scale MARGIN_STEP} 1..(MARGIN / MARGIN_STEP)) { $diff = diff( - offset($diff, $_), - \@slices_margin, + offset([ map $_->p, @{$layerm->slices} ], -$d), + [ map @$_, @{$lower_layer->slices} ], ); + + # only enforce spacing from the object ($fw/2) if the threshold angle + # is not too high: in that case, $d will be very small (as we need to catch + # very short overhangs), and such contact area would be eaten by the + # enforced spacing, resulting in high threshold angles to be almost ignored + $diff = diff( + offset($diff, $d - $fw/2), + [ map @$_, @{$lower_layer->slices} ], + ) if $d > $fw/2; + } else { + $diff = diff( + offset([ map $_->p, @{$layerm->slices} ], -$fw/2), + [ map @$_, @{$lower_layer->slices} ], + ); + + # collapse very tiny spots + $diff = offset2($diff, -$fw/10, +$fw/10); + + # $diff now contains the ring or stripe comprised between the boundary of + # lower slices and the centerline of the last perimeter in this overhanging layer. + # Void $diff means that there's no upper perimeter whose centerline is + # outside the lower slice boundary, thus no overhang } + + # TODO: this is the place to remove bridged areas + + next if !@$diff; + push @overhang, @$diff; # NOTE: this is not the full overhang as it misses the outermost half of the perimeter width! + + # Let's define the required contact area by using a max gap of half the upper + # extrusion width and extending the area according to the configured margin. + # We increment the area in steps because we don't want our support to overflow + # on the other side of the object (if it's very thin). + { + my @slices_margin = @{offset([ map @$_, @{$lower_layer->slices} ], $fw/2)}; + for ($fw/2, map {scale MARGIN_STEP} 1..(MARGIN / MARGIN_STEP)) { + $diff = diff( + offset($diff, $_), + \@slices_margin, + ); + } + } + push @contact, @$diff; } - push @contact, @$diff; } next if !@contact; @@ -176,7 +187,7 @@ sub contact_area { require "Slic3r/SVG.pm"; Slic3r::SVG::output("contact_" . $contact_z . ".svg", expolygons => union_ex(\@contact), - red_expolygons => \@overhang, + red_expolygons => union_ex(\@overhang), ); } } diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 6f2b5d403..8c4a969eb 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -107,6 +107,7 @@ sub init_print { $model_name = [$model_name] if ref($model_name) ne 'ARRAY'; for my $model (map model($_, %params), @$model_name) { + die "Unknown model in test" if !defined $model; $model->arrange_objects($config); $print->add_model($model); } diff --git a/t/support.t b/t/support.t index 768ddc095..b7d1810df 100644 --- a/t/support.t +++ b/t/support.t @@ -1,4 +1,4 @@ -use Test::More tests => 13; +use Test::More tests => 14; use strict; use warnings; @@ -9,7 +9,8 @@ BEGIN { use List::Util qw(first); use Slic3r; -use Slic3r::Geometry qw(epsilon); +use Slic3r::Geometry qw(epsilon scale); +use Slic3r::Geometry::Clipper qw(diff); use Slic3r::Test; { @@ -88,4 +89,38 @@ use Slic3r::Test; }); } +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('skirts', 0); + $config->set('raft_layers', 3); + $config->set('support_material_extrusion_width', 0.6); + $config->set('first_layer_extrusion_width', '100%'); + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + + my $layer_id = 0; + my @raft = my @first_object_layer = (); + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { + my ($self, $cmd, $args, $info) = @_; + + if ($info->{extruding} && $info->{dist_XY} > 0) { + if ($layer_id <= $config->raft_layers) { + # this is a raft layer or the first object layer + my $line = Slic3r::Line->new_scale([ $self->X, $self->Y ], [ $info->{new_X}, $info->{new_Y} ]); + my @path = $line->grow(scale($config->support_material_extrusion_width/2)); + if ($layer_id < $config->raft_layers) { + # this is a raft layer + push @raft, @path; + } else { + push @first_object_layer, @path; + } + } + } elsif ($cmd eq 'G1' && $info->{dist_Z} > 0) { + $layer_id++; + } + }); + + ok !@{diff(\@first_object_layer, \@raft)}, + 'first object layer is completely supported by raft'; +} + __END__