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') {
%prereqs = qw(
Wx 0.9901
Wx 0.9918
);
%recommends = qw(
Wx::GLCanvas 0

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

@ -20,7 +20,7 @@ use constant TB_ARRANGE => &Wx::NewId;
use constant TB_EXPORT_GCODE => &Wx::NewId;
use constant TB_EXPORT_STL => &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_45CW => &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_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_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}->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_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}->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), '');
@ -84,10 +84,22 @@ sub new {
$self->{htoolbar}->AddSeparator;
$self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new("$Slic3r::var/shape_ungroup.png", wxBITMAP_TYPE_PNG), '');
} else {
my %tbar_buttons = (info => "Open", increase => "More", decrease => "Less", rotate45ccw => "45°", rotate45cw => "45°",
rotate => "Rotate…", changescale => "Scale…", split => "Split");
my %tbar_buttons = (
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);
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->{btoolbar}->Add($self->{"btn_$_"});
}
@ -124,7 +136,7 @@ sub new {
export_gcode cog_go.png
export_stl brick_go.png
open package.png
info package.png
increase add.png
decrease delete.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_RESET, \&reset);
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_LESS, \&decrease);
EVT_TOOL($self, TB_INFO, sub { $self->list_item_activated(undef, $self->{selected_objects}->[0][0]) });
EVT_TOOL($self, TB_FEWER, \&decrease);
EVT_TOOL($self, TB_45CW, 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_SCALE, \&changescale);
EVT_TOOL($self, TB_SPLIT, \&split_object);
} 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_decrease}, \&decrease);
EVT_BUTTON($self, $self->{btn_rotate45cw}, sub { $_[0]->rotate(-45) });
@ -379,7 +395,8 @@ sub object_loaded {
my $object = $self->{objects}[$obj_idx];
$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, 2, ($object->scale * 100) . "%");
@ -747,6 +764,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}) {
@ -980,8 +998,7 @@ sub mouse_event {
$self->{drag_object} = undef;
$self->SetCursor(wxSTANDARD_CURSOR);
} elsif ($event->ButtonDClick) {
$parent->list_item_activated(undef, $parent->{selected_objects}->[0][0])
if @{$parent->{selected_objects}};
$parent->object_dialog if @{$parent->{selected_objects}};
} elsif ($event->Dragging) {
return if !$self->{drag_start_pos}; # concurrency problems
for my $preview ($self->{drag_object}) {
@ -1025,7 +1042,18 @@ sub list_item_activated {
my ($self, $event, $obj_idx) = @_;
$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],
);
$dlg->ShowModal;
@ -1045,11 +1073,11 @@ sub selection_changed {
my $method = $have_sel ? 'Enable' : 'Disable';
$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}) {
$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?
@ -1139,6 +1167,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 {