From 432a87e73d27af53503d52f0fb8c82397914595d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 25 Mar 2014 01:11:28 +0100 Subject: [PATCH] New option for forcing the generation of interface shells. #1847 --- lib/Slic3r/GCode.pm | 2 +- lib/Slic3r/GUI/Tab.pm | 4 ++++ lib/Slic3r/Layer/Region.pm | 7 ++++--- lib/Slic3r/Print/Object.pm | 39 +++++++++++++++++++++++++++++--------- lib/Slic3r/Surface.pm | 2 +- t/multi.t | 10 ++++++++-- xs/src/PrintConfig.hpp | 9 +++++++++ xs/src/Surface.cpp | 13 +++++++++++-- xs/src/Surface.hpp | 3 ++- xs/xsp/Surface.xsp | 2 ++ 10 files changed, 72 insertions(+), 19 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index b895a1d99..f1269debb 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -114,7 +114,7 @@ sub change_layer { if ($layer->id > 0 && ($self->print_config->overhangs || $self->print_config->start_perimeters_at_non_overhang)) { $self->_layer_overhangs->append( # clone ExPolygons because they come from Surface objects but will be used outside here - map $_->expolygon, map @{$_->slices->filter_by_type(S_TYPE_BOTTOM)}, @{$layer->regions} + map $_->expolygon, map @{$_->slices->filter_by_type(S_TYPE_BOTTOMBRIDGE)}, @{$layer->regions} ); } if ($self->print_config->avoid_crossing_perimeters) { diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 15b98b518..493938979 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -538,6 +538,10 @@ sub build { title => 'Ooze prevention', options => [qw(ooze_prevention standby_temperature_delta)], }, + { + title => 'Advanced', + options => [qw(interface_shells)], + }, ]); $self->add_options_page('Advanced', 'wrench.png', optgroups => [ diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index baa775608..83a755ddb 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -324,7 +324,8 @@ sub prepare_fill_surfaces { $_->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)}; + $_->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 @@ -342,7 +343,7 @@ sub process_external_surfaces { my $margin = scale &Slic3r::EXTERNAL_INFILL_MARGIN; my @bottom = (); - foreach my $surface (grep $_->surface_type == S_TYPE_BOTTOM, @surfaces) { + foreach my $surface (grep $_->is_bottom, @surfaces) { my $grown = $surface->expolygon->offset_ex(+$margin); # detect bridge direction before merging grown surfaces otherwise adjacent bridges @@ -385,7 +386,7 @@ sub process_external_surfaces { } # subtract the new top surfaces from the other non-top surfaces and re-add them - my @other = grep $_->surface_type != S_TYPE_TOP && $_->surface_type != S_TYPE_BOTTOM, @surfaces; + 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 ], diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index a5ed5d18d..981999641 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -468,17 +468,21 @@ sub detect_surfaces_type { }; # comparison happens against the *full* slices (considering all regions) + # unless internal shells are requested my $upper_layer = $self->layers->[$i+1]; my $lower_layer = $i > 0 ? $self->layers->[$i-1] : undef; - my (@bottom, @top, @internal) = (); - # find top surfaces (difference between current surfaces # of current layer and upper one) + my @top = (); if ($upper_layer) { + my $upper_slices = $self->config->interface_shells + ? [ map $_->expolygon, @{$upper_layer->regions->[$region_id]->slices} ] + : $upper_layer->slices; + @top = $difference->( [ map $_->expolygon, @{$layerm->slices} ], - $upper_layer->slices, + $upper_slices, S_TYPE_TOP, ); } else { @@ -490,13 +494,30 @@ sub detect_surfaces_type { # find bottom surfaces (difference between current surfaces # of current layer and lower one) + my @bottom = (); if ($lower_layer) { - # lower layer's slices are already Surface objects - @bottom = $difference->( + # any surface lying on the void is a true bottom bridge + push @bottom, $difference->( [ map $_->expolygon, @{$layerm->slices} ], $lower_layer->slices, - S_TYPE_BOTTOM, + S_TYPE_BOTTOMBRIDGE, ); + + # if user requested internal shells, we need to identify surfaces + # lying on other slices not belonging to this region + if ($self->config->interface_shells) { + # non-bridging bottom surfaces: any part of this layer lying + # on something else, excluding those lying on our own region + my $supported = intersection_ex( + [ map @{$_->expolygon}, @{$layerm->slices} ], + [ map @$_, @{$lower_layer->slices} ], + ); + push @bottom, $difference->( + $supported, + [ map $_->expolygon, @{$lower_layer->regions->[$region_id]->slices} ], + S_TYPE_BOTTOM, + ); + } } else { # if no lower layer, all surfaces of this one are solid # we clone surfaces because we're going to clear the slices collection @@ -515,7 +536,7 @@ sub detect_surfaces_type { } # find internal surfaces (difference between top/bottom surfaces and others) - @internal = $difference->( + my @internal = $difference->( [ map $_->expolygon, @{$layerm->slices} ], [ map $_->expolygon, @top, @bottom ], S_TYPE_INTERNAL, @@ -703,7 +724,7 @@ sub discover_horizontal_shells { $_->surface_type(S_TYPE_INTERNALSOLID) for @{$layerm->fill_surfaces->filter_by_type(S_TYPE_INTERNAL)}; } - EXTERNAL: foreach my $type (S_TYPE_TOP, S_TYPE_BOTTOM) { + EXTERNAL: foreach my $type (S_TYPE_TOP, S_TYPE_BOTTOM, S_TYPE_BOTTOMBRIDGE) { # find slices of current type for current layer # use slices instead of fill_surfaces because they also include the perimeter area # which needs to be propagated in shells; we need to grow slices like we did for @@ -822,7 +843,7 @@ sub discover_horizontal_shells { (expolygon => $_, surface_type => S_TYPE_INTERNALSOLID), @$internal_solid); # assign top and bottom surfaces to layer - foreach my $s (@{Slic3r::Surface::Collection->new(grep { ($_->surface_type == S_TYPE_TOP) || ($_->surface_type == S_TYPE_BOTTOM) } @neighbor_fill_surfaces)->group}) { + foreach my $s (@{Slic3r::Surface::Collection->new(grep { ($_->surface_type == S_TYPE_TOP) || $_->is_bottom } @neighbor_fill_surfaces)->group}) { my $solid_surfaces = diff_ex( [ map $_->p, @$s ], [ map @$_, @$internal_solid, @$internal ], diff --git a/lib/Slic3r/Surface.pm b/lib/Slic3r/Surface.pm index 97a998b58..0c5bb5d23 100644 --- a/lib/Slic3r/Surface.pm +++ b/lib/Slic3r/Surface.pm @@ -4,7 +4,7 @@ use warnings; require Exporter; our @ISA = qw(Exporter); -our @EXPORT_OK = qw(S_TYPE_TOP S_TYPE_BOTTOM S_TYPE_INTERNAL S_TYPE_INTERNALSOLID S_TYPE_INTERNALBRIDGE S_TYPE_INTERNALVOID); +our @EXPORT_OK = qw(S_TYPE_TOP S_TYPE_BOTTOM S_TYPE_BOTTOMBRIDGE S_TYPE_INTERNAL S_TYPE_INTERNALSOLID S_TYPE_INTERNALBRIDGE S_TYPE_INTERNALVOID); our %EXPORT_TAGS = (types => \@EXPORT_OK); sub p { diff --git a/t/multi.t b/t/multi.t index c6b14324f..cf1885837 100644 --- a/t/multi.t +++ b/t/multi.t @@ -1,4 +1,4 @@ -use Test::More tests => 4; +use Test::More tests => 6; use strict; use warnings; @@ -92,7 +92,7 @@ use Slic3r::Test; my $print = Slic3r::Test::init_print($model, config => $config); my $tool = undef; my %T0_shells = my %T1_shells = (); # Z => 1 - Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { + Slic3r::GCode::Reader->new->parse(my $gcode = Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; if ($cmd =~ /^T(\d+)/) { @@ -115,6 +115,12 @@ use Slic3r::Test; is scalar(@$t0), 0, 'no interface shells'; is scalar(@$t1), 0, 'no interface shells'; } + { + $config->set('interface_shells', 1); + my ($t0, $t1) = $test->(); + is scalar(@$t0), $lower_config->top_solid_layers, 'top interface shells'; + is scalar(@$t1), $upper_config->bottom_solid_layers, 'bottom interface shells'; + } } __END__ diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index b7b4250d3..533805693 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -433,6 +433,12 @@ class PrintConfigDef Options["infill_speed"].aliases.push_back("print_feed_rate"); Options["infill_speed"].aliases.push_back("infill_feed_rate"); + Options["interface_shells"].type = coBool; + Options["interface_shells"].label = "Interface shells"; + Options["interface_shells"].tooltip = "Force the generation of solid shells between adjacent materials/volumes. Useful for multi-extruder prints with translucent materials or manual soluble support material."; + Options["interface_shells"].cli = "interface-shells!"; + Options["interface_shells"].category = "Layers and Perimeters"; + Options["layer_gcode"].type = coString; Options["layer_gcode"].label = "Layer change G-code"; Options["layer_gcode"].tooltip = "This custom code is inserted at every layer change, right after the Z move and before the extruder moves to the first layer point. Note that you can use placeholder variables for all Slic3r settings."; @@ -934,6 +940,7 @@ class PrintObjectConfig : public virtual StaticConfig ConfigOptionFloatOrPercent extrusion_width; ConfigOptionFloatOrPercent first_layer_height; ConfigOptionBool infill_only_where_needed; + ConfigOptionBool interface_shells; ConfigOptionFloat layer_height; ConfigOptionInt raft_layers; ConfigOptionBool support_material; @@ -957,6 +964,7 @@ class PrintObjectConfig : public virtual StaticConfig this->first_layer_height.value = 0.35; this->first_layer_height.percent = false; this->infill_only_where_needed.value = false; + this->interface_shells.value = false; this->layer_height.value = 0.4; this->raft_layers.value = 0; this->support_material.value = false; @@ -978,6 +986,7 @@ class PrintObjectConfig : public virtual StaticConfig if (opt_key == "extrusion_width") return &this->extrusion_width; if (opt_key == "first_layer_height") return &this->first_layer_height; if (opt_key == "infill_only_where_needed") return &this->infill_only_where_needed; + if (opt_key == "interface_shells") return &this->interface_shells; if (opt_key == "layer_height") return &this->layer_height; if (opt_key == "raft_layers") return &this->raft_layers; if (opt_key == "support_material") return &this->support_material; diff --git a/xs/src/Surface.cpp b/xs/src/Surface.cpp index 4f3b5b96a..1cc7cf586 100644 --- a/xs/src/Surface.cpp +++ b/xs/src/Surface.cpp @@ -13,6 +13,7 @@ Surface::is_solid() const { return this->surface_type == stTop || this->surface_type == stBottom + || this->surface_type == stBottomBridge || this->surface_type == stInternalSolid; } @@ -20,13 +21,21 @@ bool Surface::is_external() const { return this->surface_type == stTop - || this->surface_type == stBottom; + || this->surface_type == stBottom + || this->surface_type == stBottomBridge; +} + +bool +Surface::is_bottom() const +{ + return this->surface_type == stBottom + || this->surface_type == stBottomBridge; } bool Surface::is_bridge() const { - return this->surface_type == stBottom + return this->surface_type == stBottomBridge || this->surface_type == stInternalBridge; } diff --git a/xs/src/Surface.hpp b/xs/src/Surface.hpp index 6d6fec1de..162c9cb66 100644 --- a/xs/src/Surface.hpp +++ b/xs/src/Surface.hpp @@ -5,7 +5,7 @@ namespace Slic3r { -enum SurfaceType { stTop, stBottom, stInternal, stInternalSolid, stInternalBridge, stInternalVoid }; +enum SurfaceType { stTop, stBottom, stBottomBridge, stInternal, stInternalSolid, stInternalBridge, stInternalVoid }; class Surface { @@ -19,6 +19,7 @@ class Surface double area() const; bool is_solid() const; bool is_external() const; + bool is_bottom() const; bool is_bridge() const; #ifdef SLIC3RXS diff --git a/xs/xsp/Surface.xsp b/xs/xsp/Surface.xsp index 466ada1b7..0dbd66ad0 100644 --- a/xs/xsp/Surface.xsp +++ b/xs/xsp/Surface.xsp @@ -17,6 +17,7 @@ double area(); bool is_solid() const; bool is_external() const; + bool is_bottom() const; bool is_bridge() const; %{ @@ -104,6 +105,7 @@ _constant() ALIAS: S_TYPE_TOP = stTop S_TYPE_BOTTOM = stBottom + S_TYPE_BOTTOMBRIDGE = stBottomBridge S_TYPE_INTERNAL = stInternal S_TYPE_INTERNALSOLID = stInternalSolid S_TYPE_INTERNALBRIDGE = stInternalBridge