Merge branch 'master' into plater2

Conflicts:
	lib/Slic3r/GUI/Plater.pm
This commit is contained in:
Alessandro Ranellucci 2013-08-25 16:10:53 +02:00
commit 5b734f4a13
13 changed files with 292 additions and 72 deletions

View File

@ -30,7 +30,7 @@ my %recommends = qw(
); );
if (defined $ARGV[0] && $ARGV[0] eq '--gui') { if (defined $ARGV[0] && $ARGV[0] eq '--gui') {
%prereqs = qw( %prereqs = qw(
Wx 0.9901 Wx 0.9918
); );
%recommends = qw( %recommends = qw(
Wx::GLCanvas 0 Wx::GLCanvas 0

View File

@ -397,9 +397,12 @@ our $Options = {
default => 0.35, default => 0.35,
}, },
'infill_every_layers' => { '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.', 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', sidetext => 'layers',
scope => 'object',
category => 'Infill',
cli => 'infill-every-layers=i', cli => 'infill-every-layers=i',
type => 'i', type => 'i',
min => 1, min => 1,
@ -409,6 +412,8 @@ our $Options = {
label => 'Solid infill every', label => 'Solid infill every',
tooltip => 'This feature allows to force a solid layer every given number of layers. Zero to disable.', tooltip => 'This feature allows to force a solid layer every given number of layers. Zero to disable.',
sidetext => 'layers', sidetext => 'layers',
scope => 'object',
category => 'Infill',
cli => 'solid-infill-every-layers=i', cli => 'solid-infill-every-layers=i',
type => 'i', type => 'i',
min => 0, min => 0,
@ -417,6 +422,8 @@ our $Options = {
'infill_only_where_needed' => { 'infill_only_where_needed' => {
label => 'Only infill 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).', 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!', cli => 'infill-only-where-needed!',
type => 'bool', type => 'bool',
default => 0, default => 0,
@ -506,7 +513,9 @@ our $Options = {
# print options # print options
'perimeters' => { 'perimeters' => {
label => 'Perimeters (minimum)', 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', cli => 'perimeters=i',
type => 'i', type => 'i',
aliases => [qw(perimeter_offsets)], aliases => [qw(perimeter_offsets)],
@ -521,14 +530,20 @@ our $Options = {
}, },
'top_solid_layers' => { 'top_solid_layers' => {
label => 'Top', label => 'Top',
full_label => 'Top solid layers',
tooltip => 'Number of solid layers to generate on top surfaces.', tooltip => 'Number of solid layers to generate on top surfaces.',
scope => 'object',
category => 'Layers and Perimeters',
cli => 'top-solid-layers=i', cli => 'top-solid-layers=i',
type => 'i', type => 'i',
default => 3, default => 3,
}, },
'bottom_solid_layers' => { 'bottom_solid_layers' => {
label => 'Bottom', label => 'Bottom',
full_label => 'Bottom solid layers',
tooltip => 'Number of solid layers to generate on bottom surfaces.', tooltip => 'Number of solid layers to generate on bottom surfaces.',
scope => 'object',
category => 'Layers and Perimeters',
cli => 'bottom-solid-layers=i', cli => 'bottom-solid-layers=i',
type => 'i', type => 'i',
default => 3, default => 3,
@ -536,6 +551,8 @@ our $Options = {
'fill_pattern' => { 'fill_pattern' => {
label => 'Fill pattern', label => 'Fill pattern',
tooltip => 'Fill pattern for general low-density infill.', tooltip => 'Fill pattern for general low-density infill.',
scope => 'object',
category => 'Infill',
cli => 'fill-pattern=s', cli => 'fill-pattern=s',
type => 'select', type => 'select',
values => [qw(rectilinear line concentric honeycomb hilbertcurve archimedeanchords octagramspiral)], values => [qw(rectilinear line concentric honeycomb hilbertcurve archimedeanchords octagramspiral)],
@ -545,6 +562,8 @@ our $Options = {
'solid_fill_pattern' => { 'solid_fill_pattern' => {
label => 'Top/bottom fill pattern', label => 'Top/bottom fill pattern',
tooltip => 'Fill pattern for top/bottom infill.', tooltip => 'Fill pattern for top/bottom infill.',
scope => 'object',
category => 'Infill',
cli => 'solid-fill-pattern=s', cli => 'solid-fill-pattern=s',
type => 'select', type => 'select',
values => [qw(rectilinear concentric hilbertcurve archimedeanchords octagramspiral)], values => [qw(rectilinear concentric hilbertcurve archimedeanchords octagramspiral)],
@ -554,6 +573,8 @@ our $Options = {
'fill_density' => { 'fill_density' => {
label => 'Fill density', label => 'Fill density',
tooltip => 'Density of internal infill, expressed in the range 0 - 1.', tooltip => 'Density of internal infill, expressed in the range 0 - 1.',
scope => 'object',
category => 'Infill',
cli => 'fill-density=f', cli => 'fill-density=f',
type => 'f', type => 'f',
default => 0.4, default => 0.4,
@ -571,6 +592,8 @@ our $Options = {
label => 'Solid infill threshold area', label => 'Solid infill threshold area',
tooltip => 'Force solid infill for regions having a smaller area than the specified threshold.', tooltip => 'Force solid infill for regions having a smaller area than the specified threshold.',
sidetext => 'mm²', sidetext => 'mm²',
scope => 'object',
category => 'Infill',
cli => 'solid-infill-below-area=f', cli => 'solid-infill-below-area=f',
type => 'f', type => 'f',
default => 70, default => 70,
@ -578,6 +601,8 @@ our $Options = {
'extra_perimeters' => { 'extra_perimeters' => {
label => 'Extra perimeters if needed', label => 'Extra perimeters if needed',
tooltip => 'Add more perimeters when needed for avoiding gaps in sloping walls.', tooltip => 'Add more perimeters when needed for avoiding gaps in sloping walls.',
scope => 'object',
category => 'Layers and Perimeters',
cli => 'extra-perimeters!', cli => 'extra-perimeters!',
type => 'bool', type => 'bool',
default => 1, default => 1,
@ -606,6 +631,8 @@ our $Options = {
'thin_walls' => { 'thin_walls' => {
label => 'Detect 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).', 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!', cli => 'thin-walls!',
type => 'bool', type => 'bool',
default => 1, default => 1,
@ -613,6 +640,8 @@ our $Options = {
'overhangs' => { 'overhangs' => {
label => 'Detect 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.', 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!', cli => 'overhangs!',
type => 'bool', type => 'bool',
default => 1, default => 1,
@ -647,6 +676,8 @@ our $Options = {
}, },
'support_material' => { 'support_material' => {
label => 'Generate support material', label => 'Generate support material',
scope => 'object',
category => 'Support material',
tooltip => 'Enable support material generation.', tooltip => 'Enable support material generation.',
cli => 'support-material!', cli => 'support-material!',
type => 'bool', type => 'bool',
@ -656,6 +687,8 @@ our $Options = {
label => 'Overhang threshold', 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.', tooltip => 'Support material will not generated for overhangs whose slope angle is above the given threshold. Set to zero for automatic detection.',
sidetext => '°', sidetext => '°',
scope => 'object',
category => 'Support material',
cli => 'support-material-threshold=i', cli => 'support-material-threshold=i',
type => 'i', type => 'i',
default => 0, default => 0,
@ -663,6 +696,8 @@ our $Options = {
'support_material_pattern' => { 'support_material_pattern' => {
label => 'Pattern', label => 'Pattern',
tooltip => 'Pattern used to generate support material.', tooltip => 'Pattern used to generate support material.',
scope => 'object',
category => 'Support material',
cli => 'support-material-pattern=s', cli => 'support-material-pattern=s',
type => 'select', type => 'select',
values => [qw(rectilinear rectilinear-grid honeycomb)], values => [qw(rectilinear rectilinear-grid honeycomb)],
@ -673,6 +708,8 @@ our $Options = {
label => 'Pattern spacing', label => 'Pattern spacing',
tooltip => 'Spacing between support material lines.', tooltip => 'Spacing between support material lines.',
sidetext => 'mm', sidetext => 'mm',
scope => 'object',
category => 'Support material',
cli => 'support-material-spacing=f', cli => 'support-material-spacing=f',
type => 'f', type => 'f',
default => 2.5, default => 2.5,
@ -680,6 +717,8 @@ our $Options = {
'support_material_angle' => { 'support_material_angle' => {
label => 'Pattern angle', label => 'Pattern angle',
tooltip => 'Use this setting to rotate the support material pattern on the horizontal plane.', tooltip => 'Use this setting to rotate the support material pattern on the horizontal plane.',
scope => 'object',
category => 'Support material',
sidetext => '°', sidetext => '°',
cli => 'support-material-angle=i', cli => 'support-material-angle=i',
type => 'i', type => 'i',
@ -689,6 +728,8 @@ our $Options = {
label => 'Interface layers', label => 'Interface layers',
tooltip => 'Number of interface layers to insert between the object(s) and support material.', tooltip => 'Number of interface layers to insert between the object(s) and support material.',
sidetext => 'layers', sidetext => 'layers',
scope => 'object',
category => 'Support material',
cli => 'support-material-interface-layers=i', cli => 'support-material-interface-layers=i',
type => 'i', type => 'i',
default => 3, default => 3,
@ -696,6 +737,8 @@ our $Options = {
'support_material_interface_spacing' => { 'support_material_interface_spacing' => {
label => 'Interface pattern spacing', label => 'Interface pattern spacing',
tooltip => 'Spacing between interface lines. Set zero to get a solid interface.', tooltip => 'Spacing between interface lines. Set zero to get a solid interface.',
scope => 'object',
category => 'Support material',
sidetext => 'mm', sidetext => 'mm',
cli => 'support-material-interface-spacing=f', cli => 'support-material-interface-spacing=f',
type => 'f', type => 'f',
@ -703,8 +746,11 @@ our $Options = {
}, },
'support_material_enforce_layers' => { 'support_material_enforce_layers' => {
label => 'Enforce support for the first', 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.', 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', sidetext => 'layers',
scope => 'object',
category => 'Support material',
cli => 'support-material-enforce-layers=f', cli => 'support-material-enforce-layers=f',
type => 'i', type => 'i',
default => 0, default => 0,
@ -713,6 +759,8 @@ our $Options = {
label => 'Raft layers', label => 'Raft layers',
tooltip => 'The object will be raised by this number of layers, and support material will be generated under it.', tooltip => 'The object will be raised by this number of layers, and support material will be generated under it.',
sidetext => 'layers', sidetext => 'layers',
scope => 'object',
category => 'Support material',
cli => 'raft-layers=i', cli => 'raft-layers=i',
type => 'i', type => 'i',
default => 0, default => 0,
@ -1098,7 +1146,7 @@ sub new {
sub new_from_defaults { sub new_from_defaults {
my $class = shift; my $class = shift;
my @opt_keys =
return $class->new( return $class->new(
map { $_ => $Options->{$_}{default} } map { $_ => $Options->{$_}{default} }
grep !$Options->{$_}{shortcut}, grep !$Options->{$_}{shortcut},

View File

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

View File

@ -108,7 +108,7 @@ sub change_layer {
$self->_upper_layer_islands([]); $self->_upper_layer_islands([]);
} }
$self->_layer_overhangs( $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} ] ? [ map $_->expolygon, grep $_->surface_type == S_TYPE_BOTTOM, map @{$_->slices}, @{$layer->regions} ]
: [] : []
); );
@ -227,7 +227,7 @@ sub extrude_loop {
my @paths = (); my @paths = ();
# detect overhanging/bridging perimeters # 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 # get non-overhang paths by subtracting overhangs from the loop
push @paths, push @paths,
$extrusion_path->subtract_expolygons($self->_layer_overhangs); $extrusion_path->subtract_expolygons($self->_layer_overhangs);
@ -256,7 +256,7 @@ sub extrude_loop {
$self->wipe_path($extrusion_path->polyline) if $self->enable_wipe; $self->wipe_path($extrusion_path->polyline) if $self->enable_wipe;
# make a little move inwards before leaving loop # 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 # detect angle between last and first segment
# the side depends on the original winding order of the polygon (left for contours, right for holes) # 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); 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"; our $have_OpenGL = eval "use Slic3r::GUI::PreviewCanvas; 1";
use Wx 0.9901 qw(:bitmap :dialog :frame :icon :id :misc :systemsettings :toplevelwindow); 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 base 'Wx::App';
use constant MI_LOAD_CONF => &Wx::NewId; use constant MI_LOAD_CONF => &Wx::NewId;
@ -47,6 +47,7 @@ our $datadir;
our $no_plater; our $no_plater;
our $mode; our $mode;
our $autosave; our $autosave;
our @cb;
our $Settings = { our $Settings = {
_ => { _ => {
@ -210,6 +211,12 @@ sub OnInit {
&& ($Settings->{_}{version_check} // 1) && ($Settings->{_}{version_check} // 1)
&& (!$Settings->{_}{last_version_check} || (time - $Settings->{_}{last_version_check}) >= 86400); && (!$Settings->{_}{last_version_check} || (time - $Settings->{_}{last_version_check}) >= 86400);
EVT_IDLE($frame, sub {
while (my $cb = shift @cb) {
$cb->();
}
});
return 1; return 1;
} }
@ -324,6 +331,12 @@ sub output_path {
: $dir; : $dir;
} }
sub CallAfter {
my $class = shift;
my ($cb) = @_;
push @cb, $cb;
}
package Slic3r::GUI::ProgressStatusBar; package Slic3r::GUI::ProgressStatusBar;
use Wx qw(:gauge :misc); use Wx qw(:gauge :misc);
use base 'Wx::StatusBar'; 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" }, on_change => sub { print "new value for $_[0] is $_[1]\n" },
no_labels => 0, no_labels => 0,
label_width => 180, label_width => 180,
extra_column => sub { ... },
); );
$sizer->Add($optgroup->sizer); $sizer->Add($optgroup->sizer);
@ -48,6 +49,7 @@ has 'lines' => (is => 'lazy');
has 'on_change' => (is => 'ro', default => sub { sub {} }); has 'on_change' => (is => 'ro', default => sub { sub {} });
has 'no_labels' => (is => 'ro', default => sub { 0 }); has 'no_labels' => (is => 'ro', default => sub { 0 });
has 'label_width' => (is => 'ro', default => sub { 180 }); has 'label_width' => (is => 'ro', default => sub { 180 });
has 'extra_column' => (is => 'ro');
has 'sizer' => (is => 'rw'); has 'sizer' => (is => 'rw');
has '_triggers' => (is => 'ro', default => sub { {} }); has '_triggers' => (is => 'ro', default => sub { {} });
@ -63,7 +65,8 @@ sub BUILD {
$self->sizer(Wx::StaticBoxSizer->new($box, wxVERTICAL)); $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->SetFlexibleDirection(wxHORIZONTAL);
$grid_sizer->AddGrowableCol($self->no_labels ? 0 : 1); $grid_sizer->AddGrowableCol($self->no_labels ? 0 : 1);
@ -113,6 +116,10 @@ sub _build_line {
my $self = shift; my $self = shift;
my ($line, $grid_sizer) = @_; my ($line, $grid_sizer) = @_;
if ($self->extra_column) {
$grid_sizer->Add($self->extra_column->($line), 0, wxALIGN_CENTER_VERTICAL, 0);
}
my $label; my $label;
if (!$self->no_labels) { if (!$self->no_labels) {
$label = Wx::StaticText->new($self->parent, -1, $line->{label} ? "$line->{label}:" : "", wxDefaultPosition, [$self->label_width, -1]); $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); use List::Util qw(first);
has 'config' => (is => 'ro', required => 1); has 'config' => (is => 'ro', required => 1);
has 'full_labels' => (is => 'ro', default => sub {0});
sub _trigger_options { sub _trigger_options {
my $self = shift; my $self = shift;
@ -304,7 +312,8 @@ sub _trigger_options {
$opt = { $opt = {
opt_key => $full_key, opt_key => $full_key,
config => 1, 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), default => $self->_get_config($opt_key, $index),
on_change => sub { $self->_set_config($opt_key, $index, $_[0]) }, on_change => sub { $self->_set_config($opt_key, $index, $_[0]) },
}; };

View File

@ -20,7 +20,7 @@ use constant TB_ARRANGE => &Wx::NewId;
use constant TB_EXPORT_GCODE => &Wx::NewId; use constant TB_EXPORT_GCODE => &Wx::NewId;
use constant TB_EXPORT_STL => &Wx::NewId; use constant TB_EXPORT_STL => &Wx::NewId;
use constant TB_MORE => &Wx::NewId; use constant TB_MORE => &Wx::NewId;
use constant TB_LESS => &Wx::NewId; use constant TB_FEWER => &Wx::NewId;
use constant TB_INFO => &Wx::NewId; use constant TB_INFO => &Wx::NewId;
use constant TB_45CW => &Wx::NewId; use constant TB_45CW => &Wx::NewId;
use constant TB_45CCW => &Wx::NewId; use constant TB_45CCW => &Wx::NewId;
@ -71,11 +71,11 @@ sub new {
$self->{htoolbar}->AddTool(TB_LOAD, "Add…", Wx::Bitmap->new("$Slic3r::var/brick_add.png", wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_LOAD, "Add…", Wx::Bitmap->new("$Slic3r::var/brick_add.png", wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_REMOVE, "Delete", Wx::Bitmap->new("$Slic3r::var/brick_delete.png", wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_REMOVE, "Delete", Wx::Bitmap->new("$Slic3r::var/brick_delete.png", wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_RESET, "Delete All", Wx::Bitmap->new("$Slic3r::var/cross.png", wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_RESET, "Delete All", Wx::Bitmap->new("$Slic3r::var/cross.png", wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_ARRANGE, "Autoarrange", Wx::Bitmap->new("$Slic3r::var/bricks.png", wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_ARRANGE, "Arrange", Wx::Bitmap->new("$Slic3r::var/bricks.png", wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddSeparator; $self->{htoolbar}->AddSeparator;
$self->{htoolbar}->AddTool(TB_INFO, "Open", Wx::Bitmap->new("$Slic3r::var/package.png", wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_INFO, "Open", Wx::Bitmap->new("$Slic3r::var/package.png", wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_MORE, "More", Wx::Bitmap->new("$Slic3r::var/add.png", wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_MORE, "More", Wx::Bitmap->new("$Slic3r::var/add.png", wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_LESS, "Fewer", Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_FEWER, "Fewer", Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddSeparator; $self->{htoolbar}->AddSeparator;
$self->{htoolbar}->AddTool(TB_45CCW, "45° ccw", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_anticlockwise.png", wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_45CCW, "45° ccw", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_anticlockwise.png", wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_45CW, "45° cw", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_45CW, "45° cw", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", wxBITMAP_TYPE_PNG), '');
@ -84,10 +84,22 @@ sub new {
$self->{htoolbar}->AddSeparator; $self->{htoolbar}->AddSeparator;
$self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new("$Slic3r::var/shape_ungroup.png", wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new("$Slic3r::var/shape_ungroup.png", wxBITMAP_TYPE_PNG), '');
} else { } else {
my %tbar_buttons = (info => "Open", increase => "More", decrease => "Less", rotate45ccw => "45°", rotate45cw => "45°", my %tbar_buttons = (
rotate => "Rotate…", changescale => "Scale…", split => "Split"); load => "Add…",
remove => "Delete",
reset => "Delete All",
arrange => "Arrange",
info => "Open",
increase => "",
decrease => "",
rotate45ccw => "",
rotate45cw => "",
rotate => "Rotate…",
changescale => "Scale…",
split => "Split",
);
$self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL); $self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL);
for (qw(load remove reset arrange open increase decrease rotate45ccw rotate45cw rotate changescale split)) { for (qw(load remove reset arrange info increase decrease rotate45ccw rotate45cw rotate changescale split)) {
$self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); $self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
$self->{btoolbar}->Add($self->{"btn_$_"}); $self->{btoolbar}->Add($self->{"btn_$_"});
} }
@ -124,7 +136,7 @@ sub new {
export_gcode cog_go.png export_gcode cog_go.png
export_stl brick_go.png export_stl brick_go.png
open package.png info package.png
increase add.png increase add.png
decrease delete.png decrease delete.png
rotate45cw arrow_rotate_clockwise.png rotate45cw arrow_rotate_clockwise.png
@ -147,16 +159,20 @@ sub new {
EVT_TOOL($self, TB_REMOVE, sub { $self->remove() }); # explicitly pass no argument to remove EVT_TOOL($self, TB_REMOVE, sub { $self->remove() }); # explicitly pass no argument to remove
EVT_TOOL($self, TB_RESET, \&reset); EVT_TOOL($self, TB_RESET, \&reset);
EVT_TOOL($self, TB_ARRANGE, \&arrange); EVT_TOOL($self, TB_ARRANGE, \&arrange);
EVT_TOOL($self, TB_INFO, sub { $_[0]->object_dialog });
EVT_TOOL($self, TB_MORE, \&increase); EVT_TOOL($self, TB_MORE, \&increase);
EVT_TOOL($self, TB_LESS, \&decrease); EVT_TOOL($self, TB_FEWER, \&decrease);
EVT_TOOL($self, TB_INFO, sub { $self->list_item_activated(undef, $self->{selected_objects}->[0][0]) });
EVT_TOOL($self, TB_45CW, sub { $_[0]->rotate(-45) }); EVT_TOOL($self, TB_45CW, sub { $_[0]->rotate(-45) });
EVT_TOOL($self, TB_45CCW, sub { $_[0]->rotate(45) }); EVT_TOOL($self, TB_45CCW, sub { $_[0]->rotate(45) });
EVT_TOOL($self, TB_ROTATE, sub { $_[0]->rotate(undef) }); EVT_TOOL($self, TB_ROTATE, sub { $_[0]->rotate(undef) });
EVT_TOOL($self, TB_SCALE, \&changescale); EVT_TOOL($self, TB_SCALE, \&changescale);
EVT_TOOL($self, TB_SPLIT, \&split_object); EVT_TOOL($self, TB_SPLIT, \&split_object);
} else { } else {
EVT_BUTTON($self, $self->{btn_open}, sub { $self->list_item_activated(undef, $self->{selected_objects}->[0][0]) }); EVT_BUTTON($self, $self->{btn_load}, \&load);
EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove
EVT_BUTTON($self, $self->{btn_reset}, \&reset);
EVT_BUTTON($self, $self->{btn_arrange}, \&arrange);
EVT_BUTTON($self, $self->{btn_info}, sub { $_[0]->object_dialog });
EVT_BUTTON($self, $self->{btn_increase}, \&increase); EVT_BUTTON($self, $self->{btn_increase}, \&increase);
EVT_BUTTON($self, $self->{btn_decrease}, \&decrease); EVT_BUTTON($self, $self->{btn_decrease}, \&decrease);
EVT_BUTTON($self, $self->{btn_rotate45cw}, sub { $_[0]->rotate(-45) }); EVT_BUTTON($self, $self->{btn_rotate45cw}, sub { $_[0]->rotate(-45) });
@ -379,7 +395,8 @@ sub object_loaded {
my $object = $self->{objects}[$obj_idx]; my $object = $self->{objects}[$obj_idx];
$self->{list}->InsertStringItem($obj_idx, $object->name); $self->{list}->InsertStringItem($obj_idx, $object->name);
$self->{list}->SetItemFont($obj_idx, Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL)); $self->{list}->SetItemFont($obj_idx, Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL))
if $self->{list}->can('SetItemFont'); # legacy code for wxPerl < 0.9918 not supporting SetItemFont()
$self->{list}->SetItem($obj_idx, 1, $object->instances_count); $self->{list}->SetItem($obj_idx, 1, $object->instances_count);
$self->{list}->SetItem($obj_idx, 2, ($object->scale * 100) . "%"); $self->{list}->SetItem($obj_idx, 2, ($object->scale * 100) . "%");
@ -747,6 +764,7 @@ sub make_model {
my $new_model_object = $model->add_object( my $new_model_object = $model->add_object(
vertices => $model_object->vertices, vertices => $model_object->vertices,
input_file => $plater_object->input_file, input_file => $plater_object->input_file,
config => $plater_object->config,
layer_height_ranges => $plater_object->layer_height_ranges, layer_height_ranges => $plater_object->layer_height_ranges,
); );
foreach my $volume (@{$model_object->volumes}) { foreach my $volume (@{$model_object->volumes}) {
@ -980,8 +998,7 @@ sub mouse_event {
$self->{drag_object} = undef; $self->{drag_object} = undef;
$self->SetCursor(wxSTANDARD_CURSOR); $self->SetCursor(wxSTANDARD_CURSOR);
} elsif ($event->ButtonDClick) { } elsif ($event->ButtonDClick) {
$parent->list_item_activated(undef, $parent->{selected_objects}->[0][0]) $parent->object_dialog if @{$parent->{selected_objects}};
if @{$parent->{selected_objects}};
} elsif ($event->Dragging) { } elsif ($event->Dragging) {
return if !$self->{drag_start_pos}; # concurrency problems return if !$self->{drag_start_pos}; # concurrency problems
for my $preview ($self->{drag_object}) { for my $preview ($self->{drag_object}) {
@ -1025,7 +1042,18 @@ sub list_item_activated {
my ($self, $event, $obj_idx) = @_; my ($self, $event, $obj_idx) = @_;
$obj_idx //= $event->GetIndex; $obj_idx //= $event->GetIndex;
my $dlg = Slic3r::GUI::Plater::ObjectDialog->new($self, $self->object_dialog($obj_idx);
}
sub object_dialog {
my $self = shift;
my ($obj_idx) = @_;
if (!defined $obj_idx) {
($obj_idx, undef) = $self->selected_object;
}
my $dlg = Slic3r::GUI::Plater::ObjectDialog->new($self,
object => $self->{objects}[$obj_idx], object => $self->{objects}[$obj_idx],
); );
$dlg->ShowModal; $dlg->ShowModal;
@ -1045,11 +1073,11 @@ sub selection_changed {
my $method = $have_sel ? 'Enable' : 'Disable'; my $method = $have_sel ? 'Enable' : 'Disable';
$self->{"btn_$_"}->$method $self->{"btn_$_"}->$method
for grep $self->{"btn_$_"}, qw(remove open increase decrease rotate45cw rotate45ccw rotate changescale split); for grep $self->{"btn_$_"}, qw(remove info increase decrease rotate45cw rotate45ccw rotate changescale split);
if ($self->{htoolbar}) { if ($self->{htoolbar}) {
$self->{htoolbar}->EnableTool($_, $have_sel) $self->{htoolbar}->EnableTool($_, $have_sel)
for (TB_REMOVE, TB_INFO, TB_MORE, TB_LESS, TB_45CW, TB_45CCW, TB_ROTATE, TB_SCALE, TB_SPLIT); for (TB_REMOVE, TB_INFO, TB_MORE, TB_FEWER, TB_45CW, TB_45CCW, TB_ROTATE, TB_SCALE, TB_SPLIT);
} }
if ($self->{object_info_size}) { # have we already loaded the info pane? if ($self->{object_info_size}) { # have we already loaded the info pane?
@ -1139,6 +1167,7 @@ has 'instances' => (is => 'rw', default => sub { [] }); # upward Y a
has 'thumbnail' => (is => 'rw', trigger => \&_transform_thumbnail); has 'thumbnail' => (is => 'rw', trigger => \&_transform_thumbnail);
has 'transformed_thumbnail' => (is => 'rw'); has 'transformed_thumbnail' => (is => 'rw');
has 'thumbnail_scaling_factor' => (is => 'rw', trigger => \&_transform_thumbnail); 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 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ]
has 'mesh_stats' => (is => 'rw'); 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") $self->{tabpanel}->AddPage($self->{preview} = Slic3r::GUI::Plater::ObjectDialog::PreviewTab->new($self->{tabpanel}, object => $self->{object}), "Preview")
if $Slic3r::GUI::have_OpenGL; 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->{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"); $self->{tabpanel}->AddPage($self->{layers} = Slic3r::GUI::Plater::ObjectDialog::LayersTab->new($self->{tabpanel}, object => $self->{object}), "Layers");
my $buttons = $self->CreateStdDialogButtonSizer(wxOK); my $buttons = $self->CreateStdDialogButtonSizer(wxOK);
EVT_BUTTON($self, wxID_OK, sub { EVT_BUTTON($self, wxID_OK, sub {
# validate user input # validate user input
return if !$self->{settings}->CanClose;
return if !$self->{layers}->CanClose; return if !$self->{layers}->CanClose;
# notify tabs # notify tabs
@ -124,6 +126,111 @@ sub new {
return $self; 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; package Slic3r::GUI::Plater::ObjectDialog::LayersTab;
use Wx qw(:dialog :id :misc :sizer :systemsettings); use Wx qw(:dialog :id :misc :sizer :systemsettings);
use Wx::Grid; use Wx::Grid;

View File

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

View File

@ -14,7 +14,7 @@ has 'layer' => (
weak_ref => 1, weak_ref => 1,
required => 1, required => 1,
trigger => 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 'region' => (is => 'ro', required => 1, handles => [qw(extruders)]);
has 'perimeter_flow' => (is => 'rw'); has 'perimeter_flow' => (is => 'rw');
@ -103,7 +103,7 @@ sub make_surfaces {
} }
# detect thin walls by offsetting slices by half extrusion inwards # detect thin walls by offsetting slices by half extrusion inwards
if ($Slic3r::Config->thin_walls) { if ($self->config->thin_walls) {
$self->thin_walls([]); $self->thin_walls([]);
# we use spacing here because there could be a case where # we use spacing here because there could be a case where
# the slice collapses with width but doesn't collapse with spacing, # the slice collapses with width but doesn't collapse with spacing,
@ -183,7 +183,7 @@ sub make_perimeters {
# extra perimeters for each one # extra perimeters for each one
foreach my $surface (@{$self->slices}) { foreach my $surface (@{$self->slices}) {
# detect how many perimeters must be generated for this island # 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 # generate loops
# (one more than necessary so that we can detect gaps even after the desired # (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 # where offset2() collapses the expolygon, then there's no room for an inner loop
# and we can extract the gap for later processing # 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( my $diff = diff_ex(
[ offset(\@last, -0.5*$spacing) ], [ offset(\@last, -0.5*$spacing) ],
# +2 on the offset here makes sure that Clipper float truncation # +2 on the offset here makes sure that Clipper float truncation
@ -411,16 +411,16 @@ sub prepare_fill_surfaces {
my $self = shift; my $self = shift;
# if no solid layers are requested, turn top/bottom surfaces to internal # 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}; $_->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}; $_->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 # turn too small internal regions into solid regions according to the user setting
if ($Slic3r::Config->fill_density > 0) { if ($self->object->config->fill_density > 0) {
my $min_area = scale scale $Slic3r::Config->solid_infill_below_area; # scaling an area requires two calls! 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}; my @small = grep $_->surface_type == S_TYPE_INTERNAL && $_->expolygon->contour->area <= $min_area, @{$self->fill_surfaces};
$_->surface_type(S_TYPE_INTERNALSOLID) for @small; $_->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; 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 # if we're slicing with no infill, we can't extend external surfaces
# over non-existent infill # over non-existent infill
my @fill_boundaries = $Slic3r::Config->fill_density > 0 my @fill_boundaries = $self->object->config->fill_density > 0
? @{$self->fill_surfaces} ? @{$self->fill_surfaces}
: grep $_->surface_type != S_TYPE_INTERNAL, @{$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( my $new_object = $new_model->add_object(
input_file => $object->input_file, input_file => $object->input_file,
vertices => $object->vertices, vertices => $object->vertices,
config => $object->config,
layer_height_ranges => $object->layer_height_ranges, layer_height_ranges => $object->layer_height_ranges,
); );
@ -255,6 +256,7 @@ sub split_meshes {
foreach my $mesh ($volume->mesh->split_mesh) { foreach my $mesh ($volume->mesh->split_mesh) {
my $new_object = $self->add_object( my $new_object = $self->add_object(
input_file => $object->input_file, input_file => $object->input_file,
config => $object->config,
layer_height_ranges => $object->layer_height_ranges, layer_height_ranges => $object->layer_height_ranges,
); );
$new_object->add_volume( $new_object->add_volume(
@ -302,6 +304,7 @@ has 'model' => (is => 'ro', weak_ref => 1, required => 1);
has 'vertices' => (is => 'ro', default => sub { [] }); has 'vertices' => (is => 'ro', default => sub { [] });
has 'volumes' => (is => 'ro', default => sub { [] }); has 'volumes' => (is => 'ro', default => sub { [] });
has 'instances' => (is => 'rw'); 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 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ]
has 'mesh_stats' => (is => 'rw'); has 'mesh_stats' => (is => 'rw');
has '_bounding_box' => (is => 'rw'); has '_bounding_box' => (is => 'rw');

View File

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

View File

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