New feature: ability to override specific settings for individual objects in the plater. #344

This commit is contained in:
Alessandro Ranellucci 2013-08-25 14:37:50 +02:00
parent 2dd6325bf8
commit 3d6fb1b05c
12 changed files with 248 additions and 55 deletions

View file

@ -397,9 +397,12 @@ our $Options = {
default => 0.35,
},
'infill_every_layers' => {
label => 'Infill every',
label => 'Combine infill every',
full_label => 'Combine infill every n layers',
tooltip => 'This feature allows to combine infill and speed up your print by extruding thicker infill layers while preserving thin perimeters, thus accuracy.',
sidetext => 'layers',
scope => 'object',
category => 'Infill',
cli => 'infill-every-layers=i',
type => 'i',
min => 1,
@ -409,6 +412,8 @@ our $Options = {
label => 'Solid infill every',
tooltip => 'This feature allows to force a solid layer every given number of layers. Zero to disable.',
sidetext => 'layers',
scope => 'object',
category => 'Infill',
cli => 'solid-infill-every-layers=i',
type => 'i',
min => 0,
@ -417,6 +422,8 @@ our $Options = {
'infill_only_where_needed' => {
label => 'Only infill where needed',
tooltip => 'This option will limit infill to the areas actually needed for supporting ceilings (it will act as internal support material).',
scope => 'object',
category => 'Infill',
cli => 'infill-only-where-needed!',
type => 'bool',
default => 0,
@ -506,7 +513,9 @@ our $Options = {
# print options
'perimeters' => {
label => 'Perimeters (minimum)',
tooltip => 'This option sets the number of perimeters to generate for each layer. Note that Slic3r will increase this number automatically when it detects sloping surfaces which benefit from a higher number of perimeters.',
tooltip => 'This option sets the number of perimeters to generate for each layer. Note that Slic3r may increase this number automatically when it detects sloping surfaces which benefit from a higher number of perimeters if the Extra Perimeters option is enabled.',
scope => 'object',
category => 'Layers and Perimeters',
cli => 'perimeters=i',
type => 'i',
aliases => [qw(perimeter_offsets)],
@ -521,14 +530,20 @@ our $Options = {
},
'top_solid_layers' => {
label => 'Top',
full_label => 'Top solid layers',
tooltip => 'Number of solid layers to generate on top surfaces.',
scope => 'object',
category => 'Layers and Perimeters',
cli => 'top-solid-layers=i',
type => 'i',
default => 3,
},
'bottom_solid_layers' => {
label => 'Bottom',
full_label => 'Bottom solid layers',
tooltip => 'Number of solid layers to generate on bottom surfaces.',
scope => 'object',
category => 'Layers and Perimeters',
cli => 'bottom-solid-layers=i',
type => 'i',
default => 3,
@ -536,6 +551,8 @@ our $Options = {
'fill_pattern' => {
label => 'Fill pattern',
tooltip => 'Fill pattern for general low-density infill.',
scope => 'object',
category => 'Infill',
cli => 'fill-pattern=s',
type => 'select',
values => [qw(rectilinear line concentric honeycomb hilbertcurve archimedeanchords octagramspiral)],
@ -545,6 +562,8 @@ our $Options = {
'solid_fill_pattern' => {
label => 'Top/bottom fill pattern',
tooltip => 'Fill pattern for top/bottom infill.',
scope => 'object',
category => 'Infill',
cli => 'solid-fill-pattern=s',
type => 'select',
values => [qw(rectilinear concentric hilbertcurve archimedeanchords octagramspiral)],
@ -554,6 +573,8 @@ our $Options = {
'fill_density' => {
label => 'Fill density',
tooltip => 'Density of internal infill, expressed in the range 0 - 1.',
scope => 'object',
category => 'Infill',
cli => 'fill-density=f',
type => 'f',
default => 0.4,
@ -571,6 +592,8 @@ our $Options = {
label => 'Solid infill threshold area',
tooltip => 'Force solid infill for regions having a smaller area than the specified threshold.',
sidetext => 'mm²',
scope => 'object',
category => 'Infill',
cli => 'solid-infill-below-area=f',
type => 'f',
default => 70,
@ -578,6 +601,8 @@ our $Options = {
'extra_perimeters' => {
label => 'Extra perimeters if needed',
tooltip => 'Add more perimeters when needed for avoiding gaps in sloping walls.',
scope => 'object',
category => 'Layers and Perimeters',
cli => 'extra-perimeters!',
type => 'bool',
default => 1,
@ -606,6 +631,8 @@ our $Options = {
'thin_walls' => {
label => 'Detect thin walls',
tooltip => 'Detect single-width walls (parts where two extrusions don\'t fit and we need to collapse them into a single trace).',
scope => 'object',
category => 'Layers and Perimeters',
cli => 'thin-walls!',
type => 'bool',
default => 1,
@ -613,6 +640,8 @@ our $Options = {
'overhangs' => {
label => 'Detect overhangs',
tooltip => 'Experimental option to adjust flow for overhangs (bridge flow will be used), to apply bridge speed to them and enable fan.',
scope => 'object',
category => 'Layers and Perimeters',
cli => 'overhangs!',
type => 'bool',
default => 1,
@ -647,6 +676,8 @@ our $Options = {
},
'support_material' => {
label => 'Generate support material',
scope => 'object',
category => 'Support material',
tooltip => 'Enable support material generation.',
cli => 'support-material!',
type => 'bool',
@ -656,6 +687,8 @@ our $Options = {
label => 'Overhang threshold',
tooltip => 'Support material will not generated for overhangs whose slope angle is above the given threshold. Set to zero for automatic detection.',
sidetext => '°',
scope => 'object',
category => 'Support material',
cli => 'support-material-threshold=i',
type => 'i',
default => 0,
@ -663,6 +696,8 @@ our $Options = {
'support_material_pattern' => {
label => 'Pattern',
tooltip => 'Pattern used to generate support material.',
scope => 'object',
category => 'Support material',
cli => 'support-material-pattern=s',
type => 'select',
values => [qw(rectilinear rectilinear-grid honeycomb)],
@ -673,6 +708,8 @@ our $Options = {
label => 'Pattern spacing',
tooltip => 'Spacing between support material lines.',
sidetext => 'mm',
scope => 'object',
category => 'Support material',
cli => 'support-material-spacing=f',
type => 'f',
default => 2.5,
@ -680,6 +717,8 @@ our $Options = {
'support_material_angle' => {
label => 'Pattern angle',
tooltip => 'Use this setting to rotate the support material pattern on the horizontal plane.',
scope => 'object',
category => 'Support material',
sidetext => '°',
cli => 'support-material-angle=i',
type => 'i',
@ -689,6 +728,8 @@ our $Options = {
label => 'Interface layers',
tooltip => 'Number of interface layers to insert between the object(s) and support material.',
sidetext => 'layers',
scope => 'object',
category => 'Support material',
cli => 'support-material-interface-layers=i',
type => 'i',
default => 3,
@ -696,6 +737,8 @@ our $Options = {
'support_material_interface_spacing' => {
label => 'Interface pattern spacing',
tooltip => 'Spacing between interface lines. Set zero to get a solid interface.',
scope => 'object',
category => 'Support material',
sidetext => 'mm',
cli => 'support-material-interface-spacing=f',
type => 'f',
@ -703,8 +746,11 @@ our $Options = {
},
'support_material_enforce_layers' => {
label => 'Enforce support for the first',
full_label => 'Enforce support for the first n layers',
tooltip => 'Generate support material for the specified number of layers counting from bottom, regardless of whether normal support material is enabled or not and regardless of any angle threshold. This is useful for getting more adhesion of objects having a very thin or poor footprint on the build plate.',
sidetext => 'layers',
scope => 'object',
category => 'Support material',
cli => 'support-material-enforce-layers=f',
type => 'i',
default => 0,
@ -713,6 +759,8 @@ our $Options = {
label => 'Raft layers',
tooltip => 'The object will be raised by this number of layers, and support material will be generated under it.',
sidetext => 'layers',
scope => 'object',
category => 'Support material',
cli => 'raft-layers=i',
type => 'i',
default => 0,
@ -1098,7 +1146,7 @@ sub new {
sub new_from_defaults {
my $class = shift;
my @opt_keys =
return $class->new(
map { $_ => $Options->{$_}{default} }
grep !$Options->{$_}{shortcut},

View file

@ -50,12 +50,13 @@ sub make_fill {
my ($layerm) = @_;
Slic3r::debugf "Filling layer %d:\n", $layerm->id;
my $fill_density = $layerm->config->fill_density;
my @surfaces = ();
# if hollow object is requested, remove internal surfaces
# (this needs to be done after internal-solid shells are created)
if ($Slic3r::Config->fill_density == 0) {
if ($fill_density == 0) {
@surfaces = grep $_->surface_type != S_TYPE_INTERNAL, @surfaces;
}
@ -140,8 +141,8 @@ sub make_fill {
my @fills_ordering_points = ();
SURFACE: foreach my $surface (@surfaces) {
next if $surface->surface_type == S_TYPE_INTERNALVOID;
my $filler = $Slic3r::Config->fill_pattern;
my $density = $Slic3r::Config->fill_density;
my $filler = $layerm->config->fill_pattern;
my $density = $fill_density;
my $flow = ($surface->surface_type == S_TYPE_TOP)
? $layerm->top_infill_flow
: $surface->is_solid
@ -154,7 +155,7 @@ sub make_fill {
# force 100% density and rectilinear fill for external surfaces
if ($surface->surface_type != S_TYPE_INTERNAL) {
$density = 1;
$filler = $Slic3r::Config->solid_fill_pattern;
$filler = $layerm->config->solid_fill_pattern;
if ($is_bridge) {
$filler = 'rectilinear';
$flow_spacing = $layerm->extruders->{infill}->bridge_flow->spacing;

View file

@ -108,7 +108,7 @@ sub change_layer {
$self->_upper_layer_islands([]);
}
$self->_layer_overhangs(
$layer->id > 0 && ($Slic3r::Config->overhangs || $Slic3r::Config->start_perimeters_at_non_overhang)
$layer->id > 0 && ($layer->config->overhangs || $Slic3r::Config->start_perimeters_at_non_overhang)
? [ map $_->expolygon, grep $_->surface_type == S_TYPE_BOTTOM, map @{$_->slices}, @{$layer->regions} ]
: []
);
@ -227,7 +227,7 @@ sub extrude_loop {
my @paths = ();
# detect overhanging/bridging perimeters
if ($Slic3r::Config->overhangs && $extrusion_path->is_perimeter && @{$self->_layer_overhangs}) {
if ($self->layer->config->overhangs && $extrusion_path->is_perimeter && @{$self->_layer_overhangs}) {
# get non-overhang paths by subtracting overhangs from the loop
push @paths,
$extrusion_path->subtract_expolygons($self->_layer_overhangs);
@ -256,7 +256,7 @@ sub extrude_loop {
$self->wipe_path($extrusion_path->polyline) if $self->enable_wipe;
# make a little move inwards before leaving loop
if ($loop->role == EXTR_ROLE_EXTERNAL_PERIMETER && $self->config->perimeters > 1) {
if ($loop->role == EXTR_ROLE_EXTERNAL_PERIMETER && defined $self->layer && $self->layer->object->config->perimeters > 1) {
# detect angle between last and first segment
# the side depends on the original winding order of the polygon (left for contours, right for holes)
my @points = $was_clockwise ? (-2, 1) : (1, -2);

View file

@ -17,7 +17,7 @@ use Slic3r::GUI::Tab;
our $have_OpenGL = eval "use Slic3r::GUI::PreviewCanvas; 1";
use Wx 0.9901 qw(:bitmap :dialog :frame :icon :id :misc :systemsettings :toplevelwindow);
use Wx::Event qw(EVT_CLOSE EVT_MENU);
use Wx::Event qw(EVT_CLOSE EVT_MENU EVT_IDLE);
use base 'Wx::App';
use constant MI_LOAD_CONF => &Wx::NewId;
@ -47,6 +47,7 @@ our $datadir;
our $no_plater;
our $mode;
our $autosave;
our @cb;
our $Settings = {
_ => {
@ -210,6 +211,12 @@ sub OnInit {
&& ($Settings->{_}{version_check} // 1)
&& (!$Settings->{_}{last_version_check} || (time - $Settings->{_}{last_version_check}) >= 86400);
EVT_IDLE($frame, sub {
while (my $cb = shift @cb) {
$cb->();
}
});
return 1;
}
@ -324,6 +331,12 @@ sub output_path {
: $dir;
}
sub CallAfter {
my $class = shift;
my ($cb) = @_;
push @cb, $cb;
}
package Slic3r::GUI::ProgressStatusBar;
use Wx qw(:gauge :misc);
use base 'Wx::StatusBar';

View file

@ -36,6 +36,7 @@ Slic3r::GUI::OptionsGroup - pre-filled Wx::StaticBoxSizer wrapper containing one
on_change => sub { print "new value for $_[0] is $_[1]\n" },
no_labels => 0,
label_width => 180,
extra_column => sub { ... },
);
$sizer->Add($optgroup->sizer);
@ -48,6 +49,7 @@ has 'lines' => (is => 'lazy');
has 'on_change' => (is => 'ro', default => sub { sub {} });
has 'no_labels' => (is => 'ro', default => sub { 0 });
has 'label_width' => (is => 'ro', default => sub { 180 });
has 'extra_column' => (is => 'ro');
has 'sizer' => (is => 'rw');
has '_triggers' => (is => 'ro', default => sub { {} });
@ -63,7 +65,8 @@ sub BUILD {
$self->sizer(Wx::StaticBoxSizer->new($box, wxVERTICAL));
}
my $grid_sizer = Wx::FlexGridSizer->new(scalar(@{$self->options}), 2, 0, 0);
my $num_columns = $self->extra_column ? 3 : 2;
my $grid_sizer = Wx::FlexGridSizer->new(scalar(@{$self->options}), $num_columns, 0, 0);
$grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
$grid_sizer->AddGrowableCol($self->no_labels ? 0 : 1);
@ -113,6 +116,10 @@ sub _build_line {
my $self = shift;
my ($line, $grid_sizer) = @_;
if ($self->extra_column) {
$grid_sizer->Add($self->extra_column->($line), 0, wxALIGN_CENTER_VERTICAL, 0);
}
my $label;
if (!$self->no_labels) {
$label = Wx::StaticText->new($self->parent, -1, $line->{label} ? "$line->{label}:" : "", wxDefaultPosition, [$self->label_width, -1]);
@ -291,6 +298,7 @@ Slic3r::GUI::ConfigOptionsGroup - pre-filled Wx::StaticBoxSizer wrapper containi
use List::Util qw(first);
has 'config' => (is => 'ro', required => 1);
has 'full_labels' => (is => 'ro', default => sub {0});
sub _trigger_options {
my $self = shift;
@ -304,7 +312,8 @@ sub _trigger_options {
$opt = {
opt_key => $full_key,
config => 1,
(map { $_ => $config_opt->{$_} } qw(type label tooltip sidetext width height full_width min max labels values multiline readonly)),
label => ($self->full_labels && defined $config_opt->{full_label}) ? $config_opt->{full_label} : $config_opt->{label},
(map { $_ => $config_opt->{$_} } qw(type tooltip sidetext width height full_width min max labels values multiline readonly)),
default => $self->_get_config($opt_key, $index),
on_change => sub { $self->_set_config($opt_key, $index, $_[0]) },
};

View file

@ -713,6 +713,7 @@ sub make_model {
my $new_model_object = $model->add_object(
vertices => $model_object->vertices,
input_file => $plater_object->input_file,
config => $plater_object->config,
layer_height_ranges => $plater_object->layer_height_ranges,
);
foreach my $volume (@{$model_object->volumes}) {
@ -1090,6 +1091,7 @@ has 'instances' => (is => 'rw', default => sub { [] }); # upward Y a
has 'thumbnail' => (is => 'rw', trigger => \&_transform_thumbnail);
has 'transformed_thumbnail' => (is => 'rw');
has 'thumbnail_scaling_factor' => (is => 'rw', trigger => \&_transform_thumbnail);
has 'config' => (is => 'rw', default => sub { Slic3r::Config->new });
has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ]
has 'mesh_stats' => (is => 'rw');

View file

@ -17,11 +17,13 @@ sub new {
$self->{tabpanel}->AddPage($self->{preview} = Slic3r::GUI::Plater::ObjectDialog::PreviewTab->new($self->{tabpanel}, object => $self->{object}), "Preview")
if $Slic3r::GUI::have_OpenGL;
$self->{tabpanel}->AddPage($self->{info} = Slic3r::GUI::Plater::ObjectDialog::InfoTab->new($self->{tabpanel}, object => $self->{object}), "Info");
$self->{tabpanel}->AddPage($self->{settings} = Slic3r::GUI::Plater::ObjectDialog::SettingsTab->new($self->{tabpanel}, object => $self->{object}), "Settings");
$self->{tabpanel}->AddPage($self->{layers} = Slic3r::GUI::Plater::ObjectDialog::LayersTab->new($self->{tabpanel}, object => $self->{object}), "Layers");
my $buttons = $self->CreateStdDialogButtonSizer(wxOK);
EVT_BUTTON($self, wxID_OK, sub {
# validate user input
return if !$self->{settings}->CanClose;
return if !$self->{layers}->CanClose;
# notify tabs
@ -124,6 +126,111 @@ sub new {
return $self;
}
package Slic3r::GUI::Plater::ObjectDialog::SettingsTab;
use Wx qw(:dialog :id :misc :sizer :systemsettings :button :icon);
use Wx::Grid;
use Wx::Event qw(EVT_BUTTON);
use base 'Wx::Panel';
sub new {
my $class = shift;
my ($parent, %params) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize);
$self->{object} = $params{object};
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
# descriptive text
{
my $label = Wx::StaticText->new($self, -1, "You can use this section to override some settings just for this object.",
wxDefaultPosition, [-1, 25]);
$label->SetFont(Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
$self->{sizer}->Add($label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
}
# option selector
{
# get all options with object scope and sort them by category+label
my %settings = map { $_ => sprintf('%s > %s', $Slic3r::Config::Options->{$_}{category}, $Slic3r::Config::Options->{$_}{full_label} // $Slic3r::Config::Options->{$_}{label}) }
grep { ($Slic3r::Config::Options->{$_}{scope} // '') eq 'object' }
keys %$Slic3r::Config::Options;
$self->{options} = [ sort { $settings{$a} cmp $settings{$b} } keys %settings ];
my $choice = Wx::Choice->new($self, -1, wxDefaultPosition, [150, -1], [ map $settings{$_}, @{$self->{options}} ]);
# create the button
my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/add.png", wxBITMAP_TYPE_PNG));
EVT_BUTTON($self, $btn, sub {
my $opt_key = $self->{options}[$choice->GetSelection];
$self->{object}->config->apply(Slic3r::Config->new_from_defaults($opt_key));
$self->update_optgroup;
});
my $h_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
$h_sizer->Add($choice, 1, wxEXPAND | wxALL, 0);
$h_sizer->Add($btn, 0, wxEXPAND | wxLEFT, 10);
$self->{sizer}->Add($h_sizer, 0, wxEXPAND | wxALL, 10);
}
$self->{options_sizer} = Wx::BoxSizer->new(wxVERTICAL);
$self->{sizer}->Add($self->{options_sizer}, 0, wxEXPAND | wxALL, 10);
$self->update_optgroup;
$self->SetSizer($self->{sizer});
$self->{sizer}->SetSizeHints($self);
return $self;
}
sub update_optgroup {
my $self = shift;
$self->{options_sizer}->Clear(1);
my $config = $self->{object}->config;
my %categories = ();
foreach my $opt_key (keys %$config) {
my $category = $Slic3r::Config::Options->{$opt_key}{category};
$categories{$category} ||= [];
push @{$categories{$category}}, $opt_key;
}
foreach my $category (sort keys %categories) {
my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
parent => $self,
title => $category,
config => $config,
options => [ sort @{$categories{$category}} ],
full_labels => 1,
extra_column => sub {
my ($line) = @_;
my ($opt_key) = @{$line->{options}}; # we assume that we have one option per line
my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG));
EVT_BUTTON($self, $btn, sub {
delete $self->{object}->config->{$opt_key};
Slic3r::GUI->CallAfter(sub { $self->update_optgroup });
});
return $btn;
},
);
$self->{options_sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM, 10);
}
$self->Layout;
}
sub CanClose {
my $self = shift;
# validate options before allowing user to dismiss the dialog
# the validate method only works on full configs so we have
# to merge our settings with the default ones
my $config = Slic3r::Config->merge($self->GetParent->GetParent->GetParent->GetParent->GetParent->config, $self->{object}->config);
eval {
$config->validate;
};
return 0 if Slic3r::GUI::catch_error($self);
return 1;
}
package Slic3r::GUI::Plater::ObjectDialog::LayersTab;
use Wx qw(:dialog :id :misc :sizer :systemsettings);
use Wx::Grid;

View file

@ -6,7 +6,7 @@ use Slic3r::Geometry qw(scale);
use Slic3r::Geometry::Clipper qw(union_ex);
has 'id' => (is => 'rw', required => 1, trigger => 1); # sequential number of layer, 0-based
has 'object' => (is => 'ro', weak_ref => 1, required => 1);
has 'object' => (is => 'ro', weak_ref => 1, required => 1, handles => [qw(print config)]);
has 'regions' => (is => 'ro', default => sub { [] });
has 'slicing_errors' => (is => 'rw');

View file

@ -14,7 +14,7 @@ has 'layer' => (
weak_ref => 1,
required => 1,
trigger => 1,
handles => [qw(id slice_z print_z height flow)],
handles => [qw(id slice_z print_z height flow object print config)],
);
has 'region' => (is => 'ro', required => 1, handles => [qw(extruders)]);
has 'perimeter_flow' => (is => 'rw');
@ -103,7 +103,7 @@ sub make_surfaces {
}
# detect thin walls by offsetting slices by half extrusion inwards
if ($Slic3r::Config->thin_walls) {
if ($self->config->thin_walls) {
$self->thin_walls([]);
# we use spacing here because there could be a case where
# the slice collapses with width but doesn't collapse with spacing,
@ -183,7 +183,7 @@ sub make_perimeters {
# extra perimeters for each one
foreach my $surface (@{$self->slices}) {
# detect how many perimeters must be generated for this island
my $loop_number = $Slic3r::Config->perimeters + ($surface->extra_perimeters || 0);
my $loop_number = $self->config->perimeters + ($surface->extra_perimeters || 0);
# generate loops
# (one more than necessary so that we can detect gaps even after the desired
@ -203,7 +203,7 @@ sub make_perimeters {
# where offset2() collapses the expolygon, then there's no room for an inner loop
# and we can extract the gap for later processing
if ($Slic3r::Config->gap_fill_speed > 0 && $Slic3r::Config->fill_density > 0) {
if ($Slic3r::Config->gap_fill_speed > 0 && $self->object->config->fill_density > 0) {
my $diff = diff_ex(
[ offset(\@last, -0.5*$spacing) ],
# +2 on the offset here makes sure that Clipper float truncation
@ -411,16 +411,16 @@ sub prepare_fill_surfaces {
my $self = shift;
# if no solid layers are requested, turn top/bottom surfaces to internal
if ($Slic3r::Config->top_solid_layers == 0) {
if ($self->config->top_solid_layers == 0) {
$_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type == S_TYPE_TOP, @{$self->fill_surfaces};
}
if ($Slic3r::Config->bottom_solid_layers == 0) {
if ($self->config->bottom_solid_layers == 0) {
$_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type == S_TYPE_BOTTOM, @{$self->fill_surfaces};
}
# turn too small internal regions into solid regions according to the user setting
if ($Slic3r::Config->fill_density > 0) {
my $min_area = scale scale $Slic3r::Config->solid_infill_below_area; # scaling an area requires two calls!
if ($self->object->config->fill_density > 0) {
my $min_area = scale scale $self->config->solid_infill_below_area; # scaling an area requires two calls!
my @small = grep $_->surface_type == S_TYPE_INTERNAL && $_->expolygon->contour->area <= $min_area, @{$self->fill_surfaces};
$_->surface_type(S_TYPE_INTERNALSOLID) for @small;
Slic3r::debugf "identified %d small solid surfaces at layer %d\n", scalar(@small), $self->id if @small > 0;
@ -459,7 +459,7 @@ sub process_external_surfaces {
# if we're slicing with no infill, we can't extend external surfaces
# over non-existent infill
my @fill_boundaries = $Slic3r::Config->fill_density > 0
my @fill_boundaries = $self->object->config->fill_density > 0
? @{$self->fill_surfaces}
: grep $_->surface_type != S_TYPE_INTERNAL, @{$self->fill_surfaces};

View file

@ -35,6 +35,7 @@ sub merge {
my $new_object = $new_model->add_object(
input_file => $object->input_file,
vertices => $object->vertices,
config => $object->config,
layer_height_ranges => $object->layer_height_ranges,
);
@ -255,6 +256,7 @@ sub split_meshes {
foreach my $mesh ($volume->mesh->split_mesh) {
my $new_object = $self->add_object(
input_file => $object->input_file,
config => $object->config,
layer_height_ranges => $object->layer_height_ranges,
);
$new_object->add_volume(
@ -302,6 +304,7 @@ has 'model' => (is => 'ro', weak_ref => 1, required => 1);
has 'vertices' => (is => 'ro', default => sub { [] });
has 'volumes' => (is => 'ro', default => sub { [] });
has 'instances' => (is => 'rw');
has 'config' => (is => 'rw', default => sub { Slic3r::Config->new });
has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ]
has 'mesh_stats' => (is => 'rw');
has '_bounding_box' => (is => 'rw');

View file

@ -80,9 +80,9 @@ sub _trigger_config {
sub _build_has_support_material {
my $self = shift;
return $self->config->support_material
|| $self->config->raft_layers > 0
|| $self->config->support_material_enforce_layers > 0;
return (first { $_->config->support_material } @{$self->objects})
|| (first { $_->config->raft_layers > 0 } @{$self->objects})
|| (first { $_->config->support_material_enforce_layers > 0 } @{$self->objects});
}
# caller is responsible for supplying models whose objects don't collide
@ -160,6 +160,7 @@ sub add_model {
],
size => $bb->size, # transformed size
input_file => $object->input_file,
config_overrides => $object->config,
layer_height_ranges => $object->layer_height_ranges,
);
}

View file

@ -15,18 +15,22 @@ has 'size' => (is => 'rw', required => 1); # XYZ in scaled coordina
has 'copies' => (is => 'rw', trigger => 1); # in scaled coordinates
has 'layers' => (is => 'rw', default => sub { [] });
has 'support_layers' => (is => 'rw', default => sub { [] });
has 'config_overrides' => (is => 'rw', default => sub { Slic3r::Config->new });
has 'config' => (is => 'rw');
has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ]
has 'fill_maker' => (is => 'lazy');
has '_slice_z_table' => (is => 'lazy');
sub BUILD {
my $self = shift;
$self->init_config;
# make layers taking custom heights into account
my $print_z = my $slice_z = my $height = 0;
# add raft layers
for my $id (0 .. $Slic3r::Config->raft_layers-1) {
for my $id (0 .. $self->config->raft_layers-1) {
$height = ($id == 0)
? $Slic3r::Config->get_value('first_layer_height')
: $Slic3r::Config->layer_height;
@ -99,6 +103,11 @@ sub _trigger_copies {
@{$self->copies} = @{chained_path_points($self->copies)}
}
sub init_config {
my $self = shift;
$self->config(Slic3r::Config->merge($self->print->config, $self->config_overrides));
}
sub layer_count {
my $self = shift;
return scalar @{ $self->layers };
@ -249,7 +258,7 @@ sub slice {
}
# remove empty layers from bottom
my $first_object_layer_id = $Slic3r::Config->raft_layers;
my $first_object_layer_id = $self->config->raft_layers;
while (@{$self->layers} && !@{$self->layers->[$first_object_layer_id]->slices} && !map @{$_->thin_walls}, @{$self->layers->[$first_object_layer_id]->regions}) {
splice @{$self->layers}, $first_object_layer_id, 1;
for (my $i = $first_object_layer_id; $i <= $#{$self->layers}; $i++) {
@ -268,7 +277,7 @@ sub make_perimeters {
# but we don't generate any extra perimeter if fill density is zero, as they would be floating
# inside the object - infill_only_where_needed should be the method of choice for printing
# hollow objects
if ($Slic3r::Config->extra_perimeters && $Slic3r::Config->perimeters > 0 && $Slic3r::Config->fill_density > 0) {
if ($self->config->extra_perimeters && $self->config->perimeters > 0 && $self->config->fill_density > 0) {
for my $region_id (0 .. ($self->print->regions_count-1)) {
for my $layer_id (0 .. $self->layer_count-2) {
my $layerm = $self->layers->[$layer_id]->regions->[$region_id];
@ -278,7 +287,7 @@ sub make_perimeters {
my $overlap = $perimeter_spacing; # one perimeter
my $diff = diff(
[ offset([ map @{$_->expolygon}, @{$layerm->slices} ], -($Slic3r::Config->perimeters * $perimeter_spacing)) ],
[ offset([ map @{$_->expolygon}, @{$layerm->slices} ], -($self->config->perimeters * $perimeter_spacing)) ],
[ offset([ map @{$_->expolygon}, @{$upper_layerm->slices} ], -$overlap) ],
);
next if !@$diff;
@ -299,8 +308,8 @@ sub make_perimeters {
# of our slice
$extra_perimeters++;
my $hypothetical_perimeter = diff(
[ offset($slice->expolygon, -($perimeter_spacing * ($Slic3r::Config->perimeters + $extra_perimeters-1))) ],
[ offset($slice->expolygon, -($perimeter_spacing * ($Slic3r::Config->perimeters + $extra_perimeters))) ],
[ offset($slice->expolygon, -($perimeter_spacing * ($self->config->perimeters + $extra_perimeters-1))) ],
[ offset($slice->expolygon, -($perimeter_spacing * ($self->config->perimeters + $extra_perimeters))) ],
);
last CYCLE if !@$hypothetical_perimeter; # no extra perimeter is possible
@ -451,7 +460,7 @@ sub detect_surfaces_type {
sub clip_fill_surfaces {
my $self = shift;
return unless $Slic3r::Config->infill_only_where_needed;
return unless $self->config->infill_only_where_needed;
# We only want infill under ceilings; this is almost like an
# internal support material.
@ -498,7 +507,7 @@ sub clip_fill_surfaces {
sub bridge_over_infill {
my $self = shift;
return if $Slic3r::Config->fill_density == 1;
return if $self->config->fill_density == 1;
for my $layer_id (1..$#{$self->layers}) {
my $layer = $self->layers->[$layer_id];
@ -578,8 +587,8 @@ sub discover_horizontal_shells {
for (my $i = 0; $i < $self->layer_count; $i++) {
my $layerm = $self->layers->[$i]->regions->[$region_id];
if ($Slic3r::Config->solid_infill_every_layers && $Slic3r::Config->fill_density > 0
&& ($i % $Slic3r::Config->solid_infill_every_layers) == 0) {
if ($self->config->solid_infill_every_layers && $self->config->fill_density > 0
&& ($i % $self->config->solid_infill_every_layers) == 0) {
$_->surface_type(S_TYPE_INTERNALSOLID)
for grep $_->surface_type == S_TYPE_INTERNAL, @{$layerm->fill_surfaces};
}
@ -597,8 +606,8 @@ sub discover_horizontal_shells {
Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == S_TYPE_TOP ? 'top' : 'bottom');
my $solid_layers = ($type == S_TYPE_TOP)
? $Slic3r::Config->top_solid_layers
: $Slic3r::Config->bottom_solid_layers;
? $self->config->top_solid_layers
: $self->config->bottom_solid_layers;
NEIGHBOR: for (my $n = $type == S_TYPE_TOP ? $i-1 : $i+1;
abs($n - $i) <= $solid_layers-1;
$type == S_TYPE_TOP ? $n-- : $n++) {
@ -636,7 +645,7 @@ sub discover_horizontal_shells {
# if some parts are going to collapse, use a different strategy according to fill density
if (@$too_narrow) {
if ($Slic3r::Config->fill_density > 0) {
if ($self->config->fill_density > 0) {
# if we have internal infill, grow the collapsing parts and add the extra area to
# the neighbor layer as well as to our original surfaces so that we support this
# additional area in the next shell too
@ -704,8 +713,8 @@ sub discover_horizontal_shells {
# combine fill surfaces across layers
sub combine_infill {
my $self = shift;
return unless $Slic3r::Config->infill_every_layers > 1 && $Slic3r::Config->fill_density > 0;
my $every = $Slic3r::Config->infill_every_layers;
return unless $self->config->infill_every_layers > 1 && $self->config->fill_density > 0;
my $every = $self->config->infill_every_layers;
my $layer_count = $self->layer_count;
my @layer_heights = map $self->layers->[$_]->height, 0 .. $layer_count-1;
@ -768,7 +777,7 @@ sub combine_infill {
+ $layerms[-1]->perimeter_flow->scaled_width / 2
# Because fill areas for rectilinear and honeycomb are grown
# later to overlap perimeters, we need to counteract that too.
+ (($type == S_TYPE_INTERNALSOLID || $Slic3r::Config->fill_pattern =~ /(rectilinear|honeycomb)/)
+ (($type == S_TYPE_INTERNALSOLID || $self->config->fill_pattern =~ /(rectilinear|honeycomb)/)
? $layerms[-1]->solid_infill_flow->scaled_width * &Slic3r::INFILL_OVERLAP_OVER_SPACING
: 0)
), @$intersection;
@ -813,7 +822,7 @@ sub combine_infill {
sub generate_support_material {
my $self = shift;
return if $self->layer_count < 2;
return unless $self->config->support_material && $self->layer_count >= 2;
my $flow = $self->print->support_material_flow;
@ -826,8 +835,8 @@ sub generate_support_material {
# if user specified a custom angle threshold, convert it to radians
my $threshold_rad;
if ($Slic3r::Config->support_material_threshold) {
$threshold_rad = deg2rad($Slic3r::Config->support_material_threshold + 1); # +1 makes the threshold inclusive
if ($self->config->support_material_threshold) {
$threshold_rad = deg2rad($self->config->support_material_threshold + 1); # +1 makes the threshold inclusive
Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad);
}
@ -851,7 +860,7 @@ sub generate_support_material {
my $diff;
# If a threshold angle was specified, use a different logic for detecting overhangs.
if (defined $threshold_rad || $layer_id <= $Slic3r::Config->support_material_enforce_layers) {
if (defined $threshold_rad || $layer_id <= $self->config->support_material_enforce_layers) {
my $d = defined $threshold_rad
? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad))
: 0;
@ -963,14 +972,14 @@ sub generate_support_material {
# 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_layers = _compute_support_layers(\@contact_z, \@top_z, $Slic3r::Config, $flow);
my @support_layers = _compute_support_layers(\@contact_z, \@top_z, $self->config, $flow);
# 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
# let's now generate interface layers below contact areas
my %interface = (); # layer_id => [ polygons ]
my $interface_layers = $Slic3r::Config->support_material_interface_layers;
my $interface_layers = $self->config->support_material_interface_layers;
for my $layer_id (0 .. $#support_layers) {
my $z = $support_layers[$layer_id];
my $this = $contact{$z} // next;
@ -1029,8 +1038,8 @@ sub generate_support_material {
Slic3r::debugf "Generating patterns\n";
# prepare fillers
my $pattern = $Slic3r::Config->support_material_pattern;
my @angles = ($Slic3r::Config->support_material_angle);
my $pattern = $self->config->support_material_pattern;
my @angles = ($self->config->support_material_angle);
if ($pattern eq 'rectilinear-grid') {
$pattern = 'rectilinear';
push @angles, $angles[0] + 90;
@ -1041,10 +1050,10 @@ sub generate_support_material {
support => $self->fill_maker->filler($pattern),
);
my $interface_angle = $Slic3r::Config->support_material_angle + 90;
my $interface_spacing = $Slic3r::Config->support_material_interface_spacing + $flow->spacing;
my $interface_angle = $self->config->support_material_angle + 90;
my $interface_spacing = $self->config->support_material_interface_spacing + $flow->spacing;
my $interface_density = $interface_spacing == 0 ? 1 : $flow->spacing / $interface_spacing;
my $support_spacing = $Slic3r::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 $process_layer = sub {
@ -1167,7 +1176,7 @@ sub generate_support_material {
# base flange
if ($layer_id == 0) {
$filler = $fillers{interface};
$filler->angle($Slic3r::Config->support_material_angle + 90);
$filler->angle($self->config->support_material_angle + 90);
$density = 0.5;
$flow_spacing = $self->print->first_layer_support_material_flow->spacing;
} else {