New option for forcing the generation of interface shells. #1847
This commit is contained in:
parent
6feff7fe53
commit
432a87e73d
10 changed files with 72 additions and 19 deletions
|
@ -114,7 +114,7 @@ sub change_layer {
|
||||||
if ($layer->id > 0 && ($self->print_config->overhangs || $self->print_config->start_perimeters_at_non_overhang)) {
|
if ($layer->id > 0 && ($self->print_config->overhangs || $self->print_config->start_perimeters_at_non_overhang)) {
|
||||||
$self->_layer_overhangs->append(
|
$self->_layer_overhangs->append(
|
||||||
# clone ExPolygons because they come from Surface objects but will be used outside here
|
# 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) {
|
if ($self->print_config->avoid_crossing_perimeters) {
|
||||||
|
|
|
@ -538,6 +538,10 @@ sub build {
|
||||||
title => 'Ooze prevention',
|
title => 'Ooze prevention',
|
||||||
options => [qw(ooze_prevention standby_temperature_delta)],
|
options => [qw(ooze_prevention standby_temperature_delta)],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title => 'Advanced',
|
||||||
|
options => [qw(interface_shells)],
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$self->add_options_page('Advanced', 'wrench.png', optgroups => [
|
$self->add_options_page('Advanced', 'wrench.png', optgroups => [
|
||||||
|
|
|
@ -324,7 +324,8 @@ sub prepare_fill_surfaces {
|
||||||
$_->surface_type(S_TYPE_INTERNAL) for @{$self->fill_surfaces->filter_by_type(S_TYPE_TOP)};
|
$_->surface_type(S_TYPE_INTERNAL) for @{$self->fill_surfaces->filter_by_type(S_TYPE_TOP)};
|
||||||
}
|
}
|
||||||
if ($self->config->bottom_solid_layers == 0) {
|
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
|
# 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 $margin = scale &Slic3r::EXTERNAL_INFILL_MARGIN;
|
||||||
|
|
||||||
my @bottom = ();
|
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);
|
my $grown = $surface->expolygon->offset_ex(+$margin);
|
||||||
|
|
||||||
# detect bridge direction before merging grown surfaces otherwise adjacent bridges
|
# 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
|
# 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}) {
|
foreach my $group (@{Slic3r::Surface::Collection->new(@other)->group}) {
|
||||||
push @new_surfaces, map $group->[0]->clone(expolygon => $_), @{diff_ex(
|
push @new_surfaces, map $group->[0]->clone(expolygon => $_), @{diff_ex(
|
||||||
[ map $_->p, @$group ],
|
[ map $_->p, @$group ],
|
||||||
|
|
|
@ -468,17 +468,21 @@ sub detect_surfaces_type {
|
||||||
};
|
};
|
||||||
|
|
||||||
# comparison happens against the *full* slices (considering all regions)
|
# comparison happens against the *full* slices (considering all regions)
|
||||||
|
# unless internal shells are requested
|
||||||
my $upper_layer = $self->layers->[$i+1];
|
my $upper_layer = $self->layers->[$i+1];
|
||||||
my $lower_layer = $i > 0 ? $self->layers->[$i-1] : undef;
|
my $lower_layer = $i > 0 ? $self->layers->[$i-1] : undef;
|
||||||
|
|
||||||
my (@bottom, @top, @internal) = ();
|
|
||||||
|
|
||||||
# find top surfaces (difference between current surfaces
|
# find top surfaces (difference between current surfaces
|
||||||
# of current layer and upper one)
|
# of current layer and upper one)
|
||||||
|
my @top = ();
|
||||||
if ($upper_layer) {
|
if ($upper_layer) {
|
||||||
|
my $upper_slices = $self->config->interface_shells
|
||||||
|
? [ map $_->expolygon, @{$upper_layer->regions->[$region_id]->slices} ]
|
||||||
|
: $upper_layer->slices;
|
||||||
|
|
||||||
@top = $difference->(
|
@top = $difference->(
|
||||||
[ map $_->expolygon, @{$layerm->slices} ],
|
[ map $_->expolygon, @{$layerm->slices} ],
|
||||||
$upper_layer->slices,
|
$upper_slices,
|
||||||
S_TYPE_TOP,
|
S_TYPE_TOP,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -490,13 +494,30 @@ sub detect_surfaces_type {
|
||||||
|
|
||||||
# find bottom surfaces (difference between current surfaces
|
# find bottom surfaces (difference between current surfaces
|
||||||
# of current layer and lower one)
|
# of current layer and lower one)
|
||||||
|
my @bottom = ();
|
||||||
if ($lower_layer) {
|
if ($lower_layer) {
|
||||||
# lower layer's slices are already Surface objects
|
# any surface lying on the void is a true bottom bridge
|
||||||
@bottom = $difference->(
|
push @bottom, $difference->(
|
||||||
[ map $_->expolygon, @{$layerm->slices} ],
|
[ map $_->expolygon, @{$layerm->slices} ],
|
||||||
$lower_layer->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 {
|
} else {
|
||||||
# if no lower layer, all surfaces of this one are solid
|
# if no lower layer, all surfaces of this one are solid
|
||||||
# we clone surfaces because we're going to clear the slices collection
|
# 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)
|
# find internal surfaces (difference between top/bottom surfaces and others)
|
||||||
@internal = $difference->(
|
my @internal = $difference->(
|
||||||
[ map $_->expolygon, @{$layerm->slices} ],
|
[ map $_->expolygon, @{$layerm->slices} ],
|
||||||
[ map $_->expolygon, @top, @bottom ],
|
[ map $_->expolygon, @top, @bottom ],
|
||||||
S_TYPE_INTERNAL,
|
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)};
|
$_->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
|
# find slices of current type for current layer
|
||||||
# use slices instead of fill_surfaces because they also include the perimeter area
|
# 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
|
# 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);
|
(expolygon => $_, surface_type => S_TYPE_INTERNALSOLID), @$internal_solid);
|
||||||
|
|
||||||
# assign top and bottom surfaces to layer
|
# 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(
|
my $solid_surfaces = diff_ex(
|
||||||
[ map $_->p, @$s ],
|
[ map $_->p, @$s ],
|
||||||
[ map @$_, @$internal_solid, @$internal ],
|
[ map @$_, @$internal_solid, @$internal ],
|
||||||
|
|
|
@ -4,7 +4,7 @@ use warnings;
|
||||||
|
|
||||||
require Exporter;
|
require Exporter;
|
||||||
our @ISA = qw(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);
|
our %EXPORT_TAGS = (types => \@EXPORT_OK);
|
||||||
|
|
||||||
sub p {
|
sub p {
|
||||||
|
|
10
t/multi.t
10
t/multi.t
|
@ -1,4 +1,4 @@
|
||||||
use Test::More tests => 4;
|
use Test::More tests => 6;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ use Slic3r::Test;
|
||||||
my $print = Slic3r::Test::init_print($model, config => $config);
|
my $print = Slic3r::Test::init_print($model, config => $config);
|
||||||
my $tool = undef;
|
my $tool = undef;
|
||||||
my %T0_shells = my %T1_shells = (); # Z => 1
|
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) = @_;
|
my ($self, $cmd, $args, $info) = @_;
|
||||||
|
|
||||||
if ($cmd =~ /^T(\d+)/) {
|
if ($cmd =~ /^T(\d+)/) {
|
||||||
|
@ -115,6 +115,12 @@ use Slic3r::Test;
|
||||||
is scalar(@$t0), 0, 'no interface shells';
|
is scalar(@$t0), 0, 'no interface shells';
|
||||||
is scalar(@$t1), 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__
|
__END__
|
||||||
|
|
|
@ -433,6 +433,12 @@ class PrintConfigDef
|
||||||
Options["infill_speed"].aliases.push_back("print_feed_rate");
|
Options["infill_speed"].aliases.push_back("print_feed_rate");
|
||||||
Options["infill_speed"].aliases.push_back("infill_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"].type = coString;
|
||||||
Options["layer_gcode"].label = "Layer change G-code";
|
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.";
|
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 extrusion_width;
|
||||||
ConfigOptionFloatOrPercent first_layer_height;
|
ConfigOptionFloatOrPercent first_layer_height;
|
||||||
ConfigOptionBool infill_only_where_needed;
|
ConfigOptionBool infill_only_where_needed;
|
||||||
|
ConfigOptionBool interface_shells;
|
||||||
ConfigOptionFloat layer_height;
|
ConfigOptionFloat layer_height;
|
||||||
ConfigOptionInt raft_layers;
|
ConfigOptionInt raft_layers;
|
||||||
ConfigOptionBool support_material;
|
ConfigOptionBool support_material;
|
||||||
|
@ -957,6 +964,7 @@ class PrintObjectConfig : public virtual StaticConfig
|
||||||
this->first_layer_height.value = 0.35;
|
this->first_layer_height.value = 0.35;
|
||||||
this->first_layer_height.percent = false;
|
this->first_layer_height.percent = false;
|
||||||
this->infill_only_where_needed.value = false;
|
this->infill_only_where_needed.value = false;
|
||||||
|
this->interface_shells.value = false;
|
||||||
this->layer_height.value = 0.4;
|
this->layer_height.value = 0.4;
|
||||||
this->raft_layers.value = 0;
|
this->raft_layers.value = 0;
|
||||||
this->support_material.value = false;
|
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 == "extrusion_width") return &this->extrusion_width;
|
||||||
if (opt_key == "first_layer_height") return &this->first_layer_height;
|
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 == "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 == "layer_height") return &this->layer_height;
|
||||||
if (opt_key == "raft_layers") return &this->raft_layers;
|
if (opt_key == "raft_layers") return &this->raft_layers;
|
||||||
if (opt_key == "support_material") return &this->support_material;
|
if (opt_key == "support_material") return &this->support_material;
|
||||||
|
|
|
@ -13,6 +13,7 @@ Surface::is_solid() const
|
||||||
{
|
{
|
||||||
return this->surface_type == stTop
|
return this->surface_type == stTop
|
||||||
|| this->surface_type == stBottom
|
|| this->surface_type == stBottom
|
||||||
|
|| this->surface_type == stBottomBridge
|
||||||
|| this->surface_type == stInternalSolid;
|
|| this->surface_type == stInternalSolid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,13 +21,21 @@ bool
|
||||||
Surface::is_external() const
|
Surface::is_external() const
|
||||||
{
|
{
|
||||||
return this->surface_type == stTop
|
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
|
bool
|
||||||
Surface::is_bridge() const
|
Surface::is_bridge() const
|
||||||
{
|
{
|
||||||
return this->surface_type == stBottom
|
return this->surface_type == stBottomBridge
|
||||||
|| this->surface_type == stInternalBridge;
|
|| this->surface_type == stInternalBridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
enum SurfaceType { stTop, stBottom, stInternal, stInternalSolid, stInternalBridge, stInternalVoid };
|
enum SurfaceType { stTop, stBottom, stBottomBridge, stInternal, stInternalSolid, stInternalBridge, stInternalVoid };
|
||||||
|
|
||||||
class Surface
|
class Surface
|
||||||
{
|
{
|
||||||
|
@ -19,6 +19,7 @@ class Surface
|
||||||
double area() const;
|
double area() const;
|
||||||
bool is_solid() const;
|
bool is_solid() const;
|
||||||
bool is_external() const;
|
bool is_external() const;
|
||||||
|
bool is_bottom() const;
|
||||||
bool is_bridge() const;
|
bool is_bridge() const;
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
double area();
|
double area();
|
||||||
bool is_solid() const;
|
bool is_solid() const;
|
||||||
bool is_external() const;
|
bool is_external() const;
|
||||||
|
bool is_bottom() const;
|
||||||
bool is_bridge() const;
|
bool is_bridge() const;
|
||||||
%{
|
%{
|
||||||
|
|
||||||
|
@ -104,6 +105,7 @@ _constant()
|
||||||
ALIAS:
|
ALIAS:
|
||||||
S_TYPE_TOP = stTop
|
S_TYPE_TOP = stTop
|
||||||
S_TYPE_BOTTOM = stBottom
|
S_TYPE_BOTTOM = stBottom
|
||||||
|
S_TYPE_BOTTOMBRIDGE = stBottomBridge
|
||||||
S_TYPE_INTERNAL = stInternal
|
S_TYPE_INTERNAL = stInternal
|
||||||
S_TYPE_INTERNALSOLID = stInternalSolid
|
S_TYPE_INTERNALSOLID = stInternalSolid
|
||||||
S_TYPE_INTERNALBRIDGE = stInternalBridge
|
S_TYPE_INTERNALBRIDGE = stInternalBridge
|
||||||
|
|
Loading…
Reference in a new issue