From ee645007f29cd8df4d2f085efb9b61bf958025e9 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 25 Oct 2017 12:53:31 +0200 Subject: [PATCH 01/83] Another step towards C++ presets. --- lib/Slic3r.pm | 5 +- lib/Slic3r/Config.pm | 174 ------ lib/Slic3r/GUI.pm | 100 +--- lib/Slic3r/GUI/3DScene.pm | 2 +- lib/Slic3r/GUI/AboutDialog.pm | 2 +- lib/Slic3r/GUI/ConfigWizard.pm | 8 +- lib/Slic3r/GUI/Controller.pm | 19 +- .../GUI/Controller/ManualControlDialog.pm | 2 +- lib/Slic3r/GUI/Controller/PrinterPanel.pm | 26 +- lib/Slic3r/GUI/MainFrame.pm | 101 +--- lib/Slic3r/GUI/Notifier.pm | 2 +- lib/Slic3r/GUI/Plater.pm | 237 ++++---- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 20 +- .../GUI/Plater/OverrideSettingsPanel.pm | 4 +- lib/Slic3r/GUI/Tab.pm | 420 ++++--------- lib/Slic3r/Print/Object.pm | 3 - slic3r.pl | 7 +- xs/src/libslic3r/BoundingBox.cpp | 20 +- xs/src/libslic3r/Config.hpp | 93 ++- xs/src/libslic3r/TriangleMesh.cpp | 2 + xs/src/libslic3r/Utils.hpp | 14 +- xs/src/libslic3r/utils.cpp | 27 +- xs/src/slic3r/GUI/GUI.cpp | 12 +- xs/src/slic3r/GUI/Preset.cpp | 565 ++++++++++++++++-- xs/src/slic3r/GUI/Preset.hpp | 121 +++- xs/xsp/GUI_Preset.xsp | 63 +- xs/xsp/XS.xsp | 19 + 27 files changed, 1161 insertions(+), 907 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 7729b6126..dc870edf3 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -50,8 +50,9 @@ warn "Running Slic3r under Perl 5.16 is neither supported nor recommended\n" if $^V == v5.16; use FindBin; -# Path to the images. -our $var = sub { decode_path($FindBin::Bin) . "/var/" . $_[0] }; + +# Let the XS module know where the GUI resources reside. +set_var_dir(decode_path($FindBin::Bin) . "/var"); use Moo 1.003001; diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 638a02c9b..5af2fa992 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -111,16 +111,6 @@ sub load { } } -# Deserialize a perl hash into the underlying C++ Slic3r::DynamicConfig class, -# convert legacy configuration names. -# Used to load a config bundle. -sub load_ini_hash { - my ($class, $ini_hash) = @_; - my $config = $class->new; - $config->set_deserialize($_, $ini_hash->{$_}) for keys %$ini_hash; - return $config; -} - sub clone { my $self = shift; my $new = (ref $self)->new; @@ -135,170 +125,6 @@ sub get_value { : $self->get($opt_key); } -# Create a hash of hashes from the underlying C++ Slic3r::DynamicPrintConfig. -# The first hash key is '_' meaning no category. -# Used to create a config bundle. -sub as_ini { - my ($self) = @_; - my $ini = { _ => {} }; - foreach my $opt_key (sort @{$self->get_keys}) { - next if $Options->{$opt_key}{shortcut}; - $ini->{_}{$opt_key} = $self->serialize($opt_key); - } - return $ini; -} - -# this method is idempotent by design and only applies to ::DynamicConfig or ::Full -# objects because it performs cross checks -sub validate { - my $self = shift; - - # -j, --threads - die "Invalid value for --threads\n" - if $self->threads < 1; - - # --layer-height - die "Invalid value for --layer-height\n" - if $self->layer_height <= 0; - die "--layer-height must be a multiple of print resolution\n" - if $self->layer_height / &Slic3r::SCALING_FACTOR % 1 != 0; - - # --first-layer-height - die "Invalid value for --first-layer-height\n" - if $self->first_layer_height !~ /^(?:\d*(?:\.\d+)?)%?$/; - die "Invalid value for --first-layer-height\n" - if $self->get_value('first_layer_height') <= 0; - - # --filament-diameter - die "Invalid value for --filament-diameter\n" - if grep $_ < 1, @{$self->filament_diameter}; - - # --nozzle-diameter - die "Invalid value for --nozzle-diameter\n" - if grep $_ < 0, @{$self->nozzle_diameter}; - - # --perimeters - die "Invalid value for --perimeters\n" - if $self->perimeters < 0; - - # --solid-layers - die "Invalid value for --solid-layers\n" if defined $self->solid_layers && $self->solid_layers < 0; - die "Invalid value for --top-solid-layers\n" if $self->top_solid_layers < 0; - die "Invalid value for --bottom-solid-layers\n" if $self->bottom_solid_layers < 0; - - # --gcode-flavor - die "Invalid value for --gcode-flavor\n" - if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}}; - - die "--use-firmware-retraction is only supported by Marlin, Smoothie, Repetier and Machinekit firmware\n" - if $self->use_firmware_retraction && $self->gcode_flavor ne 'smoothie' - && $self->gcode_flavor ne 'reprap' - && $self->gcode_flavor ne 'machinekit' - && $self->gcode_flavor ne 'repetier'; - - die "--use-firmware-retraction is not compatible with --wipe\n" - if $self->use_firmware_retraction && first {$_} @{$self->wipe}; - - # --fill-pattern - die "Invalid value for --fill-pattern\n" - if !first { $_ eq $self->fill_pattern } @{$Options->{fill_pattern}{values}}; - - # --external-fill-pattern - die "Invalid value for --external-fill-pattern\n" - if !first { $_ eq $self->external_fill_pattern } @{$Options->{external_fill_pattern}{values}}; - - # --fill-density - die "The selected fill pattern is not supposed to work at 100% density\n" - if $self->fill_density == 100 - && !first { $_ eq $self->fill_pattern } @{$Options->{external_fill_pattern}{values}}; - - # --infill-every-layers - die "Invalid value for --infill-every-layers\n" - if $self->infill_every_layers !~ /^\d+$/ || $self->infill_every_layers < 1; - - # --skirt-height - die "Invalid value for --skirt-height\n" - if $self->skirt_height < -1; # -1 means as tall as the object - - # --bridge-flow-ratio - die "Invalid value for --bridge-flow-ratio\n" - if $self->bridge_flow_ratio <= 0; - - # extruder clearance - die "Invalid value for --extruder-clearance-radius\n" - if $self->extruder_clearance_radius <= 0; - die "Invalid value for --extruder-clearance-height\n" - if $self->extruder_clearance_height <= 0; - - # --extrusion-multiplier - die "Invalid value for --extrusion-multiplier\n" - if defined first { $_ <= 0 } @{$self->extrusion_multiplier}; - - # --default-acceleration - die "Invalid zero value for --default-acceleration when using other acceleration settings\n" - if ($self->perimeter_acceleration || $self->infill_acceleration || $self->bridge_acceleration || $self->first_layer_acceleration) - && !$self->default_acceleration; - - # --spiral-vase - if ($self->spiral_vase) { - # Note that we might want to have more than one perimeter on the bottom - # solid layers. - die "Can't make more than one perimeter when spiral vase mode is enabled\n" - if $self->perimeters > 1; - - die "Can't make less than one perimeter when spiral vase mode is enabled\n" - if $self->perimeters < 1; - - die "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0\n" - if $self->fill_density > 0; - - die "Spiral vase mode is not compatible with top solid layers\n" - if $self->top_solid_layers > 0; - - die "Spiral vase mode is not compatible with support material\n" - if $self->support_material || $self->support_material_enforce_layers > 0; - } - - # extrusion widths - { - my $max_nozzle_diameter = max(@{ $self->nozzle_diameter }); - die "Invalid extrusion width (too large)\n" - if defined first { $_ > 10 * $max_nozzle_diameter } - map $self->get_abs_value_over("${_}_extrusion_width", $max_nozzle_diameter), - qw(perimeter infill solid_infill top_infill support_material first_layer); - } - - # general validation, quick and dirty - foreach my $opt_key (@{$self->get_keys}) { - my $opt = $Options->{$opt_key}; - next unless defined $self->$opt_key; - next unless defined $opt->{cli} && $opt->{cli} =~ /=(.+)$/; - my $type = $1; - my @values = (); - if ($type =~ s/\@$//) { - die "Invalid value for $opt_key\n" if ref($self->$opt_key) ne 'ARRAY'; - @values = @{ $self->$opt_key }; - } else { - @values = ($self->$opt_key); - } - foreach my $value (@values) { - if ($type eq 'i' || $type eq 'f' || $opt->{type} eq 'percent') { - $value =~ s/%$// if $opt->{type} eq 'percent'; - die "Invalid value for $opt_key\n" - if ($type eq 'i' && $value !~ /^-?\d+$/) - || (($type eq 'f' || $opt->{type} eq 'percent') && $value !~ /^-?(?:\d+|\d*\.\d+)$/) - || (defined $opt->{min} && $value < $opt->{min}) - || (defined $opt->{max} && $value > $opt->{max}); - } elsif ($type eq 's' && $opt->{type} eq 'select') { - die "Invalid value for $opt_key\n" - unless first { $_ eq $value } @{ $opt->{values} }; - } - } - } - - return 1; -} - # CLASS METHODS: # Write a "Windows" style ini file with categories enclosed in squre brackets. diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index dc93e513f..ca88f2e7d 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -52,7 +52,6 @@ use constant FILE_WILDCARDS => { }; use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf prusa)}; -our $datadir; # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. our $no_controller; our $no_plater; @@ -84,8 +83,6 @@ our $grey = Wx::Colour->new(200,200,200); #our $VERSION_CHECK_EVENT : shared = Wx::NewEventType; -our $DLP_projection_screen; - sub OnInit { my ($self) = @_; @@ -95,17 +92,11 @@ sub OnInit { $self->{notifier} = Slic3r::GUI::Notifier->new; $self->{preset_bundle} = Slic3r::GUI::PresetBundle->new; - - # locate or create data directory - # Unix: ~/.Slic3r - # Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r" - # Mac: "~/Library/Application Support/Slic3r" - $datadir ||= Wx::StandardPaths::Get->GetUserDataDir; - my $enc_datadir = Slic3r::encode_path($datadir); - Slic3r::debugf "Data directory: %s\n", $datadir; - # just checking for existence of $datadir is not enough: it may be an empty directory + # just checking for existence of Slic3r::data_dir is not enough: it may be an empty directory # supplied as argument to --datadir; in that case we should still run the wizard + my $enc_datadir = Slic3r::encode_path(Slic3r::data_dir); + Slic3r::debugf "Data directory: %s\n", $enc_datadir; my $run_wizard = (-d $enc_datadir && -e "$enc_datadir/slic3r.ini") ? 0 : 1; foreach my $dir ($enc_datadir, "$enc_datadir/print", "$enc_datadir/filament", "$enc_datadir/printer") { next if -d $dir; @@ -119,7 +110,7 @@ sub OnInit { # load settings my $last_version; if (-f "$enc_datadir/slic3r.ini") { - my $ini = eval { Slic3r::Config->read_ini("$datadir/slic3r.ini") }; + my $ini = eval { Slic3r::Config->read_ini(Slic3r::data_dir . "/slic3r.ini") }; $Settings = $ini if $ini; $last_version = $Settings->{_}{version}; $Settings->{_}{autocenter} //= 1; @@ -131,6 +122,8 @@ sub OnInit { } $Settings->{_}{version} = $Slic3r::VERSION; $self->save_settings; + + eval { $self->{preset_bundle}->load_presets(Slic3r::data_dir) }; # application frame Wx::Image::AddHandler(Wx::PNGHandler->new); @@ -142,20 +135,21 @@ sub OnInit { $self->SetTopWindow($frame); # load init bundle - { - my @dirs = ($FindBin::Bin); - if (&Wx::wxMAC) { - push @dirs, qw(); - } elsif (&Wx::wxMSW) { - push @dirs, qw(); - } - my $init_bundle = first { -e $_ } map "$_/.init_bundle.ini", @dirs; - if ($init_bundle) { - Slic3r::debugf "Loading config bundle from %s\n", $init_bundle; - $self->{mainframe}->load_configbundle($init_bundle, 1); - $run_wizard = 0; - } - } + #FIXME this is undocumented and the use case is unclear. +# { +# my @dirs = ($FindBin::Bin); +# if (&Wx::wxMAC) { +# push @dirs, qw(); +# } elsif (&Wx::wxMSW) { +# push @dirs, qw(); +# } +# my $init_bundle = first { -e $_ } map "$_/.init_bundle.ini", @dirs; +# if ($init_bundle) { +# Slic3r::debugf "Loading config bundle from %s\n", $init_bundle; +# $self->{mainframe}->load_configbundle($init_bundle, 1); +# $run_wizard = 0; +# } +# } if (!$run_wizard && (!defined $last_version || $last_version ne $Slic3r::VERSION)) { # user was running another Slic3r version on this computer @@ -171,8 +165,10 @@ sub OnInit { . "to Print Settings and click the \"Set\" button next to \"Bed Shape\".", "Bed Shape"); } } - $self->{mainframe}->config_wizard if $run_wizard; - eval { $self->{preset_bundle}->load_presets($datadir) }; + if ($run_wizard) { + $self->{mainframe}->config_wizard; + eval { $self->{preset_bundle}->load_presets(Slic3r::data_dir) }; + } # $self->check_version # if $self->have_version_check @@ -297,7 +293,7 @@ sub notify { sub save_settings { my ($self) = @_; - Slic3r::Config->write_ini("$datadir/slic3r.ini", $Settings); + Slic3r::Config->write_ini(Slic3r::data_dir . "/slic3r.ini", $Settings); } # Called after the Preferences dialog is closed and the program settings are saved. @@ -307,48 +303,6 @@ sub update_ui_from_settings { $self->{mainframe}->update_ui_from_settings; } -sub presets { - my ($self, $section) = @_; - - my %presets = (); - opendir my $dh, Slic3r::encode_path("$Slic3r::GUI::datadir/$section") - or die "Failed to read directory $Slic3r::GUI::datadir/$section (errno: $!)\n"; - # Instead of using the /i modifier for case-insensitive matching, the case insensitivity is expressed - # explicitely to avoid having to bundle the UTF8 Perl library. - foreach my $file (grep /\.[iI][nN][iI]$/, readdir $dh) { - $file = Slic3r::decode_path($file); - my $name = basename($file); - $name =~ s/\.ini$//; - $presets{$name} = "$Slic3r::GUI::datadir/$section/$file"; - } - closedir $dh; - - return %presets; -} - -#sub have_version_check { -# my ($self) = @_; -# -# # return an explicit 0 -# return ($Slic3r::have_threads && $Slic3r::build && $have_LWP) || 0; -#} - -#sub check_version { -# my ($self, $manual_check) = @_; -# -# Slic3r::debugf "Checking for updates...\n"; -# -# @_ = (); -# threads->create(sub { -# my $ua = LWP::UserAgent->new; -# $ua->timeout(10); -# my $response = $ua->get('http://slic3r.org/updatecheck'); -# Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $VERSION_CHECK_EVENT, -# threads::shared::shared_clone([ $response->is_success, $response->decoded_content, $manual_check ]))); -# Slic3r::thread_cleanup(); -# })->detach; -#} - sub output_path { my ($self, $dir) = @_; @@ -434,7 +388,7 @@ sub set_menu_item_icon { # SetBitmap was not available on OS X before Wx 0.9927 if ($icon && $menuItem->can('SetBitmap')) { - $menuItem->SetBitmap(Wx::Bitmap->new($Slic3r::var->($icon), wxBITMAP_TYPE_PNG)); + $menuItem->SetBitmap(Wx::Bitmap->new(Slic3r::var($icon), wxBITMAP_TYPE_PNG)); } } diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index bb77ebd57..51c822590 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1390,7 +1390,7 @@ sub _load_image_set_texture { my ($self, $file_name) = @_; # Load a PNG with an alpha channel. my $img = Wx::Image->new; - $img->LoadFile($Slic3r::var->($file_name), wxBITMAP_TYPE_PNG); + $img->LoadFile(Slic3r::var($file_name), wxBITMAP_TYPE_PNG); # Get RGB & alpha raw data from wxImage, interleave them into a Perl array. my @rgb = unpack 'C*', $img->GetData(); my @alpha = $img->HasAlpha ? unpack 'C*', $img->GetAlpha() : (255) x (int(@rgb) / 3); diff --git a/lib/Slic3r/GUI/AboutDialog.pm b/lib/Slic3r/GUI/AboutDialog.pm index 398fde97d..0879ea35b 100644 --- a/lib/Slic3r/GUI/AboutDialog.pm +++ b/lib/Slic3r/GUI/AboutDialog.pm @@ -97,7 +97,7 @@ sub new { my $class = shift; my $self = $class->SUPER::new(@_); - $self->{logo} = Wx::Bitmap->new($Slic3r::var->("Slic3r_192px.png"), wxBITMAP_TYPE_PNG); + $self->{logo} = Wx::Bitmap->new(Slic3r::var("Slic3r_192px.png"), wxBITMAP_TYPE_PNG); $self->SetMinSize(Wx::Size->new($self->{logo}->GetWidth, $self->{logo}->GetHeight)); EVT_PAINT($self, \&repaint); diff --git a/lib/Slic3r/GUI/ConfigWizard.pm b/lib/Slic3r/GUI/ConfigWizard.pm index 3d94f9a68..19310b3c1 100644 --- a/lib/Slic3r/GUI/ConfigWizard.pm +++ b/lib/Slic3r/GUI/ConfigWizard.pm @@ -89,11 +89,11 @@ sub new { push @{$self->{titles}}, $title; $self->{own_index} = 0; - $self->{bullets}->{before} = Wx::Bitmap->new($Slic3r::var->("bullet_black.png"), wxBITMAP_TYPE_PNG); - $self->{bullets}->{own} = Wx::Bitmap->new($Slic3r::var->("bullet_blue.png"), wxBITMAP_TYPE_PNG); - $self->{bullets}->{after} = Wx::Bitmap->new($Slic3r::var->("bullet_white.png"), wxBITMAP_TYPE_PNG); + $self->{bullets}->{before} = Wx::Bitmap->new(Slic3r::var("bullet_black.png"), wxBITMAP_TYPE_PNG); + $self->{bullets}->{own} = Wx::Bitmap->new(Slic3r::var("bullet_blue.png"), wxBITMAP_TYPE_PNG); + $self->{bullets}->{after} = Wx::Bitmap->new(Slic3r::var("bullet_white.png"), wxBITMAP_TYPE_PNG); - $self->{background} = Wx::Bitmap->new($Slic3r::var->("Slic3r_192px_transparent.png"), wxBITMAP_TYPE_PNG); + $self->{background} = Wx::Bitmap->new(Slic3r::var("Slic3r_192px_transparent.png"), wxBITMAP_TYPE_PNG); $self->SetMinSize(Wx::Size->new($self->{background}->GetWidth, $self->{background}->GetHeight)); EVT_PAINT($self, \&repaint); diff --git a/lib/Slic3r/GUI/Controller.pm b/lib/Slic3r/GUI/Controller.pm index 8c4dcbec7..c4a22cace 100644 --- a/lib/Slic3r/GUI/Controller.pm +++ b/lib/Slic3r/GUI/Controller.pm @@ -32,21 +32,21 @@ sub new { # button for adding new printer panels { - my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("add.png"), wxBITMAP_TYPE_PNG), + my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE); $btn->SetToolTipString("Add printer…") if $btn->can('SetToolTipString'); EVT_LEFT_DOWN($btn, sub { my $menu = Wx::Menu->new; - my %presets = wxTheApp->presets('printer'); + my $presets = wxTheApp->{preset_bundle}->printer->presets_hash; # remove printers that already exist my @panels = $self->print_panels; - delete $presets{$_} for map $_->printer_name, @panels; + delete $presets->{$_} for map $_->printer_name, @panels; - foreach my $preset_name (sort keys %presets) { - my $config = Slic3r::Config->load($presets{$preset_name}); + foreach my $preset_name (sort keys %{$presets}) { + my $config = Slic3r::Config->load($presets->{$preset_name}); next if !$config->serial_port; my $id = &Wx::NewId(); @@ -100,9 +100,9 @@ sub OnActivate { # get all available presets my %presets = (); { - my %all = wxTheApp->presets('printer'); - my %configs = map { my $name = $_; $name => Slic3r::Config->load($all{$name}) } keys %all; - %presets = map { $_ => $configs{$_} } grep $configs{$_}->serial_port, keys %all; + my $all = wxTheApp->{preset_bundle}->printer->presets_hash; + my %configs = map { my $name = $_; $name => Slic3r::Config->load($all->{$name}) } keys %{$all}; + %presets = map { $_ => $configs{$_} } grep $configs{$_}->serial_port, keys %{$all}; } # decide which ones we want to keep @@ -183,8 +183,7 @@ sub print_panels { # Slic3r::GUI::Tab::Printer::_on_presets_changed # when the presets are loaded or the user select another preset. sub update_presets { - my $self = shift; - my ($group, $presets, $default_suppressed, $selected, $is_dirty) = @_; + my ($self, $group, $presets, $default_suppressed, $selected, $is_dirty) = @_; # update configs of currently loaded print panels foreach my $panel ($self->print_panels) { diff --git a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm index 8dcc05424..de0565255 100644 --- a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm +++ b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm @@ -34,7 +34,7 @@ sub new { my $btn = Wx::Button->new($self, -1, $label, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); $btn->SetFont($bold ? $Slic3r::GUI::small_bold_font : $Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("$icon.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("$icon.png"), wxBITMAP_TYPE_PNG)); $btn->SetBitmapPosition($pos); EVT_BUTTON($self, $btn, $handler); $sizer->Add($btn, 1, wxEXPAND | wxALL, 0); diff --git a/lib/Slic3r/GUI/Controller/PrinterPanel.pm b/lib/Slic3r/GUI/Controller/PrinterPanel.pm index 6dba614e3..1bf46c456 100644 --- a/lib/Slic3r/GUI/Controller/PrinterPanel.pm +++ b/lib/Slic3r/GUI/Controller/PrinterPanel.pm @@ -103,7 +103,7 @@ sub new { $serial_port_sizer->Add($self->{serial_port_combobox}, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 1); } { - $self->{btn_rescan_serial} = my $btn = Wx::BitmapButton->new($box, -1, Wx::Bitmap->new($Slic3r::var->("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG), + $self->{btn_rescan_serial} = my $btn = Wx::BitmapButton->new($box, -1, Wx::Bitmap->new(Slic3r::var("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxDefaultSize, &Wx::wxBORDER_NONE); $btn->SetToolTipString("Rescan serial ports") if $btn->can('SetToolTipString'); @@ -127,7 +127,7 @@ sub new { { $self->{btn_disconnect} = my $btn = Wx::Button->new($box, -1, "Disconnect", wxDefaultPosition, wxDefaultSize); $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG)); $serial_speed_sizer->Add($btn, 0, wxLEFT, 5); EVT_BUTTON($self, $btn, \&disconnect); } @@ -140,7 +140,7 @@ sub new { my $font = $btn->GetFont; $font->SetPointSize($font->GetPointSize + 2); $btn->SetFont($font); - $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("arrow_up.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("arrow_up.png"), wxBITMAP_TYPE_PNG)); $left_sizer->Add($btn, 0, wxTOP, 15); EVT_BUTTON($self, $btn, \&connect); } @@ -160,7 +160,7 @@ sub new { { $self->{btn_manual_control} = my $btn = Wx::Button->new($box, -1, "Manual control", wxDefaultPosition, wxDefaultSize); $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG)); $btn->Hide; $left_sizer->Add($btn, 0, wxTOP, 15); EVT_BUTTON($self, $btn, sub { @@ -577,7 +577,7 @@ sub new { $btn->SetToolTipString("Delete this job from print queue") if $btn->can('SetToolTipString'); $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG)); if ($job->printing) { $btn->Hide; } @@ -597,8 +597,8 @@ sub new { my $btn = $self->{btn_print} = Wx::Button->new($self, -1, $label, wxDefaultPosition, wxDefaultSize, $button_style); $btn->SetFont($Slic3r::GUI::small_bold_font); - $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("control_play.png"), wxBITMAP_TYPE_PNG)); - $btn->SetBitmapCurrent(Wx::Bitmap->new($Slic3r::var->("control_play_blue.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_play.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_play_blue.png"), wxBITMAP_TYPE_PNG)); #$btn->SetBitmapPosition(wxRIGHT); $btn->Hide; $buttons_sizer->Add($btn, 0, wxBOTTOM, 2); @@ -616,8 +616,8 @@ sub new { if (!$job->printing || $job->paused) { $btn->Hide; } - $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("control_pause.png"), wxBITMAP_TYPE_PNG)); - $btn->SetBitmapCurrent(Wx::Bitmap->new($Slic3r::var->("control_pause_blue.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_pause.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_pause_blue.png"), wxBITMAP_TYPE_PNG)); $buttons_sizer->Add($btn, 0, wxBOTTOM, 2); EVT_BUTTON($self, $btn, sub { @@ -633,8 +633,8 @@ sub new { if (!$job->printing || !$job->paused) { $btn->Hide; } - $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("control_play.png"), wxBITMAP_TYPE_PNG)); - $btn->SetBitmapCurrent(Wx::Bitmap->new($Slic3r::var->("control_play_blue.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_play.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_play_blue.png"), wxBITMAP_TYPE_PNG)); $buttons_sizer->Add($btn, 0, wxBOTTOM, 2); EVT_BUTTON($self, $btn, sub { @@ -650,8 +650,8 @@ sub new { if (!$job->printing) { $btn->Hide; } - $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("control_stop.png"), wxBITMAP_TYPE_PNG)); - $btn->SetBitmapCurrent(Wx::Bitmap->new($Slic3r::var->("control_stop_blue.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_stop.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_stop_blue.png"), wxBITMAP_TYPE_PNG)); $buttons_sizer->Add($btn, 0, wxBOTTOM, 2); EVT_BUTTON($self, $btn, sub { diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index ad2eca91d..554c9fbf5 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -6,6 +6,7 @@ use warnings; use utf8; use File::Basename qw(basename dirname); +use FindBin; use List::Util qw(min); use Slic3r::Geometry qw(X Y); use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog @@ -22,12 +23,12 @@ sub new { my $self = $class->SUPER::new(undef, -1, $Slic3r::FORK_NAME . ' - ' . $Slic3r::VERSION, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE); if ($^O eq 'MSWin32') { - # Load the icon either from the exe, or fron the ico file. - my $iconfile = $Slic3r::var->('..\slic3r.exe'); - $iconfile = $Slic3r::var->("Slic3r.ico") unless -f $iconfile; + # Load the icon either from the exe, or from the ico file. + my $iconfile = Slic3r::decode_path($FindBin::Bin) . '\slic3r.exe'; + $iconfile = Slic3r::var("Slic3r.ico") unless -f $iconfile; $self->SetIcon(Wx::Icon->new($iconfile, wxBITMAP_TYPE_ICO)); } else { - $self->SetIcon(Wx::Icon->new($Slic3r::var->("Slic3r_128px.png"), wxBITMAP_TYPE_PNG)); + $self->SetIcon(Wx::Icon->new(Slic3r::var("Slic3r_128px.png"), wxBITMAP_TYPE_PNG)); } # store input params @@ -112,7 +113,7 @@ sub _init_tabpanel { # Callback to be executed after any of the configuration fields (Perl class Slic3r::GUI::OptionsGroup::Field) change their value. $tab->on_value_change(sub { my ($opt_key, $value) = @_; - my $config = $tab->config; + my $config = $tab->{presets}->get_current_preset->config; if ($self->{plater}) { $self->{plater}->on_config_change($config); # propagate config change events to the plater $self->{plater}->on_extruders_change($value) if $opt_key eq 'extruders_count'; @@ -126,7 +127,7 @@ sub _init_tabpanel { if ($self->{plater}) { # Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs. $self->{plater}->update_presets($tab_name, @_); - $self->{plater}->on_config_change($tab->config); + $self->{plater}->on_config_change($tab->{presets}->get_current_preset->config); if ($self->{controller}) { $self->{controller}->update_presets($tab_name, @_); } @@ -143,7 +144,7 @@ sub _init_tabpanel { }); # load initial config - $self->{plater}->on_config_change($self->config); + $self->{plater}->on_config_change(wxTheApp->{preset_bundle}->full_config); } } @@ -495,7 +496,7 @@ sub repair_stl { sub extra_variables { my $self = shift; my %extra_variables = (); - $extra_variables{"${_}_preset"} = $self->{options_tabs}{$_}->get_current_preset->name + $extra_variables{"${_}_preset"} = wxTheApp->{preset_bundle}->{$_}->get_current_preset_name for qw(print filament printer); return { %extra_variables }; } @@ -569,9 +570,9 @@ sub export_configbundle { $ini->{presets} = $Slic3r::GUI::Settings->{presets}; foreach my $section (qw(print filament printer)) { - my %presets = wxTheApp->presets($section); - foreach my $preset_name (keys %presets) { - my $config = Slic3r::Config->load($presets{$preset_name}); + my $presets = wxTheApp->{preset_bundle}->$section->presets_hash; + foreach my $preset_name (keys %{$presets}) { + my $config = Slic3r::Config->load($presets->{$preset_name}); $ini->{"$section:$preset_name"} = $config->as_ini->{_}; } } @@ -616,15 +617,15 @@ sub load_configbundle { next if $skip_no_id && !$config->get($section . "_settings_id"); { - my %current_presets = Slic3r::GUI->presets($section); + my $current_presets = wxTheApp->{preset_bundle}->$section->presets_hash; my %current_ids = map { $_ => 1 } grep $_, map Slic3r::Config->load($_)->get($section . "_settings_id"), - values %current_presets; + values %{$current_presets}; next INI_BLOCK if exists $current_ids{$config->get($section . "_settings_id")}; } - $config->save(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $section, $preset_name); + $config->save(sprintf Slic3r::data_dir . "/%s/%s.ini", $section, $preset_name); Slic3r::debugf "Imported %s preset %s\n", $section, $preset_name; $imported++; } @@ -658,7 +659,8 @@ sub config_wizard { return unless $self->check_unsaved_changes; if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) { for my $tab (values %{$self->{options_tabs}}) { - $tab->select_default_preset; + # Select the first visible preset. + $tab->select_preset(undef); } $self->load_config($config); for my $tab (values %{$self->{options_tabs}}) { @@ -667,84 +669,27 @@ sub config_wizard { } } -=head2 config - -This method collects all config values from the tabs and merges them into a single config object. - -=cut - -sub config { - my $self = shift; - - return Slic3r::Config->new_from_defaults - if !exists $self->{options_tabs}{print} - || !exists $self->{options_tabs}{filament} - || !exists $self->{options_tabs}{printer}; - - # retrieve filament presets and build a single config object for them - my $filament_config; - if (!$self->{plater} || $self->{plater}->filament_presets == 1) { - $filament_config = $self->{options_tabs}{filament}->config; - } else { - my $i = -1; - foreach my $preset_idx ($self->{plater}->filament_presets) { - $i++; - my $config; - if ($preset_idx == $self->{options_tabs}{filament}->current_preset) { - # the selected preset for this extruder is the one in the tab - # use the tab's config instead of the preset in case it is dirty - # perhaps plater shouldn't expose dirty presets at all in multi-extruder environments. - $config = $self->{options_tabs}{filament}->config; - } else { - my $preset = $self->{options_tabs}{filament}->get_preset($preset_idx); - $config = $self->{options_tabs}{filament}->get_preset_config($preset); - } - if (!$filament_config) { - $filament_config = $config->clone; - next; - } - foreach my $opt_key (@{$config->get_keys}) { - my $value = $filament_config->get($opt_key); - next unless ref $value eq 'ARRAY'; - $value->[$i] = $config->get($opt_key)->[0]; - $filament_config->set($opt_key, $value); - } - } - } - - my $config = Slic3r::Config->merge( - Slic3r::Config->new_from_defaults, - $self->{options_tabs}{print}->config, - $self->{options_tabs}{printer}->config, - $filament_config, - ); - - my $extruders_count = $self->{options_tabs}{printer}{extruders_count}; - $config->set("${_}_extruder", min($config->get("${_}_extruder"), $extruders_count)) - for qw(perimeter infill solid_infill support_material support_material_interface); - - return $config; -} - sub filament_preset_names { my ($self) = @_; - return map $self->{options_tabs}{filament}->get_preset($_)->name, + return map $self->{options_tabs}{filament}->{presets}->preset($_)->name, $self->{plater}->filament_presets; } +# This is called when closing the application, when loading a config file or when starting the config wizard +# to notify the user whether he is aware that some preset changes will be lost. sub check_unsaved_changes { my $self = shift; my @dirty = (); foreach my $tab (values %{$self->{options_tabs}}) { - push @dirty, $tab->title if $tab->is_dirty; + push @dirty, $tab->title if $tab->{presets}->current_is_dirty; } if (@dirty) { my $titles = join ', ', @dirty; my $confirm = Wx::MessageDialog->new($self, "You have unsaved changes ($titles). Discard changes and continue anyway?", 'Unsaved Presets', wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT); - return ($confirm->ShowModal == wxID_YES); + return $confirm->ShowModal == wxID_YES; } return 1; @@ -779,7 +724,7 @@ sub _set_menu_item_icon { # SetBitmap was not available on OS X before Wx 0.9927 if ($icon && $menuItem->can('SetBitmap')) { - $menuItem->SetBitmap(Wx::Bitmap->new($Slic3r::var->($icon), wxBITMAP_TYPE_PNG)); + $menuItem->SetBitmap(Wx::Bitmap->new(Slic3r::var($icon), wxBITMAP_TYPE_PNG)); } } diff --git a/lib/Slic3r/GUI/Notifier.pm b/lib/Slic3r/GUI/Notifier.pm index eb548d014..54afffae0 100644 --- a/lib/Slic3r/GUI/Notifier.pm +++ b/lib/Slic3r/GUI/Notifier.pm @@ -6,7 +6,7 @@ use Moo; has 'growler' => (is => 'rw'); -my $icon = $Slic3r::var->("Slic3r.png"); +my $icon = Slic3r::var("Slic3r.png"); sub BUILD { my ($self) = @_; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index aadd159f9..d8a9ec7ba 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -166,22 +166,22 @@ sub new { if (!&Wx::wxMSW) { Wx::ToolTip::Enable(1); $self->{htoolbar} = Wx::ToolBar->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL | wxTB_TEXT | wxBORDER_SIMPLE | wxTAB_TRAVERSAL); - $self->{htoolbar}->AddTool(TB_ADD, "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, "Arrange", Wx::Bitmap->new($Slic3r::var->("bricks.png"), wxBITMAP_TYPE_PNG), ''); + $self->{htoolbar}->AddTool(TB_ADD, "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, "Arrange", Wx::Bitmap->new(Slic3r::var("bricks.png"), wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddSeparator; - $self->{htoolbar}->AddTool(TB_MORE, "More", Wx::Bitmap->new($Slic3r::var->("add.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_FEWER, "Fewer", Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG), ''); + $self->{htoolbar}->AddTool(TB_MORE, "More", Wx::Bitmap->new(Slic3r::var("add.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), ''); - $self->{htoolbar}->AddTool(TB_SCALE, "Scale…", Wx::Bitmap->new($Slic3r::var->("arrow_out.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new($Slic3r::var->("shape_ungroup.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.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_SCALE, "Scale…", Wx::Bitmap->new(Slic3r::var("arrow_out.png"), wxBITMAP_TYPE_PNG), ''); + $self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG), ''); + $self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddSeparator; - $self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new($Slic3r::var->("variable_layer_height.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, 0, 'Layer Editing'); + $self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG), ''); + $self->{htoolbar}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new(Slic3r::var("variable_layer_height.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, 0, 'Layer Editing'); } else { my %tbar_buttons = ( add => "Add…", @@ -256,7 +256,7 @@ sub new { settings cog.png ); for (grep $self->{"btn_$_"}, keys %icons) { - $self->{"btn_$_"}->SetBitmap(Wx::Bitmap->new($Slic3r::var->($icons{$_}), wxBITMAP_TYPE_PNG)); + $self->{"btn_$_"}->SetBitmap(Wx::Bitmap->new(Slic3r::var($icons{$_}), wxBITMAP_TYPE_PNG)); } $self->selection_changed(0); $self->object_list_changed; @@ -414,7 +414,7 @@ sub new { $self->{"object_info_$field"} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); $self->{"object_info_$field"}->SetFont($Slic3r::GUI::small_font); if ($field eq 'manifold') { - $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($self, -1, Wx::Bitmap->new($Slic3r::var->("error.png"), wxBITMAP_TYPE_PNG)); + $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($self, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG)); $self->{object_info_manifold_warning_icon}->Hide; my $h_sizer = Wx::BoxSizer->new(wxHORIZONTAL); @@ -527,7 +527,7 @@ sub _on_select_preset { } # get new config and generate on_config_change() event for updating plater and other things - $self->on_config_change($self->GetFrame->config); + $self->on_config_change(wxTheApp->{preset_bundle}->full_config); } sub on_layer_editing_toggled { @@ -572,50 +572,51 @@ sub update_ui_from_settings # For Print settings and Printer, synchronize the selection index with their tabs. # For Filament, synchronize the selection index for a single extruder printer only, otherwise keep the selection. sub update_presets { - my $self = shift; # $presets: one of qw(print filament printer) # $selected: index of the selected preset in the array. This may not correspond # with the index of selection in the UI element, where not all items are displayed. - my ($group, $presets, $default_suppressed, $selected, $is_dirty) = @_; - - my @choosers = @{ $self->{preset_choosers}{$group} }; - my $choice_idx = 0; - foreach my $choice (@choosers) { - if ($group eq 'filament' && @choosers > 1) { + my ($self, $group, $presets, $default_suppressed, $selected, $is_dirty) = @_; + + print "Plater.pm update_presets presets: $presets\n"; +# my @choosers = @{ $self->{preset_choosers}{$group} }; +# my $choice_idx = 0; +# foreach my $choice (@choosers) { +# if ($group eq 'filament' && @choosers > 1) { # if we have more than one filament chooser, keep our selection # instead of importing the one from the tab - $selected = $choice->GetSelection + $self->{preset_choosers_default_suppressed}{$group}; - $is_dirty = 0; - } - $choice->Clear; - foreach my $preset (@$presets) { - next if ($preset->default && $default_suppressed); - my $bitmap; - if ($group eq 'filament') { - $bitmap = Wx::Bitmap->new($Slic3r::var->("spool.png"), wxBITMAP_TYPE_PNG); - } elsif ($group eq 'print') { - $bitmap = Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG); - } elsif ($group eq 'printer') { - $bitmap = Wx::Bitmap->new($Slic3r::var->("printer_empty.png"), wxBITMAP_TYPE_PNG); - } - $choice->AppendString($preset->name, $bitmap); - } +# $selected = $choice->GetSelection + $self->{preset_choosers_default_suppressed}{$group}; +# $is_dirty = 0; +# } +# $choice->Clear; +# foreach my $preset (@{$presets}) { +# print "Prset of $presets: $preset\n"; +# next if ($preset->default && $default_suppressed); +# my $bitmap; +# if ($group eq 'filament') { +# $bitmap = Wx::Bitmap->new(Slic3r::var("spool.png"), wxBITMAP_TYPE_PNG); +# } elsif ($group eq 'print') { +# $bitmap = Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG); +# } elsif ($group eq 'printer') { +# $bitmap = Wx::Bitmap->new(Slic3r::var("printer_empty.png"), wxBITMAP_TYPE_PNG); +# } +# $choice->AppendString($preset->name, $bitmap); +# } - if ($selected <= $#$presets) { - my $idx = $selected - $default_suppressed; - if ($idx >= 0) { - if ($is_dirty) { - $choice->SetString($idx, $choice->GetString($idx) . " (modified)"); - } - # call SetSelection() only after SetString() otherwise the new string - # won't be picked up as the visible string - $choice->SetSelection($idx); - } - } - $choice_idx += 1; - } +# if ($selected <= $#$presets) { +# my $idx = $selected - $default_suppressed; +# if ($idx >= 0) { +# if ($is_dirty) { +# $choice->SetString($idx, $choice->GetString($idx) . " (modified)"); +# } +# # call SetSelection() only after SetString() otherwise the new string +# # won't be picked up as the visible string +# $choice->SetSelection($idx); +# } +# } +# $choice_idx += 1; +# } - $self->{preset_choosers_default_suppressed}{$group} = $default_suppressed; +# $self->{preset_choosers_default_suppressed}{$group} = $default_suppressed; wxTheApp->CallAfter(sub { $self->update_filament_colors_preview }) if $group eq 'filament' || $group eq 'printer'; } @@ -626,63 +627,65 @@ sub update_presets { sub update_filament_colors_preview { my ($self, $extruder_idx) = shift; - my @choosers = @{$self->{preset_choosers}{filament}}; +# my @choosers = @{$self->{preset_choosers}{filament}}; - if (ref $extruder_idx) { +# if (ref $extruder_idx) { # $extruder_idx is the chooser. - foreach my $chooser (@choosers) { - if ($extruder_idx == $chooser) { - $extruder_idx = $chooser; - last; - } - } - } +# foreach my $chooser (@choosers) { +# if ($extruder_idx == $chooser) { +# $extruder_idx = $chooser; +# last; +# } +# } +# } - my @extruder_colors = @{$self->{config}->extruder_colour}; +# my @extruder_colors = @{$self->{config}->extruder_colour}; - my @extruder_list; - if (defined $extruder_idx) { - @extruder_list = ($extruder_idx); - } else { +# my @extruder_list; +# if (defined $extruder_idx) { +# @extruder_list = ($extruder_idx); +# } else { # Collect extruder indices. - @extruder_list = (0..$#extruder_colors); - } +# @extruder_list = (0..$#extruder_colors); +# } - my $filament_tab = $self->GetFrame->{options_tabs}{filament}; - my $presets = $filament_tab->{presets}; - my $default_suppressed = $filament_tab->{default_suppressed}; +# my $filament_tab = $self->GetFrame->{options_tabs}{filament}; +# my $presets = $filament_tab->{presets}; +# my $default_suppressed = $filament_tab->{default_suppressed}; - foreach my $extruder_idx (@extruder_list) { - my $chooser = $choosers[$extruder_idx]; - my $extruder_color = $self->{config}->extruder_colour->[$extruder_idx]; - my $preset_idx = 0; - my $selection_idx = $chooser->GetSelection; - foreach my $preset (@$presets) { - my $bitmap; - if ($preset->default) { - next if $default_suppressed; - } else { +# foreach my $extruder_idx (@extruder_list) { +# my $chooser = $choosers[$extruder_idx]; +# my $extruder_color = $self->{config}->extruder_colour->[$extruder_idx]; +# my $preset_idx = 0; +# my $selection_idx = $chooser->GetSelection; +# foreach my $preset (@$presets) { +# my $bitmap; +# if ($preset->default) { +# next if $default_suppressed; +# } else { # Assign an extruder color to the selected item if the extruder color is defined. - my $filament_rgb = $preset->config(['filament_colour'])->filament_colour->[0]; - my $extruder_rgb = ($preset_idx == $selection_idx && $extruder_color =~ m/^#[[:xdigit:]]{6}/) ? $extruder_color : $filament_rgb; - $filament_rgb =~ s/^#//; - $extruder_rgb =~ s/^#//; - my $image = Wx::Image->new(24,16); - if ($filament_rgb ne $extruder_rgb) { - my @rgb = unpack 'C*', pack 'H*', $extruder_rgb; - $image->SetRGB(Wx::Rect->new(0,0,16,16), @rgb); - @rgb = unpack 'C*', pack 'H*', $filament_rgb; - $image->SetRGB(Wx::Rect->new(16,0,8,16), @rgb); - } else { - my @rgb = unpack 'C*', pack 'H*', $filament_rgb; - $image->SetRGB(Wx::Rect->new(0,0,24,16), @rgb); - } - $bitmap = Wx::Bitmap->new($image); - } - $chooser->SetItemBitmap($preset_idx, $bitmap) if $bitmap; - $preset_idx += 1; - } - } +# my $filament_colour_cfg = $preset->config_ref->filament_colour; +# print $filament_colour_cfg . "\n"; +# my $filament_rgb = $filament_colour_cfg->[0]; +# my $extruder_rgb = ($preset_idx == $selection_idx && $extruder_color =~ m/^#[[:xdigit:]]{6}/) ? $extruder_color : $filament_rgb; +# $filament_rgb =~ s/^#//; +# $extruder_rgb =~ s/^#//; +# my $image = Wx::Image->new(24,16); +# if ($filament_rgb ne $extruder_rgb) { +# my @rgb = unpack 'C*', pack 'H*', $extruder_rgb; +# $image->SetRGB(Wx::Rect->new(0,0,16,16), @rgb); +# @rgb = unpack 'C*', pack 'H*', $filament_rgb; +# $image->SetRGB(Wx::Rect->new(16,0,8,16), @rgb); +# } else { +# my @rgb = unpack 'C*', pack 'H*', $filament_rgb; +# $image->SetRGB(Wx::Rect->new(0,0,24,16), @rgb); +# } +# $bitmap = Wx::Bitmap->new($image); +# } +# $chooser->SetItemBitmap($preset_idx, $bitmap) if $bitmap; +# $preset_idx += 1; +# } +# } } # Return a vector of indices of filaments selected by the $self->{preset_choosers}{filament} combo boxes. @@ -1163,7 +1166,7 @@ sub arrange { $self->pause_background_process; my $bb = Slic3r::Geometry::BoundingBoxf->new_from_points($self->{config}->bed_shape); - my $success = $self->{model}->arrange_objects($self->GetFrame->config->min_object_distance, $bb); + my $success = $self->{model}->arrange_objects(wxTheApp->{preset_bundle}->full_config->min_object_distance, $bb); # ignore arrange failures on purpose: user has visual feedback and we don't need to warn him # when parts don't fit in print bed @@ -1224,7 +1227,7 @@ sub async_apply_config { $self->pause_background_process; # apply new config - my $invalidated = $self->{print}->apply_config($self->GetFrame->config); + my $invalidated = $self->{print}->apply_config(wxTheApp->{preset_bundle}->full_config); # Just redraw the 3D canvas without reloading the scene. # $self->{canvas3D}->Refresh if ($invalidated && $self->{canvas3D}->layer_editing_enabled); @@ -1266,7 +1269,7 @@ sub start_background_process { # don't start process thread if config is not valid eval { # this will throw errors if config is not valid - $self->GetFrame->config->validate; + wxTheApp->{preset_bundle}->full_config->validate; $self->{print}->validate; }; if ($@) { @@ -1378,14 +1381,14 @@ sub export_gcode { # (we assume that if it is running, config is valid) eval { # this will throw errors if config is not valid - $self->GetFrame->config->validate; + wxTheApp->{preset_bundle}->full_config->validate; $self->{print}->validate; }; Slic3r::GUI::catch_error($self) and return; # apply config and validate print - my $config = $self->GetFrame->config; + my $config = wxTheApp->{preset_bundle}->full_config; eval { # this will throw errors if config is not valid $config->validate; @@ -1549,11 +1552,9 @@ sub on_export_completed { sub do_print { my ($self) = @_; - my $printer_tab = $self->GetFrame->{options_tabs}{printer}; - my $printer_name = $printer_tab->get_current_preset->name; - my $controller = $self->GetFrame->{controller}; - my $printer_panel = $controller->add_printer($printer_name, $printer_tab->config); + my $printer_preset = wxTheApp->{preset_bundle}->printer->get_edited_preset; + my $printer_panel = $controller->add_printer($printer_preset->name, $printer_preset->config); my $filament_stats = $self->{print}->filament_stats; my @filament_names = $self->GetFrame->filament_preset_names; @@ -1859,7 +1860,7 @@ sub filament_color_box_lmouse_down my $dialog = Wx::ColourDialog->new($self->GetFrame, $data); if ($dialog->ShowModal == wxID_OK) { my $cfg = Slic3r::Config->new; - my $colors = $self->GetFrame->config->get('extruder_colour'); + my $colors = wxTheApp->{preset_bundle}->full_config->get('extruder_colour'); $colors->[$extruder_idx] = $dialog->GetColourData->GetColour->GetAsString(wxC2S_HTML_SYNTAX); $cfg->set('extruder_colour', $colors); $self->GetFrame->{options_tabs}{printer}->load_config($cfg); @@ -1912,7 +1913,7 @@ sub object_settings_dialog { my $dlg = Slic3r::GUI::Plater::ObjectSettingsDialog->new($self, object => $self->{objects}[$obj_idx], model_object => $model_object, - config => $self->GetFrame->config, + config => wxTheApp->{preset_bundle}->full_config, ); $self->pause_background_process; $dlg->ShowModal; @@ -2048,18 +2049,14 @@ sub selected_object { } sub validate_config { - my $self = shift; - eval { - $self->GetFrame->config->validate; + wxTheApp->{preset_bundle}->full_config->validate; }; - return 0 if Slic3r::GUI::catch_error($self); - return 1; + return Slic3r::GUI::catch_error($_[0]) ? 0 : 1; } sub statusbar { - my $self = shift; - return $self->GetFrame->{statusbar}; + return $_[0]->GetFrame->{statusbar}; } sub object_menu { diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 707929e33..469794766 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -42,9 +42,9 @@ sub new { { $self->{tree_icons} = Wx::ImageList->new(16, 16, 1); $tree->AssignImageList($self->{tree_icons}); - $self->{tree_icons}->Add(Wx::Bitmap->new($Slic3r::var->("brick.png"), wxBITMAP_TYPE_PNG)); # ICON_OBJECT - $self->{tree_icons}->Add(Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG)); # ICON_SOLIDMESH - $self->{tree_icons}->Add(Wx::Bitmap->new($Slic3r::var->("plugin.png"), wxBITMAP_TYPE_PNG)); # ICON_MODIFIERMESH + $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("brick.png"), wxBITMAP_TYPE_PNG)); # ICON_OBJECT + $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG)); # ICON_SOLIDMESH + $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("plugin.png"), wxBITMAP_TYPE_PNG)); # ICON_MODIFIERMESH my $rootId = $tree->AddRoot("Object", ICON_OBJECT); $tree->SetPlData($rootId, { type => 'object' }); @@ -58,13 +58,13 @@ sub new { $self->{btn_split} = Wx::Button->new($self, -1, "Split part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); $self->{btn_move_up} = Wx::Button->new($self, -1, "", wxDefaultPosition, [40, -1], wxBU_LEFT); $self->{btn_move_down} = Wx::Button->new($self, -1, "", wxDefaultPosition, [40, -1], wxBU_LEFT); - $self->{btn_load_part}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_load_modifier}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_load_lambda_modifier}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_delete}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_delete.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_split}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("shape_ungroup.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_move_up}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("bullet_arrow_up.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_move_down}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("bullet_arrow_down.png"), wxBITMAP_TYPE_PNG)); + $self->{btn_load_part}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG)); + $self->{btn_load_modifier}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG)); + $self->{btn_load_lambda_modifier}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG)); + $self->{btn_delete}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_delete.png"), wxBITMAP_TYPE_PNG)); + $self->{btn_split}->SetBitmap(Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG)); + $self->{btn_move_up}->SetBitmap(Wx::Bitmap->new(Slic3r::var("bullet_arrow_up.png"), wxBITMAP_TYPE_PNG)); + $self->{btn_move_down}->SetBitmap(Wx::Bitmap->new(Slic3r::var("bullet_arrow_down.png"), wxBITMAP_TYPE_PNG)); # buttons sizer my $buttons_sizer = Wx::GridSizer->new(2, 3); diff --git a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm index 2f8b8ce73..3b10ed99f 100644 --- a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm +++ b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm @@ -46,7 +46,7 @@ sub new { # option selector { # create the button - my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("add.png"), wxBITMAP_TYPE_PNG), + my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE); EVT_LEFT_DOWN($btn, sub { my $menu = Wx::Menu->new; @@ -146,7 +146,7 @@ sub update_optgroup { # disallow deleting fixed options return undef if $self->{fixed_options}{$opt_key}; - my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG), + my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE); EVT_BUTTON($self, $btn, sub { $self->{config}->erase($opt_key); diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index fb9d7e8b0..6de0985ac 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -24,12 +24,8 @@ use Wx qw(:bookctrl :dialog :keycode :icon :id :misc :panel :sizer :treectrl :wi use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_KEY_DOWN EVT_CHECKBOX EVT_TREE_SEL_CHANGED); use base qw(Wx::Panel Class::Accessor); -# Index of the currently active preset. -__PACKAGE__->mk_accessors(qw(current_preset)); - sub new { - my $class = shift; - my ($parent, %params) = @_; + my ($class, $parent, %params) = @_; my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL); # Vertical sizer to hold the choice menu and the rest of the page. @@ -45,9 +41,9 @@ sub new { $self->{presets_choice}->SetFont($Slic3r::GUI::small_font); # buttons - $self->{btn_save_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("disk.png"), wxBITMAP_TYPE_PNG), + $self->{btn_save_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("disk.png"), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); - $self->{btn_delete_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG), + $self->{btn_delete_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); $self->{btn_save_preset}->SetToolTipString("Save current " . lc($self->title)); $self->{btn_delete_preset}->SetToolTipString("Delete this preset"); @@ -97,54 +93,18 @@ sub new { }); EVT_CHOICE($parent, $self->{presets_choice}, sub { - $self->on_select_preset; + $self->select_preset($self->{presets_choice}->GetStringSelection); $self->_on_presets_changed; }); EVT_BUTTON($self, $self->{btn_save_preset}, sub { $self->save_preset }); + EVT_BUTTON($self, $self->{btn_delete_preset}, sub { $self->delete_preset }); - EVT_BUTTON($self, $self->{btn_delete_preset}, sub { - my $i = $self->current_preset; - # Don't let the user delete the '- default -' configuration. - # This shall not happen as the 'delete' button is disabled for the '- default -' entry, - # but better be safe than sorry. - return if ($i == 0 && $self->get_current_preset->{default}); - my $res = Wx::MessageDialog->new($self, "Are you sure you want to delete the selected preset?", 'Delete Preset', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)->ShowModal; - return unless $res == wxID_YES; - # Delete the file. - my $path = $self->{presets}[$i]->file; - my $enc_path = Slic3r::encode_path($path); - if (-e $enc_path && ! unlink $enc_path) { - # Cannot delete the file, therefore the item will not be removed from the selection. - Slic3r::GUI::show_error($self, "Cannot delete file $path : $!"); - return; - } - # Delete the preset. - splice @{$self->{presets}}, $i, 1; - # Delete the item from the UI component. - $self->{presets_choice}->Delete($i - $self->{default_suppressed}); - $self->current_preset(undef); - if ($self->{default_suppressed} && scalar(@{$self->{presets}}) == 1) { - # Empty selection. Add the '- default -' item into the drop down selection. - $self->{presets_choice}->Append($self->{presets}->[0]->name); - # and remember that the '- default -' is shown. - $self->{default_suppressed} = 0; - } - # Select the 0th item. If default is suppressed, select the first valid. - $self->select_preset($self->{default_suppressed}); - $self->_on_presets_changed; - }); - - # C++ instance DynamicPrintConfig - $self->{config} = Slic3r::Config->new; # Initialize the DynamicPrintConfig by default keys/values. # Possible %params keys: no_controller $self->build(%params); $self->update_tree; $self->_update; - if ($self->hidden_options) { - $self->{config}->apply(Slic3r::Config->new_from_defaults($self->hidden_options)); - } return $self; } @@ -154,18 +114,6 @@ sub no_defaults { return $Slic3r::GUI::Settings->{_}{no_defaults} ? 1 : 0; } -# Get a currently active preset (Perl class Slic3r::GUI::Tab::Preset). -sub get_current_preset { - my $self = shift; - return $self->get_preset($self->current_preset); -} - -# Get a preset (Perl class Slic3r::GUI::Tab::Preset) with an index $i. -sub get_preset { - my ($self, $i) = @_; - return $self->{presets}[$i]; -} - sub save_preset { my ($self, $name) = @_; @@ -175,7 +123,7 @@ sub save_preset { $self->{treectrl}->SetFocus; if (!defined $name) { - my $preset = $self->get_current_preset; + my $preset = $self->{presets}->get_edited_preset; my $default_name = $preset->default ? 'Untitled' : $preset->name; $default_name =~ s/\.[iI][nN][iI]$//; @@ -185,12 +133,27 @@ sub save_preset { values => [ map $_->name, grep !$_->default && !$_->external, @{$self->{presets}} ], ); return unless $dlg->ShowModal == wxID_OK; - $name = $dlg->get_name; + $name = Slic3r::normalize_utf8_nfc($dlg->get_name); } - $self->config->save(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $self->name, $name); + $self->{config}->save(sprintf Slic3r::data_dir . "/%s/%s.ini", $self->name, $name); $self->load_presets; - $self->select_preset_by_name($name); + $self->select_preset($name); + $self->_on_presets_changed; +} + +# Called for a currently selected preset. +sub delete_preset { + my ($self) = @_; + my $current_preset = $self->{presets}->get_selected_preset; + # Don't let the user delete the '- default -' configuration. + return if $current_preset->{default} || + wxID_YES == Wx::MessageDialog->new($self, "Are you sure you want to delete the selected preset?", 'Delete Preset', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)->ShowModal; + # Delete the file and select some other reasonable preset. + eval { $self->{presets}->delete_file($current_preset->name); }; + Slic3r::GUI::catch_error($self) and return; + # Delete the item from the UI component. + $self->{presets}->update_platter_ui($self->{presets_choice}); $self->_on_presets_changed; } @@ -210,116 +173,79 @@ sub on_presets_changed { sub _on_value_change { my ($self, $key, $value) = @_; $self->{on_value_change}->($key, $value) if $self->{on_value_change}; - $self->_update({ $key => 1 }); + $self->_update; } # Override this to capture changes of configuration caused either by loading or switching a preset, # or by a user changing an option field. sub _update {} +# Call a callback to update the selection of presets on the platter. sub _on_presets_changed { - my $self = shift; - + my ($self) = @_; $self->{on_presets_changed}->( $self->{presets}, $self->{default_suppressed}, scalar($self->{presets_choice}->GetSelection) + $self->{default_suppressed}, - $self->is_dirty, + $self->{presets}->current_is_dirty, ) if $self->{on_presets_changed}; } sub on_preset_loaded {} -sub hidden_options {} -sub config { $_[0]->{config}->clone } - -sub select_default_preset { - my $self = shift; - $self->select_preset(0); -} +# Called by the UI combo box when the user switches profiles. +# Select a preset by a name. If ! defined(name), then the first visible preset is selected. +# If the current profile is modified, user is asked to save the changes. sub select_preset { - my ($self, $i) = @_; - if ($self->{default_suppressed} && $i == 0) { - # Selecting the '- default -'. Add it to the combo box. - $self->{default_suppressed} = 0; - $self->{presets_choice}->Insert($self->{presets}->[0]->name, 0); - } elsif ($self->no_defaults && ! $self->{default_suppressed} && $i > 0) { - # The user wants to hide the '- default -' items and a non-default item has been added to the presets. - # Hide the '- default -' item. - $self->{presets_choice}->Delete(0); - $self->{default_suppressed} = 1; - } - $self->{presets_choice}->SetSelection($i - $self->{default_suppressed}); - $self->on_select_preset; -} - -sub select_preset_by_name { my ($self, $name) = @_; - - $name = Slic3r::normalize_utf8_nfc($name); - $self->select_preset(first { $self->{presets}[$_]->name eq $name } 0 .. $#{$self->{presets}}); -} -sub on_select_preset { - my $self = shift; - - if ($self->is_dirty) { + if ($self->{presets}->current_is_dirty) { # Display a dialog showing the dirty options in a human readable form. - my $old_preset = $self->get_current_preset; + my $old_preset = $self->{presets}->get_current_preset; my $name = $old_preset->default ? 'Default preset' : "Preset \"" . $old_preset->name . "\""; - + # Collect descriptions of the dirty options. my @option_names = (); - foreach my $opt_key (@{$self->dirty_options}) { + foreach my $opt_key (@{$self->{presets}->current_dirty_options}) { my $opt = $Slic3r::Config::Options->{$opt_key}; my $name = $opt->{full_label} // $opt->{label}; - if ($opt->{category}) { - $name = $opt->{category} . " > $name"; - } + $name = $opt->{category} . " > $name" if $opt->{category}; push @option_names, $name; } - + # Show a confirmation dialog with the list of dirty options. my $changes = join "\n", map "- $_", @option_names; my $confirm = Wx::MessageDialog->new($self, "$name has unsaved changes:\n$changes\n\nDiscard changes and continue anyway?", 'Unsaved Changes', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); if ($confirm->ShowModal == wxID_NO) { - $self->{presets_choice}->SetSelection($self->current_preset - $self->{default_suppressed}); - - # trigger the on_presets_changed event so that we also restore the previous value - # in the plater selector + $self->{presets}->update_platter_ui($self->{presets_choice}); + # Trigger the on_presets_changed event so that we also restore the previous value in the plater selector. $self->_on_presets_changed; return; } } - - $self->current_preset($self->{presets_choice}->GetSelection + $self->{default_suppressed}); - my $preset = $self->get_current_preset; - my $preset_config = $self->get_preset_config($preset); + + $self->{presets}->select_by_name_ui(defined $name ? $name : "", $self->{presets_choice}); + my $preset = $self->{presets}->get_current_preset; + my $preset_config = $preset->config; eval { local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); - my %keys_modified = (); foreach my $opt_key (@{$self->{config}->get_keys}) { - if ($preset_config->has($opt_key)) { - if ($self->{config}->serialize($opt_key) ne $preset_config->serialize($opt_key)) { - $self->{config}->set($opt_key, $preset_config->get($opt_key)); - $keys_modified{$opt_key} = 1; - } + if ($preset_config->has($opt_key) && + $self->{config}->serialize($opt_key) ne $preset_config->serialize($opt_key)) { + $self->{config}->set($opt_key, $preset_config->get($opt_key)); } } ($preset->default || $preset->external) ? $self->{btn_delete_preset}->Disable : $self->{btn_delete_preset}->Enable; - - $self->_update(\%keys_modified); + $self->_update; + # For the printer profile, generate the extruder pages. $self->on_preset_loaded; + # Reload preset pages with the new configuration values. $self->reload_config; + # Use this preset the next time Slic3r starts. $Slic3r::GUI::Settings->{presets}{$self->name} = $preset->file ? basename($preset->file) : ''; }; - if ($@) { - $@ = "I was unable to load the selected config file: $@"; - Slic3r::GUI::catch_error($self); - $self->select_default_preset; - } - + # use CallAfter because some field triggers schedule on_change calls using CallAfter, # and we don't want them to be called after this update_dirty() as they would mark the # preset dirty again @@ -329,20 +255,16 @@ sub on_select_preset { $self->update_dirty; }); + # Save the current application settings with the newly selected preset name. wxTheApp->save_settings; } -sub init_config_options { - my ($self, @opt_keys) = @_; - $self->{config}->apply(Slic3r::Config->new_from_defaults(@opt_keys)); -} - sub add_options_page { my $self = shift; my ($title, $icon, %params) = @_; if ($icon) { - my $bitmap = Wx::Bitmap->new($Slic3r::var->($icon), wxBITMAP_TYPE_PNG); + my $bitmap = Wx::Bitmap->new(Slic3r::var($icon), wxBITMAP_TYPE_PNG); $self->{icons}->Add($bitmap); $self->{iconcount}++; } @@ -354,8 +276,9 @@ sub add_options_page { return $page; } +# Reload current $self->{config} (aka $self->{presets}->edited_preset->config) into the UI fields. sub reload_config { - my $self = shift; + my ($self) = @_; $_->reload_config for @{$self->{pages}}; } @@ -388,87 +311,40 @@ sub update_tree { # comparing the selected preset config with $self->{config}. sub update_dirty { my ($self) = @_; - my $list_updated; - foreach my $i ($self->{default_suppressed}..$#{$self->{presets}}) { - my $preset = $self->get_preset($i); - my $label = ($i == $self->current_preset && $self->is_dirty) ? $preset->name . " (modified)" : $preset->name; - my $idx = $i - $self->{default_suppressed}; - if ($self->{presets_choice}->GetString($idx) ne $label) { - $self->{presets_choice}->SetString($idx, $label); - $list_updated = 1; - } - } - $self->{presets_choice}->SetSelection($self->current_preset - $self->{default_suppressed}) if ($list_updated); # http://trac.wxwidgets.org/ticket/13769 + $self->{presets}->update_dirty_ui($self->{presets_choice}); $self->_on_presets_changed; } -# Has the selected preset been modified? -sub is_dirty { - my ($self) = @_; - return @{$self->dirty_options} > 0; -} - -# Which options of the selected preset were modified? -sub dirty_options { - my ($self) = @_; - return [] if !defined $self->current_preset; # happens during initialization - return $self->get_preset_config($self->get_current_preset)->diff($self->{config}); -} - # Search all ini files in the presets directory, add them into the list of $self->{presets} in the form of Slic3r::GUI::Tab::Preset. # Initialize the drop down list box. sub load_presets { my ($self) = @_; - $self->{presets} = [ - Slic3r::GUI::Tab::Preset->new( - default => 1, - name => '- default -', - ), - ]; - - my %presets = wxTheApp->presets($self->name); - foreach my $preset_name (sort keys %presets) { - push @{$self->{presets}}, Slic3r::GUI::Tab::Preset->new( - name => $preset_name, - file => $presets{$preset_name}, - ); - } - $self->current_preset(undef); - $self->{default_suppressed} = Slic3r::GUI::Tab->no_defaults && scalar(@{$self->{presets}}) > 1; - $self->{presets_choice}->Clear; - foreach my $preset (@{$self->{presets}}) { - next if ($preset->default && $self->{default_suppressed}); - $self->{presets_choice}->Append($preset->name); - } - { - # load last used preset - my $i = first { basename($self->{presets}[$_]->file) eq ($Slic3r::GUI::Settings->{presets}{$self->name} || '') } 1 .. $#{$self->{presets}}; - $self->select_preset($i || $self->{default_suppressed}); - } + print "Load presets, ui: " . $self->{presets_choice} . "\n"; +# $self->current_preset(undef); +# $self->{presets}->set_default_suppressed(Slic3r::GUI::Tab->no_defaults); +# $self->{presets_choice}->Clear; +# foreach my $preset (@{$self->{presets}}) { +# if ($preset->visible) { +# # Set client data of the choice item to $preset. +# $self->{presets_choice}->Append($preset->name, $preset); +# } +# } +# { +# # load last used preset +# my $i = first { basename($self->{presets}[$_]->file) eq ($Slic3r::GUI::Settings->{presets}{$self->name} || '') } 1 .. $#{$self->{presets}}; +# $self->select_preset($i || $self->{default_suppressed}); +# } + + $self->{presets}->update_platter_ui($self->{presets_choice}); $self->_on_presets_changed; } # Load a config file containing a Print, Filament & Printer preset. sub load_config_file { my ($self, $file) = @_; - # look for the loaded config among the existing menu items - my $i = first { $self->{presets}[$_]{file} eq $file && $self->{presets}[$_]{external} } 1..$#{$self->{presets}}; - if (!$i) { - my $preset_name = basename($file); # keep the .ini suffix - my $preset_new = Slic3r::GUI::Tab::Preset->new( - file => $file, - name => $preset_name, - external => 1, - ); - # Try to load the config file before it is entered into the list. If the loading fails, an undef is returned. - return undef if ! defined $preset_new->config; - push @{$self->{presets}}, $preset_new; - $self->{presets_choice}->Append($preset_name); - $i = $#{$self->{presets}}; - } - $self->{presets_choice}->SetSelection($i - $self->{default_suppressed}); - $self->on_select_preset; + $self->{presets}->update_platter_ui($self->{presets_choice}); + $self->select_preset; $self->_on_presets_changed; return 1; } @@ -478,28 +354,22 @@ sub load_config_file { sub load_config { my ($self, $config) = @_; - my %keys_modified = (); + my $modified = 0; foreach my $opt_key (@{$self->{config}->diff($config)}) { $self->{config}->set($opt_key, $config->get($opt_key)); - $keys_modified{$opt_key} = 1; + $modified = 1; } - if (keys(%keys_modified)) { + if ($modified) { $self->update_dirty; # Initialize UI components with the config values. $self->reload_config; - $self->_update(\%keys_modified); + $self->_update; } } -# Load and return a config from the file associated with the $preset (Perl type Slic3r::GUI::Tab::Preset). -sub get_preset_config { - my ($self, $preset) = @_; - return $preset->config($self->{config}->get_keys); -} - +# Find a field with an index over all pages of this tab. sub get_field { my ($self, $opt_key, $opt_index) = @_; - foreach my $page (@{ $self->{pages} }) { my $field = $page->get_field($opt_key, $opt_index); return $field if defined $field; @@ -507,10 +377,9 @@ sub get_field { return undef; } +# Set a key/value pair on this page. Return true if the value has been modified. sub set_value { - my $self = shift; - my ($opt_key, $value) = @_; - + my ($self, $opt_key, $value) = @_; my $changed = 0; foreach my $page (@{ $self->{pages} }) { $changed = 1 if $page->set_value($opt_key, $value); @@ -530,7 +399,8 @@ sub title { 'Print Settings' } sub build { my $self = shift; - $self->{config}->apply(wxTheApp->{preset_bundle}->prints->default_preset->config); + $self->{presets} = wxTheApp->{preset_bundle}->print; + $self->{config} = $self->{presets}->get_edited_preset->config_ref; { my $page = $self->add_options_page('Layers and perimeters', 'layers.png'); @@ -789,6 +659,7 @@ sub build { } } +# Reload current $self->{config} (aka $self->{presets}->edited_preset->config) into the UI fields. sub reload_config { my ($self) = @_; # $self->_reload_compatible_printers_widget; @@ -797,11 +668,9 @@ sub reload_config { # Slic3r::GUI::Tab::Print::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user. sub _update { - # $keys_modified is a reference to hash with modified keys set to 1, unmodified keys missing. - my ($self, $keys_modified) = @_; - $keys_modified //= {}; + my ($self) = @_; - my $config = $self->{config}; + my $config = $self->{presets}->get_edited_preset->config_ref; if ($config->spiral_vase && !($config->perimeters == 1 && $config->top_solid_layers == 0 && $config->fill_density == 0)) { my $dialog = Wx::MessageDialog->new($self, @@ -879,10 +748,6 @@ sub _update { $self->load_config($new_conf); } - if ($keys_modified->{'layer_height'}) { - # If the user had set the variable layer height, reset it and let the user know. - } - if ($config->support_material) { # Ask only once. if (! $self->{support_material_overhangs_queried}) { @@ -999,8 +864,6 @@ sub _update { for qw(wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe); } -#sub hidden_options { !$Slic3r::have_threads ? qw(threads) : () } - package Slic3r::GUI::Tab::Filament; use base 'Slic3r::GUI::Tab'; use Wx qw(wxTheApp); @@ -1011,7 +874,8 @@ sub title { 'Filament Settings' } sub build { my $self = shift; - $self->{config}->apply(wxTheApp->{preset_bundle}->filaments->default_preset->config); + $self->{presets} = wxTheApp->{preset_bundle}->filament; + $self->{config} = $self->{presets}->get_edited_preset->config_ref; { my $page = $self->add_options_page('Filament', 'spool.png'); @@ -1139,8 +1003,7 @@ sub build { # Slic3r::GUI::Tab::Filament::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user. sub _update { - # $keys_modified is a reference to hash with modified keys set to 1, unmodified keys missing. - my ($self, $keys_modified) = @_; + my ($self) = @_; $self->_update_description; @@ -1153,10 +1016,8 @@ sub _update { } sub _update_description { - my $self = shift; - - my $config = $self->config; - + my ($self) = @_; + my $config = $self->{config}; my $msg = ""; my $fan_other_layers = $config->fan_always_on->[0] ? sprintf "will always run at %d%%%s.", $config->min_fan_speed->[0], @@ -1190,10 +1051,10 @@ sub name { 'printer' } sub title { 'Printer Settings' } sub build { - my $self = shift; - my (%params) = @_; + my ($self, %params) = @_; - $self->{config}->apply(wxTheApp->{preset_bundle}->printers->default_preset->config); + $self->{presets} = wxTheApp->{preset_bundle}->printer; + $self->{config} = $self->{presets}->get_edited_preset->config_ref; my $bed_shape_widget = sub { my ($parent) = @_; @@ -1201,7 +1062,7 @@ sub build { my $btn = Wx::Button->new($parent, -1, "Set…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG)); my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); $sizer->Add($btn); @@ -1268,7 +1129,7 @@ sub build { $serial_port->side_widget(sub { my ($parent) = @_; - my $btn = Wx::BitmapButton->new($parent, -1, Wx::Bitmap->new($Slic3r::var->("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG), + my $btn = Wx::BitmapButton->new($parent, -1, Wx::Bitmap->new(Slic3r::var("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxDefaultSize, &Wx::wxBORDER_NONE); $btn->SetToolTipString("Rescan serial ports") if $btn->can('SetToolTipString'); @@ -1282,7 +1143,7 @@ sub build { my $btn = $self->{serial_test_btn} = Wx::Button->new($parent, -1, "Test", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("wrench.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("wrench.png"), wxBITMAP_TYPE_PNG)); EVT_BUTTON($self, $btn, sub { my $sender = Slic3r::GCode::Sender->new; @@ -1312,7 +1173,7 @@ sub build { my $btn = Wx::Button->new($parent, -1, "Browse…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("zoom.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("zoom.png"), wxBITMAP_TYPE_PNG)); if (!eval "use Net::Bonjour; 1") { $btn->Disable; @@ -1348,7 +1209,7 @@ sub build { my $btn = $self->{octoprint_host_test_btn} = Wx::Button->new($parent, -1, "Test", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("wrench.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("wrench.png"), wxBITMAP_TYPE_PNG)); EVT_BUTTON($self, $btn, sub { my $ua = LWP::UserAgent->new; @@ -1565,8 +1426,7 @@ sub _build_extruder_pages { # Slic3r::GUI::Tab::Printer::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user. sub _update { - # $keys_modified is a reference to hash with modified keys set to 1, unmodified keys missing. - my ($self, $keys_modified) = @_; + my ($self) = @_; my $config = $self->{config}; @@ -1654,27 +1514,13 @@ sub on_preset_loaded { } } -# Load a config file containing a Print, Filament & Printer preset. -sub load_config_file { - my $self = shift; - if ($self->SUPER::load_config_file(@_)) { - Slic3r::GUI::warning_catcher($self)->( - "Your configuration was imported. However, Slic3r is currently only able to import settings " - . "for the first defined filament. We recommend you don't use exported configuration files " - . "for multi-extruder setups and rely on the built-in preset management system instead.") - if @{ $self->{config}->nozzle_diameter } > 1; - return 1; - } - return undef; -} - +# Single Tab page containing a {vsizer} of {optgroups} package Slic3r::GUI::Tab::Page; use Wx qw(wxTheApp :misc :panel :sizer); use base 'Wx::ScrolledWindow'; sub new { - my $class = shift; - my ($parent, $title, $iconID) = @_; + my ($class, $parent, $title, $iconID) = @_; my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); $self->{optgroups} = []; $self->{title} = $title; @@ -1718,7 +1564,6 @@ sub reload_config { sub get_field { my ($self, $opt_key, $opt_index) = @_; - foreach my $optgroup (@{ $self->{optgroups} }) { my $field = $optgroup->get_fieldc($opt_key, $opt_index); return $field if defined $field; @@ -1727,9 +1572,7 @@ sub get_field { } sub set_value { - my $self = shift; - my ($opt_key, $value) = @_; - + my ($self, $opt_key, $value) = @_; my $changed = 0; foreach my $optgroup (@{$self->{optgroups}}) { $changed = 1 if $optgroup->set_value($opt_key, $value); @@ -1743,8 +1586,7 @@ use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER); use base 'Wx::Dialog'; sub new { - my $class = shift; - my ($parent, %params) = @_; + my ($class, $parent, %params) = @_; my $self = $class->SUPER::new($parent, -1, "Save preset", wxDefaultPosition, wxDefaultSize); my @values = @{$params{values}}; @@ -1770,7 +1612,6 @@ sub new { sub accept { my ($self, $event) = @_; - if (($self->{chosen_name} = $self->{combo}->GetValue)) { if ($self->{chosen_name} !~ /^[^<>:\/\\|?*\"]+$/) { Slic3r::GUI::show_error($self, "The supplied name is not valid; the following characters are not allowed: <>:/\|?*\""); @@ -1783,57 +1624,8 @@ sub accept { } sub get_name { - my $self = shift; + my ($self) = @_; return $self->{chosen_name}; } -package Slic3r::GUI::Tab::Preset; -use Moo; -use List::Util qw(any); - -# The preset represents a "default" set of properties. -has 'default' => (is => 'ro', default => sub { 0 }); -has 'external' => (is => 'ro', default => sub { 0 }); -has 'name' => (is => 'rw', required => 1); -has 'file' => (is => 'rw'); - -# Load a config file, return a C++ class Slic3r::DynamicPrintConfig with $keys initialized from the config file. -# In case of a "default" config item, return the default values. -sub config { - my ($self, $keys) = @_; - - if ($self->default) { - # Perl class Slic3r::Config extends the C++ class Slic3r::DynamicPrintConfig - return Slic3r::Config->new_from_defaults(@$keys); - } else { - if (!-e Slic3r::encode_path($self->file)) { - Slic3r::GUI::show_error(undef, "The selected preset does not exist anymore (" . $self->file . ")."); - return undef; - } - - # apply preset values on top of defaults - my $config = Slic3r::Config->new_from_defaults(@$keys); - my $external_config = eval { Slic3r::Config->load($self->file); }; - if ($@) { - Slic3r::GUI::show_error(undef, $@); - return undef; - } - $config->set($_, $external_config->get($_)) - for grep $external_config->has($_), @$keys; - - if (any { $_ eq 'nozzle_diameter' } @$keys) { - # Loaded the Printer settings. Verify, that all extruder dependent values have enough values. - my $nozzle_diameter = $config->nozzle_diameter; - my $num_extruders = scalar(@{$nozzle_diameter}); - foreach my $key (qw(deretract_speed extruder_colour retract_before_wipe)) { - my $vec = $config->get($key); - push @{$vec}, ($vec->[0]) x ($num_extruders - @{$vec}); - $config->set($key, $vec); - } - } - - return $config; - } -} - 1; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 793654300..e275fdde5 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -11,9 +11,6 @@ use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union use Slic3r::Print::State ':steps'; use Slic3r::Surface ':types'; -# If enabled, phases of prepare_infill will be written into SVG files to an "out" directory. -our $SLIC3R_DEBUG_SLICE_PROCESSING = 0; - sub layers { my $self = shift; return [ map $self->get_layer($_), 0..($self->layer_count - 1) ]; diff --git a/slic3r.pl b/slic3r.pl index 4b6e43e42..76def3d28 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -100,12 +100,17 @@ if ($opt{save}) { my $config = Slic3r::Config->new_from_defaults; $config->apply($cli_config); +# locate or create data directory +# Unix: ~/.Slic3r +# Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r" +# Mac: "~/Library/Application Support/Slic3r" +Slic3r::set_data_dir($opt{datadir} || Wx::StandardPaths::Get->GetUserDataDir); + # launch GUI my $gui; if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") { { no warnings 'once'; - $Slic3r::GUI::datadir = $opt{datadir} // ''; $Slic3r::GUI::no_controller = $opt{no_controller}; $Slic3r::GUI::no_plater = $opt{no_plater}; $Slic3r::GUI::autosave = $opt{autosave}; diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp index 08be120b6..46a43ca69 100644 --- a/xs/src/libslic3r/BoundingBox.cpp +++ b/xs/src/libslic3r/BoundingBox.cpp @@ -7,7 +7,8 @@ namespace Slic3r { template BoundingBoxBase::BoundingBoxBase(const std::vector &points) { - if (points.empty()) CONFESS("Empty point set supplied to BoundingBoxBase constructor"); + if (points.empty()) + CONFESS("Empty point set supplied to BoundingBoxBase constructor"); typename std::vector::const_iterator it = points.begin(); this->min.x = this->max.x = it->x; this->min.y = this->max.y = it->y; @@ -26,7 +27,8 @@ template BoundingBox3Base::BoundingBox3Base(const std::vector &points) : BoundingBoxBase(points) { - if (points.empty()) CONFESS("Empty point set supplied to BoundingBox3Base constructor"); + if (points.empty()) + CONFESS("Empty point set supplied to BoundingBox3Base constructor"); typename std::vector::const_iterator it = points.begin(); this->min.z = this->max.z = it->z; for (++it; it != points.end(); ++it) { @@ -39,9 +41,10 @@ template BoundingBox3Base::BoundingBox3Base(const std::vector BoundingBox::BoundingBox(const Lines &lines) { Points points; - for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { - points.push_back(line->a); - points.push_back(line->b); + points.reserve(lines.size()); + for (const Line &line : lines) { + points.emplace_back(line.a); + points.emplace_back(line.b); } *this = BoundingBox(points); } @@ -190,9 +193,9 @@ BoundingBox3Base::size() const } template Pointf3 BoundingBox3Base::size() const; -template double -BoundingBoxBase::radius() const +template double BoundingBoxBase::radius() const { + assert(this->defined); double x = this->max.x - this->min.x; double y = this->max.y - this->min.y; return 0.5 * sqrt(x*x+y*y); @@ -200,8 +203,7 @@ BoundingBoxBase::radius() const template double BoundingBoxBase::radius() const; template double BoundingBoxBase::radius() const; -template double -BoundingBox3Base::radius() const +template double BoundingBox3Base::radius() const { double x = this->max.x - this->min.x; double y = this->max.y - this->min.y; diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index e4968a24b..3808e9bb2 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -27,35 +27,36 @@ extern bool unescape_strings_cstyle(const std::string &str, std::vector< // Type of a configuration value. enum ConfigOptionType { - coNone, + coVectorType = 0x4000, + coNone = 0, // single float - coFloat, + coFloat = 1, // vector of floats - coFloats, + coFloats = coFloat + coVectorType, // single int - coInt, + coInt = 2, // vector of ints - coInts, + coInts = coInt + coVectorType, // single string - coString, + coString = 3, // vector of strings - coStrings, + coStrings = coString + coVectorType, // percent value. Currently only used for infill. - coPercent, + coPercent = 4, // percents value. Currently used for retract before wipe only. - coPercents, + coPercents = coPercent + coVectorType, // a fraction or an absolute value - coFloatOrPercent, + coFloatOrPercent = 5, // single 2d point. Currently not used. - coPoint, + coPoint = 6, // vector of 2d points. Currently used for the definition of the print bed and for the extruder offsets. - coPoints, + coPoints = coPoint + coVectorType, // single boolean value - coBool, + coBool = 7, // vector of boolean values - coBools, + coBools = coBool + coVectorType, // a generic enum - coEnum, + coEnum = 8, }; // A generic value of a configuration option. @@ -75,6 +76,8 @@ public: virtual void setInt(int /* val */) { throw std::runtime_error("Calling ConfigOption::setInt on a non-int ConfigOption"); } virtual bool operator==(const ConfigOption &rhs) const = 0; bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); } + bool is_scalar() const { return (int(this->type()) & int(coVectorType)) == 0; } + bool is_vector() const { return ! this->is_scalar(); } }; // Value of a single valued option (bool, int, float, string, point, enum) @@ -110,6 +113,18 @@ class ConfigOptionVectorBase : public ConfigOption { public: // Currently used only to initialize the PlaceholderParser. virtual std::vector vserialize() const = 0; + // Set from a vector of ConfigOptions. + // If the rhs ConfigOption is scalar, then its value is used, + // otherwise for each of rhs, the first value of a vector is used. + // This function is useful to collect values for multiple extrder / filament settings. + virtual void set(const std::vector &rhs) = 0; + // Set a single vector item from either a scalar option or the first value of a vector option.vector of ConfigOptions. + // This function is useful to split values from multiple extrder / filament settings into separate configurations. + virtual void set_at(const ConfigOption *rhs, size_t i, size_t j) = 0; + +protected: + // Used to verify type compatibility when assigning to / from a scalar ConfigOption. + ConfigOptionType scalar_type() const { return static_cast(this->type() - coVectorType); } }; // Value of a vector valued option (bools, ints, floats, strings, points), template @@ -125,13 +140,57 @@ public: throw std::runtime_error("ConfigOptionVector: Assigning an incompatible type"); assert(dynamic_cast*>(rhs)); this->values = static_cast*>(rhs)->values; - }; + } + + // Set from a vector of ConfigOptions. + // If the rhs ConfigOption is scalar, then its value is used, + // otherwise for each of rhs, the first value of a vector is used. + // This function is useful to collect values for multiple extrder / filament settings. + void set(const std::vector &rhs) override + { + this->values.clear(); + this->values.reserve(rhs.size()); + for (const ConfigOption *opt : rhs) { + if (opt->type() == this->type()) { + auto other = static_cast*>(opt); + if (other->values.empty()) + throw std::runtime_error("ConfigOptionVector::set(): Assigning from an empty vector"); + this->values.emplace_back(other->values.front()); + } else if (opt->type() == this->scalar_type()) + this->values.emplace_back(static_cast*>(opt)->value); + else + throw std::runtime_error("ConfigOptionVector::set():: Assigning an incompatible type"); + } + } + + // Set a single vector item from either a scalar option or the first value of a vector option.vector of ConfigOptions. + // This function is useful to split values from multiple extrder / filament settings into separate configurations. + void set_at(const ConfigOption *rhs, size_t i, size_t j) override + { + // It is expected that the vector value has at least one value, which is the default, if not overwritten. + assert(! this->values.empty()); + if (this->values.size() <= i) { + // Resize this vector, fill in the new vector fields with the copy of the first field. + T v = this->values.front(); + this->values.resize(i + 1, v); + } + if (rhs->type() == this->type()) { + // Assign the first value of the rhs vector. + auto other = static_cast*>(rhs); + if (other->values.empty()) + throw std::runtime_error("ConfigOptionVector::set_at(): Assigning from an empty vector"); + this->values[i] = other->get_at(j); + } else if (rhs->type() == this->scalar_type()) + this->values[i] = static_cast*>(rhs)->value; + else + throw std::runtime_error("ConfigOptionVector::set_at(): Assigning an incompatible type"); + } T& get_at(size_t i) { assert(! this->values.empty()); return (i < this->values.size()) ? this->values[i] : this->values.front(); - }; + } const T& get_at(size_t i) const { return const_cast*>(this)->get_at(i); } diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 38d89938a..0dbbf5f8d 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -1208,6 +1208,8 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic // perform a safety offset to merge very close facets (TODO: find test case for this) double safety_offset = scale_(0.0499); +//FIXME see https://github.com/prusa3d/Slic3r/issues/520 +// double safety_offset = scale_(0.0001); ExPolygons ex_slices = offset2_ex(p_slices, +safety_offset, -safety_offset); #ifdef SLIC3R_TRIANGLEMESH_DEBUG diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 79e1c2e2e..23dc330a1 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -8,11 +8,21 @@ extern void trace(unsigned int level, const char *message); // Set a path with GUI resource files. void set_var_dir(const std::string &path); -// Return a path to the GUI resource files. +// Return a full path to the GUI resource files. const std::string& var_dir(); -// Return a resource path for a file_name. +// Return a full resource path for a file_name. std::string var(const std::string &file_name); +// Set a path with preset files. +void set_data_dir(const std::string &path); +// Return a full path to the GUI resource files. +const std::string& data_dir(); +// Return a full path to a configuration file given its file name.. +std::string config_path(const std::string &file_name); +// Return a full path to a configuration file given the section and name. +// The suffix ".ini" will be added if it is missing in the name. +std::string config_path(const std::string §ion, const std::string &name); + extern std::string encode_path(const char *src); extern std::string decode_path(const char *src); extern std::string normalize_utf8_nfc(const char *src); diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 6e60f978d..3910a0586 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -6,8 +6,8 @@ #include +#include #include - #include #include @@ -87,6 +87,31 @@ std::string var(const std::string &file_name) return file.string(); } +static std::string g_data_dir; + +void set_data_dir(const std::string &dir) +{ + g_data_dir = dir; +} + +const std::string& data_dir() +{ + return g_data_dir; +} + +std::string config_path(const std::string &file_name) +{ + auto file = boost::filesystem::canonical(boost::filesystem::path(g_data_dir) / file_name).make_preferred(); + return file.string(); +} + +std::string config_path(const std::string §ion, const std::string &name) +{ + auto file_name = boost::algorithm::iends_with(name, ".ini") ? name : name + ".ini"; + auto file = boost::filesystem::canonical(boost::filesystem::path(g_data_dir) / file_name).make_preferred(); + return file.string(); +} + } // namespace Slic3r #ifdef SLIC3R_HAS_BROKEN_CROAK diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 934d07ebd..17da135a0 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -13,8 +13,7 @@ namespace Slic3r { namespace GUI { IOPMAssertionID assertionID; #endif -void -disable_screensaver() +void disable_screensaver() { #if __APPLE__ CFStringRef reasonForActivity = CFSTR("Slic3r"); @@ -26,8 +25,7 @@ disable_screensaver() #endif } -void -enable_screensaver() +void enable_screensaver() { #if __APPLE__ IOReturn success = IOPMAssertionRelease(assertionID); @@ -36,8 +34,7 @@ enable_screensaver() #endif } -bool -debugged() +bool debugged() { #ifdef _WIN32 return IsDebuggerPresent(); @@ -46,8 +43,7 @@ debugged() #endif /* _WIN32 */ } -void -break_to_debugger() +void break_to_debugger() { #ifdef _WIN32 if (IsDebuggerPresent()) diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 0accb51a1..8e1eb93eb 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -4,9 +4,17 @@ #include #include +#include +#include +#include +#include + #include +#include #include +#include "../../libslic3r/Utils.hpp" + #if 0 #define DEBUG #define _DEBUG @@ -17,6 +25,42 @@ namespace Slic3r { +static std::string g_suffix_modified = " (modified)"; + +// Load keys from a config file or a G-code. +// Throw exceptions with reasonable messages if something goes wrong. +static void load_config_file(DynamicPrintConfig &config, const std::string &path) +{ + try { + if (boost::algorithm::iends_with(path, ".gcode") || boost::algorithm::iends_with(path, ".g")) + config.load_from_gcode(path); + else + config.load(path); + } catch (const std::ifstream::failure&) { + throw std::runtime_error(std::string("The selected preset does not exist anymore: ") + path); + } catch (const std::runtime_error&) { + throw std::runtime_error(std::string("Failed loading the preset file: ") + path); + } + + // Update new extruder fields at the printer profile. + auto keys = config.keys(); + const auto &defaults = FullPrintConfig::defaults(); + if (std::find(keys.begin(), keys.end(), "nozzle_diameter") != keys.end()) { + // Loaded the Printer settings. Verify, that all extruder dependent values have enough values. + auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); + size_t num_extruders = nozzle_diameter->values.size(); + auto *deretract_speed = dynamic_cast(config.option("deretract_speed")); + deretract_speed->values.resize(num_extruders, deretract_speed->values.empty() ? + defaults.deretract_speed.values.front() : deretract_speed->values.front()); + auto *extruder_colour = dynamic_cast(config.option("extruder_colour")); + extruder_colour->values.resize(num_extruders, extruder_colour->values.empty() ? + defaults.extruder_colour.values.front() : extruder_colour->values.front()); + auto *retract_before_wipe = dynamic_cast(config.option("retract_before_wipe")); + retract_before_wipe->values.resize(num_extruders, retract_before_wipe->values.empty() ? + defaults.retract_before_wipe.values.front() : retract_before_wipe->values.front()); + } +} + // Load a config file, return a C++ class Slic3r::DynamicPrintConfig with $keys initialized from the config file. // In case of a "default" config item, return the default values. DynamicPrintConfig& Preset::load(const std::vector &keys) @@ -24,40 +68,24 @@ DynamicPrintConfig& Preset::load(const std::vector &keys) // Set the configuration from the defaults. Slic3r::FullPrintConfig defaults; this->config.apply_only(defaults, keys.empty() ? defaults.keys() : keys); - - if (! this->is_default) { + if (! this->is_default) // Load the preset file, apply preset values on top of defaults. - try { - if (boost::algorithm::iends_with(this->file, ".gcode") || boost::algorithm::iends_with(this->file, ".g")) - this->config.load_from_gcode(this->file); - else - this->config.load(this->file); - } catch (const std::ifstream::failure&) { - throw std::runtime_error(std::string("The selected preset does not exist anymore: ") + this->file); - } catch (const std::runtime_error&) { - throw std::runtime_error(std::string("Failed loading the preset file: ") + this->file); - } - - if (this->type == TYPE_PRINTER && std::find(keys.begin(), keys.end(), "nozzle_diameter") != keys.end()) { - // Loaded the Printer settings. Verify, that all extruder dependent values have enough values. - auto *nozzle_diameter = dynamic_cast(this->config.option("nozzle_diameter")); - size_t num_extruders = nozzle_diameter->values.size(); - auto *deretract_speed = dynamic_cast(this->config.option("deretract_speed")); - deretract_speed->values.resize(num_extruders, deretract_speed->values.empty() ? - defaults.deretract_speed.values.front() : deretract_speed->values.front()); - auto *extruder_colour = dynamic_cast(this->config.option("extruder_colour")); - extruder_colour->values.resize(num_extruders, extruder_colour->values.empty() ? - defaults.extruder_colour.values.front() : extruder_colour->values.front()); - auto *retract_before_wipe = dynamic_cast(this->config.option("retract_before_wipe")); - retract_before_wipe->values.resize(num_extruders, retract_before_wipe->values.empty() ? - defaults.retract_before_wipe.values.front() : retract_before_wipe->values.front()); - } - } - + load_config_file(this->config, this->file); this->loaded = true; return this->config; } +void Preset::save() +{ + this->config.save(this->file); +} + +// Return a label of this preset, consisting of a name and a "(modified)" suffix, if this preset is dirty. +std::string Preset::label() const +{ + return this->name + (this->is_dirty ? g_suffix_modified : ""); +} + bool Preset::enable_compatible(const std::string &active_printer) { auto *compatible_printers = dynamic_cast(this->config.optptr("compatible_printers")); @@ -69,11 +97,20 @@ bool Preset::enable_compatible(const std::string &active_printer) PresetCollection::PresetCollection(Preset::Type type, const std::vector &keys) : m_type(type), - m_edited_preset(type, "", false) + m_edited_preset(type, "", false), + m_idx_selected(0), + m_bitmap_main_frame(new wxBitmap) { // Insert just the default preset. m_presets.emplace_back(Preset(type, "- default -", true)); m_presets.front().load(keys); + m_edited_preset.config.apply(m_presets.front().config); +} + +PresetCollection::~PresetCollection() +{ + delete m_bitmap_main_frame; + m_bitmap_main_frame = nullptr; } // Load all presets found in dir_path. @@ -96,6 +133,58 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri } } + std::sort(m_presets.begin() + 1, m_presets.end(), [](const Preset &p1, const Preset &p2){ return p1.name < p2.name; }); +} + +// Load a preset from an already parsed config file, insert it into the sorted sequence of presets +// and select it, losing previous modifications. +Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select) +{ + Preset key(m_type, name); + auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key); + if (it != m_presets.end() && it->name == name) { + // The preset with the same name was found. + it->is_dirty = false; + } else { + it = m_presets.emplace(it, Preset(m_type, name, false)); + } + Preset &preset = *it; + preset.file = path; + preset.config = this->default_preset().config; + preset.loaded = true; + this->get_selected_preset().is_dirty = false; + if (select) + this->select_preset_by_name(name, true); + return preset; +} + +bool PresetCollection::load_bitmap_default(const std::string &file_name) +{ + return m_bitmap_main_frame->LoadFile(wxString::FromUTF8(Slic3r::var(file_name).c_str()), wxBITMAP_TYPE_PNG); +} + +// Return a preset by its name. If the preset is active, a temporary copy is returned. +// If a preset is not found by its name, null is returned. +Preset* PresetCollection::find_preset(const std::string &name, bool first_visible_if_not_found) +{ + Preset key(m_type, name, false); + auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key, + [](const Preset &p1, const Preset &p2) { return p1.name < p2.name; } ); + // Ensure that a temporary copy is returned if the preset found is currently selected. + return (it != m_presets.end() && it->name == key.name) ? &this->preset(it - m_presets.begin()) : + first_visible_if_not_found ? &this->first_visible() : nullptr; +} + +// Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible. +size_t PresetCollection::first_visible_idx() const +{ + size_t idx = 0; + for (; idx < this->m_presets.size(); ++ idx) + if (m_presets[idx].is_visible) + break; + if (idx == this->m_presets.size()) + idx = 0; + return idx; } void PresetCollection::set_default_suppressed(bool default_suppressed) @@ -117,7 +206,13 @@ void PresetCollection::enable_disable_compatible_to_printer(const std::string &a m_presets.front().is_visible = true; } -static std::string g_suffix_modified = " (modified)"; +// Save the preset under a new name. If the name is different from the old one, +// a new preset is stored into the list of presets. +// All presets are marked as not modified and the new preset is activated. +//void PresetCollection::save_current_preset(const std::string &new_name); + +// Delete the current preset, activate the first visible preset. +//void PresetCollection::delete_current_preset(); // Update the wxChoice UI component from this list of presets. // Hide the @@ -175,6 +270,113 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) } } +void PresetCollection::update_platter_ui(wxChoice *ui) +{ + if (ui == nullptr) + return; + + size_t n_visible = this->num_visible(); + size_t n_choice = size_t(ui->GetCount()); + if (std::abs(int(n_visible) - int(n_choice)) <= 1) { + // The number of items differs by at most one, update the choice. + } else { + // Otherwise fill in the list from scratch. + } + + std::string name_selected = dynamic_cast(ui)->GetStringSelection().ToUTF8().data(); + if (boost::algorithm::iends_with(name_selected, g_suffix_modified)) + // Remove the g_suffix_modified. + name_selected.erase(name_selected.end() - g_suffix_modified.size(), name_selected.end()); + + ui->Clear(); + for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { + const Preset &preset = this->m_presets[i]; + const wxBitmap *bmp = (i == 0 || preset.is_visible) ? m_bitmap_compatible : m_bitmap_incompatible; + ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (void*)&preset); + if (name_selected == preset.name) + ui->SetSelection(ui->GetCount() - 1); + } +} + +// Update a dirty floag of the current preset, update the labels of the UI component accordingly. +// Return true if the dirty flag changed. +bool PresetCollection::update_dirty_ui(wxItemContainer *ui) +{ + // 1) Update the dirty flag of the current preset. + bool was_dirty = this->get_selected_preset().is_dirty; + bool is_dirty = current_is_dirty(); + this->get_selected_preset().is_dirty = is_dirty; + // 2) Update the labels. + for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { + std::string old_label = ui->GetString(ui_id).utf8_str().data(); + std::string preset_name = boost::algorithm::ends_with(old_label, g_suffix_modified) ? + old_label.substr(0, g_suffix_modified.size()) : + old_label; + const Preset *preset = this->find_preset(preset_name, false); + assert(preset != nullptr); + std::string new_label = preset->is_dirty ? preset->name + g_suffix_modified : preset->name; + if (old_label != new_label) + ui->SetString(ui_id, wxString::FromUTF8(new_label.c_str())); + } + return was_dirty != is_dirty; +} + +bool PresetCollection::update_dirty_ui(wxChoice *ui) +{ + return update_dirty_ui(dynamic_cast(ui)); +} + +Preset& PresetCollection::select_preset(size_t idx) +{ + if (idx >= m_presets.size()) + idx = first_visible_idx(); + m_idx_selected = idx; + m_edited_preset = m_presets[idx]; + return m_presets[idx]; +} + +bool PresetCollection::select_preset_by_name(const std::string &name, bool force) +{ + // 1) Try to find the preset by its name. + Preset key(m_type, name, false); + auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key, + [](const Preset &p1, const Preset &p2) { return p1.name < p2.name; } ); + size_t idx = 0; + if (it != m_presets.end() && it->name == key.name) + // Preset found by its name. + idx = it - m_presets.begin(); + else { + // Find the first visible preset. + for (size_t i = 0; i < m_presets.size(); ++ i) + if (m_presets[i].is_visible) { + idx = i; + break; + } + // If the first visible preset was not found, return the 0th element, which is the default preset. + } + + // 2) Select the new preset. + if (m_idx_selected != idx || force) { + this->select_preset(idx); + return true; + } + + return false; +} + +bool PresetCollection::select_by_name_ui(char *name, wxItemContainer *ui) +{ + this->select_preset_by_name(name, true); + //FIXME this is not finished yet. + //this->update_platter_ui(wxChoice *ui) + return true; +} + +bool PresetCollection::select_by_name_ui(char *name, wxChoice *ui) +{ + return this->select_by_name_ui(name, dynamic_cast(ui)); +} + PresetBundle::PresetBundle() : prints(Preset::TYPE_PRINT, print_options()), filaments(Preset::TYPE_FILAMENT, filament_options()), @@ -182,6 +384,8 @@ PresetBundle::PresetBundle() : m_bitmapCompatible(new wxBitmap), m_bitmapIncompatible(new wxBitmap) { + ::wxInitAllImageHandlers(); + // Create the ID config keys, as they are not part of the Static print config classes. this->prints.preset(0).config.opt_string("print_settings_id", true); this->filaments.preset(0).config.opt_string("filament_settings_id", true); @@ -189,6 +393,13 @@ PresetBundle::PresetBundle() : // Create the "compatible printers" keys, as they are not part of the Static print config classes. this->filaments.preset(0).config.optptr("compatible_printers", true); this->prints.preset(0).config.optptr("compatible_printers", true); + + this->prints .load_bitmap_default("cog.png"); + this->filaments.load_bitmap_default("spool.png"); + this->printers .load_bitmap_default("printer_empty.png"); + + // FIXME select some icons indicating compatibility. + this->load_compatible_bitmaps("cog.png", "cog.png"); } PresetBundle::~PresetBundle() @@ -205,15 +416,17 @@ PresetBundle::~PresetBundle() void PresetBundle::load_presets(const std::string &dir_path) { - this->prints.load_presets(dir_path, "print"); - this->prints.load_presets(dir_path, "filament"); - this->prints.load_presets(dir_path, "printer"); + this->prints .load_presets(dir_path, "print"); + this->filaments.load_presets(dir_path, "filament"); + this->printers .load_presets(dir_path, "printer"); } -bool PresetBundle::load_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible) +bool PresetBundle::load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible) { - bool loaded_compatible = m_bitmapCompatible ->LoadFile(wxString::FromUTF8(path_bitmap_compatible.c_str())); - bool loaded_incompatible = m_bitmapIncompatible->LoadFile(wxString::FromUTF8(path_bitmap_incompatible.c_str())); + bool loaded_compatible = m_bitmapCompatible ->LoadFile( + wxString::FromUTF8(Slic3r::var(path_bitmap_compatible).c_str()), wxBITMAP_TYPE_PNG); + bool loaded_incompatible = m_bitmapIncompatible->LoadFile( + wxString::FromUTF8(Slic3r::var(path_bitmap_incompatible).c_str()), wxBITMAP_TYPE_PNG); if (loaded_compatible) { prints .set_bitmap_compatible(m_bitmapCompatible); filaments.set_bitmap_compatible(m_bitmapCompatible); @@ -227,6 +440,269 @@ bool PresetBundle::load_bitmaps(const std::string &path_bitmap_compatible, const return loaded_compatible && loaded_incompatible; } +DynamicPrintConfig PresetBundle::full_config() const +{ + DynamicPrintConfig out; + out.apply(FullPrintConfig()); + out.apply(this->prints.get_edited_preset().config); + out.apply(this->printers.get_edited_preset().config); + + auto *nozzle_diameter = dynamic_cast(out.option("nozzle_diameter")); + size_t num_extruders = nozzle_diameter->values.size(); + + if (num_extruders <= 1) { + out.apply(this->filaments.get_edited_preset().config); + } else { + // Retrieve filament presets and build a single config object for them. + // First collect the filament configurations based on the user selection of this->filament_presets. + std::vector filament_configs; + for (const std::string &filament_preset_name : this->filament_presets) + filament_configs.emplace_back(&this->filaments.find_preset(filament_preset_name, true)->config); + while (filament_configs.size() < num_extruders) + filament_configs.emplace_back(&this->filaments.first_visible().config); + // Option values to set a ConfigOptionVector from. + std::vector filament_opts(num_extruders, nullptr); + // loop through options and apply them to the resulting config. + for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { + // Get a destination option. + ConfigOption *opt_dst = out.option(key, false); + if (opt_dst->is_scalar()) { + // Get an option, do not create if it does not exist. + const ConfigOption *opt_src = filament_configs.front()->option(key); + if (opt_src != nullptr) + opt_dst->set(opt_src); + } else { + // Setting a vector value from all filament_configs. + for (size_t i = 0; i < filament_opts.size(); ++ i) + filament_opts[i] = filament_configs[i]->option(key); + static_cast(opt_dst)->set(filament_opts); + } + } + } + + static const char *keys[] = { "perimeter", "infill", "solid_infill", "support_material", "support_material_interface" }; + for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++ i) { + std::string key = std::string(keys[i]) + "_extruder"; + auto *opt = dynamic_cast(out.option(key, false)); + assert(opt != nullptr); + opt->value = std::min(opt->value, std::min(0, int(num_extruders) - 1)); + } + + return out; +} + +// Load an external config file containing the print, filament and printer presets. +// Instead of a config file, a G-code may be loaded containing the full set of parameters. +// In the future the configuration will likely be read from an AMF file as well. +// If the file is loaded successfully, its print / filament / printer profiles will be activated. +void PresetBundle::load_config_file(const std::string &path) +{ + // 1) Initialize a config from full defaults. + DynamicPrintConfig config; + config.apply(FullPrintConfig()); + + // 2) Try to load the config file. + // Throw exceptions with reasonable messages if something goes wrong. + Slic3r::load_config_file(config, path); + + // 3) Create a name from the file name. + // Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles. + std::string name = boost::filesystem::path(path).filename().string(); + + // 3) If the loading succeeded, split and load the config into print / filament / printer settings. + // First load the print and printer presets. + for (size_t i_group = 0; i_group < 2; ++ i_group) { + PresetCollection &presets = (i_group == 0) ? this->prints : this->printers; + presets.load_preset(path, name, config).is_external = true; + } + + // Now load the filaments. If there are multiple filament presets, split them and load them. + auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); + auto *filament_diameter = dynamic_cast(config.option("filament_diameter")); + size_t num_extruders = std::min(nozzle_diameter->values.size(), filament_diameter->values.size()); + if (num_extruders <= 1) { + this->filaments.load_preset(path, name, config).is_external = true; + this->filament_presets.clear(); + this->filament_presets.emplace_back(name); + } else { + // Split the filament presets, load each of them separately. + std::vector configs(num_extruders, this->filaments.default_preset().config); + // loop through options and scatter them into configs. + for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { + const ConfigOption *other_opt = config.option(key, false); + if (other_opt == nullptr) + continue; + if (other_opt->is_scalar()) { + for (size_t i = 0; i < configs.size(); ++ i) + configs[i].option(key, false)->set(other_opt); + } else { + for (size_t i = 0; i < configs.size(); ++ i) + static_cast(configs[i].option(key, false))->set_at(other_opt, 0, i); + } + } + // Load the configs into this->filaments and make them active. + filament_presets.clear(); + for (size_t i = 0; i < configs.size(); ++ i) { + char suffix[64]; + if (i == 0) + suffix[0] = 0; + else + sprintf(suffix, " (%d)", i); + // Load all filament presets, but only select the first one in the preset dialog. + this->filaments.load_preset(path, name + suffix, configs[i], i == 0).is_external = true; + filament_presets.emplace_back(name + suffix); + } + } +} + +std::string PresetCollection::name() const +{ + switch (this->type()) { + case Preset::TYPE_PRINT: return "print"; + case Preset::TYPE_FILAMENT: return "filament"; + case Preset::TYPE_PRINTER: return "printer"; + default: return "invalid"; + } +} + +// Load a config bundle file, into presets and store the loaded presets into separate files +// of the local configuration directory. +// Load settings into the provided settings instance. +void PresetBundle::load_configbundle(const std::string &path, const DynamicPrintConfig &settings) +{ + // 1) Read the complete config file into the boost::property_tree. + namespace pt = boost::property_tree; + pt::ptree tree; + boost::nowide::ifstream ifs(path); + pt::read_ini(ifs, tree); + + // 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files. + std::vector loaded_prints; + std::vector loaded_filaments; + std::vector loaded_printers; + std::string active_print; + std::vector active_filaments; + std::string active_printer; + for (const auto §ion : tree) { + PresetCollection *presets = nullptr; + std::vector *loaded = nullptr; + std::string preset_name; + if (boost::starts_with(section.first, "print:")) { + presets = &prints; + loaded = &loaded_prints; + preset_name = section.first.substr(6); + } else if (boost::starts_with(section.first, "filament:")) { + presets = &filaments; + loaded = &loaded_filaments; + preset_name = section.first.substr(9); + } else if (boost::starts_with(section.first, "printer:")) { + presets = &printers; + loaded = &loaded_printers; + preset_name = section.first.substr(8); + } else if (section.first == "presets") { + // Load the names of the active presets. + for (auto &kvp : section.second) { + if (kvp.first == "print") { + active_print = kvp.second.data(); + } else if (boost::starts_with(kvp.first, "filament")) { + int idx = 0; + if (kvp.first == "filament" || sscanf(kvp.first.c_str(), "filament_%d", &idx) == 1) { + if (int(active_filaments.size()) <= idx) + active_filaments.resize(idx + 1, std::string()); + active_filaments[idx] = kvp.second.data(); + } + } else if (kvp.first == "printer") { + active_printer = kvp.second.data(); + } + } + } else if (section.first == "settings") { + // Load the settings. + for (auto &kvp : section.second) { + if (kvp.first == "autocenter") { + } + } + } else + // Ignore an unknown section. + continue; + if (presets != nullptr) { + // Load the print, filament or printer preset. + DynamicPrintConfig config(presets->default_preset().config); + for (auto &kvp : section.second) + config.set_deserialize(kvp.first, kvp.second.data()); + // Load the preset into the list of presets, save it to disk. + presets->load_preset(Slic3r::config_path(presets->name(), preset_name), preset_name, config, false).save(); + } + } + + // 3) Activate the presets. + if (! active_print.empty()) + prints.select_preset_by_name(active_print, true); + if (! active_printer.empty()) + printers.select_preset_by_name(active_printer, true); + // Activate the first filament preset. + if (! active_filaments.empty() && ! active_filaments.front().empty()) + filaments.select_preset_by_name(active_filaments.front(), true); + // Verify and select the filament presets. + auto *nozzle_diameter = static_cast(printers.get_selected_preset().config.option("nozzle_diameter")); + size_t num_extruders = nozzle_diameter->values.size(); + if (this->filament_presets.size() < num_extruders) + this->filament_presets.resize(num_extruders, filaments.get_selected_preset().name); + for (size_t i = 0; i < num_extruders; ++ i) + this->filament_presets[i] = (i < active_filaments.size()) ? + filaments.find_preset(active_filaments[i], true)->name : + filaments.first_visible().name; +} + +void PresetBundle::export_configbundle(const std::string &path, const DynamicPrintConfig &settings) +{ + boost::nowide::ofstream c; + c.open(path, std::ios::out | std::ios::trunc); + + // Put a comment at the first line including the time stamp and Slic3r version. + { + std::time_t now; + time(&now); + char buf[sizeof "0000-00-00 00:00:00"]; + strftime(buf, sizeof(buf), "%F %T", gmtime(&now)); + c << "# generated by Slic3r " << SLIC3R_VERSION << " on " << buf << std::endl; + } + + // Export the print, filament and printer profiles. + for (size_t i_group = 0; i_group < 3; ++ i_group) { + const PresetCollection &presets = (i_group == 0) ? this->prints : (i_group == 1) ? this->filaments : this->printers; + for (const Preset &preset : presets()) { + if (preset.is_default || preset.is_external) + // Only export the common presets, not external files or the default preset. + continue; + c << "[" << presets.name() << ":" << preset.name << "]" << std::endl; + for (const std::string &opt_key : preset.config.keys()) + c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl; + } + } + + // Export the names of the active presets. + c << "[presets]" << std::endl; + c << "print = " << this->prints.get_selected_preset().name << std::endl; + c << "printer = " << this->printers.get_selected_preset().name << std::endl; + for (size_t i = 0; i < this->filament_presets.size(); ++ i) { + char suffix[64]; + if (i > 0) + sprintf(suffix, "_%d", i); + else + suffix[0] = 0; + c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl; + } + + // Export the following setting values from the provided setting repository. + static const char *settings_keys[] = { "autocenter" }; + c << "[presets]" << std::endl; + c << "print = " << this->prints.get_selected_preset().name << std::endl; + for (size_t i = 0; i < sizeof(settings_keys) / sizeof(settings_keys[0]); ++ i) + c << settings_keys[i] << " = " << settings.serialize(settings_keys[i]) << std::endl; + + c.close(); +} + static inline int hex_digit_to_int(const char c) { return @@ -261,12 +737,12 @@ void PresetBundle::update_platter_filament_ui_colors(wxBitmapComboBox *ui, unsig extruder_color.clear(); for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { - if (! ui->HasClientUntypedData()) - continue; + std::string preset_name = ui->GetString(ui_id).utf8_str().data(); size_t filament_preset_id = size_t(ui->GetClientData(ui_id)); - const Preset &filament_preset = filaments.preset(filament_preset_id); + const Preset *filament_preset = filaments.find_preset(preset_name, false); + assert(filament_preset != nullptr); // Assign an extruder color to the selected item if the extruder color is defined. - std::string filament_rgb = filament_preset.config.opt_string("filament_colour", 0); + std::string filament_rgb = filament_preset->config.opt_string("filament_colour", 0); std::string extruder_rgb = (int(ui_id) == ui->GetSelection() && ! extruder_color.empty()) ? extruder_color : filament_rgb; wxBitmap *bitmap = nullptr; if (filament_rgb == extruder_rgb) { @@ -362,4 +838,11 @@ const std::vector& PresetBundle::printer_options() return s_opts; } +void PresetBundle::set_default_suppressed(bool default_suppressed) +{ + prints.set_default_suppressed(default_suppressed); + filaments.set_default_suppressed(default_suppressed); + printers.set_default_suppressed(default_suppressed); +} + } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp index 5c8ec8106..ae84c2480 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -1,11 +1,15 @@ #ifndef slic3r_Preset_hpp_ #define slic3r_Preset_hpp_ +#include + #include "../../libslic3r/libslic3r.h" #include "../../libslic3r/PrintConfig.hpp" class wxBitmap; +class wxChoice; class wxBitmapComboBox; +class wxItemContainer; namespace Slic3r { @@ -53,10 +57,21 @@ public: // Throws std::runtime_error in case the file cannot be read. DynamicPrintConfig& load(const std::vector &keys); + void save(); + + // Return a label of this preset, consisting of a name and a "(modified)" suffix, if this preset is dirty. + std::string label() const; + // Set the is_dirty flag if the provided config is different from the active one. void set_dirty(const DynamicPrintConfig &config) { this->is_dirty = ! this->config.diff(config).empty(); } + void set_dirty(bool dirty = true) { this->is_dirty = dirty; } void reset_dirty() { this->is_dirty = false; } + + // Mark this preset as visible if it is compatible with active_printer. bool enable_compatible(const std::string &active_printer); + + // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection. + bool operator<(const Preset &other) const { return this->name < other.name; } }; // Collections of presets of the same type (one of the Print, Filament or Printer type). @@ -65,17 +80,29 @@ class PresetCollection public: // Initialize the PresetCollection with the "- default -" preset. PresetCollection(Preset::Type type, const std::vector &keys); + ~PresetCollection(); + + Preset::Type type() const { return m_type; } + std::string name() const; + const std::deque& operator()() const { return m_presets; } // Load ini files of the particular type from the provided directory path. void load_presets(const std::string &dir_path, const std::string &subdir); + // Load a preset from an already parsed config file, insert it into the sorted sequence of presets + // and select it, losing previous modifications. + Preset& load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select = true); + + // Load default bitmap to be placed at the wxBitmapComboBox of a MainFrame. + bool load_bitmap_default(const std::string &file_name); + // Compatible & incompatible marks, to be placed at the wxBitmapComboBox items. void set_bitmap_compatible (const wxBitmap *bmp) { m_bitmap_compatible = bmp; } void set_bitmap_incompatible(const wxBitmap *bmp) { m_bitmap_incompatible = bmp; } // Enable / disable the "- default -" preset. void set_default_suppressed(bool default_suppressed); - bool is_default_suppressed() const { return m_default_suppressed; } + bool is_default_suppressed() const { return m_default_suppressed || m_presets.size() <= 1; } // Select a preset. If an invalid index is provided, the first visible preset is selected. Preset& select_preset(size_t idx); @@ -87,33 +114,86 @@ public: const Preset& get_edited_preset() const { return m_edited_preset; } // Return a preset possibly with modifications. const Preset& default_preset() const { return m_presets.front(); } + // Return a preset by an index. If the preset is active, a temporary copy is returned. Preset& preset(size_t idx) { return (int(idx) == m_idx_selected) ? m_edited_preset : m_presets[idx]; } const Preset& preset(size_t idx) const { return const_cast(this)->preset(idx); } + + // Return a preset by its name. If the preset is active, a temporary copy is returned. + // If a preset is not found by its name, null is returned. + Preset* find_preset(const std::string &name, bool first_visible_if_not_found = false); + const Preset* find_preset(const std::string &name, bool first_visible_if_not_found = false) const + { return const_cast(this)->find_preset(name, first_visible_if_not_found); } + + size_t first_visible_idx() const; + // Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible. + // Return the first visible preset. Certainly at least the '- default -' preset shall be visible. + Preset& first_visible() { return this->preset(this->first_visible_idx()); } + const Preset& first_visible() const { return this->preset(this->first_visible_idx()); } + + // Return number of presets including the "- default -" preset. size_t size() const { return this->m_presets.size(); } // For Print / Filament presets, disable those, which are not compatible with the printer. void enable_disable_compatible_to_printer(const std::string &active_printer); size_t num_visible() const { return std::count_if(m_presets.begin(), m_presets.end(), [](const Preset &preset){return preset.is_visible;}); } - void delete_preset(const size_t idx); + + // Compare the content of get_selected_preset() with get_edited_preset() configs, return true if they differ. + bool current_is_dirty() { return ! this->current_dirty_options().empty(); } + // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ. + std::vector current_dirty_options() { return this->get_selected_preset().config.diff(this->get_edited_preset().config); } + + // Save the preset under a new name. If the name is different from the old one, + // a new preset is stored into the list of presets. + // All presets are marked as not modified and the new preset is activated. + void save_current_preset(const std::string &new_name); + + // Delete the current preset, activate the first visible preset. + void delete_current_preset(); // Update the choice UI from the list of presets. void update_editor_ui(wxBitmapComboBox *ui); + void update_platter_ui(wxChoice *ui); void update_platter_ui(wxBitmapComboBox *ui); + // Update a dirty floag of the current preset, update the labels of the UI component accordingly. + // Return true if the dirty flag changed. + bool update_dirty_ui(wxItemContainer *ui); + bool update_dirty_ui(wxChoice *ui); + + // Select a profile by its name. Return true if the selection changed. + // Without force, the selection is only updated if the index changes. + // With force, the changes are reverted if the new index is the same as the old index. + bool select_preset_by_name(const std::string &name, bool force); + // Select a profile by its name, update selection at the UI component. + // Return true if the selection changed. + bool select_by_name_ui(char *name, wxItemContainer *ui); + bool select_by_name_ui(char *name, wxChoice *ui); + private: + PresetCollection(); + PresetCollection(const PresetCollection &other); + PresetCollection& operator=(const PresetCollection &other); + // Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER. Preset::Type m_type; // List of presets, starting with the "- default -" preset. - std::vector m_presets; + // Use deque to force the container to allocate an object per each entry, + // so that the addresses of the presets don't change during resizing of the container. + std::deque m_presets; + // Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user. Preset m_edited_preset; // Selected preset. int m_idx_selected; // Is the "- default -" preset suppressed? bool m_default_suppressed = true; - // Compatible & incompatible marks, to be placed at the wxBitmapComboBox items. + // Compatible & incompatible marks, to be placed at the wxBitmapComboBox items of a Platter. + // These bitmaps are not owned by PresetCollection, but by a PresetBundle. const wxBitmap *m_bitmap_compatible = nullptr; const wxBitmap *m_bitmap_incompatible = nullptr; + // Marks placed at the wxBitmapComboBox of a MainFrame. + // These bitmaps are owned by PresetCollection. + wxBitmap *m_bitmap_main_frame; }; // Bundle of Print + Filament + Printer presets. @@ -123,14 +203,32 @@ public: PresetBundle(); ~PresetBundle(); - bool load_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible); - // Load ini files of all types (print, filament, printer) from the provided directory path. void load_presets(const std::string &dir_path); - PresetCollection prints; - PresetCollection filaments; - PresetCollection printers; + PresetCollection prints; + PresetCollection filaments; + PresetCollection printers; + // Filament preset names for a multi-extruder or multi-material print. + // extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size() + std::vector filament_presets; + + DynamicPrintConfig full_config() const; + + // Load an external config file containing the print, filament and printer presets. + // Instead of a config file, a G-code may be loaded containing the full set of parameters. + // In the future the configuration will likely be read from an AMF file as well. + // If the file is loaded successfully, its print / filament / printer profiles will be activated. + void load_config_file(const std::string &path); + + // Load a config bundle file, into presets and store the loaded presets into separate files + // of the local configuration directory. + // Load settings into the provided settings instance. + // Activate the presets stored in the + void load_configbundle(const std::string &path, const DynamicPrintConfig &settings); + + // Export a config bundle file containing all the presets and the names of the active presets. + void export_configbundle(const std::string &path, const DynamicPrintConfig &settings); // Update the colors preview at the platter extruder combo box. void update_platter_filament_ui_colors(wxBitmapComboBox *ui, unsigned int idx_extruder, unsigned int idx_filament); @@ -139,7 +237,12 @@ public: static const std::vector& filament_options(); static const std::vector& printer_options(); + // Enable / disable the "- default -" preset. + void set_default_suppressed(bool default_suppressed); + private: + bool load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible); + // Indicator, that the preset is compatible with the selected printer. wxBitmap *m_bitmapCompatible; // Indicator, that the preset is NOT compatible with the selected printer. diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp index c6d97cabd..a665c0733 100644 --- a/xs/xsp/GUI_Preset.xsp +++ b/xs/xsp/GUI_Preset.xsp @@ -8,17 +8,18 @@ %name{Slic3r::GUI::Preset} class Preset { // owned by PresetCollection, no constructor/destructor - bool is_default() %code%{ RETVAL = THIS->is_default; %}; - bool is_external() %code%{ RETVAL = THIS->is_external; %}; - bool is_visible() %code%{ RETVAL = THIS->is_visible; %}; - bool is_dirty() %code%{ RETVAL = THIS->is_dirty; %}; + bool default() %code%{ RETVAL = THIS->is_default; %}; + bool external() %code%{ RETVAL = THIS->is_external; %}; + bool visible() %code%{ RETVAL = THIS->is_visible; %}; + bool dirty() %code%{ RETVAL = THIS->is_dirty; %}; - const char* name() %code%{ RETVAL = THIS->name.c_str(); %}; - const char* file() %code%{ RETVAL = THIS->file.c_str(); %}; + const char* name() %code%{ RETVAL = THIS->name.c_str(); %}; + const char* file() %code%{ RETVAL = THIS->file.c_str(); %}; - bool loaded() %code%{ RETVAL = THIS->loaded; %}; + bool loaded() %code%{ RETVAL = THIS->loaded; %}; - Ref config() %code%{ RETVAL = &THIS->config; %}; + Ref config_ref() %code%{ RETVAL = &THIS->config; %}; + Clone config() %code%{ RETVAL = &THIS->config; %}; }; %name{Slic3r::GUI::PresetCollection} class PresetCollection { @@ -27,6 +28,29 @@ Ref default_preset() %code%{ RETVAL = &THIS->default_preset(); %}; size_t size() const; size_t num_visible() const; + + Ref get_selected_preset() %code%{ RETVAL = &THIS->get_selected_preset(); %}; + Ref get_current_preset() %code%{ RETVAL = &THIS->get_edited_preset(); %}; + std::string get_current_preset_name() %code%{ RETVAL = THIS->get_selected_preset().name; %}; + Ref get_edited_preset() %code%{ RETVAL = &THIS->get_edited_preset(); %}; + void set_default_suppressed(bool default_suppressed); + + Ref find_preset(char *name, bool first_visible_if_not_found = false) %code%{ RETVAL = THIS->find_preset(name, first_visible_if_not_found); %}; + + bool current_is_dirty(); + std::vector current_dirty_options(); + + void update_platter_ui(SV *ui) + %code%{ wxChoice* cb = (wxChoice*)wxPli_sv_2_object( aTHX_ ui, "Wx::Choice" ); + THIS->update_platter_ui(cb); %}; + + bool update_dirty_ui(SV *ui) + %code%{ RETVAL = THIS->update_dirty_ui((wxChoice*)wxPli_sv_2_object(aTHX_ ui, "Wx::Choice")); %}; + + bool select_preset_by_name(char *name) %code%{ RETVAL = THIS->select_preset_by_name(name, true); %}; + bool select_by_name_ui(char *name, SV *ui) + %code%{ RETVAL = THIS->select_by_name_ui(name, (wxChoice*)wxPli_sv_2_object(aTHX_ ui, "Wx::Choice")); %}; + %{ SV* @@ -43,6 +67,19 @@ PresetCollection::arrayref() OUTPUT: RETVAL +SV* +PresetCollection::presets_hash() + CODE: + HV* hv = newHV(); + for (size_t i = 1; i < THIS->size(); ++ i) { + const Slic3r::Preset &preset = THIS->preset(i); + if (! preset.is_default && ! preset.is_external) + (void)hv_store(hv, preset.name.c_str(), - int(preset.name.size()), newSVpvn_utf8(preset.file.c_str(), preset.file.size(), true), 0); + } + RETVAL = (SV*)newRV_noinc((SV*)hv); + OUTPUT: + RETVAL + %} }; @@ -51,10 +88,12 @@ PresetCollection::arrayref() PresetBundle(); ~PresetBundle(); - void load_bitmaps(std::string path_bitmap_compatible, std::string path_bitmap_incompatible); void load_presets(std::string dir_path); + void set_default_suppressed(bool default_suppressed); - Ref prints() %code%{ RETVAL = &THIS->prints; %}; - Ref filaments() %code%{ RETVAL = &THIS->filaments; %}; - Ref printers() %code%{ RETVAL = &THIS->printers; %}; + Ref print() %code%{ RETVAL = &THIS->prints; %}; + Ref filament() %code%{ RETVAL = &THIS->filaments; %}; + Ref printer() %code%{ RETVAL = &THIS->printers; %}; + + Clone full_config() %code%{ RETVAL = THIS->full_config(); %}; }; diff --git a/xs/xsp/XS.xsp b/xs/xsp/XS.xsp index 7265334ae..194c22b23 100644 --- a/xs/xsp/XS.xsp +++ b/xs/xsp/XS.xsp @@ -67,6 +67,25 @@ var(file_name) RETVAL = Slic3r::var(file_name); OUTPUT: RETVAL +void +set_data_dir(dir) + char *dir; + CODE: + Slic3r::set_data_dir(dir); + +char* +data_dir() + CODE: + RETVAL = const_cast(Slic3r::data_dir().c_str()); + OUTPUT: RETVAL + +std::string +config_path(file_name) + const char *file_name; + CODE: + RETVAL = Slic3r::config_path(file_name); + OUTPUT: RETVAL + std::string encode_path(src) const char *src; From 5a99e694cee58b35183ff1fa9300576883e0373b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 26 Oct 2017 17:17:39 +0200 Subject: [PATCH 02/83] Another step towards the C++ presets. --- lib/Slic3r/GUI.pm | 32 +---- lib/Slic3r/GUI/MainFrame.pm | 114 +++++----------- lib/Slic3r/GUI/Plater.pm | 197 +++++++-------------------- lib/Slic3r/GUI/Preferences.pm | 3 +- lib/Slic3r/GUI/Tab.pm | 223 +++++++++++++++--------------- xs/src/libslic3r/Utils.hpp | 3 + xs/src/libslic3r/utils.cpp | 3 +- xs/src/slic3r/GUI/Preset.cpp | 247 ++++++++++++++++++++++------------ xs/src/slic3r/GUI/Preset.hpp | 42 +++--- xs/xsp/GUI_Preset.xsp | 43 ++++-- xs/xsp/XS.xsp | 7 +- 11 files changed, 431 insertions(+), 483 deletions(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index ca88f2e7d..a75b0d83b 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -81,8 +81,6 @@ our $medium_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); $medium_font->SetPointSize(12); our $grey = Wx::Colour->new(200,200,200); -#our $VERSION_CHECK_EVENT : shared = Wx::NewEventType; - sub OnInit { my ($self) = @_; @@ -123,6 +121,8 @@ sub OnInit { $Settings->{_}{version} = $Slic3r::VERSION; $self->save_settings; + # Suppress the '- default -' presets. + $self->{preset_bundle}->set_default_suppressed($Slic3r::GUI::Settings->{_}{no_defaults} ? 1 : 0); eval { $self->{preset_bundle}->load_presets(Slic3r::data_dir) }; # application frame @@ -169,43 +169,18 @@ sub OnInit { $self->{mainframe}->config_wizard; eval { $self->{preset_bundle}->load_presets(Slic3r::data_dir) }; } - -# $self->check_version -# if $self->have_version_check -# && ($Settings->{_}{version_check} // 1) -# && (!$Settings->{_}{last_version_check} || (time - $Settings->{_}{last_version_check}) >= 86400); - + EVT_IDLE($frame, sub { while (my $cb = shift @cb) { $cb->(); } }); -# EVT_COMMAND($self, -1, $VERSION_CHECK_EVENT, sub { -# my ($self, $event) = @_; -# my ($success, $response, $manual_check) = @{$event->GetData}; -# -# if ($success) { -# if ($response =~ /^obsolete ?= ?([a-z0-9.-]+,)*\Q$Slic3r::VERSION\E(?:,|$)/) { -# my $res = Wx::MessageDialog->new(undef, "A new version is available. Do you want to open the Slic3r website now?", -# 'Update', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_INFORMATION | wxICON_ERROR)->ShowModal; -# Wx::LaunchDefaultBrowser('http://slic3r.org/') if $res == wxID_YES; -# } else { -# Slic3r::GUI::show_info(undef, "You're using the latest version. No updates are available.") if $manual_check; -# } -# $Settings->{_}{last_version_check} = time(); -# $self->save_settings; -# } else { -# Slic3r::GUI::show_error(undef, "Failed to check for updates. Try later.") if $manual_check; -# } -# }); - return 1; } sub about { my ($self) = @_; - my $about = Slic3r::GUI::AboutDialog->new(undef); $about->ShowModal; $about->Destroy; @@ -213,7 +188,6 @@ sub about { sub system_info { my ($self) = @_; - my $slic3r_info = Slic3r::slic3r_info(format => 'html'); my $copyright_info = Slic3r::copyright_info(format => 'html'); my $system_info = Slic3r::system_info(format => 'html'); diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 554c9fbf5..3847fe4c3 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -133,14 +133,16 @@ sub _init_tabpanel { } } }); - $tab->load_presets; + # Load the currently selected preset into the GUI, update the preset selection box. + $tab->load_current_preset; $panel->AddPage($tab, $tab->title); } if ($self->{plater}) { $self->{plater}->on_select_preset(sub { - my ($group, $i) = @_; - $self->{options_tabs}{$group}->select_preset($i); + my ($group, $name) = @_; + print "MainFrame::on_select_preset callback, group: $group, name: $name\n"; + $self->{options_tabs}{$group}->select_preset($name); }); # load initial config @@ -503,19 +505,19 @@ sub extra_variables { sub export_config { my $self = shift; - - my $config = $self->config; - eval { - # validate configuration - $config->validate; - }; + # Generate a cummulative configuration for the selected print, filaments and printer. + my $config = wxTheApp->{preset_bundle}->full_config(); + # Validate the cummulative configuration. + eval { $config->validate; }; Slic3r::GUI::catch_error($self) and return; - + # Ask user for the file name for the config file. my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || ''; my $filename = $last_config ? basename($last_config) : "config.ini"; my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', $dir, $filename, &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if ($dlg->ShowModal == wxID_OK) { + my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef; + $dlg->Destroy; + if (defined $file) { my $file = $dlg->GetPath; $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); wxTheApp->save_settings; @@ -537,10 +539,10 @@ sub load_config_file { $file = $dlg->GetPaths; $dlg->Destroy; } - for my $tab (values %{$self->{options_tabs}}) { - # Dont proceed further if the config file cannot be loaded. - return undef if ! $tab->load_config_file($file); - } + eval { wxTheApp->{preset_bundle}->load_config_file($file); }; + # Dont proceed further if the config file cannot be loaded. + return if Slic3r::GUI::catch_error($self); + $_->load_current_preset for (values %{$self->{options_tabs}}); $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); wxTheApp->save_settings; $last_config = $file; @@ -548,38 +550,23 @@ sub load_config_file { sub export_configbundle { my $self = shift; - - eval { - # validate current configuration in case it's dirty - $self->config->validate; - }; + # validate current configuration in case it's dirty + eval { $self->config->validate; }; Slic3r::GUI::catch_error($self) and return; - + # Ask user for a file name. my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || ''; my $filename = "Slic3r_config_bundle.ini"; my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:', $dir, $filename, &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if ($dlg->ShowModal == wxID_OK) { - my $file = $dlg->GetPath; + my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef; + $dlg->Destroy; + if (defined $file) { + # Export the config bundle. $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); wxTheApp->save_settings; - - # leave default category empty to prevent the bundle from being parsed as a normal config file - my $ini = { _ => {} }; - $ini->{settings}{$_} = $Slic3r::GUI::Settings->{_}{$_} for qw(autocenter); - $ini->{presets} = $Slic3r::GUI::Settings->{presets}; - - foreach my $section (qw(print filament printer)) { - my $presets = wxTheApp->{preset_bundle}->$section->presets_hash; - foreach my $preset_name (keys %{$presets}) { - my $config = Slic3r::Config->load($presets->{$preset_name}); - $ini->{"$section:$preset_name"} = $config->as_ini->{_}; - } - } - - Slic3r::Config->write_ini($file, $ini); + eval { $self->{presets}->export_configbundle($file); }; + Slic3r::GUI::catch_error($self) and return; } - $dlg->Destroy; } sub load_configbundle { @@ -596,46 +583,17 @@ sub load_configbundle { $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); wxTheApp->save_settings; - - # load .ini file - my $ini = Slic3r::Config->read_ini($file); - - if ($ini->{settings}) { - $Slic3r::GUI::Settings->{_}{$_} = $ini->{settings}{$_} for keys %{$ini->{settings}}; - wxTheApp->save_settings; - } - if ($ini->{presets}) { - $Slic3r::GUI::Settings->{presets} = $ini->{presets}; - wxTheApp->save_settings; - } - my $imported = 0; - INI_BLOCK: foreach my $ini_category (sort keys %$ini) { - next unless $ini_category =~ /^(print|filament|printer):(.+)$/; - my ($section, $preset_name) = ($1, $2); - my $config = Slic3r::Config->load_ini_hash($ini->{$ini_category}); - next if $skip_no_id && !$config->get($section . "_settings_id"); - - { - my $current_presets = wxTheApp->{preset_bundle}->$section->presets_hash; - my %current_ids = map { $_ => 1 } - grep $_, - map Slic3r::Config->load($_)->get($section . "_settings_id"), - values %{$current_presets}; - next INI_BLOCK if exists $current_ids{$config->get($section . "_settings_id")}; - } - - $config->save(sprintf Slic3r::data_dir . "/%s/%s.ini", $section, $preset_name); - Slic3r::debugf "Imported %s preset %s\n", $section, $preset_name; - $imported++; - } + my $presets_imported = 0; + eval { $presets_imported = $self->{presets}->load_configbundle($file); }; + Slic3r::GUI::catch_error($self) and return; + + # Load the currently selected preset into the GUI, update the preset selection box. foreach my $tab (values %{$self->{options_tabs}}) { - $tab->load_presets; + $tab->load_current_preset; } - return if !$imported; - - my $message = sprintf "%d presets successfully imported.", $imported; + my $message = sprintf "%d presets successfully imported.", $presets_imported; Slic3r::GUI::show_info($self, $message); } @@ -669,12 +627,6 @@ sub config_wizard { } } -sub filament_preset_names { - my ($self) = @_; - return map $self->{options_tabs}{filament}->{presets}->preset($_)->name, - $self->{plater}->filament_presets; -} - # This is called when closing the application, when loading a config file or when starting the config wizard # to notify the user whether he is aware that some preset changes will be lost. sub check_unsaved_changes { diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d8a9ec7ba..827170a8b 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -366,20 +366,17 @@ sub new { # once a printer preset with multiple extruders is activated. # $self->{preset_choosers}{$group}[$idx] $self->{preset_choosers} = {}; - # Boolean indicating whether the '- default -' preset is shown by the combo box. - $self->{preset_choosers_default_suppressed} = {}; for my $group (qw(print filament printer)) { my $text = Wx::StaticText->new($self, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); $text->SetFont($Slic3r::GUI::small_font); my $choice = Wx::BitmapComboBox->new($self, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY); EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down(0, @_); } ); $self->{preset_choosers}{$group} = [$choice]; - $self->{preset_choosers_default_suppressed}{$group} = 0; # setup the listener EVT_COMBOBOX($choice, $choice, sub { my ($choice) = @_; wxTheApp->CallAfter(sub { - $self->_on_select_preset($group, $choice); + $self->_on_select_preset($group, $choice, 0); }); }); $presets->Add($text, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4); @@ -504,28 +501,23 @@ sub on_select_preset { $self->{on_select_preset} = $cb; } +# Called from the platter combo boxes selecting the active print, filament or printer. sub _on_select_preset { - my $self = shift; - my ($group, $choice) = @_; - + my ($self, $group, $choice, $idx) = @_; # If user changed a filament preset and the selected machine is equipped with multiple extruders, # there are multiple filament selection combo boxes shown at the platter. In that case # don't propagate the filament selection changes to the tab. - my $default_suppressed = $self->{preset_choosers_default_suppressed}{$group}; + if ($group eq 'filament') { + wxTheApp->{preset_bundle}->set_filament_preset($idx, $choice->GetStringSelection); + } if ($group eq 'filament' && @{$self->{preset_choosers}{filament}} > 1) { - # Indices of the filaments selected. - my @filament_presets = $self->filament_presets; - $Slic3r::GUI::Settings->{presets}{filament} = $choice->GetString($filament_presets[0] - $default_suppressed) . ".ini"; - $Slic3r::GUI::Settings->{presets}{"filament_${_}"} = $choice->GetString($filament_presets[$_] - $default_suppressed) - for 1 .. $#filament_presets; wxTheApp->save_settings; - $self->update_filament_colors_preview($choice); + wxTheApp->{preset_bundle}->update_platter_filament_ui_colors($idx, $choice); } else { # call GetSelection() in scalar context as it's context-aware - $self->{on_select_preset}->($group, scalar($choice->GetSelection) + $default_suppressed) + $self->{on_select_preset}->($group, $choice->GetStringSelection) if $self->{on_select_preset}; } - # get new config and generate on_config_change() event for updating plater and other things $self->on_config_change(wxTheApp->{preset_bundle}->full_config); } @@ -572,127 +564,31 @@ sub update_ui_from_settings # For Print settings and Printer, synchronize the selection index with their tabs. # For Filament, synchronize the selection index for a single extruder printer only, otherwise keep the selection. sub update_presets { - # $presets: one of qw(print filament printer) - # $selected: index of the selected preset in the array. This may not correspond - # with the index of selection in the UI element, where not all items are displayed. - my ($self, $group, $presets, $default_suppressed, $selected, $is_dirty) = @_; - - print "Plater.pm update_presets presets: $presets\n"; -# my @choosers = @{ $self->{preset_choosers}{$group} }; -# my $choice_idx = 0; -# foreach my $choice (@choosers) { -# if ($group eq 'filament' && @choosers > 1) { - # if we have more than one filament chooser, keep our selection - # instead of importing the one from the tab -# $selected = $choice->GetSelection + $self->{preset_choosers_default_suppressed}{$group}; -# $is_dirty = 0; -# } -# $choice->Clear; -# foreach my $preset (@{$presets}) { -# print "Prset of $presets: $preset\n"; -# next if ($preset->default && $default_suppressed); -# my $bitmap; -# if ($group eq 'filament') { -# $bitmap = Wx::Bitmap->new(Slic3r::var("spool.png"), wxBITMAP_TYPE_PNG); -# } elsif ($group eq 'print') { -# $bitmap = Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG); -# } elsif ($group eq 'printer') { -# $bitmap = Wx::Bitmap->new(Slic3r::var("printer_empty.png"), wxBITMAP_TYPE_PNG); -# } -# $choice->AppendString($preset->name, $bitmap); -# } - -# if ($selected <= $#$presets) { -# my $idx = $selected - $default_suppressed; -# if ($idx >= 0) { -# if ($is_dirty) { -# $choice->SetString($idx, $choice->GetString($idx) . " (modified)"); -# } -# # call SetSelection() only after SetString() otherwise the new string -# # won't be picked up as the visible string -# $choice->SetSelection($idx); -# } -# } -# $choice_idx += 1; -# } - -# $self->{preset_choosers_default_suppressed}{$group} = $default_suppressed; - - wxTheApp->CallAfter(sub { $self->update_filament_colors_preview }) if $group eq 'filament' || $group eq 'printer'; -} - -# Update the color icon in front of each filament selection on the platter. -# If the extruder has a preview color assigned, apply the extruder color to the active selection. -# Always apply the filament color to the non-active selections. -sub update_filament_colors_preview { - my ($self, $extruder_idx) = shift; - -# my @choosers = @{$self->{preset_choosers}{filament}}; - -# if (ref $extruder_idx) { - # $extruder_idx is the chooser. -# foreach my $chooser (@choosers) { -# if ($extruder_idx == $chooser) { -# $extruder_idx = $chooser; -# last; -# } -# } -# } - -# my @extruder_colors = @{$self->{config}->extruder_colour}; - -# my @extruder_list; -# if (defined $extruder_idx) { -# @extruder_list = ($extruder_idx); -# } else { - # Collect extruder indices. -# @extruder_list = (0..$#extruder_colors); -# } - -# my $filament_tab = $self->GetFrame->{options_tabs}{filament}; -# my $presets = $filament_tab->{presets}; -# my $default_suppressed = $filament_tab->{default_suppressed}; - -# foreach my $extruder_idx (@extruder_list) { -# my $chooser = $choosers[$extruder_idx]; -# my $extruder_color = $self->{config}->extruder_colour->[$extruder_idx]; -# my $preset_idx = 0; -# my $selection_idx = $chooser->GetSelection; -# foreach my $preset (@$presets) { -# my $bitmap; -# if ($preset->default) { -# next if $default_suppressed; -# } else { - # Assign an extruder color to the selected item if the extruder color is defined. -# my $filament_colour_cfg = $preset->config_ref->filament_colour; -# print $filament_colour_cfg . "\n"; -# my $filament_rgb = $filament_colour_cfg->[0]; -# my $extruder_rgb = ($preset_idx == $selection_idx && $extruder_color =~ m/^#[[:xdigit:]]{6}/) ? $extruder_color : $filament_rgb; -# $filament_rgb =~ s/^#//; -# $extruder_rgb =~ s/^#//; -# my $image = Wx::Image->new(24,16); -# if ($filament_rgb ne $extruder_rgb) { -# my @rgb = unpack 'C*', pack 'H*', $extruder_rgb; -# $image->SetRGB(Wx::Rect->new(0,0,16,16), @rgb); -# @rgb = unpack 'C*', pack 'H*', $filament_rgb; -# $image->SetRGB(Wx::Rect->new(16,0,8,16), @rgb); -# } else { -# my @rgb = unpack 'C*', pack 'H*', $filament_rgb; -# $image->SetRGB(Wx::Rect->new(0,0,24,16), @rgb); -# } -# $bitmap = Wx::Bitmap->new($image); -# } -# $chooser->SetItemBitmap($preset_idx, $bitmap) if $bitmap; -# $preset_idx += 1; -# } -# } -} - -# Return a vector of indices of filaments selected by the $self->{preset_choosers}{filament} combo boxes. -sub filament_presets { - my $self = shift; - # force scalar context for GetSelection() as it's context-aware - return map scalar($_->GetSelection) + $self->{preset_choosers_default_suppressed}{filament}, @{ $self->{preset_choosers}{filament} }; + # $group: one of qw(print filament printer) + # $presets: PresetCollection + my ($self, $group, $presets) = @_; + print "Platter::update_presets\n"; + my @choosers = @{$self->{preset_choosers}{$group}}; + if ($group eq 'filament') { + my $choice_idx = 0; + if (int(@choosers) == 1) { + # Single filament printer, synchronize the filament presets. + wxTheApp->{preset_bundle}->set_filament_preset(0, wxTheApp->{preset_bundle}->filament->get_selected_preset->name); + } + foreach my $choice (@choosers) { + wxTheApp->{preset_bundle}->update_platter_filament_ui($choice_idx, $choice); + $choice_idx += 1; + } + } elsif ($group eq 'print') { + wxTheApp->{preset_bundle}->print->update_platter_ui($choosers[0]); + } elsif ($group eq 'printer') { + wxTheApp->{preset_bundle}->printer->update_platter_ui($choosers[0]); + my $choice_idx = 0; + foreach my $choice (@{$self->{preset_choosers}{filament}}) { + wxTheApp->{preset_bundle}->update_platter_filament_ui_colors($choice_idx, $choice); + $choice_idx += 1; + } + } } sub add { @@ -1557,7 +1453,7 @@ sub do_print { my $printer_panel = $controller->add_printer($printer_preset->name, $printer_preset->config); my $filament_stats = $self->{print}->filament_stats; - my @filament_names = $self->GetFrame->filament_preset_names; + my @filament_names = wxTheApp->{preset_bundle}->filament_presets; $filament_stats = { map { $filament_names[$_] => $filament_stats->{$_} } keys %$filament_stats }; $printer_panel->load_print_job($self->{print_file}, $filament_stats); @@ -1713,11 +1609,18 @@ sub update { } # When a number of extruders changes, the UI needs to be updated to show a single filament selection combo box per extruder. +# Also the wxTheApp->{preset_bundle}->filament_presets needs to be resized accordingly +# and some reasonable default has to be selected for the additional extruders. sub on_extruders_change { my ($self, $num_extruders) = @_; my $choices = $self->{preset_choosers}{filament}; - while (@$choices < $num_extruders) { + print "on_extruders_change1 $num_extruders, current choices: " . int(@$choices) . "\n"; + wxTheApp->{preset_bundle}->update_multi_material_filament_presets; + print "on_extruders_change2 $num_extruders, current choices: " . int(@$choices) . "\n"; + + while (int(@$choices) < $num_extruders) { + print "Adding an extruder selection combo box\n"; # copy strings from first choice my @presets = $choices->[0]->GetStrings; @@ -1726,25 +1629,20 @@ sub on_extruders_change { my $extruder_idx = scalar @$choices; EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down($extruder_idx, @_); } ); push @$choices, $choice; - # copy icons from first choice $choice->SetItemBitmap($_, $choices->[0]->GetItemBitmap($_)) for 0..$#presets; - # insert new choice into sizer $self->{presets_sizer}->Insert(4 + ($#$choices-1)*2, 0, 0); $self->{presets_sizer}->Insert(5 + ($#$choices-1)*2, $choice, 0, wxEXPAND | wxBOTTOM, FILAMENT_CHOOSERS_SPACING); - # setup the listener EVT_COMBOBOX($choice, $choice, sub { my ($choice) = @_; wxTheApp->CallAfter(sub { - $self->_on_select_preset('filament', $choice); + $self->_on_select_preset('filament', $choice, $extruder_idx); }); }); - # initialize selection - my $i = first { $choice->GetString($_) eq ($Slic3r::GUI::Settings->{presets}{"filament_" . $#$choices} || '') } 0 .. $#presets; - $choice->SetSelection($i || 0); + wxTheApp->{preset_bundle}->update_platter_filament_ui($extruder_idx, $choice); } # remove unused choices if any @@ -1758,8 +1656,7 @@ sub on_extruders_change { } sub on_config_change { - my $self = shift; - my ($config) = @_; + my ($self, $config) = @_; my $update_scheduled; foreach my $opt_key (@{$self->{config}->diff($config)}) { @@ -1864,7 +1761,7 @@ sub filament_color_box_lmouse_down $colors->[$extruder_idx] = $dialog->GetColourData->GetColour->GetAsString(wxC2S_HTML_SYNTAX); $cfg->set('extruder_colour', $colors); $self->GetFrame->{options_tabs}{printer}->load_config($cfg); - $self->update_filament_colors_preview($extruder_idx); + wxTheApp->{preset_bundle}->update_platter_filament_ui_colors($extruder_idx, $combobox); } $dialog->Destroy(); } @@ -1964,9 +1861,9 @@ sub object_list_changed { for grep $self->{"btn_$_"}, qw(reslice export_gcode print send_gcode); } +# Selection of an active 3D object changed. sub selection_changed { my $self = shift; - my ($obj_idx, $object) = $self->selected_object; my $have_sel = defined $obj_idx; diff --git a/lib/Slic3r/GUI/Preferences.pm b/lib/Slic3r/GUI/Preferences.pm index b19d623cd..4b6c665b7 100644 --- a/lib/Slic3r/GUI/Preferences.pm +++ b/lib/Slic3r/GUI/Preferences.pm @@ -81,7 +81,8 @@ sub new { sub _accept { my $self = shift; - if (defined($self->{values}{no_controller})) { + if (defined($self->{values}{no_controller}) || + defined($self->{values}{no_defaults})) { Slic3r::GUI::warning_catcher($self)->("You need to restart Slic3r to make the changes effective."); } diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 6de0985ac..34fa9f44b 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -68,8 +68,11 @@ sub new { $self->{treectrl} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [185, -1], wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_SUNKEN | wxWANTS_CHARS); $left_sizer->Add($self->{treectrl}, 1, wxEXPAND); $self->{icons} = Wx::ImageList->new(16, 16, 1); + # Map from an icon file name to its index in $self->{icons}. + $self->{icon_index} = {}; + # Index of the last icon inserted into $self->{icons}. + $self->{icon_count} = -1; $self->{treectrl}->AssignImageList($self->{icons}); - $self->{iconcount} = -1; $self->{treectrl}->AddRoot("root"); $self->{pages} = []; $self->{treectrl}->SetIndent(0); @@ -94,7 +97,6 @@ sub new { EVT_CHOICE($parent, $self->{presets_choice}, sub { $self->select_preset($self->{presets_choice}->GetStringSelection); - $self->_on_presets_changed; }); EVT_BUTTON($self, $self->{btn_save_preset}, sub { $self->save_preset }); @@ -103,17 +105,17 @@ sub new { # Initialize the DynamicPrintConfig by default keys/values. # Possible %params keys: no_controller $self->build(%params); - $self->update_tree; + $self->rebuild_page_tree; $self->_update; return $self; } -# Are the '- default -' selections suppressed by the Slic3r GUI preferences? -sub no_defaults { - return $Slic3r::GUI::Settings->{_}{no_defaults} ? 1 : 0; -} - +# Save the current preset into file. +# This removes the "dirty" flag of the preset, possibly creates a new preset under a new name, +# and activates the new preset. +# Wizard calls save_preset with a name "My Settings", otherwise no name is provided and this method +# opens a Slic3r::GUI::SavePresetWindow dialog. sub save_preset { my ($self, $name) = @_; @@ -127,18 +129,25 @@ sub save_preset { my $default_name = $preset->default ? 'Untitled' : $preset->name; $default_name =~ s/\.[iI][nN][iI]$//; + my @prsts = @{$self->{presets}}; + print "Num of presets: ". int(@prsts) . "\n"; + for my $pr (@prsts) { + print "Name: " . $pr->name . " default " . $pr->default . "\n"; + } my $dlg = Slic3r::GUI::SavePresetWindow->new($self, title => lc($self->title), default => $default_name, values => [ map $_->name, grep !$_->default && !$_->external, @{$self->{presets}} ], ); return unless $dlg->ShowModal == wxID_OK; - $name = Slic3r::normalize_utf8_nfc($dlg->get_name); + $name = $dlg->get_name; } - - $self->{config}->save(sprintf Slic3r::data_dir . "/%s/%s.ini", $self->name, $name); - $self->load_presets; - $self->select_preset($name); + # Save the preset into Slic3r::data_dir/section_name/preset_name.ini + eval { $self->{presets}->save_current_preset($name); }; + Slic3r::GUI::catch_error($self) and return; + # Add the new item into the UI component, remove dirty flags and activate the saved item. + $self->{presets}->update_tab_ui($self->{presets_choice}); + # Update the selection boxes at the platter. $self->_on_presets_changed; } @@ -147,29 +156,35 @@ sub delete_preset { my ($self) = @_; my $current_preset = $self->{presets}->get_selected_preset; # Don't let the user delete the '- default -' configuration. - return if $current_preset->{default} || - wxID_YES == Wx::MessageDialog->new($self, "Are you sure you want to delete the selected preset?", 'Delete Preset', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)->ShowModal; + return if $current_preset->default || + wxID_YES != Wx::MessageDialog->new($self, "Are you sure you want to delete the selected preset?", 'Delete Preset', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)->ShowModal; # Delete the file and select some other reasonable preset. - eval { $self->{presets}->delete_file($current_preset->name); }; + # The 'external' presets will only be removed from the preset list, their files will not be deleted. + eval { $self->{presets}->delete_current_preset; }; Slic3r::GUI::catch_error($self) and return; - # Delete the item from the UI component. - $self->{presets}->update_platter_ui($self->{presets_choice}); + # Delete the item from the UI component and activate another preset. + $self->{presets}->update_tab_ui($self->{presets_choice}); + # Update the selection boxes at the patter. $self->_on_presets_changed; } +# Register the on_value_change callback. sub on_value_change { my ($self, $cb) = @_; $self->{on_value_change} = $cb; } +# Register the on_presets_changed callback. sub on_presets_changed { my ($self, $cb) = @_; $self->{on_presets_changed} = $cb; } # This method is called whenever an option field is changed by the user. -# Propagate event to the parent through the 'on_value_changed' callback +# Propagate event to the parent through the 'on_value_change' callback # and call _update. +# The on_value_change callback triggers Platter::on_config_change() to configure the 3D preview +# (colors, wipe tower positon etc) and to restart the background slicing process. sub _on_value_change { my ($self, $key, $value) = @_; $self->{on_value_change}->($key, $value) if $self->{on_value_change}; @@ -178,27 +193,28 @@ sub _on_value_change { # Override this to capture changes of configuration caused either by loading or switching a preset, # or by a user changing an option field. +# This callback is useful for cross-validating configuration values of a single preset. sub _update {} -# Call a callback to update the selection of presets on the platter. +# Call a callback to update the selection of presets on the platter: +# To update the content of the selection boxes, +# to update the filament colors of the selection boxes, +# to update the "dirty" flags of the selection boxes, +# to uddate number of "filament" selection boxes when the number of extruders change. sub _on_presets_changed { my ($self) = @_; - $self->{on_presets_changed}->( - $self->{presets}, - $self->{default_suppressed}, - scalar($self->{presets_choice}->GetSelection) + $self->{default_suppressed}, - $self->{presets}->current_is_dirty, - ) if $self->{on_presets_changed}; + print "Tab::_on_presets_changed\n"; + $self->{on_presets_changed}->($self->{presets}) if $self->{on_presets_changed}; } +# For the printer profile, generate the extruder pages after a preset is loaded. sub on_preset_loaded {} -# Called by the UI combo box when the user switches profiles. -# Select a preset by a name. If ! defined(name), then the first visible preset is selected. -# If the current profile is modified, user is asked to save the changes. -sub select_preset { - my ($self, $name) = @_; - +# If the current preset is dirty, the user is asked whether the changes may be discarded. +# if the current preset was not dirty, or the user agreed to discard the changes, 1 is returned. +sub may_discard_current_preset_if_dirty +{ + my ($self) = @_; if ($self->{presets}->current_is_dirty) { # Display a dialog showing the dirty options in a human readable form. my $old_preset = $self->{presets}->get_current_preset; @@ -215,37 +231,52 @@ sub select_preset { my $changes = join "\n", map "- $_", @option_names; my $confirm = Wx::MessageDialog->new($self, "$name has unsaved changes:\n$changes\n\nDiscard changes and continue anyway?", 'Unsaved Changes', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); - if ($confirm->ShowModal == wxID_NO) { - $self->{presets}->update_platter_ui($self->{presets_choice}); - # Trigger the on_presets_changed event so that we also restore the previous value in the plater selector. - $self->_on_presets_changed; - return; - } + return 0 if $confirm->ShowModal == wxID_NO; } + return 1; +} - $self->{presets}->select_by_name_ui(defined $name ? $name : "", $self->{presets_choice}); +# Called by the UI combo box when the user switches profiles. +# Select a preset by a name. If ! defined(name), then the first visible preset is selected. +# If the current profile is modified, user is asked to save the changes. +sub select_preset { + my ($self, $name, $force) = @_; + print "select_preset 1\n"; + if (! $self->may_discard_current_preset_if_dirty) { + $self->{presets}->update_tab_ui($self->{presets_choice}); + # Trigger the on_presets_changed event so that we also restore the previous value in the plater selector. + $self->_on_presets_changed; + return; + } + print "select_preset 2\n"; + $self->{presets}->select_preset_by_name(defined $name ? $name : ""); + print "select_preset 3\n"; + # Initialize the UI from the current preset. + $self->load_current_preset; + print "select_preset 4\n"; + # Save the current application settings with the newly selected preset name. + wxTheApp->save_settings; + print "select_preset 5\n"; + +} + +# Initialize the UI from the current preset. +sub load_current_preset { + my ($self) = @_; + print "load_current_preset 1\n"; + $self->{presets}->update_tab_ui($self->{presets_choice}); + print "load_current_preset 2\n"; my $preset = $self->{presets}->get_current_preset; - my $preset_config = $preset->config; eval { local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); - foreach my $opt_key (@{$self->{config}->get_keys}) { - if ($preset_config->has($opt_key) && - $self->{config}->serialize($opt_key) ne $preset_config->serialize($opt_key)) { - $self->{config}->set($opt_key, $preset_config->get($opt_key)); - } - } - ($preset->default || $preset->external) - ? $self->{btn_delete_preset}->Disable - : $self->{btn_delete_preset}->Enable; + my $method = ($preset->default || $preset->external) ? 'Disable' : 'Enable'; + $self->{btn_delete_preset}->$method; $self->_update; # For the printer profile, generate the extruder pages. $self->on_preset_loaded; # Reload preset pages with the new configuration values. - $self->reload_config; - # Use this preset the next time Slic3r starts. - $Slic3r::GUI::Settings->{presets}{$self->name} = $preset->file ? basename($preset->file) : ''; + $self->_reload_config; }; - # use CallAfter because some field triggers schedule on_change calls using CallAfter, # and we don't want them to be called after this update_dirty() as they would mark the # preset dirty again @@ -254,22 +285,25 @@ sub select_preset { $self->_on_presets_changed; $self->update_dirty; }); - - # Save the current application settings with the newly selected preset name. - wxTheApp->save_settings; } sub add_options_page { - my $self = shift; - my ($title, $icon, %params) = @_; - + my ($self, $title, $icon, %params) = @_; + # Index of $icon in an icon list $self->{icons}. + my $icon_idx = 0; if ($icon) { - my $bitmap = Wx::Bitmap->new(Slic3r::var($icon), wxBITMAP_TYPE_PNG); - $self->{icons}->Add($bitmap); - $self->{iconcount}++; + $icon_idx = $self->{icon_index}->{$icon}; + if (! defined $icon_idx) { + # Add a new icon to the icon list. + my $bitmap = Wx::Bitmap->new(Slic3r::var($icon), wxBITMAP_TYPE_PNG); + $self->{icons}->Add($bitmap); + $icon_idx = $self->{icon_count} + 1; + $self->{icon_count} = $icon_idx; + $self->{icon_index}->{$icon} = $icon_idx; + } } - - my $page = Slic3r::GUI::Tab::Page->new($self, $title, $self->{iconcount}); + # Initialize the page. + my $page = Slic3r::GUI::Tab::Page->new($self, $title, $icon_idx); $page->Hide; $self->{hsizer}->Add($page, 1, wxEXPAND | wxLEFT, 5); push @{$self->{pages}}, $page; @@ -277,14 +311,16 @@ sub add_options_page { } # Reload current $self->{config} (aka $self->{presets}->edited_preset->config) into the UI fields. -sub reload_config { +sub _reload_config { my ($self) = @_; $_->reload_config for @{$self->{pages}}; } -sub update_tree { +# Regerenerate content of the page tree. +sub rebuild_page_tree { my ($self) = @_; + print "Tab::rebuild_page_tree " . $self->title . "\n"; # get label of the currently selected item my $selected = $self->{treectrl}->GetItemText($self->{treectrl}->GetSelection); @@ -315,40 +351,6 @@ sub update_dirty { $self->_on_presets_changed; } -# Search all ini files in the presets directory, add them into the list of $self->{presets} in the form of Slic3r::GUI::Tab::Preset. -# Initialize the drop down list box. -sub load_presets { - my ($self) = @_; - - print "Load presets, ui: " . $self->{presets_choice} . "\n"; -# $self->current_preset(undef); -# $self->{presets}->set_default_suppressed(Slic3r::GUI::Tab->no_defaults); -# $self->{presets_choice}->Clear; -# foreach my $preset (@{$self->{presets}}) { -# if ($preset->visible) { -# # Set client data of the choice item to $preset. -# $self->{presets_choice}->Append($preset->name, $preset); -# } -# } -# { -# # load last used preset -# my $i = first { basename($self->{presets}[$_]->file) eq ($Slic3r::GUI::Settings->{presets}{$self->name} || '') } 1 .. $#{$self->{presets}}; -# $self->select_preset($i || $self->{default_suppressed}); -# } - - $self->{presets}->update_platter_ui($self->{presets_choice}); - $self->_on_presets_changed; -} - -# Load a config file containing a Print, Filament & Printer preset. -sub load_config_file { - my ($self, $file) = @_; - $self->{presets}->update_platter_ui($self->{presets_choice}); - $self->select_preset; - $self->_on_presets_changed; - return 1; -} - # Load a provied DynamicConfig into the tab, modifying the active preset. # This could be used for example by setting a Wipe Tower position by interactive manipulation in the 3D view. sub load_config { @@ -362,15 +364,16 @@ sub load_config { if ($modified) { $self->update_dirty; # Initialize UI components with the config values. - $self->reload_config; + $self->_reload_config; $self->_update; } } # Find a field with an index over all pages of this tab. +# This method is used often and everywhere, therefore it shall be quick. sub get_field { my ($self, $opt_key, $opt_index) = @_; - foreach my $page (@{ $self->{pages} }) { + foreach my $page (@{$self->{pages}}) { my $field = $page->get_field($opt_key, $opt_index); return $field if defined $field; } @@ -378,10 +381,12 @@ sub get_field { } # Set a key/value pair on this page. Return true if the value has been modified. +# Currently used for distributing extruders_count over preset pages of Slic3r::GUI::Tab::Printer +# after a preset is loaded. sub set_value { my ($self, $opt_key, $value) = @_; my $changed = 0; - foreach my $page (@{ $self->{pages} }) { + foreach my $page (@{$self->{pages}}) { $changed = 1 if $page->set_value($opt_key, $value); } return $changed; @@ -660,10 +665,10 @@ sub build { } # Reload current $self->{config} (aka $self->{presets}->edited_preset->config) into the UI fields. -sub reload_config { +sub _reload_config { my ($self) = @_; # $self->_reload_compatible_printers_widget; - $self->SUPER::reload_config; + $self->SUPER::_reload_config; } # Slic3r::GUI::Tab::Print::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user. @@ -1194,7 +1199,7 @@ sub build { $self->{config}->set('octoprint_host', $value); $self->update_dirty; $self->_on_value_change('octoprint_host', $value); - $self->reload_config; + $self->_reload_config; } } else { Wx::MessageDialog->new($self, 'No Bonjour device found', 'Device Browser', wxOK | wxICON_INFORMATION)->ShowModal; @@ -1421,7 +1426,7 @@ sub _build_extruder_pages { @{$self->{extruder_pages}}[ 0 .. $self->{extruders_count}-1 ], $page_notes ); - $self->update_tree; + $self->rebuild_page_tree; } # Slic3r::GUI::Tab::Printer::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user. @@ -1580,6 +1585,8 @@ sub set_value { return $changed; } +# Dialog to select a new file name for a modified preset to be saved. +# Called from Tab::save_preset(). package Slic3r::GUI::SavePresetWindow; use Wx qw(:combobox :dialog :id :misc :sizer); use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER); @@ -1612,7 +1619,7 @@ sub new { sub accept { my ($self, $event) = @_; - if (($self->{chosen_name} = $self->{combo}->GetValue)) { + if (($self->{chosen_name} = Slic3r::normalize_utf8_nfc($self->{combo}->GetValue))) { if ($self->{chosen_name} !~ /^[^<>:\/\\|?*\"]+$/) { Slic3r::GUI::show_error($self, "The supplied name is not valid; the following characters are not allowed: <>:/\|?*\""); } elsif ($self->{chosen_name} eq '- default -') { diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 23dc330a1..d4da25d87 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -1,6 +1,8 @@ #ifndef slic3r_Utils_hpp_ #define slic3r_Utils_hpp_ +#include + namespace Slic3r { extern void set_logging_level(unsigned int level); @@ -23,6 +25,7 @@ std::string config_path(const std::string &file_name); // The suffix ".ini" will be added if it is missing in the name. std::string config_path(const std::string §ion, const std::string &name); +extern std::locale locale_utf8; extern std::string encode_path(const char *src); extern std::string decode_path(const char *src); extern std::string normalize_utf8_nfc(const char *src); diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 3910a0586..98f709f1e 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -226,9 +226,10 @@ std::string decode_path(const char *src) #endif /* WIN32 */ } +std::locale locale_utf8(boost::locale::generator().generate("")); + std::string normalize_utf8_nfc(const char *src) { - static std::locale locale_utf8(boost::locale::generator().generate("")); return boost::locale::normalize(src, boost::locale::norm_nfc, locale_utf8); } diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 8e1eb93eb..b2c980447 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -5,9 +5,11 @@ #include #include +#include #include #include #include +#include #include #include @@ -25,7 +27,16 @@ namespace Slic3r { +// Suffix to be added to a modified preset name in the combo box. static std::string g_suffix_modified = " (modified)"; +// Remove an optional "(modified)" suffix from a name. +// This converts a UI name to a unique preset identifier. +std::string remove_suffix_modified(const std::string &name) +{ + return boost::algorithm::ends_with(name, g_suffix_modified) ? + name.substr(0, name.size() - g_suffix_modified.size()) : + name; +} // Load keys from a config file or a G-code. // Throw exceptions with reasonable messages if something goes wrong. @@ -117,9 +128,11 @@ PresetCollection::~PresetCollection() // Throws an exception on error. void PresetCollection::load_presets(const std::string &dir_path, const std::string &subdir) { + boost::filesystem::path dir = boost::filesystem::canonical(boost::filesystem::path(dir_path) / subdir).make_preferred(); + m_dir_path = dir.string(); m_presets.erase(m_presets.begin()+1, m_presets.end()); t_config_option_keys keys = this->default_preset().config.keys(); - for (auto &file : boost::filesystem::directory_iterator(boost::filesystem::canonical(boost::filesystem::path(dir_path) / subdir).make_preferred())) + for (auto &file : boost::filesystem::directory_iterator(dir)) if (boost::filesystem::is_regular_file(file.status()) && boost::algorithm::iends_with(file.path().filename().string(), ".ini")) { std::string name = file.path().filename().string(); // Remove the .ini suffix. @@ -133,7 +146,9 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri } } - std::sort(m_presets.begin() + 1, m_presets.end(), [](const Preset &p1, const Preset &p2){ return p1.name < p2.name; }); + std::sort(m_presets.begin() + 1, m_presets.end()); + m_presets.front().is_visible = ! m_default_suppressed || m_presets.size() > 1; + this->select_preset(first_visible_idx()); } // Load a preset from an already parsed config file, insert it into the sorted sequence of presets @@ -158,6 +173,50 @@ Preset& PresetCollection::load_preset(const std::string &path, const std::string return preset; } +void PresetCollection::save_current_preset(const std::string &new_name) +{ + Preset key(m_type, new_name, false); + auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key); + if (it != m_presets.end() && it->name == key.name) { + // Preset with the same name found. + Preset &preset = *it; + if (preset.is_default) + // Cannot overwrite the default preset. + return; + // Overwriting an existing preset. + preset.config = std::move(m_edited_preset.config); + m_idx_selected = it - m_presets.begin(); + } else { + // Creating a new preset. + m_idx_selected = m_presets.insert(it, m_edited_preset) - m_presets.begin(); + Preset &preset = m_presets[m_idx_selected]; + std::string file_name = new_name; + if (! boost::iends_with(file_name, ".ini")) + file_name += ".ini"; + preset.name = new_name; + preset.file = (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string(); + } + m_edited_preset = m_presets[m_idx_selected]; + m_presets[m_idx_selected].save(); +} + +void PresetCollection::delete_current_preset() +{ + const Preset &selected = this->get_selected_preset(); + if (selected.is_default || selected.is_external) + return; + // Erase the preset file. + boost::nowide::remove(selected.file.c_str()); + // Remove the preset from the list. + m_presets.erase(m_presets.begin() + m_idx_selected); + // Find the next visible preset. + m_presets.front().is_visible = ! m_default_suppressed || m_presets.size() > 1; + for (; m_idx_selected < m_presets.size() && ! m_presets[m_idx_selected].is_visible; ++ m_idx_selected) ; + if (m_idx_selected == m_presets.size()) + m_idx_selected = this->first_visible_idx(); + m_edited_preset = m_presets[m_idx_selected]; +} + bool PresetCollection::load_bitmap_default(const std::string &file_name) { return m_bitmap_main_frame->LoadFile(wxString::FromUTF8(Slic3r::var(file_name).c_str()), wxBITMAP_TYPE_PNG); @@ -168,8 +227,7 @@ bool PresetCollection::load_bitmap_default(const std::string &file_name) Preset* PresetCollection::find_preset(const std::string &name, bool first_visible_if_not_found) { Preset key(m_type, name, false); - auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key, - [](const Preset &p1, const Preset &p2) { return p1.name < p2.name; } ); + auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key); // Ensure that a temporary copy is returned if the preset found is currently selected. return (it != m_presets.end() && it->name == key.name) ? &this->preset(it - m_presets.begin()) : first_visible_if_not_found ? &this->first_visible() : nullptr; @@ -216,84 +274,31 @@ void PresetCollection::enable_disable_compatible_to_printer(const std::string &a // Update the wxChoice UI component from this list of presets. // Hide the -void PresetCollection::update_editor_ui(wxBitmapComboBox *ui) -{ - if (ui == nullptr) - return; - - size_t n_visible = this->num_visible(); - size_t n_choice = size_t(ui->GetCount()); - std::string name_selected = dynamic_cast(ui)->GetStringSelection().ToUTF8().data(); - if (boost::algorithm::iends_with(name_selected, g_suffix_modified)) - // Remove the g_suffix_modified. - name_selected.erase(name_selected.end() - g_suffix_modified.size(), name_selected.end()); -#if 0 - if (std::abs(int(n_visible) - int(n_choice)) <= 1) { - // The number of items differs by at most one, update the choice. - size_t i_preset = 0; - size_t i_ui = 0; - while (i_preset < presets.size()) { - std::string name_ui = ui->GetString(i_ui).ToUTF8(); - if (boost::algorithm::iends_with(name_ui, g_suffix_modified)) - // Remove the g_suffix_modified. - name_ui.erase(name_ui.end() - g_suffix_modified.size(), name_ui.end()); - while (this->presets[i_preset].name ) - const Preset &preset = this->presets[i_preset]; - if (preset) - } - } else -#endif - { - // Otherwise fill in the list from scratch. - ui->Clear(); - for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { - const Preset &preset = this->m_presets[i]; - const wxBitmap *bmp = (i == 0 || preset.is_visible) ? m_bitmap_compatible : m_bitmap_incompatible; - ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (bmp == 0) ? wxNullBitmap : *bmp, (void*)i); - if (name_selected == preset.name) - ui->SetSelection(ui->GetCount() - 1); - } - } -} - void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) { if (ui == nullptr) return; - - size_t n_visible = this->num_visible(); - size_t n_choice = size_t(ui->GetCount()); - if (std::abs(int(n_visible) - int(n_choice)) <= 1) { - // The number of items differs by at most one, update the choice. - } else { - // Otherwise fill in the list from scratch. + // Otherwise fill in the list from scratch. + ui->Clear(); + for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { + const Preset &preset = this->m_presets[i]; + const wxBitmap *bmp = (i == 0 || preset.is_visible) ? m_bitmap_compatible : m_bitmap_incompatible; + ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (bmp == 0) ? wxNullBitmap : *bmp, (void*)i); + if (i == m_idx_selected) + ui->SetSelection(ui->GetCount() - 1); } } -void PresetCollection::update_platter_ui(wxChoice *ui) +void PresetCollection::update_tab_ui(wxChoice *ui) { if (ui == nullptr) return; - - size_t n_visible = this->num_visible(); - size_t n_choice = size_t(ui->GetCount()); - if (std::abs(int(n_visible) - int(n_choice)) <= 1) { - // The number of items differs by at most one, update the choice. - } else { - // Otherwise fill in the list from scratch. - } - - std::string name_selected = dynamic_cast(ui)->GetStringSelection().ToUTF8().data(); - if (boost::algorithm::iends_with(name_selected, g_suffix_modified)) - // Remove the g_suffix_modified. - name_selected.erase(name_selected.end() - g_suffix_modified.size(), name_selected.end()); - ui->Clear(); for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { const Preset &preset = this->m_presets[i]; const wxBitmap *bmp = (i == 0 || preset.is_visible) ? m_bitmap_compatible : m_bitmap_incompatible; ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (void*)&preset); - if (name_selected == preset.name) + if (i == m_idx_selected) ui->SetSelection(ui->GetCount() - 1); } } @@ -309,9 +314,7 @@ bool PresetCollection::update_dirty_ui(wxItemContainer *ui) // 2) Update the labels. for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { std::string old_label = ui->GetString(ui_id).utf8_str().data(); - std::string preset_name = boost::algorithm::ends_with(old_label, g_suffix_modified) ? - old_label.substr(0, g_suffix_modified.size()) : - old_label; + std::string preset_name = remove_suffix_modified(old_label); const Preset *preset = this->find_preset(preset_name, false); assert(preset != nullptr); std::string new_label = preset->is_dirty ? preset->name + g_suffix_modified : preset->name; @@ -335,12 +338,12 @@ Preset& PresetCollection::select_preset(size_t idx) return m_presets[idx]; } -bool PresetCollection::select_preset_by_name(const std::string &name, bool force) -{ +bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, bool force) +{ + std::string name = remove_suffix_modified(name_w_suffix); // 1) Try to find the preset by its name. Preset key(m_type, name, false); - auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key, - [](const Preset &p1, const Preset &p2) { return p1.name < p2.name; } ); + auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key); size_t idx = 0; if (it != m_presets.end() && it->name == key.name) // Preset found by its name. @@ -419,6 +422,7 @@ void PresetBundle::load_presets(const std::string &dir_path) this->prints .load_presets(dir_path, "print"); this->filaments.load_presets(dir_path, "filament"); this->printers .load_presets(dir_path, "printer"); + this->update_multi_material_filament_presets(); } bool PresetBundle::load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible) @@ -567,8 +571,7 @@ std::string PresetCollection::name() const // Load a config bundle file, into presets and store the loaded presets into separate files // of the local configuration directory. -// Load settings into the provided settings instance. -void PresetBundle::load_configbundle(const std::string &path, const DynamicPrintConfig &settings) +size_t PresetBundle::load_configbundle(const std::string &path) { // 1) Read the complete config file into the boost::property_tree. namespace pt = boost::property_tree; @@ -583,6 +586,7 @@ void PresetBundle::load_configbundle(const std::string &path, const DynamicPrint std::string active_print; std::vector active_filaments; std::string active_printer; + size_t presets_loaded = 0; for (const auto §ion : tree) { PresetCollection *presets = nullptr; std::vector *loaded = nullptr; @@ -631,6 +635,7 @@ void PresetBundle::load_configbundle(const std::string &path, const DynamicPrint config.set_deserialize(kvp.first, kvp.second.data()); // Load the preset into the list of presets, save it to disk. presets->load_preset(Slic3r::config_path(presets->name(), preset_name), preset_name, config, false).save(); + ++ presets_loaded; } } @@ -642,15 +647,24 @@ void PresetBundle::load_configbundle(const std::string &path, const DynamicPrint // Activate the first filament preset. if (! active_filaments.empty() && ! active_filaments.front().empty()) filaments.select_preset_by_name(active_filaments.front(), true); + + this->update_multi_material_filament_presets(); + for (size_t i = 0; i < std::min(this->filament_presets.size(), active_filaments.size()); ++ i) + this->filament_presets[i] = filaments.first_visible().name; + return presets_loaded; +} + +void PresetBundle::update_multi_material_filament_presets() +{ // Verify and select the filament presets. auto *nozzle_diameter = static_cast(printers.get_selected_preset().config.option("nozzle_diameter")); size_t num_extruders = nozzle_diameter->values.size(); + // Verify validity of the current filament presets. + for (size_t i = 0; i < std::min(this->filament_presets.size(), num_extruders); ++ i) + this->filament_presets[i] = this->filaments.find_preset(this->filament_presets[i], true)->name; + // Append the rest of filament presets. if (this->filament_presets.size() < num_extruders) - this->filament_presets.resize(num_extruders, filaments.get_selected_preset().name); - for (size_t i = 0; i < num_extruders; ++ i) - this->filament_presets[i] = (i < active_filaments.size()) ? - filaments.find_preset(active_filaments[i], true)->name : - filaments.first_visible().name; + this->filament_presets.resize(num_extruders, this->filaments.first_visible().name); } void PresetBundle::export_configbundle(const std::string &path, const DynamicPrintConfig &settings) @@ -703,6 +717,15 @@ void PresetBundle::export_configbundle(const std::string &path, const DynamicPri c.close(); } +// Set the filament preset name. As the name could come from the UI selection box, +// an optional "(modified)" suffix will be removed from the filament name. +void PresetBundle::set_filament_preset(size_t idx, const std::string &name) +{ + if (idx >= filament_presets.size()) + filament_presets.resize(idx + 1, filaments.default_preset().name); + filament_presets[idx] = remove_suffix_modified(name); +} + static inline int hex_digit_to_int(const char c) { return @@ -727,9 +750,67 @@ static inline bool parse_color(const std::string &scolor, unsigned char *rgb_out return true; } -// Update the colors preview at the platter extruder combo box. -void PresetBundle::update_platter_filament_ui_colors(wxBitmapComboBox *ui, unsigned int idx_extruder, unsigned int idx_filament) +void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui) { + if (ui == nullptr) + return; + + unsigned char rgb[3]; + std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); + if (! parse_color(extruder_color, rgb)) + // Extruder color is not defined. + extruder_color.clear(); + + // Fill in the list from scratch. + ui->Clear(); + for (size_t i = this->filaments().front().is_visible ? 0 : 1; i < this->filaments().size(); ++ i) { + const Preset &preset = this->filaments.preset(i); + if (! preset.is_visible) + continue; + bool selected = this->filament_presets[idx_extruder] == preset.name; + // Assign an extruder color to the selected item if the extruder color is defined. + std::string filament_rgb = preset.config.opt_string("filament_colour", 0); + std::string extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb; + wxBitmap *bitmap = nullptr; + if (filament_rgb == extruder_rgb) { + auto it = m_mapColorToBitmap.find(filament_rgb); + if (it == m_mapColorToBitmap.end()) { + // Create the bitmap. + parse_color(filament_rgb, rgb); + wxImage image(24, 16); + image.SetRGB(wxRect(0, 0, 24, 16), rgb[0], rgb[1], rgb[2]); + m_mapColorToBitmap[filament_rgb] = bitmap = new wxBitmap(image); + } else { + bitmap = it->second; + } + } else { + std::string bitmap_key = filament_rgb + extruder_rgb; + auto it = m_mapColorToBitmap.find(bitmap_key); + if (it == m_mapColorToBitmap.end()) { + // Create the bitmap. + wxImage image(24, 16); + parse_color(extruder_rgb, rgb); + image.SetRGB(wxRect(0, 0, 16, 16), rgb[0], rgb[1], rgb[2]); + parse_color(filament_rgb, rgb); + image.SetRGB(wxRect(16, 0, 8, 16), rgb[0], rgb[1], rgb[2]); + m_mapColorToBitmap[filament_rgb] = bitmap = new wxBitmap(image); + } else { + bitmap = it->second; + } + } + + ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (bitmap == 0) ? wxNullBitmap : *bitmap); + if (selected) + ui->SetSelection(ui->GetCount() - 1); + } +} + +// Update the colors preview at the platter extruder combo box. +void PresetBundle::update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui) +{ + this->update_platter_filament_ui(idx_extruder, ui); + return; + unsigned char rgb[3]; std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); if (! parse_color(extruder_color, rgb)) diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp index ae84c2480..cce3e3cf0 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -93,6 +93,14 @@ public: // and select it, losing previous modifications. Preset& load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select = true); + // Save the preset under a new name. If the name is different from the old one, + // a new preset is stored into the list of presets. + // All presets are marked as not modified and the new preset is activated. + void save_current_preset(const std::string &new_name); + + // Delete the current preset, activate the first visible preset. + void delete_current_preset(); + // Load default bitmap to be placed at the wxBitmapComboBox of a MainFrame. bool load_bitmap_default(const std::string &file_name); @@ -143,17 +151,8 @@ public: // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ. std::vector current_dirty_options() { return this->get_selected_preset().config.diff(this->get_edited_preset().config); } - // Save the preset under a new name. If the name is different from the old one, - // a new preset is stored into the list of presets. - // All presets are marked as not modified and the new preset is activated. - void save_current_preset(const std::string &new_name); - - // Delete the current preset, activate the first visible preset. - void delete_current_preset(); - // Update the choice UI from the list of presets. - void update_editor_ui(wxBitmapComboBox *ui); - void update_platter_ui(wxChoice *ui); + void update_tab_ui(wxChoice *ui); void update_platter_ui(wxBitmapComboBox *ui); // Update a dirty floag of the current preset, update the labels of the UI component accordingly. @@ -194,6 +193,8 @@ private: // Marks placed at the wxBitmapComboBox of a MainFrame. // These bitmaps are owned by PresetCollection. wxBitmap *m_bitmap_main_frame; + // Path to the directory to store the config files into. + std::string m_dir_path; }; // Bundle of Print + Filament + Printer presets. @@ -224,24 +225,35 @@ public: // Load a config bundle file, into presets and store the loaded presets into separate files // of the local configuration directory. // Load settings into the provided settings instance. - // Activate the presets stored in the - void load_configbundle(const std::string &path, const DynamicPrintConfig &settings); + // Activate the presets stored in the config bundle. + // Returns the number of presets loaded successfully. + size_t load_configbundle(const std::string &path); // Export a config bundle file containing all the presets and the names of the active presets. void export_configbundle(const std::string &path, const DynamicPrintConfig &settings); + // Update a filament selection combo box on the platter for an idx_extruder. + void update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui); // Update the colors preview at the platter extruder combo box. - void update_platter_filament_ui_colors(wxBitmapComboBox *ui, unsigned int idx_extruder, unsigned int idx_filament); + void update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui); static const std::vector& print_options(); static const std::vector& filament_options(); static const std::vector& printer_options(); // Enable / disable the "- default -" preset. - void set_default_suppressed(bool default_suppressed); + void set_default_suppressed(bool default_suppressed); + + // Set the filament preset name. As the name could come from the UI selection box, + // an optional "(modified)" suffix will be removed from the filament name. + void set_filament_preset(size_t idx, const std::string &name); + + // Read out the number of extruders from an active printer preset, + // update size and content of filament_presets. + void update_multi_material_filament_presets(); private: - bool load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible); + bool load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible); // Indicator, that the preset is compatible with the selected printer. wxBitmap *m_bitmapCompatible; diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp index a665c0733..987574d1a 100644 --- a/xs/xsp/GUI_Preset.xsp +++ b/xs/xsp/GUI_Preset.xsp @@ -33,15 +33,18 @@ Ref get_current_preset() %code%{ RETVAL = &THIS->get_edited_preset(); %}; std::string get_current_preset_name() %code%{ RETVAL = THIS->get_selected_preset().name; %}; Ref get_edited_preset() %code%{ RETVAL = &THIS->get_edited_preset(); %}; - void set_default_suppressed(bool default_suppressed); Ref find_preset(char *name, bool first_visible_if_not_found = false) %code%{ RETVAL = THIS->find_preset(name, first_visible_if_not_found); %}; bool current_is_dirty(); std::vector current_dirty_options(); + void update_tab_ui(SV *ui) + %code%{ auto cb = (wxChoice*)wxPli_sv_2_object( aTHX_ ui, "Wx::Choice" ); + THIS->update_tab_ui(cb); %}; + void update_platter_ui(SV *ui) - %code%{ wxChoice* cb = (wxChoice*)wxPli_sv_2_object( aTHX_ ui, "Wx::Choice" ); + %code%{ auto cb = (wxBitmapComboBox*)wxPli_sv_2_object( aTHX_ ui, "Wx::BitmapComboBox" ); THIS->update_platter_ui(cb); %}; bool update_dirty_ui(SV *ui) @@ -51,6 +54,9 @@ bool select_by_name_ui(char *name, SV *ui) %code%{ RETVAL = THIS->select_by_name_ui(name, (wxChoice*)wxPli_sv_2_object(aTHX_ ui, "Wx::Choice")); %}; + void save_current_preset(char *new_name); + void delete_current_preset(); + %{ SV* @@ -58,10 +64,9 @@ PresetCollection::arrayref() CODE: AV* av = newAV(); av_fill(av, THIS->size()-1); - int i = 0; - for (size_t i = 0; i < THIS->size(); ++ i) { + for (int i = 0; i < int(THIS->size()); ++ i) { Preset &preset = THIS->preset(i); - av_store(av, i++, perl_to_SV_ref(preset)); + av_store(av, i, perl_to_SV_ref(preset)); } RETVAL = newRV_noinc((SV*)av); OUTPUT: @@ -88,12 +93,26 @@ PresetCollection::presets_hash() PresetBundle(); ~PresetBundle(); - void load_presets(std::string dir_path); - void set_default_suppressed(bool default_suppressed); + void load_presets(const char *dir_path); + size_t load_configbundle(const char *path); + void set_default_suppressed(bool default_suppressed); + + Ref print() %code%{ RETVAL = &THIS->prints; %}; + Ref filament() %code%{ RETVAL = &THIS->filaments; %}; + Ref printer() %code%{ RETVAL = &THIS->printers; %}; + + std::vector filament_presets() %code%{ RETVAL = THIS->filament_presets; %}; + void set_filament_preset(int idx, const char *name); + void update_multi_material_filament_presets(); + + Clone full_config() %code%{ RETVAL = THIS->full_config(); %}; + + void update_platter_filament_ui(int extruder_idx, SV *ui) + %code%{ auto cb = (wxBitmapComboBox*)wxPli_sv_2_object(aTHX_ ui, "Wx::BitmapComboBox"); + THIS->update_platter_filament_ui(extruder_idx, cb); %}; + + void update_platter_filament_ui_colors(int extruder_idx, SV *ui) + %code%{ auto cb = (wxBitmapComboBox*)wxPli_sv_2_object(aTHX_ ui, "Wx::BitmapComboBox"); + THIS->update_platter_filament_ui_colors(extruder_idx, cb); %}; - Ref print() %code%{ RETVAL = &THIS->prints; %}; - Ref filament() %code%{ RETVAL = &THIS->filaments; %}; - Ref printer() %code%{ RETVAL = &THIS->printers; %}; - - Clone full_config() %code%{ RETVAL = THIS->full_config(); %}; }; diff --git a/xs/xsp/XS.xsp b/xs/xsp/XS.xsp index 194c22b23..2be42d383 100644 --- a/xs/xsp/XS.xsp +++ b/xs/xsp/XS.xsp @@ -80,10 +80,11 @@ data_dir() OUTPUT: RETVAL std::string -config_path(file_name) - const char *file_name; +config_path(section, name) + const char *section; + const char *name; CODE: - RETVAL = Slic3r::config_path(file_name); + RETVAL = Slic3r::config_path(section, name); OUTPUT: RETVAL std::string From 3bc79e80d5d0618633d1ce556f561774acc59588 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 27 Oct 2017 16:11:06 +0200 Subject: [PATCH 03/83] Fixed configuration & validate C++ ports. --- lib/Slic3r/Config.pm | 17 ----------------- lib/Slic3r/Print.pm | 9 --------- xs/src/libslic3r/Config.cpp | 8 ++++++++ xs/src/libslic3r/Config.hpp | 1 + xs/src/libslic3r/PrintConfig.cpp | 2 +- xs/t/15_config.t | 3 +-- xs/xsp/Config.xsp | 30 ++++++++++++++++++++++++------ xs/xsp/Print.xsp | 3 +-- 8 files changed, 36 insertions(+), 37 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 5af2fa992..4b6e5d453 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -94,23 +94,6 @@ sub merge { return $config; } -# Load a flat ini file without a category into the underlying C++ Slic3r::DynamicConfig class, -# convert legacy configuration names. -sub load { - my ($class, $file) = @_; - # Instead of using the /i modifier for case-insensitive matching, the case insensitivity is expressed - # explicitely to avoid having to bundle the UTF8 Perl library. - if ($file =~ /\.[gG][cC][oO][dD][eE]/ || $file =~ /\.[gG]/) { - my $config = $class->new; - $config->_load_from_gcode($file); - return $config; - } else { - my $config = $class->new; - $config->_load($file); - return $config; - } -} - sub clone { my $self = shift; my $new = (ref $self)->new; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 160f70e06..1613b7e45 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -255,13 +255,4 @@ sub make_wipe_tower { $self->set_step_done(STEP_WIPE_TOWER); } -# Wrapper around the C++ Slic3r::Print::validate() -# to produce a Perl exception without a hang-up on some Strawberry perls. -sub validate -{ - my $self = shift; - my $err = $self->_validate; - die $err . "\n" if (defined($err) && $err ne ''); -} - 1; diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index b8954cc07..c8f923e89 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -320,6 +320,14 @@ void ConfigBase::setenv_() } void ConfigBase::load(const std::string &file) +{ + if (boost::iends_with(file, ".gcode") || boost::iends_with(file, ".g")) + this->load_from_gcode(file); + else + this->load_from_ini(file); +} + +void ConfigBase::load_from_ini(const std::string &file) { namespace pt = boost::property_tree; pt::ptree tree; diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 3808e9bb2..5cf24d8ed 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -952,6 +952,7 @@ public: double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const; void setenv_(); void load(const std::string &file); + void load_from_ini(const std::string &file); void load_from_gcode(const std::string &file); void save(const std::string &file) const; diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index c8a87022e..420bd4490 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1825,7 +1825,7 @@ std::string FullPrintConfig::validate() // --nozzle-diameter for (double nd : this->nozzle_diameter.values) - if (nd < 1) + if (nd < 0.005) return "Invalid value for --nozzle-diameter"; // --perimeters diff --git a/xs/t/15_config.t b/xs/t/15_config.t index 946297358..6e73c8a9a 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -244,9 +244,8 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo { use Cwd qw(abs_path); use File::Basename qw(dirname); - my $class = Slic3r::Config->new; my $path = abs_path($0); - my $config = $class->_load(dirname($path)."/inc/22_config_bad_config_options.ini"); + my $config = Slic3r::Config::load(dirname($path)."/inc/22_config_bad_config_options.ini"); ok 1, 'did not crash on reading invalid items in config'; } diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 0e76b4cf1..2f8946e8b 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -38,16 +38,24 @@ void normalize(); %name{setenv} void setenv_(); double min_object_distance() %code{% RETVAL = PrintConfig::min_object_distance(THIS); %}; - %name{_load} void load(std::string file); - %name{_load_from_gcode} void load_from_gcode(std::string input_file) + static DynamicPrintConfig* load(char *path) %code%{ + auto config = new DynamicPrintConfig(); try { - THIS->load_from_gcode(input_file); + config->load(path); + RETVAL = config; } catch (std::exception& e) { - croak("Error extracting configuration from a g-code %s:\n%s\n", input_file.c_str(), e.what()); + delete config; + croak("Error extracting configuration from %s:\n%s\n", path, e.what()); } %}; void save(std::string file); + int validate() %code%{ + std::string err = THIS->validate(); + if (! err.empty()) + croak("Configuration is not valid: %s\n", err.c_str()); + RETVAL = 1; + %}; }; %name{Slic3r::Config::Static} class StaticPrintConfig { @@ -94,8 +102,18 @@ %}; %name{setenv} void setenv_(); double min_object_distance() %code{% RETVAL = PrintConfig::min_object_distance(THIS); %}; - %name{_load} void load(std::string file); - %name{_load_from_gcode} void load_from_gcode(std::string file); + static StaticPrintConfig* load(char *path) + %code%{ + auto config = new FullPrintConfig(); + try { + config->load(path); + RETVAL = static_cast(config); + } catch (std::exception& e) { + delete config; + croak("Error extracting configuration from %s:\n%s\n", path, e.what()); + } + %}; + void save(std::string file); }; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index b937fe6e8..7565de474 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -215,8 +215,7 @@ _constant() bool has_infinite_skirt(); bool has_skirt(); std::vector extruders() const; - std::string _validate() - %code%{ RETVAL = THIS->validate(); %}; + void validate() %code%{ std::string err = THIS->validate(); if (! err.empty()) throw std::exception(err.c_str()); %}; Clone bounding_box(); Clone total_bounding_box(); double skirt_first_layer_height(); From 2455aee97cf595563ef7d61c8abe39ad31e5514e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 27 Oct 2017 18:52:35 +0200 Subject: [PATCH 04/83] Further reduction of Perl Config.pm methods. --- lib/Slic3r/Config.pm | 44 ----------------------- lib/Slic3r/GUI/ConfigWizard.pm | 4 +-- lib/Slic3r/GUI/Plater.pm | 2 +- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 8 ++--- slic3r.pl | 6 ++-- t/avoid_crossing_perimeters.t | 2 +- t/bridges.t | 2 +- t/combineinfill.t | 6 ++-- t/config.t | 2 +- t/cooling.t | 6 ++-- t/custom_gcode.t | 8 ++--- t/fill.t | 8 ++--- t/flow.t | 4 +-- t/gaps.t | 2 +- t/gcode.t | 22 ++++++------ t/layers.t | 2 +- t/multi.t | 8 ++--- t/perimeters.t | 12 +++---- t/print.t | 4 +-- t/retraction.t | 10 +++--- t/shells.t | 14 ++++---- t/skirt_brim.t | 12 +++---- t/support.t | 12 +++---- t/thin.t | 2 +- utils/view-toolpaths.pl | 2 +- xs/src/libslic3r/PrintConfig.cpp | 37 ++++++++++++------- xs/src/libslic3r/PrintConfig.hpp | 3 ++ xs/xsp/Config.xsp | 10 ++++++ 28 files changed, 118 insertions(+), 136 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 4b6e5d453..c5e49df6b 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -8,12 +8,6 @@ use utf8; use List::Util qw(first max); -# cemetery of old config settings -our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool acceleration - adjust_overhang_flow standby_temperature scale rotate duplicate duplicate_grid - rotate scale duplicate_grid start_perimeters_at_concave_points start_perimeters_at_non_overhang - randomize_start seal_position bed_size print_center g0 vibration_limit gcode_arcs pressure_advance); - # C++ Slic3r::PrintConfigDef exported as a Perl hash of hashes. # The C++ counterpart is a constant singleton. our $Options = print_config_def(); @@ -29,23 +23,6 @@ $Options->{threads}{readonly} = !$Slic3r::have_threads; } } -# Fill in the underlying C++ Slic3r::DynamicPrintConfig with the content of the defaults -# provided by the C++ class Slic3r::FullPrintConfig. -# Used by the UI. -sub new_from_defaults { - my ($class, @opt_keys) = @_; - my $self = $class->new; - # Instantiating the C++ class Slic3r::FullPrintConfig. - my $defaults = Slic3r::Config::Full->new; - if (@opt_keys) { - $self->set($_, $defaults->get($_)) - for grep $defaults->has($_), @opt_keys; - } else { - $self->apply_static($defaults); - } - return $self; -} - # From command line parameters, used by slic3r.pl sub new_from_cli { my $class = shift; @@ -87,27 +64,6 @@ sub new_from_cli { return $self; } -sub merge { - my $class = shift; - my $config = $class->new; - $config->apply($_) for @_; - return $config; -} - -sub clone { - my $self = shift; - my $new = (ref $self)->new; - $new->apply($self); - return $new; -} - -sub get_value { - my ($self, $opt_key) = @_; - return $Options->{$opt_key}{ratio_over} - ? $self->get_abs_value($opt_key) - : $self->get($opt_key); -} - # CLASS METHODS: # Write a "Windows" style ini file with categories enclosed in squre brackets. diff --git a/lib/Slic3r/GUI/ConfigWizard.pm b/lib/Slic3r/GUI/ConfigWizard.pm index 19310b3c1..8cdcac052 100644 --- a/lib/Slic3r/GUI/ConfigWizard.pm +++ b/lib/Slic3r/GUI/ConfigWizard.pm @@ -202,7 +202,7 @@ sub append_option { # populate repository with the factory default my ($opt_key, $opt_index) = split /#/, $full_key, 2; - $self->config->apply(Slic3r::Config->new_from_defaults($opt_key)); + $self->config->apply(Slic3r::Config::new_from_defaults_keys($opt_key)); # draw the control my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new( @@ -300,7 +300,7 @@ sub new { $self->append_text('Set the shape of your printer\'s bed, then click Next.'); - $self->config->apply(Slic3r::Config->new_from_defaults('bed_shape')); + $self->config->apply(Slic3r::Config::new_from_defaults_keys('bed_shape')); $self->{bed_shape_panel} = my $panel = Slic3r::GUI::BedShapePanel->new($self, $self->config->bed_shape); $self->{bed_shape_panel}->on_change(sub { $self->config->set('bed_shape', $self->{bed_shape_panel}->GetValue); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 827170a8b..c4b401cce 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -49,7 +49,7 @@ sub new { my $class = shift; my ($parent) = @_; my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - $self->{config} = Slic3r::Config->new_from_defaults(qw( + $self->{config} = Slic3r::Config::new_from_defaults_keys(qw( bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height serial_port serial_speed octoprint_host octoprint_apikey nozzle_diameter single_extruder_multi_material diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 469794766..039c642c7 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -312,7 +312,7 @@ sub selection_changed { $config = $self->{model_object}->config; } # get default values - my $default_config = Slic3r::Config->new_from_defaults(@opt_keys); + my $default_config = Slic3r::Config::new_from_defaults_keys(@opt_keys); # append default extruder push @opt_keys, 'extruder'; @@ -490,12 +490,12 @@ sub CanClose { # 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->model_object->config); + my $config = $self->GetParent->GetParent->GetParent->GetParent->GetParent->config->clone; eval { + $config->apply($self->model_object->config); $config->validate; }; - return 0 if Slic3r::GUI::catch_error($self); - return 1; + return ! Slic3r::GUI::catch_error($self); } sub PartsChanged { diff --git a/slic3r.pl b/slic3r.pl index 76def3d28..9864c0714 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -92,12 +92,12 @@ if ($opt{save}) { if (@{$cli_config->get_keys} > 0) { $cli_config->save($opt{save}); } else { - Slic3r::Config->new_from_defaults->save($opt{save}); + Slic3r::Config::new_from_defaults->save($opt{save}); } } # apply command line config on top of default config -my $config = Slic3r::Config->new_from_defaults; +my $config = Slic3r::Config::new_from_defaults; $config->apply($cli_config); # locate or create data directory @@ -242,7 +242,7 @@ if (@ARGV) { # slicing from command line sub usage { my ($exit_code) = @_; - my $config = Slic3r::Config->new_from_defaults->as_hash; + my $config = Slic3r::Config::new_from_defaults->as_hash; my $j = ''; if ($Slic3r::have_threads) { diff --git a/t/avoid_crossing_perimeters.t b/t/avoid_crossing_perimeters.t index 60442f47f..86c3e91cb 100644 --- a/t/avoid_crossing_perimeters.t +++ b/t/avoid_crossing_perimeters.t @@ -13,7 +13,7 @@ use Slic3r; use Slic3r::Test; { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('avoid_crossing_perimeters', 2); my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 2); ok my $gcode = Slic3r::Test::gcode($print), "no crash with avoid_crossing_perimeters and multiple objects"; diff --git a/t/bridges.t b/t/bridges.t index e3c89faa7..ca55862b6 100644 --- a/t/bridges.t +++ b/t/bridges.t @@ -109,7 +109,7 @@ sub check_angle { } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('top_solid_layers', 0); # to prevent bridging on sparse infill $config->set('bridge_speed', 99); diff --git a/t/combineinfill.t b/t/combineinfill.t index 66e461d45..e94cf9eb5 100644 --- a/t/combineinfill.t +++ b/t/combineinfill.t @@ -54,7 +54,7 @@ plan tests => 8; 'infill is only present in correct number of layers'; }; - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('layer_height', 0.2); $config->set('first_layer_height', 0.2); $config->set('nozzle_diameter', [0.5]); @@ -73,7 +73,7 @@ plan tests => 8; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('layer_height', 0.2); $config->set('first_layer_height', 0.2); $config->set('nozzle_diameter', [0.5]); @@ -98,7 +98,7 @@ plan tests => 8; # the following needs to be adapted to the new API if (0) { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 0); $config->set('solid_layers', 0); $config->set('bottom_solid_layers', 0); diff --git a/t/config.t b/t/config.t index 829ef5f39..f4a1867de 100644 --- a/t/config.t +++ b/t/config.t @@ -12,7 +12,7 @@ use Slic3r; use Slic3r::Test; { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('perimeter_extrusion_width', '250%'); ok $config->validate, 'percent extrusion width is validated'; } diff --git a/t/cooling.t b/t/cooling.t index 87a557a04..ee4f6abea 100644 --- a/t/cooling.t +++ b/t/cooling.t @@ -42,7 +42,7 @@ my $print_time1 = 100 / (3000 / 60); # 2 sec my $gcode2 = $gcode1 . "G1 X0 E1 F3000\n"; my $print_time2 = 2 * $print_time1; # 4 sec -my $config = Slic3r::Config->new_from_defaults; +my $config = Slic3r::Config::new_from_defaults; # Default cooling settings. $config->set('bridge_fan_speed', [ 100 ]); $config->set('cooling', [ 1 ]); @@ -138,7 +138,7 @@ $config->set('disable_fan_first_layers', [ 0 ]); } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('cooling', [ 1 ]); $config->set('bridge_fan_speed', [ 100 ]); $config->set('fan_below_layer_time', [ 0 ]); @@ -172,7 +172,7 @@ $config->set('disable_fan_first_layers', [ 0 ]); } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('cooling', [ 1 ]); $config->set('fan_below_layer_time', [ 0 ]); $config->set('slowdown_below_layer_time', [ 10 ]); diff --git a/t/custom_gcode.t b/t/custom_gcode.t index 653bb26ae..ed74e750c 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -13,7 +13,7 @@ use Slic3r; use Slic3r::Test; { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; my $test = sub { my ($conf) = @_; @@ -47,7 +47,7 @@ use Slic3r::Test; { my $parser = Slic3r::GCode::PlaceholderParser->new; - $parser->apply_config(my $config = Slic3r::Config->new_from_defaults); + $parser->apply_config(my $config = Slic3r::Config::new_from_defaults); $parser->set('foo' => '0'); is $parser->process('[temperature_[foo]]'), $config->temperature->[0], @@ -55,7 +55,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('output_filename_format', 'ts_[travel_speed]_lh_[layer_height].gcode'); $config->set('start_gcode', "TRAVEL:[travel_speed] HEIGHT:[layer_height]\n"); my $print = Slic3r::Test::init_print('20mm_cube', config => $config); @@ -108,7 +108,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('before_layer_gcode', ';BEFORE [layer_num]'); $config->set('layer_gcode', ';CHANGE [layer_num]'); $config->set('support_material', 1); diff --git a/t/fill.t b/t/fill.t index 15b1a2e04..a6fe8619c 100644 --- a/t/fill.t +++ b/t/fill.t @@ -163,7 +163,7 @@ SKIP: } for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('fill_pattern', $pattern); $config->set('external_fill_pattern', $pattern); $config->set('perimeters', 1); @@ -194,7 +194,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) { } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('infill_only_where_needed', 1); $config->set('bottom_solid_layers', 0); $config->set('infill_extruder', 2); @@ -240,7 +240,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) { } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 0); $config->set('perimeters', 1); $config->set('fill_density', 0); @@ -270,7 +270,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) { } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 0); $config->set('perimeters', 3); $config->set('fill_density', 0); diff --git a/t/flow.t b/t/flow.t index a0d445ce5..2fa0d8f10 100644 --- a/t/flow.t +++ b/t/flow.t @@ -14,7 +14,7 @@ use Slic3r::Geometry qw(scale PI); use Slic3r::Test; { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 1); $config->set('brim_width', 2); $config->set('perimeters', 3); @@ -41,7 +41,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('bridge_speed', 99); $config->set('bridge_flow_ratio', 1); $config->set('cooling', [ 0 ]); # to prevent speeds from being altered diff --git a/t/gaps.t b/t/gaps.t index 70e605a4b..f0c75c353 100644 --- a/t/gaps.t +++ b/t/gaps.t @@ -16,7 +16,7 @@ use Slic3r::Surface ':types'; use Slic3r::Test; { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 0); $config->set('perimeter_speed', 66); $config->set('external_perimeter_speed', 66); diff --git a/t/gcode.t b/t/gcode.t index 956c579d4..126c621b7 100644 --- a/t/gcode.t +++ b/t/gcode.t @@ -21,7 +21,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('wipe', [1]); $config->set('retract_layer_change', [0]); @@ -52,7 +52,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('z_offset', 5); $config->set('start_gcode', ''); @@ -86,7 +86,7 @@ use Slic3r::Test; # - Z moves are correctly generated for both objects # - no travel moves go outside skirt # - temperatures are set correctly - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('gcode_comments', 1); $config->set('complete_objects', 1); $config->set('extrusion_axis', 'A'); @@ -130,7 +130,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('retract_length', [1000000]); $config->set('use_relative_e_distances', 1); my $print = Slic3r::Test::init_print('20mm_cube', config => $config); @@ -162,7 +162,7 @@ use Slic3r::Test; }; { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('gcode_flavor', 'sailfish'); $config->set('raft_layers', 3); my $print = Slic3r::Test::init_print('20mm_cube', config => $config); @@ -170,21 +170,21 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('gcode_flavor', 'sailfish'); my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 2); $test->($print, 'two copies of single object'); } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('gcode_flavor', 'sailfish'); my $print = Slic3r::Test::init_print(['20mm_cube','20mm_cube'], config => $config); $test->($print, 'two objects'); } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('gcode_flavor', 'sailfish'); my $print = Slic3r::Test::init_print('20mm_cube', config => $config, scale_xyz => [1,1, 1/(20/$config->layer_height) ]); $test->($print, 'one layer object'); @@ -192,7 +192,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('start_gcode', 'START:[input_filename]'); my $print = Slic3r::Test::init_print('20mm_cube', config => $config); my $gcode = Slic3r::Test::gcode($print); @@ -200,7 +200,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('spiral_vase', 1); my $print = Slic3r::Test::init_print('cube_with_hole', config => $config); @@ -220,7 +220,7 @@ use Slic3r::Test; { # Tests that the Repetier flavor produces M201 Xnnn Ynnn for resetting # acceleration, also that M204 Snnn syntax is not generated. - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('gcode_flavor', 'repetier'); $config->set('default_acceleration', 1337); my $print = Slic3r::Test::init_print('cube_with_hole', config => $config); diff --git a/t/layers.t b/t/layers.t index a85998077..a9f7dfe39 100644 --- a/t/layers.t +++ b/t/layers.t @@ -13,7 +13,7 @@ use Slic3r; use Slic3r::Test qw(_eq); { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; my $test = sub { my ($conf) = @_; diff --git a/t/multi.t b/t/multi.t index caedb982e..49d35d907 100644 --- a/t/multi.t +++ b/t/multi.t @@ -15,7 +15,7 @@ use Slic3r::Geometry::Clipper qw(offset); use Slic3r::Test; { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('raft_layers', 2); $config->set('infill_extruder', 2); $config->set('solid_infill_extruder', 3); @@ -88,7 +88,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('support_material_extruder', 3); my $print = Slic3r::Test::init_print('20mm_cube', config => $config); @@ -125,7 +125,7 @@ use Slic3r::Test; $upper_config->set('extruder', 2); $upper_config->set('bottom_solid_layers', 1); $upper_config->set('top_solid_layers', 0); - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('fill_density', 0); $config->set('solid_infill_speed', 99); $config->set('top_solid_infill_speed', 99); @@ -171,7 +171,7 @@ use Slic3r::Test; my $model = stacked_cubes(); my $object = $model->objects->[0]; - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('layer_height', 0.4); $config->set('first_layer_height', '100%'); $config->set('skirts', 0); diff --git a/t/perimeters.t b/t/perimeters.t index 2c190fd69..ee332616d 100644 --- a/t/perimeters.t +++ b/t/perimeters.t @@ -156,7 +156,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 0); $config->set('fill_density', 0); $config->set('perimeters', 3); @@ -284,7 +284,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 0); $config->set('perimeters', 3); $config->set('layer_height', 0.4); @@ -314,7 +314,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('nozzle_diameter', [0.4]); $config->set('perimeters', 2); $config->set('perimeter_extrusion_width', 0.4); @@ -372,7 +372,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 0); $config->set('perimeters', 3); $config->set('layer_height', 0.4); @@ -401,7 +401,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('seam_position', 'random'); my $print = Slic3r::Test::init_print('20mm_cube', config => $config); ok Slic3r::Test::gcode($print), 'successful generation of G-code with seam_position = random'; @@ -410,7 +410,7 @@ use Slic3r::Test; { my $test = sub { my ($model_name) = @_; - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('seam_position', 'aligned'); $config->set('skirts', 0); $config->set('perimeters', 1); diff --git a/t/print.t b/t/print.t index 29ad7cbaa..6939d5f15 100644 --- a/t/print.t +++ b/t/print.t @@ -14,7 +14,7 @@ use Slic3r::Geometry qw(unscale X Y); use Slic3r::Test; { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; my $print_center = [100,100]; my $print = Slic3r::Test::init_print('20mm_cube', config => $config, print_center => $print_center); my @extrusion_points = (); @@ -33,7 +33,7 @@ use Slic3r::Test; { # this represents the aggregate config from presets - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; # user adds one object to the plater my $print = Slic3r::Test::init_print(my $model = Slic3r::Test::model('20mm_cube'), config => $config); diff --git a/t/retraction.t b/t/retraction.t index a0631f31a..d7f1ea145 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -13,7 +13,7 @@ use Slic3r; use Slic3r::Test qw(_eq); { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; my $duplicate = 1; my $test = sub { @@ -131,7 +131,7 @@ use Slic3r::Test qw(_eq); } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('start_gcode', ''); # prevent any default priming Z move from affecting our lift detection $config->set('retract_length', [0]); $config->set('retract_layer_change', [0]); @@ -165,7 +165,7 @@ use Slic3r::Test qw(_eq); } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('use_firmware_retraction', 1); my $print = Slic3r::Test::init_print('20mm_cube', config => $config); @@ -188,7 +188,7 @@ use Slic3r::Test qw(_eq); } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('use_firmware_retraction', 1); $config->set('retract_length', [0]); @@ -206,7 +206,7 @@ use Slic3r::Test qw(_eq); } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('start_gcode', ''); $config->set('retract_lift', [3, 4]); diff --git a/t/shells.t b/t/shells.t index 03bc5665c..eac95aa32 100644 --- a/t/shells.t +++ b/t/shells.t @@ -14,7 +14,7 @@ use Slic3r::Geometry qw(epsilon); use Slic3r::Test; { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 0); $config->set('perimeters', 0); $config->set('solid_infill_speed', 99); @@ -82,7 +82,7 @@ use Slic3r::Test; # issue #1161 { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('layer_height', 0.3); $config->set('first_layer_height', '100%'); $config->set('bottom_solid_layers', 0); @@ -106,7 +106,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; # we need to check against one perimeter because this test is calibrated # (shape, extrusion_width) so that perimeters cover the bottom surfaces of # their lower layer - the test checks that shells are not generated on the @@ -137,7 +137,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('perimeters', 3); $config->set('cooling', [ 0 ]); # prevent speed alteration $config->set('first_layer_speed', '100%'); # prevent speed alteration @@ -161,7 +161,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('perimeters', 1); $config->set('fill_density', 0); $config->set('top_solid_layers', 0); @@ -221,7 +221,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('spiral_vase', 1); $config->set('perimeters', 1); $config->set('fill_density', 0); @@ -292,7 +292,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('perimeters', 1); $config->set('fill_density', 0); $config->set('top_solid_layers', 0); diff --git a/t/skirt_brim.t b/t/skirt_brim.t index 4d0155eba..225b0a92f 100644 --- a/t/skirt_brim.t +++ b/t/skirt_brim.t @@ -14,7 +14,7 @@ use Slic3r::Geometry qw(unscale convex_hull); use Slic3r::Test; { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 1); $config->set('skirt_height', 2); $config->set('perimeters', 0); @@ -46,7 +46,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 0); $config->set('perimeters', 0); $config->set('top_solid_layers', 0); # to prevent solid shells and their speeds @@ -72,7 +72,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 1); $config->set('brim_width', 10); @@ -81,7 +81,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 1); $config->set('skirt_height', 0); @@ -90,7 +90,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('layer_height', 0.4); $config->set('first_layer_height', 0.4); $config->set('skirts', 1); @@ -135,7 +135,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('min_skirt_length', 20); my $print = Slic3r::Test::init_print('20mm_cube', config => $config); ok Slic3r::Test::gcode($print), 'no crash when using min_skirt_length'; diff --git a/t/support.t b/t/support.t index ed68c276e..a0cac1470 100644 --- a/t/support.t +++ b/t/support.t @@ -19,7 +19,7 @@ use Slic3r::Geometry::Clipper qw(diff); use Slic3r::Test; { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('support_material', 1); my @contact_z = my @top_z = (); @@ -77,7 +77,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('raft_layers', 3); $config->set('brim_width', 0); $config->set('skirts', 0); @@ -108,7 +108,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 0); $config->set('raft_layers', 3); $config->set('support_material_pattern', 'honeycomb'); @@ -153,7 +153,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 0); $config->set('layer_height', 0.35); $config->set('first_layer_height', 0.3); @@ -192,7 +192,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('brim_width', 0); $config->set('skirts', 0); $config->set('support_material', 1); @@ -232,7 +232,7 @@ use Slic3r::Test; } { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('skirts', 0); $config->set('start_gcode', ''); $config->set('raft_layers', 8); diff --git a/t/thin.t b/t/thin.t index 2d256d286..9147236ee 100644 --- a/t/thin.t +++ b/t/thin.t @@ -16,7 +16,7 @@ use Slic3r::Test; # Disable this until a more robust implementation is provided. It currently # fails on Linux 32bit because some spurious extrudates are generated. if (0) { - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; $config->set('layer_height', 0.2); $config->set('first_layer_height', '100%'); $config->set('extrusion_width', 0.5); diff --git a/utils/view-toolpaths.pl b/utils/view-toolpaths.pl index d4c47f072..234a8123d 100755 --- a/utils/view-toolpaths.pl +++ b/utils/view-toolpaths.pl @@ -33,7 +33,7 @@ my %opt = (); my $model = Slic3r::Model->read_from_file($ARGV[0]); # load config - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config::new_from_defaults; if ($opt{load}) { $config->apply(Slic3r::Config->load($opt{load})); } diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 420bd4490..8986bb59d 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1,7 +1,10 @@ #include "PrintConfig.hpp" + +#include #include #include #include + #include namespace Slic3r { @@ -1719,18 +1722,16 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va value = "60%"; } - // cemetery of old config settings - if (opt_key == "duplicate_x" || opt_key == "duplicate_y" || opt_key == "multiply_x" - || opt_key == "multiply_y" || opt_key == "support_material_tool" - || opt_key == "acceleration" || opt_key == "adjust_overhang_flow" - || opt_key == "standby_temperature" || opt_key == "scale" || opt_key == "rotate" - || opt_key == "duplicate" || opt_key == "duplicate_grid" || opt_key == "rotate" - || opt_key == "scale" || opt_key == "duplicate_grid" - || opt_key == "start_perimeters_at_concave_points" - || opt_key == "start_perimeters_at_non_overhang" || opt_key == "randomize_start" - || opt_key == "seal_position" || opt_key == "bed_size" || opt_key == "octoprint_host" - || opt_key == "print_center" || opt_key == "g0" || opt_key == "threads") - { + // Ignore the following obsolete configuration keys: + static std::set ignore = { + "duplicate_x", "duplicate_y", "gcode_arcs", "multiply_x", "multiply_y", + "support_material_tool", "acceleration", "adjust_overhang_flow", + "standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid", + "start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start", + "seal_position", "vibration_limit", "bed_size", "octoprint_host", + "print_center", "g0", "threads", "pressure_advance" + }; + if (ignore.find(opt_key) != ignore.end()) { opt_key = ""; return; } @@ -1744,6 +1745,18 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va PrintConfigDef print_config_def; +DynamicPrintConfig* DynamicPrintConfig::new_from_defaults() +{ + return new_from_defaults_keys(FullPrintConfig::defaults().keys()); +} + +DynamicPrintConfig* DynamicPrintConfig::new_from_defaults_keys(const std::vector &keys) +{ + auto *out = new DynamicPrintConfig(); + out->apply_only(FullPrintConfig::defaults(), keys); + return out; +} + void DynamicPrintConfig::normalize() { if (this->has("extruder")) { diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 18beeff6f..20f787b1e 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -142,6 +142,9 @@ public: DynamicPrintConfig() {} DynamicPrintConfig(const DynamicPrintConfig &other) : DynamicConfig(other) {} + static DynamicPrintConfig* new_from_defaults(); + static DynamicPrintConfig* new_from_defaults_keys(const std::vector &keys); + // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. const ConfigDef* def() const override { return &print_config_def; } diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 2f8946e8b..f78b3bf60 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -8,6 +8,9 @@ %name{Slic3r::Config} class DynamicPrintConfig { DynamicPrintConfig(); ~DynamicPrintConfig(); + static DynamicPrintConfig* new_from_defaults(); + static DynamicPrintConfig* new_from_defaults_keys(std::vector keys); + DynamicPrintConfig* clone() %code{% RETVAL = new DynamicPrintConfig(*THIS); %}; bool has(t_config_option_key opt_key); SV* as_hash() %code{% RETVAL = ConfigBase__as_hash(THIS); %}; @@ -15,6 +18,13 @@ %code{% RETVAL = ConfigBase__get(THIS, opt_key); %}; SV* get_at(t_config_option_key opt_key, int i) %code{% RETVAL = ConfigBase__get_at(THIS, opt_key, i); %}; + SV* get_value(t_config_option_key opt_key) + %code{% + const ConfigOptionDef *def = THIS->def()->get(opt_key); + RETVAL = (def != nullptr && ! def->ratio_over.empty()) ? + newSVnv(THIS->get_abs_value(opt_key)) : + ConfigBase__get(THIS, opt_key); + %}; bool set(t_config_option_key opt_key, SV* value) %code{% RETVAL = ConfigBase__set(THIS, opt_key, value); %}; bool set_deserialize(t_config_option_key opt_key, SV* str) From 21633bc0bafb7cc52fd7ab7c8ed1c9d0e84c5c00 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 27 Oct 2017 21:28:39 +0200 Subject: [PATCH 05/83] throw std::invalid_argument instead of std::exception --- xs/xsp/Print.xsp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 7565de474..852f0dde0 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -215,7 +215,11 @@ _constant() bool has_infinite_skirt(); bool has_skirt(); std::vector extruders() const; - void validate() %code%{ std::string err = THIS->validate(); if (! err.empty()) throw std::exception(err.c_str()); %}; + void validate() %code%{ + std::string err = THIS->validate(); + if (! err.empty()) + throw std::invalid_argument(err.c_str()); + %}; Clone bounding_box(); Clone total_bounding_box(); double skirt_first_layer_height(); From 857b78ddcaa02b116c64e561cac9fb0d0901db23 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 27 Oct 2017 22:49:59 +0200 Subject: [PATCH 06/83] Fix of the previous commit: Slic3r::Config::new_from_defaults_keys has to be provided with a reference to array of strings. --- lib/Slic3r/GUI/ConfigWizard.pm | 4 +- lib/Slic3r/GUI/MainFrame.pm | 30 ++----- lib/Slic3r/GUI/Plater.pm | 100 +++++++--------------- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 2 +- xs/src/libslic3r/Print.cpp | 4 +- 5 files changed, 43 insertions(+), 97 deletions(-) diff --git a/lib/Slic3r/GUI/ConfigWizard.pm b/lib/Slic3r/GUI/ConfigWizard.pm index 8cdcac052..e540f9ea7 100644 --- a/lib/Slic3r/GUI/ConfigWizard.pm +++ b/lib/Slic3r/GUI/ConfigWizard.pm @@ -202,7 +202,7 @@ sub append_option { # populate repository with the factory default my ($opt_key, $opt_index) = split /#/, $full_key, 2; - $self->config->apply(Slic3r::Config::new_from_defaults_keys($opt_key)); + $self->config->apply(Slic3r::Config::new_from_defaults_keys([$opt_key])); # draw the control my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new( @@ -300,7 +300,7 @@ sub new { $self->append_text('Set the shape of your printer\'s bed, then click Next.'); - $self->config->apply(Slic3r::Config::new_from_defaults_keys('bed_shape')); + $self->config->apply(Slic3r::Config::new_from_defaults_keys(['bed_shape'])); $self->{bed_shape_panel} = my $panel = Slic3r::GUI::BedShapePanel->new($self, $self->config->bed_shape); $self->{bed_shape_panel}->on_change(sub { $self->config->set('bed_shape', $self->{bed_shape_panel}->GetValue); diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 3847fe4c3..a0a47c547 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -70,15 +70,12 @@ sub new { # declare events EVT_CLOSE($self, sub { my (undef, $event) = @_; - if ($event->CanVeto && !$self->check_unsaved_changes) { $event->Veto; return; } - # save window size wxTheApp->save_window_pos($self, "main_frame"); - # propagate event $event->Skip; }); @@ -141,7 +138,6 @@ sub _init_tabpanel { if ($self->{plater}) { $self->{plater}->on_select_preset(sub { my ($group, $name) = @_; - print "MainFrame::on_select_preset callback, group: $group, name: $name\n"; $self->{options_tabs}{$group}->select_preset($name); }); @@ -332,7 +328,6 @@ sub is_loaded { # Selection of a 3D object changed on the platter. sub on_plater_selection_changed { my ($self, $have_selection) = @_; - return if !defined $self->{object_menu}; $self->{object_menu}->Enable($_->GetId, $have_selection) for $self->{object_menu}->GetMenuItems; @@ -340,8 +335,7 @@ sub on_plater_selection_changed { # To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG". sub quick_slice { - my $self = shift; - my %params = @_; + my ($self, %params) = @_; my $progress_dialog; eval { @@ -455,9 +449,7 @@ sub quick_slice { sub reslice_now { my ($self) = @_; - if ($self->{plater}) { - $self->{plater}->reslice; - } + $self->{plater}->reslice if $self->{plater}; } sub repair_stl { @@ -495,6 +487,7 @@ sub repair_stl { Slic3r::GUI::show_info($self, "Your file was repaired.", "Repair"); } +# Extra variables for the placeholder parser generating a G-code. sub extra_variables { my $self = shift; my %extra_variables = (); @@ -518,13 +511,11 @@ sub export_config { my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef; $dlg->Destroy; if (defined $file) { - my $file = $dlg->GetPath; $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); wxTheApp->save_settings; $last_config = $file; $config->save($file); } - $dlg->Destroy; } # Load a config file containing a Print, Filament & Printer preset. @@ -600,15 +591,9 @@ sub load_configbundle { # Load a provied DynamicConfig into the Print / Filament / Printer tabs, thus modifying the active preset. # Also update the platter with the new presets. sub load_config { - my $self = shift; - my ($config) = @_; - - foreach my $tab (values %{$self->{options_tabs}}) { - $tab->load_config($config); - } - if ($self->{plater}) { - $self->{plater}->on_config_change($config); - } + my ($self, $config) = @_; + $_->load_config($config) foreach values %{$self->{options_tabs}}; + $self->{plater}->on_config_change($config) if $self->{plater}; } sub config_wizard { @@ -662,18 +647,15 @@ sub select_view { sub _append_menu_item { my ($self, $menu, $string, $description, $cb, $id, $icon) = @_; - $id //= &Wx::NewId(); my $item = $menu->Append($id, $string, $description); $self->_set_menu_item_icon($item, $icon); - EVT_MENU($self, $id, $cb); return $item; } sub _set_menu_item_icon { my ($self, $menuItem, $icon) = @_; - # SetBitmap was not available on OS X before Wx 0.9927 if ($icon && $menuItem->can('SetBitmap')) { $menuItem->SetBitmap(Wx::Bitmap->new(Slic3r::var($icon), wxBITMAP_TYPE_PNG)); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index c4b401cce..91329e45e 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -46,15 +46,14 @@ use constant PROCESS_DELAY => 0.5 * 1000; # milliseconds my $PreventListEvents = 0; sub new { - my $class = shift; - my ($parent) = @_; + my ($class, $parent) = @_; my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - $self->{config} = Slic3r::Config::new_from_defaults_keys(qw( + $self->{config} = Slic3r::Config::new_from_defaults_keys([qw( bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height serial_port serial_speed octoprint_host octoprint_apikey nozzle_diameter single_extruder_multi_material wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe extruder_colour filament_colour - )); + )]); # C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm $self->{model} = Slic3r::Model->new; # C++ Slic3r::Print with Perl extensions in Slic3r/Print.pm @@ -567,7 +566,6 @@ sub update_presets { # $group: one of qw(print filament printer) # $presets: PresetCollection my ($self, $group, $presets) = @_; - print "Platter::update_presets\n"; my @choosers = @{$self->{preset_choosers}{$group}}; if ($group eq 'filament') { my $choice_idx = 0; @@ -592,7 +590,7 @@ sub update_presets { } sub add { - my $self = shift; + my ($self) = @_; my @input_files = wxTheApp->open_model($self); $self->load_files(\@input_files); } @@ -1494,11 +1492,11 @@ sub send_gcode { } sub export_stl { - my $self = shift; - + my ($self) = @_; return if !@{$self->{objects}}; - + # Ask user for a file name to write into. my $output_file = $self->_get_export_file('STL') or return; + # Store a binary STL. $self->{model}->store_stl($output_file, 1); $self->statusbar->SetStatusText("STL file exported to $output_file"); } @@ -1532,47 +1530,40 @@ sub reload_from_disk { } sub export_object_stl { - my $self = shift; - + my ($self) = @_; my ($obj_idx, $object) = $self->selected_object; return if !defined $obj_idx; - my $model_object = $self->{model}->objects->[$obj_idx]; - + # Ask user for a file name to write into. my $output_file = $self->_get_export_file('STL') or return; $model_object->mesh->write_binary($output_file); $self->statusbar->SetStatusText("STL file exported to $output_file"); } sub export_amf { - my $self = shift; - + my ($self) = @_; return if !@{$self->{objects}}; - + # Ask user for a file name to write into. my $output_file = $self->_get_export_file('AMF') or return; $self->{model}->store_amf($output_file); $self->statusbar->SetStatusText("AMF file exported to $output_file"); } +# Ask user to select an output file for a given file format (STl, AMF, 3MF). +# Propose a default file name based on the 'output_filename_format' configuration value. sub _get_export_file { - my $self = shift; - my ($format) = @_; - + my ($self, $format) = @_; my $suffix = $format eq 'STL' ? '.stl' : '.amf.xml'; - - my $output_file = $main::opt{output}; - { - $output_file = $self->{print}->output_filepath($output_file); - $output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/; - my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file), - basename($output_file), &Slic3r::GUI::MODEL_WILDCARD, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if ($dlg->ShowModal != wxID_OK) { - $dlg->Destroy; - return undef; - } - $output_file = $dlg->GetPath; + my $output_file = $self->{print}->output_filepath($main::opt{output}); + $output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/; + my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file), + basename($output_file), &Slic3r::GUI::MODEL_WILDCARD, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + if ($dlg->ShowModal != wxID_OK) { $dlg->Destroy; + return undef; } + $output_file = $dlg->GetPath; + $dlg->Destroy; return $output_file; } @@ -1615,12 +1606,9 @@ sub on_extruders_change { my ($self, $num_extruders) = @_; my $choices = $self->{preset_choosers}{filament}; - print "on_extruders_change1 $num_extruders, current choices: " . int(@$choices) . "\n"; wxTheApp->{preset_bundle}->update_multi_material_filament_presets; - print "on_extruders_change2 $num_extruders, current choices: " . int(@$choices) . "\n"; while (int(@$choices) < $num_extruders) { - print "Adding an extruder selection combo box\n"; # copy strings from first choice my @presets = $choices->[0]->GetStrings; @@ -1713,7 +1701,6 @@ sub on_config_change { sub list_item_deselected { my ($self, $event) = @_; return if $PreventListEvents; - if ($self->{list}->GetFirstSelected == -1) { $self->select_object(undef); $self->{canvas}->Refresh; @@ -1725,7 +1712,6 @@ sub list_item_deselected { sub list_item_selected { my ($self, $event) = @_; return if $PreventListEvents; - my $obj_idx = $event->GetIndex; $self->select_object($obj_idx); $self->{canvas}->Refresh; @@ -1768,8 +1754,7 @@ sub filament_color_box_lmouse_down } sub object_cut_dialog { - my $self = shift; - my ($obj_idx) = @_; + my ($self, $obj_idx) = @_; if (!defined $obj_idx) { ($obj_idx, undef) = $self->selected_object; @@ -1794,18 +1779,15 @@ sub object_cut_dialog { } sub object_settings_dialog { - my $self = shift; - my ($obj_idx) = @_; - - if (!defined $obj_idx) { - ($obj_idx, undef) = $self->selected_object; - } + my ($self, $obj_idx) = @_; + ($obj_idx, undef) = $self->selected_object if !defined $obj_idx; my $model_object = $self->{model}->objects->[$obj_idx]; # validate config before opening the settings dialog because # that dialog can't be closed if validation fails, but user # can't fix any error which is outside that dialog - return unless $self->validate_config; + eval { wxTheApp->{preset_bundle}->full_config->validate; }; + return if Slic3r::GUI::catch_error($_[0]); my $dlg = Slic3r::GUI::Plater::ObjectSettingsDialog->new($self, object => $self->{objects}[$obj_idx], @@ -1863,7 +1845,7 @@ sub object_list_changed { # Selection of an active 3D object changed. sub selection_changed { - my $self = shift; + my ($self) = @_; my ($obj_idx, $object) = $self->selected_object; my $have_sel = defined $obj_idx; @@ -1924,7 +1906,6 @@ sub select_object { $_->selected(0) for @{ $self->{objects} }; if (defined $obj_idx) { $self->{objects}->[$obj_idx]->selected(1); - # We use this flag to avoid circular event handling # Select() happens to fire a wxEVT_LIST_ITEM_SELECTED on Windows, # whose event handler calls this method again and again and again @@ -1938,18 +1919,9 @@ sub select_object { } sub selected_object { - my $self = shift; - + my ($self) = @_; my $obj_idx = first { $self->{objects}[$_]->selected } 0..$#{ $self->{objects} }; - return undef if !defined $obj_idx; - return ($obj_idx, $self->{objects}[$obj_idx]), -} - -sub validate_config { - eval { - wxTheApp->{preset_bundle}->full_config->validate; - }; - return Slic3r::GUI::catch_error($_[0]) ? 0 : 1; + return defined $obj_idx ? ($obj_idx, $self->{objects}[$obj_idx]) : undef; } sub statusbar { @@ -2079,24 +2051,19 @@ use Wx::DND; use base 'Wx::FileDropTarget'; sub new { - my $class = shift; - my ($window) = @_; + my ($class, $window) = @_; my $self = $class->SUPER::new; $self->{window} = $window; return $self; } sub OnDropFiles { - my $self = shift; - my ($x, $y, $filenames) = @_; - + my ($self, $x, $y, $filenames) = @_; # stop scalars leaking on older perl # https://rt.perl.org/rt3/Public/Bug/Display.html?id=70602 @_ = (); - # only accept STL, OBJ and AMF files return 0 if grep !/\.(?:[sS][tT][lL]|[oO][bB][jJ]|[aA][mM][fF](?:\.[xX][mM][lL])?|[pP][rR][uU][sS][aA])$/, @$filenames; - $self->{window}->load_files($filenames); } @@ -2112,10 +2079,8 @@ has 'selected' => (is => 'rw', default => sub { 0 }); sub make_thumbnail { my ($self, $model, $obj_idx) = @_; - # make method idempotent $self->thumbnail->clear; - # raw_mesh is the non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes. my $mesh = $model->objects->[$obj_idx]->raw_mesh; #FIXME The "correct" variant could be extremely slow. @@ -2131,7 +2096,6 @@ sub make_thumbnail { my $convex_hull = Slic3r::ExPolygon->new($mesh->convex_hull); $self->thumbnail->append($convex_hull); # } - return $self->thumbnail; } diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 039c642c7..8a8e6064c 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -312,7 +312,7 @@ sub selection_changed { $config = $self->{model_object}->config; } # get default values - my $default_config = Slic3r::Config::new_from_defaults_keys(@opt_keys); + my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys); # append default extruder push @opt_keys, 'extruder'; diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index cbf0a3b46..4b47deab2 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -1079,13 +1079,13 @@ std::string Print::output_filepath(const std::string &path) if (! input_file.empty()) break; } - return (boost::filesystem::path(input_file).parent_path() / this->output_filename()).string(); + return (boost::filesystem::path(input_file).parent_path() / this->output_filename()).make_preferred().string(); } // if we were supplied a directory, use it and append our automatically generated filename boost::filesystem::path p(path); if (boost::filesystem::is_directory(p)) - return (p / this->output_filename()).string(); + return (p / this->output_filename()).make_preferred().string(); // if we were supplied a file which is not a directory, use it return path; From 835e5b71a84d867d50109c6016420ed99316f0aa Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sat, 28 Oct 2017 00:18:02 +0200 Subject: [PATCH 07/83] Reduce UI flicker. --- lib/Slic3r/GUI/Tab.pm | 19 ++++++++++++------- xs/src/slic3r/GUI/Preset.cpp | 10 ++++++++++ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 34fa9f44b..1375bb92b 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -257,7 +257,6 @@ sub select_preset { # Save the current application settings with the newly selected preset name. wxTheApp->save_settings; print "select_preset 5\n"; - } # Initialize the UI from the current preset. @@ -313,17 +312,17 @@ sub add_options_page { # Reload current $self->{config} (aka $self->{presets}->edited_preset->config) into the UI fields. sub _reload_config { my ($self) = @_; + $self->Freeze; $_->reload_config for @{$self->{pages}}; + $self->Thaw; } # Regerenerate content of the page tree. sub rebuild_page_tree { my ($self) = @_; - - print "Tab::rebuild_page_tree " . $self->title . "\n"; + $self->Freeze; # get label of the currently selected item - my $selected = $self->{treectrl}->GetItemText($self->{treectrl}->GetSelection); - + my $selected = $self->{treectrl}->GetItemText($self->{treectrl}->GetSelection); my $rootItem = $self->{treectrl}->GetRootItem; $self->{treectrl}->DeleteChildren($rootItem); my $have_selection = 0; @@ -336,11 +335,11 @@ sub rebuild_page_tree { $have_selection = 1; } } - if (!$have_selection) { # this is triggered on first load, so we don't disable the sel change event $self->{treectrl}->SelectItem($self->{treectrl}->GetFirstChild($rootItem)); } + $self->Thaw; } # Update the combo box label of the selected preset based on its "dirty" state, @@ -674,6 +673,7 @@ sub _reload_config { # Slic3r::GUI::Tab::Print::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user. sub _update { my ($self) = @_; + $self->Freeze; my $config = $self->{presets}->get_edited_preset->config_ref; @@ -867,6 +867,8 @@ sub _update { my $have_wipe_tower = $config->wipe_tower; $self->get_field($_)->toggle($have_wipe_tower) for qw(wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe); + + $self->Thaw; } package Slic3r::GUI::Tab::Filament; @@ -1432,7 +1434,8 @@ sub _build_extruder_pages { # Slic3r::GUI::Tab::Printer::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user. sub _update { my ($self) = @_; - + $self->Freeze; + my $config = $self->{config}; my $serial_speed = $self->get_field('serial_speed'); @@ -1504,6 +1507,8 @@ sub _update { $self->get_field('retract_restart_extra_toolchange', $i)->toggle ($have_multiple_extruders && $toolchange_retraction); } + + $self->Thaw; } # this gets executed after preset is loaded and before GUI fields are updated diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index b2c980447..11240a9fd 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "../../libslic3r/Utils.hpp" @@ -279,6 +280,7 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) if (ui == nullptr) return; // Otherwise fill in the list from scratch. + ui->Freeze(); ui->Clear(); for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { const Preset &preset = this->m_presets[i]; @@ -287,12 +289,14 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) if (i == m_idx_selected) ui->SetSelection(ui->GetCount() - 1); } + ui->Thaw(); } void PresetCollection::update_tab_ui(wxChoice *ui) { if (ui == nullptr) return; + ui->Freeze(); ui->Clear(); for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { const Preset &preset = this->m_presets[i]; @@ -301,6 +305,7 @@ void PresetCollection::update_tab_ui(wxChoice *ui) if (i == m_idx_selected) ui->SetSelection(ui->GetCount() - 1); } + ui->Thaw(); } // Update a dirty floag of the current preset, update the labels of the UI component accordingly. @@ -326,6 +331,7 @@ bool PresetCollection::update_dirty_ui(wxItemContainer *ui) bool PresetCollection::update_dirty_ui(wxChoice *ui) { + wxWindowUpdateLocker noUpdates(ui); return update_dirty_ui(dynamic_cast(ui)); } @@ -762,6 +768,7 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma extruder_color.clear(); // Fill in the list from scratch. + ui->Freeze(); ui->Clear(); for (size_t i = this->filaments().front().is_visible ? 0 : 1; i < this->filaments().size(); ++ i) { const Preset &preset = this->filaments.preset(i); @@ -803,6 +810,7 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma if (selected) ui->SetSelection(ui->GetCount() - 1); } + ui->Thaw(); } // Update the colors preview at the platter extruder combo box. @@ -817,6 +825,7 @@ void PresetBundle::update_platter_filament_ui_colors(unsigned int idx_extruder, // Extruder color is not defined. extruder_color.clear(); + ui->Freeze(); for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { std::string preset_name = ui->GetString(ui_id).utf8_str().data(); size_t filament_preset_id = size_t(ui->GetClientData(ui_id)); @@ -854,6 +863,7 @@ void PresetBundle::update_platter_filament_ui_colors(unsigned int idx_extruder, } ui->SetItemBitmap(ui_id, *bitmap); } + ui->Thaw(); } const std::vector& PresetBundle::print_options() From 1fee3633a05870c5cb83c7e850f1c0e22c4d1276 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 30 Oct 2017 18:15:41 +0100 Subject: [PATCH 08/83] New C++ class AppConfig for maintaining the config.ini New helper function for generating a unified "generated by slic3r" header. --- xs/CMakeLists.txt | 3 ++ xs/src/libslic3r/Config.cpp | 10 ++---- xs/src/libslic3r/GCode.cpp | 12 ++----- xs/src/libslic3r/Utils.hpp | 6 ++++ xs/src/libslic3r/utils.cpp | 22 ++++++++++++ xs/src/perlglue.cpp | 1 + xs/src/slic3r/GUI/Preset.cpp | 67 +++++++++++++++++++++++++----------- xs/src/slic3r/GUI/Preset.hpp | 14 +++++--- xs/xsp/GUI_Preset.xsp | 6 ++-- xs/xsp/my.map | 15 ++++---- xs/xsp/typemap.xspt | 2 ++ 11 files changed, 107 insertions(+), 51 deletions(-) diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 7224353d4..562399b47 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -162,6 +162,8 @@ add_library(libslic3r STATIC ) add_library(libslic3r_gui STATIC + ${LIBDIR}/slic3r/GUI/AppConfig.cpp + ${LIBDIR}/slic3r/GUI/AppConfig.hpp ${LIBDIR}/slic3r/GUI/3DScene.cpp ${LIBDIR}/slic3r/GUI/3DScene.hpp ${LIBDIR}/slic3r/GUI/GLShader.cpp @@ -279,6 +281,7 @@ set(XS_XSP_FILES ${XSP_DIR}/GCodeSender.xsp ${XSP_DIR}/Geometry.xsp ${XSP_DIR}/GUI.xsp + ${XSP_DIR}/GUI_AppConfig.xsp ${XSP_DIR}/GUI_3DScene.xsp ${XSP_DIR}/GUI_Preset.xsp ${XSP_DIR}/Layer.xsp diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index c8f923e89..3871253fd 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -1,6 +1,6 @@ #include "Config.hpp" +#include "Utils.hpp" #include -#include #include #include #include // std::runtime_error @@ -422,13 +422,7 @@ void ConfigBase::save(const std::string &file) const { boost::nowide::ofstream c; c.open(file, std::ios::out | std::ios::trunc); - { - std::time_t now; - time(&now); - char buf[sizeof "0000-00-00 00:00:00"]; - strftime(buf, sizeof(buf), "%F %T", gmtime(&now)); - c << "# generated by Slic3r " << SLIC3R_VERSION << " on " << buf << std::endl; - } + c << "# " << Slic3r::header_slic3r_generated() << std::endl; for (const std::string &opt_key : this->keys()) c << opt_key << " = " << this->serialize(opt_key) << std::endl; c.close(); diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 48bf28882..b66861a4a 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -4,6 +4,7 @@ #include "Geometry.hpp" #include "GCode/PrintExtents.hpp" #include "GCode/WipeTowerPrusaMM.hpp" +#include "Utils.hpp" #include #include @@ -11,7 +12,6 @@ #include #include -#include #include #include @@ -462,15 +462,7 @@ bool GCode::_do_export(Print &print, FILE *file) m_enable_extrusion_role_markers = (bool)m_pressure_equalizer; // Write information on the generator. - { - const auto now = boost::posix_time::second_clock::local_time(); - const auto date = now.date(); - fprintf(file, "; generated by Slic3r %s on %04d-%02d-%02d at %02d:%02d:%02d\n\n", - SLIC3R_VERSION, - // Local date in an ANSII format. - int(now.date().year()), int(now.date().month()), int(now.date().day()), - int(now.time_of_day().hours()), int(now.time_of_day().minutes()), int(now.time_of_day().seconds())); - } + fprintf(file, "# %s\n\n", Slic3r::header_slic3r_generated().c_str()); // Write notes (content of the Print Settings tab -> Notes) { std::list lines; diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index d4da25d87..ac6021ff9 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -30,6 +30,12 @@ extern std::string encode_path(const char *src); extern std::string decode_path(const char *src); extern std::string normalize_utf8_nfc(const char *src); +// Timestamp formatted for header_slic3r_generated(). +extern std::string timestamp_str(); +// Standard "generated by Slic3r version xxx timestamp xxx" header string, +// to be placed at the top of Slic3r generated files. +inline std::string header_slic3r_generated() { return std::string("generated by Slic3r " SLIC3R_VERSION " on ") + timestamp_str(); } + // Compute the next highest power of 2 of 32-bit v // http://graphics.stanford.edu/~seander/bithacks.html template diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 98f709f1e..4dcd3855a 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -7,6 +8,7 @@ #include #include +#include #include #include #include @@ -233,4 +235,24 @@ std::string normalize_utf8_nfc(const char *src) return boost::locale::normalize(src, boost::locale::norm_nfc, locale_utf8); } +std::string timestamp_str() +{ +#if 1 + std::time_t now; + time(&now); + char buf[sizeof "0000-00-00 00:00:00"]; + strftime(buf, sizeof(buf), "%F %T", gmtime(&now)); +#else + const auto now = boost::posix_time::second_clock::local_time(); + const auto date = now.date(); + char buf[2048]; + sprintf(buf, "on %04d-%02d-%02d at %02d:%02d:%02d", + SLIC3R_VERSION, + // Local date in an ANSII format. + int(now.date().year()), int(now.date().month()), int(now.date().day()), + int(now.time_of_day().hours()), int(now.time_of_day().minutes()), int(now.time_of_day().seconds())); +#endif + return buf; +} + }; // namespace Slic3r diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index b89b6abc0..fbc8df8d2 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -54,6 +54,7 @@ REGISTER_CLASS(Surface, "Surface"); REGISTER_CLASS(SurfaceCollection, "Surface::Collection"); REGISTER_CLASS(PrintObjectSupportMaterial, "Print::SupportMaterial2"); REGISTER_CLASS(TriangleMesh, "TriangleMesh"); +REGISTER_CLASS(AppConfig, "GUI::AppConfig"); REGISTER_CLASS(GLShader, "GUI::_3DScene::GLShader"); REGISTER_CLASS(GLVolume, "GUI::_3DScene::GLVolume"); REGISTER_CLASS(GLVolumeCollection, "GUI::_3DScene::GLVolume::Collection"); diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 11240a9fd..0448ca1b2 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -373,19 +373,6 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b return false; } -bool PresetCollection::select_by_name_ui(char *name, wxItemContainer *ui) -{ - this->select_preset_by_name(name, true); - //FIXME this is not finished yet. - //this->update_platter_ui(wxChoice *ui) - return true; -} - -bool PresetCollection::select_by_name_ui(char *name, wxChoice *ui) -{ - return this->select_by_name_ui(name, dynamic_cast(ui)); -} - PresetBundle::PresetBundle() : prints(Preset::TYPE_PRINT, print_options()), filaments(Preset::TYPE_FILAMENT, filament_options()), @@ -423,6 +410,20 @@ PresetBundle::~PresetBundle() delete bitmap.second; } +void PresetBundle::setup_directories() +{ + boost::filesystem::path dir = boost::filesystem::canonical(Slic3r::data_dir()); + if (! boost::filesystem::is_directory(dir)) + throw std::runtime_error(std::string("datadir does not exist: ") + Slic3r::data_dir()); + std::initializer_list names = { "print", "filament", "printer" }; + for (const char *name : names) { + boost::filesystem::path subdir = (dir / subdir).make_preferred(); + if (! boost::filesystem::is_directory(subdir) && + ! boost::filesystem::create_directory(subdir)) + throw std::runtime_error(std::string("Slic3r was unable to create its data directory at ") + subdir.string()); + } +} + void PresetBundle::load_presets(const std::string &dir_path) { this->prints .load_presets(dir_path, "print"); @@ -431,6 +432,36 @@ void PresetBundle::load_presets(const std::string &dir_path) this->update_multi_material_filament_presets(); } +// Load selections (current print, current filaments, current printer) from config.ini +// This is done just once on application start up. +void PresetBundle::load_selections(const AppConfig &config) +{ + prints.select_preset_by_name(config.get("presets", "print"), true); + filaments.select_preset_by_name(config.get("presets", "filament"), true); + this->set_filament_preset(0, filaments.get_selected_preset().name); + for (int i = 1; i < 1000; ++ i) { + char name[64]; + sprintf(name, "filament_%d", i); + if (! config.has("presets", name)) + break; + this->set_filament_preset(i, name); + } + printers.select_preset_by_name(config.get("presets", "printer"), true); +} + +// Export selections (current print, current filaments, current printer) into config.ini +void PresetBundle::export_selections(AppConfig &config) +{ + config.set("presets", "print", prints .get_selected_preset().name); + config.set("presets", "filament", filaments.get_selected_preset().name); + for (int i = 1; i < 1000; ++ i) { + char name[64]; + sprintf(name, "filament_%d", i); + config.set("presets", name, filament_presets[i]); + } + config.set("presets", "printer", printers .get_selected_preset().name); +} + bool PresetBundle::load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible) { bool loaded_compatible = m_bitmapCompatible ->LoadFile( @@ -579,7 +610,7 @@ std::string PresetCollection::name() const // of the local configuration directory. size_t PresetBundle::load_configbundle(const std::string &path) { - // 1) Read the complete config file into the boost::property_tree. + // 1) Read the complete config file into a boost::property_tree. namespace pt = boost::property_tree; pt::ptree tree; boost::nowide::ifstream ifs(path); @@ -679,13 +710,7 @@ void PresetBundle::export_configbundle(const std::string &path, const DynamicPri c.open(path, std::ios::out | std::ios::trunc); // Put a comment at the first line including the time stamp and Slic3r version. - { - std::time_t now; - time(&now); - char buf[sizeof "0000-00-00 00:00:00"]; - strftime(buf, sizeof(buf), "%F %T", gmtime(&now)); - c << "# generated by Slic3r " << SLIC3R_VERSION << " on " << buf << std::endl; - } + c << "# " << Slic3r::header_slic3r_generated() << std::endl; // Export the print, filament and printer profiles. for (size_t i_group = 0; i_group < 3; ++ i_group) { diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp index cce3e3cf0..110057204 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -6,6 +6,8 @@ #include "../../libslic3r/libslic3r.h" #include "../../libslic3r/PrintConfig.hpp" +#include "AppConfig.hpp" + class wxBitmap; class wxChoice; class wxBitmapComboBox; @@ -164,10 +166,6 @@ public: // Without force, the selection is only updated if the index changes. // With force, the changes are reverted if the new index is the same as the old index. bool select_preset_by_name(const std::string &name, bool force); - // Select a profile by its name, update selection at the UI component. - // Return true if the selection changed. - bool select_by_name_ui(char *name, wxItemContainer *ui); - bool select_by_name_ui(char *name, wxChoice *ui); private: PresetCollection(); @@ -204,9 +202,17 @@ public: PresetBundle(); ~PresetBundle(); + void setup_directories(); + // Load ini files of all types (print, filament, printer) from the provided directory path. void load_presets(const std::string &dir_path); + // Load selections (current print, current filaments, current printer) from config.ini + // This is done just once on application start up. + void load_selections(const AppConfig &config); + // Export selections (current print, current filaments, current printer) into config.ini + void export_selections(AppConfig &config); + PresetCollection prints; PresetCollection filaments; PresetCollection printers; diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp index 987574d1a..79392c27c 100644 --- a/xs/xsp/GUI_Preset.xsp +++ b/xs/xsp/GUI_Preset.xsp @@ -51,8 +51,6 @@ %code%{ RETVAL = THIS->update_dirty_ui((wxChoice*)wxPli_sv_2_object(aTHX_ ui, "Wx::Choice")); %}; bool select_preset_by_name(char *name) %code%{ RETVAL = THIS->select_preset_by_name(name, true); %}; - bool select_by_name_ui(char *name, SV *ui) - %code%{ RETVAL = THIS->select_by_name_ui(name, (wxChoice*)wxPli_sv_2_object(aTHX_ ui, "Wx::Choice")); %}; void save_current_preset(char *new_name); void delete_current_preset(); @@ -93,10 +91,14 @@ PresetCollection::presets_hash() PresetBundle(); ~PresetBundle(); + void setup_directories(); void load_presets(const char *dir_path); size_t load_configbundle(const char *path); void set_default_suppressed(bool default_suppressed); + void load_selections (AppConfig *config) %code%{ THIS->load_selections(*config); %}; + void export_selections(AppConfig *config) %code%{ THIS->export_selections(*config); %}; + Ref print() %code%{ RETVAL = &THIS->prints; %}; Ref filament() %code%{ RETVAL = &THIS->filaments; %}; Ref printer() %code%{ RETVAL = &THIS->printers; %}; diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 50073d87d..d6af00009 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -210,12 +210,15 @@ PrintObjectSupportMaterial* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T -GLShader* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -GLVolume* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -GLVolumeCollection* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T +AppConfig* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T + +GLShader* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +GLVolume* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +GLVolumeCollection* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T Preset* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index d9d547b2b..6a97e7a73 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -191,6 +191,8 @@ %typemap{ModelInstancePtrs*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; +%typemap{AppConfig*}; +%typemap{Ref}{simple}; %typemap{GLShader*}; %typemap{Ref}{simple}; %typemap{GLVolume*}; From d564fc95df8209145422022cfcd757640b3c7111 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 30 Oct 2017 18:41:50 +0100 Subject: [PATCH 09/83] Split Preset.cpp,hpp to Preset.cpp,hpp and PresetBundle.cpp,hpp --- xs/src/slic3r/GUI/AppConfig.cpp | 154 ++++++++ xs/src/slic3r/GUI/AppConfig.hpp | 88 +++++ xs/src/slic3r/GUI/Preset.cpp | 590 +--------------------------- xs/src/slic3r/GUI/Preset.hpp | 83 +--- xs/src/slic3r/GUI/PresetBundle.cpp | 603 +++++++++++++++++++++++++++++ xs/src/slic3r/GUI/PresetBundle.hpp | 85 ++++ xs/xsp/GUI_AppConfig.xsp | 29 ++ xs/xsp/GUI_Preset.xsp | 1 + 8 files changed, 975 insertions(+), 658 deletions(-) create mode 100644 xs/src/slic3r/GUI/AppConfig.cpp create mode 100644 xs/src/slic3r/GUI/AppConfig.hpp create mode 100644 xs/src/slic3r/GUI/PresetBundle.cpp create mode 100644 xs/src/slic3r/GUI/PresetBundle.hpp create mode 100644 xs/xsp/GUI_AppConfig.xsp diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp new file mode 100644 index 000000000..b0a94b486 --- /dev/null +++ b/xs/src/slic3r/GUI/AppConfig.cpp @@ -0,0 +1,154 @@ +#include + +#include "../../libslic3r/libslic3r.h" +#include "../../libslic3r/Utils.hpp" +#include "AppConfig.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace Slic3r { + +void AppConfig::reset() +{ + m_storage.clear(); + set_defaults(); +}; + +// Override missing or keys with their defaults. +void AppConfig::set_defaults() +{ + // 2) Reset to defaults. + if (get("version_check").empty()) + set("version_check", "1"); + if (get("autocenter").empty()) + set("autocenter", "1"); + // Disable background processing by default as it is not stable. + if (get("background_processing").empty()) + set("background_processing", "0"); + // If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. + // By default, Prusa has the controller hidden. + if (get("no_controller").empty()) + set("no_controller", "0"); + // If set, the "- default -" selections of print/filament/printer are suppressed, if there is a valid preset available. + if (get("no_defaults").empty()) + set("no_defaults", "1"); +} + +void AppConfig::load() +{ + // 1) Read the complete config file into a boost::property_tree. + namespace pt = boost::property_tree; + pt::ptree tree; + boost::nowide::ifstream ifs(AppConfig::config_path()); + pt::read_ini(ifs, tree); + + // 2) Parse the property_tree, extract the sections and key / value pairs. + for (const auto §ion : tree) { + if (section.second.empty()) { + // This may be a top level (no section) entry, or an empty section. + std::string data = section.second.data(); + if (! data.empty()) + // If there is a non-empty data, then it must be a top-level (without a section) config entry. + m_storage[""][section.first] = data; + } else { + // This must be a section name. Read the entries of a section. + std::map &storage = m_storage[section.first]; + for (auto &kvp : section.second) + storage[kvp.first] = kvp.second.data(); + } + } + + // Override missing or keys with their defaults. + this->set_defaults(); + m_dirty = false; +} + +void AppConfig::save() +{ + boost::nowide::ofstream c; + c.open(AppConfig::config_path(), std::ios::out | std::ios::trunc); + c << "# " << Slic3r::header_slic3r_generated() << std::endl; + // Make sure the "no" category is written first. + for (const std::pair &kvp : m_storage[""]) + c << kvp.first << " = " << kvp.second << std::endl; + // Write the other categories. + for (const auto category : m_storage) { + if (category.first.empty()) + continue; + c << std::endl << "[" << category.first << "]" << std::endl; + for (const std::pair &kvp : category.second) + c << kvp.first << " = " << kvp.second << std::endl; + } + c.close(); + m_dirty = false; +} + +std::string AppConfig::get_last_dir() const +{ + const auto it = m_storage.find("recent"); + if (it != m_storage.end()) { + { + const auto it2 = it->second.find("skein_directory"); + if (it2 != it->second.end() && ! it2->second.empty()) + return it2->second; + } + { + const auto it2 = it->second.find("config_directory"); + if (it2 != it->second.end() && ! it2->second.empty()) + return it2->second; + } + } + return std::string(); +} + +void AppConfig::update_config_dir(const std::string &dir) +{ + this->set("recent", "config_directory", dir); + this->save(); +} + +void AppConfig::update_skein_dir(const std::string &dir) +{ + this->set("recent", "skein_directory", dir); + this->save(); +} + +std::string AppConfig::get_last_output_dir(const std::string &alt) const +{ + const auto it = m_storage.find(""); + if (it != m_storage.end()) { + const auto it2 = it->second.find("last_output_path"); + const auto it3 = it->second.find("remember_output_path"); + if (it2 != it->second.end() && it3 != it->second.end() && ! it2->second.empty() && it3->second == "1") + return it2->second; + } + return alt; +} + +void AppConfig::update_last_output_dir(const std::string &dir) +{ + this->set("", "last_output_path", dir); + this->save(); +} + +std::string AppConfig::config_path() +{ + return boost::filesystem::canonical(boost::filesystem::path(Slic3r::data_dir()) / "config.ini").make_preferred().string(); +} + +bool AppConfig::exists() +{ + return boost::filesystem::exists(AppConfig::config_path()); +} + +}; // namespace Slic3r diff --git a/xs/src/slic3r/GUI/AppConfig.hpp b/xs/src/slic3r/GUI/AppConfig.hpp new file mode 100644 index 000000000..d64680011 --- /dev/null +++ b/xs/src/slic3r/GUI/AppConfig.hpp @@ -0,0 +1,88 @@ +#ifndef slic3r_AppConfig_hpp_ +#define slic3r_AppConfig_hpp_ + +#include +#include + +namespace Slic3r { + +class AppConfig +{ +public: + AppConfig() : m_dirty(false) { this->reset(); } + + // Clear and reset to defaults. + void reset(); + // Override missing or keys with their defaults. + void set_defaults(); + + // Load the config.ini from a user profile directory (or a datadir, if configured). + void load(); + // Store the config.ini into a user profile directory (or a datadir, if configured). + void save(); + + // Does this config need to be saved? + bool dirty() const { return m_dirty; } + + // Const accessor, it will return false if a section or a key does not exist. + bool get(const std::string §ion, const std::string &key, std::string &value) const + { + value.clear(); + auto it = m_storage.find(section); + if (it == m_storage.end()) + return false; + auto it2 = it->second.find(key); + if (it2 == it->second.end()) + return false; + value = it2->second; + return true; + } + std::string get(const std::string §ion, const std::string &key) const + { std::string value; this->get(section, key, value); return value; } + std::string get(const std::string &key) const + { std::string value; this->get("", key, value); return value; } + void set(const std::string §ion, const std::string &key, const std::string &value) + { + std::string &old = m_storage[section][key]; + if (old != value) { + old = value; + m_dirty = true; + } + } + void set(const std::string &key, const std::string &value) + { this->set("", key, value); } + bool has(const std::string §ion, const std::string &key) const + { + auto it = m_storage.find(section); + if (it == m_storage.end()) + return false; + auto it2 = it->second.find(key); + return it2 != it->second.end() && ! it2->second.empty(); + } + bool has(const std::string &key) const + { return this->has("", key); } + + // return recent/skein_directory or recent/config_directory or empty string. + std::string get_last_dir() const; + void update_config_dir(const std::string &dir); + void update_skein_dir(const std::string &dir); + + std::string get_last_output_dir(const std::string &alt) const; + void update_last_output_dir(const std::string &dir); + + // Get the default config path from Slic3r::data_dir(). + static std::string config_path(); + + // Does the config file exist? + static bool exists(); + +private: + // Map of section, name -> value + std::map> m_storage; + // Has any value been modified since the config.ini has been last saved or loaded? + bool m_dirty; +}; + +}; // namespace Slic3r + +#endif /* slic3r_AppConfig_hpp_ */ diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 0448ca1b2..fcc20cd9c 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -30,9 +30,13 @@ namespace Slic3r { // Suffix to be added to a modified preset name in the combo box. static std::string g_suffix_modified = " (modified)"; +const std::string& Preset::suffix_modified() +{ + return g_suffix_modified; +} // Remove an optional "(modified)" suffix from a name. // This converts a UI name to a unique preset identifier. -std::string remove_suffix_modified(const std::string &name) +std::string Preset::remove_suffix_modified(const std::string &name) { return boost::algorithm::ends_with(name, g_suffix_modified) ? name.substr(0, name.size() - g_suffix_modified.size()) : @@ -41,7 +45,7 @@ std::string remove_suffix_modified(const std::string &name) // Load keys from a config file or a G-code. // Throw exceptions with reasonable messages if something goes wrong. -static void load_config_file(DynamicPrintConfig &config, const std::string &path) +void Preset::load_config_file(DynamicPrintConfig &config, const std::string &path) { try { if (boost::algorithm::iends_with(path, ".gcode") || boost::algorithm::iends_with(path, ".g")) @@ -319,7 +323,7 @@ bool PresetCollection::update_dirty_ui(wxItemContainer *ui) // 2) Update the labels. for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { std::string old_label = ui->GetString(ui_id).utf8_str().data(); - std::string preset_name = remove_suffix_modified(old_label); + std::string preset_name = Preset::remove_suffix_modified(old_label); const Preset *preset = this->find_preset(preset_name, false); assert(preset != nullptr); std::string new_label = preset->is_dirty ? preset->name + g_suffix_modified : preset->name; @@ -346,7 +350,7 @@ Preset& PresetCollection::select_preset(size_t idx) bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, bool force) { - std::string name = remove_suffix_modified(name_w_suffix); + std::string name = Preset::remove_suffix_modified(name_w_suffix); // 1) Try to find the preset by its name. Preset key(m_type, name, false); auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key); @@ -373,229 +377,6 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b return false; } -PresetBundle::PresetBundle() : - prints(Preset::TYPE_PRINT, print_options()), - filaments(Preset::TYPE_FILAMENT, filament_options()), - printers(Preset::TYPE_PRINTER, printer_options()), - m_bitmapCompatible(new wxBitmap), - m_bitmapIncompatible(new wxBitmap) -{ - ::wxInitAllImageHandlers(); - - // Create the ID config keys, as they are not part of the Static print config classes. - this->prints.preset(0).config.opt_string("print_settings_id", true); - this->filaments.preset(0).config.opt_string("filament_settings_id", true); - this->printers.preset(0).config.opt_string("print_settings_id", true); - // Create the "compatible printers" keys, as they are not part of the Static print config classes. - this->filaments.preset(0).config.optptr("compatible_printers", true); - this->prints.preset(0).config.optptr("compatible_printers", true); - - this->prints .load_bitmap_default("cog.png"); - this->filaments.load_bitmap_default("spool.png"); - this->printers .load_bitmap_default("printer_empty.png"); - - // FIXME select some icons indicating compatibility. - this->load_compatible_bitmaps("cog.png", "cog.png"); -} - -PresetBundle::~PresetBundle() -{ - assert(m_bitmapCompatible != nullptr); - assert(m_bitmapIncompatible != nullptr); - delete m_bitmapCompatible; - m_bitmapCompatible = nullptr; - delete m_bitmapIncompatible; - m_bitmapIncompatible = nullptr; - for (std::pair &bitmap : m_mapColorToBitmap) - delete bitmap.second; -} - -void PresetBundle::setup_directories() -{ - boost::filesystem::path dir = boost::filesystem::canonical(Slic3r::data_dir()); - if (! boost::filesystem::is_directory(dir)) - throw std::runtime_error(std::string("datadir does not exist: ") + Slic3r::data_dir()); - std::initializer_list names = { "print", "filament", "printer" }; - for (const char *name : names) { - boost::filesystem::path subdir = (dir / subdir).make_preferred(); - if (! boost::filesystem::is_directory(subdir) && - ! boost::filesystem::create_directory(subdir)) - throw std::runtime_error(std::string("Slic3r was unable to create its data directory at ") + subdir.string()); - } -} - -void PresetBundle::load_presets(const std::string &dir_path) -{ - this->prints .load_presets(dir_path, "print"); - this->filaments.load_presets(dir_path, "filament"); - this->printers .load_presets(dir_path, "printer"); - this->update_multi_material_filament_presets(); -} - -// Load selections (current print, current filaments, current printer) from config.ini -// This is done just once on application start up. -void PresetBundle::load_selections(const AppConfig &config) -{ - prints.select_preset_by_name(config.get("presets", "print"), true); - filaments.select_preset_by_name(config.get("presets", "filament"), true); - this->set_filament_preset(0, filaments.get_selected_preset().name); - for (int i = 1; i < 1000; ++ i) { - char name[64]; - sprintf(name, "filament_%d", i); - if (! config.has("presets", name)) - break; - this->set_filament_preset(i, name); - } - printers.select_preset_by_name(config.get("presets", "printer"), true); -} - -// Export selections (current print, current filaments, current printer) into config.ini -void PresetBundle::export_selections(AppConfig &config) -{ - config.set("presets", "print", prints .get_selected_preset().name); - config.set("presets", "filament", filaments.get_selected_preset().name); - for (int i = 1; i < 1000; ++ i) { - char name[64]; - sprintf(name, "filament_%d", i); - config.set("presets", name, filament_presets[i]); - } - config.set("presets", "printer", printers .get_selected_preset().name); -} - -bool PresetBundle::load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible) -{ - bool loaded_compatible = m_bitmapCompatible ->LoadFile( - wxString::FromUTF8(Slic3r::var(path_bitmap_compatible).c_str()), wxBITMAP_TYPE_PNG); - bool loaded_incompatible = m_bitmapIncompatible->LoadFile( - wxString::FromUTF8(Slic3r::var(path_bitmap_incompatible).c_str()), wxBITMAP_TYPE_PNG); - if (loaded_compatible) { - prints .set_bitmap_compatible(m_bitmapCompatible); - filaments.set_bitmap_compatible(m_bitmapCompatible); - printers .set_bitmap_compatible(m_bitmapCompatible); - } - if (loaded_incompatible) { - prints .set_bitmap_compatible(m_bitmapIncompatible); - filaments.set_bitmap_compatible(m_bitmapIncompatible); - printers .set_bitmap_compatible(m_bitmapIncompatible); - } - return loaded_compatible && loaded_incompatible; -} - -DynamicPrintConfig PresetBundle::full_config() const -{ - DynamicPrintConfig out; - out.apply(FullPrintConfig()); - out.apply(this->prints.get_edited_preset().config); - out.apply(this->printers.get_edited_preset().config); - - auto *nozzle_diameter = dynamic_cast(out.option("nozzle_diameter")); - size_t num_extruders = nozzle_diameter->values.size(); - - if (num_extruders <= 1) { - out.apply(this->filaments.get_edited_preset().config); - } else { - // Retrieve filament presets and build a single config object for them. - // First collect the filament configurations based on the user selection of this->filament_presets. - std::vector filament_configs; - for (const std::string &filament_preset_name : this->filament_presets) - filament_configs.emplace_back(&this->filaments.find_preset(filament_preset_name, true)->config); - while (filament_configs.size() < num_extruders) - filament_configs.emplace_back(&this->filaments.first_visible().config); - // Option values to set a ConfigOptionVector from. - std::vector filament_opts(num_extruders, nullptr); - // loop through options and apply them to the resulting config. - for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { - // Get a destination option. - ConfigOption *opt_dst = out.option(key, false); - if (opt_dst->is_scalar()) { - // Get an option, do not create if it does not exist. - const ConfigOption *opt_src = filament_configs.front()->option(key); - if (opt_src != nullptr) - opt_dst->set(opt_src); - } else { - // Setting a vector value from all filament_configs. - for (size_t i = 0; i < filament_opts.size(); ++ i) - filament_opts[i] = filament_configs[i]->option(key); - static_cast(opt_dst)->set(filament_opts); - } - } - } - - static const char *keys[] = { "perimeter", "infill", "solid_infill", "support_material", "support_material_interface" }; - for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++ i) { - std::string key = std::string(keys[i]) + "_extruder"; - auto *opt = dynamic_cast(out.option(key, false)); - assert(opt != nullptr); - opt->value = std::min(opt->value, std::min(0, int(num_extruders) - 1)); - } - - return out; -} - -// Load an external config file containing the print, filament and printer presets. -// Instead of a config file, a G-code may be loaded containing the full set of parameters. -// In the future the configuration will likely be read from an AMF file as well. -// If the file is loaded successfully, its print / filament / printer profiles will be activated. -void PresetBundle::load_config_file(const std::string &path) -{ - // 1) Initialize a config from full defaults. - DynamicPrintConfig config; - config.apply(FullPrintConfig()); - - // 2) Try to load the config file. - // Throw exceptions with reasonable messages if something goes wrong. - Slic3r::load_config_file(config, path); - - // 3) Create a name from the file name. - // Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles. - std::string name = boost::filesystem::path(path).filename().string(); - - // 3) If the loading succeeded, split and load the config into print / filament / printer settings. - // First load the print and printer presets. - for (size_t i_group = 0; i_group < 2; ++ i_group) { - PresetCollection &presets = (i_group == 0) ? this->prints : this->printers; - presets.load_preset(path, name, config).is_external = true; - } - - // Now load the filaments. If there are multiple filament presets, split them and load them. - auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); - auto *filament_diameter = dynamic_cast(config.option("filament_diameter")); - size_t num_extruders = std::min(nozzle_diameter->values.size(), filament_diameter->values.size()); - if (num_extruders <= 1) { - this->filaments.load_preset(path, name, config).is_external = true; - this->filament_presets.clear(); - this->filament_presets.emplace_back(name); - } else { - // Split the filament presets, load each of them separately. - std::vector configs(num_extruders, this->filaments.default_preset().config); - // loop through options and scatter them into configs. - for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { - const ConfigOption *other_opt = config.option(key, false); - if (other_opt == nullptr) - continue; - if (other_opt->is_scalar()) { - for (size_t i = 0; i < configs.size(); ++ i) - configs[i].option(key, false)->set(other_opt); - } else { - for (size_t i = 0; i < configs.size(); ++ i) - static_cast(configs[i].option(key, false))->set_at(other_opt, 0, i); - } - } - // Load the configs into this->filaments and make them active. - filament_presets.clear(); - for (size_t i = 0; i < configs.size(); ++ i) { - char suffix[64]; - if (i == 0) - suffix[0] = 0; - else - sprintf(suffix, " (%d)", i); - // Load all filament presets, but only select the first one in the preset dialog. - this->filaments.load_preset(path, name + suffix, configs[i], i == 0).is_external = true; - filament_presets.emplace_back(name + suffix); - } - } -} - std::string PresetCollection::name() const { switch (this->type()) { @@ -606,359 +387,4 @@ std::string PresetCollection::name() const } } -// Load a config bundle file, into presets and store the loaded presets into separate files -// of the local configuration directory. -size_t PresetBundle::load_configbundle(const std::string &path) -{ - // 1) Read the complete config file into a boost::property_tree. - namespace pt = boost::property_tree; - pt::ptree tree; - boost::nowide::ifstream ifs(path); - pt::read_ini(ifs, tree); - - // 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files. - std::vector loaded_prints; - std::vector loaded_filaments; - std::vector loaded_printers; - std::string active_print; - std::vector active_filaments; - std::string active_printer; - size_t presets_loaded = 0; - for (const auto §ion : tree) { - PresetCollection *presets = nullptr; - std::vector *loaded = nullptr; - std::string preset_name; - if (boost::starts_with(section.first, "print:")) { - presets = &prints; - loaded = &loaded_prints; - preset_name = section.first.substr(6); - } else if (boost::starts_with(section.first, "filament:")) { - presets = &filaments; - loaded = &loaded_filaments; - preset_name = section.first.substr(9); - } else if (boost::starts_with(section.first, "printer:")) { - presets = &printers; - loaded = &loaded_printers; - preset_name = section.first.substr(8); - } else if (section.first == "presets") { - // Load the names of the active presets. - for (auto &kvp : section.second) { - if (kvp.first == "print") { - active_print = kvp.second.data(); - } else if (boost::starts_with(kvp.first, "filament")) { - int idx = 0; - if (kvp.first == "filament" || sscanf(kvp.first.c_str(), "filament_%d", &idx) == 1) { - if (int(active_filaments.size()) <= idx) - active_filaments.resize(idx + 1, std::string()); - active_filaments[idx] = kvp.second.data(); - } - } else if (kvp.first == "printer") { - active_printer = kvp.second.data(); - } - } - } else if (section.first == "settings") { - // Load the settings. - for (auto &kvp : section.second) { - if (kvp.first == "autocenter") { - } - } - } else - // Ignore an unknown section. - continue; - if (presets != nullptr) { - // Load the print, filament or printer preset. - DynamicPrintConfig config(presets->default_preset().config); - for (auto &kvp : section.second) - config.set_deserialize(kvp.first, kvp.second.data()); - // Load the preset into the list of presets, save it to disk. - presets->load_preset(Slic3r::config_path(presets->name(), preset_name), preset_name, config, false).save(); - ++ presets_loaded; - } - } - - // 3) Activate the presets. - if (! active_print.empty()) - prints.select_preset_by_name(active_print, true); - if (! active_printer.empty()) - printers.select_preset_by_name(active_printer, true); - // Activate the first filament preset. - if (! active_filaments.empty() && ! active_filaments.front().empty()) - filaments.select_preset_by_name(active_filaments.front(), true); - - this->update_multi_material_filament_presets(); - for (size_t i = 0; i < std::min(this->filament_presets.size(), active_filaments.size()); ++ i) - this->filament_presets[i] = filaments.first_visible().name; - return presets_loaded; -} - -void PresetBundle::update_multi_material_filament_presets() -{ - // Verify and select the filament presets. - auto *nozzle_diameter = static_cast(printers.get_selected_preset().config.option("nozzle_diameter")); - size_t num_extruders = nozzle_diameter->values.size(); - // Verify validity of the current filament presets. - for (size_t i = 0; i < std::min(this->filament_presets.size(), num_extruders); ++ i) - this->filament_presets[i] = this->filaments.find_preset(this->filament_presets[i], true)->name; - // Append the rest of filament presets. - if (this->filament_presets.size() < num_extruders) - this->filament_presets.resize(num_extruders, this->filaments.first_visible().name); -} - -void PresetBundle::export_configbundle(const std::string &path, const DynamicPrintConfig &settings) -{ - boost::nowide::ofstream c; - c.open(path, std::ios::out | std::ios::trunc); - - // Put a comment at the first line including the time stamp and Slic3r version. - c << "# " << Slic3r::header_slic3r_generated() << std::endl; - - // Export the print, filament and printer profiles. - for (size_t i_group = 0; i_group < 3; ++ i_group) { - const PresetCollection &presets = (i_group == 0) ? this->prints : (i_group == 1) ? this->filaments : this->printers; - for (const Preset &preset : presets()) { - if (preset.is_default || preset.is_external) - // Only export the common presets, not external files or the default preset. - continue; - c << "[" << presets.name() << ":" << preset.name << "]" << std::endl; - for (const std::string &opt_key : preset.config.keys()) - c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl; - } - } - - // Export the names of the active presets. - c << "[presets]" << std::endl; - c << "print = " << this->prints.get_selected_preset().name << std::endl; - c << "printer = " << this->printers.get_selected_preset().name << std::endl; - for (size_t i = 0; i < this->filament_presets.size(); ++ i) { - char suffix[64]; - if (i > 0) - sprintf(suffix, "_%d", i); - else - suffix[0] = 0; - c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl; - } - - // Export the following setting values from the provided setting repository. - static const char *settings_keys[] = { "autocenter" }; - c << "[presets]" << std::endl; - c << "print = " << this->prints.get_selected_preset().name << std::endl; - for (size_t i = 0; i < sizeof(settings_keys) / sizeof(settings_keys[0]); ++ i) - c << settings_keys[i] << " = " << settings.serialize(settings_keys[i]) << std::endl; - - c.close(); -} - -// Set the filament preset name. As the name could come from the UI selection box, -// an optional "(modified)" suffix will be removed from the filament name. -void PresetBundle::set_filament_preset(size_t idx, const std::string &name) -{ - if (idx >= filament_presets.size()) - filament_presets.resize(idx + 1, filaments.default_preset().name); - filament_presets[idx] = remove_suffix_modified(name); -} - -static inline int hex_digit_to_int(const char c) -{ - return - (c >= '0' && c <= '9') ? int(c - '0') : - (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : - (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; -} - -static inline bool parse_color(const std::string &scolor, unsigned char *rgb_out) -{ - rgb_out[0] = rgb_out[1] = rgb_out[2] = 0; - const char *c = scolor.data() + 1; - if (scolor.size() != 7 || scolor.front() != '#') - return false; - for (size_t i = 0; i < 3; ++ i) { - int digit1 = hex_digit_to_int(*c ++); - int digit2 = hex_digit_to_int(*c ++); - if (digit1 == -1 || digit2 == -1) - return false; - rgb_out[i] = (unsigned char)(digit1 * 16 + digit2); - } - return true; -} - -void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui) -{ - if (ui == nullptr) - return; - - unsigned char rgb[3]; - std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); - if (! parse_color(extruder_color, rgb)) - // Extruder color is not defined. - extruder_color.clear(); - - // Fill in the list from scratch. - ui->Freeze(); - ui->Clear(); - for (size_t i = this->filaments().front().is_visible ? 0 : 1; i < this->filaments().size(); ++ i) { - const Preset &preset = this->filaments.preset(i); - if (! preset.is_visible) - continue; - bool selected = this->filament_presets[idx_extruder] == preset.name; - // Assign an extruder color to the selected item if the extruder color is defined. - std::string filament_rgb = preset.config.opt_string("filament_colour", 0); - std::string extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb; - wxBitmap *bitmap = nullptr; - if (filament_rgb == extruder_rgb) { - auto it = m_mapColorToBitmap.find(filament_rgb); - if (it == m_mapColorToBitmap.end()) { - // Create the bitmap. - parse_color(filament_rgb, rgb); - wxImage image(24, 16); - image.SetRGB(wxRect(0, 0, 24, 16), rgb[0], rgb[1], rgb[2]); - m_mapColorToBitmap[filament_rgb] = bitmap = new wxBitmap(image); - } else { - bitmap = it->second; - } - } else { - std::string bitmap_key = filament_rgb + extruder_rgb; - auto it = m_mapColorToBitmap.find(bitmap_key); - if (it == m_mapColorToBitmap.end()) { - // Create the bitmap. - wxImage image(24, 16); - parse_color(extruder_rgb, rgb); - image.SetRGB(wxRect(0, 0, 16, 16), rgb[0], rgb[1], rgb[2]); - parse_color(filament_rgb, rgb); - image.SetRGB(wxRect(16, 0, 8, 16), rgb[0], rgb[1], rgb[2]); - m_mapColorToBitmap[filament_rgb] = bitmap = new wxBitmap(image); - } else { - bitmap = it->second; - } - } - - ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (bitmap == 0) ? wxNullBitmap : *bitmap); - if (selected) - ui->SetSelection(ui->GetCount() - 1); - } - ui->Thaw(); -} - -// Update the colors preview at the platter extruder combo box. -void PresetBundle::update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui) -{ - this->update_platter_filament_ui(idx_extruder, ui); - return; - - unsigned char rgb[3]; - std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); - if (! parse_color(extruder_color, rgb)) - // Extruder color is not defined. - extruder_color.clear(); - - ui->Freeze(); - for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { - std::string preset_name = ui->GetString(ui_id).utf8_str().data(); - size_t filament_preset_id = size_t(ui->GetClientData(ui_id)); - const Preset *filament_preset = filaments.find_preset(preset_name, false); - assert(filament_preset != nullptr); - // Assign an extruder color to the selected item if the extruder color is defined. - std::string filament_rgb = filament_preset->config.opt_string("filament_colour", 0); - std::string extruder_rgb = (int(ui_id) == ui->GetSelection() && ! extruder_color.empty()) ? extruder_color : filament_rgb; - wxBitmap *bitmap = nullptr; - if (filament_rgb == extruder_rgb) { - auto it = m_mapColorToBitmap.find(filament_rgb); - if (it == m_mapColorToBitmap.end()) { - // Create the bitmap. - parse_color(filament_rgb, rgb); - wxImage image(24, 16); - image.SetRGB(wxRect(0, 0, 24, 16), rgb[0], rgb[1], rgb[2]); - m_mapColorToBitmap[filament_rgb] = new wxBitmap(image); - } else { - bitmap = it->second; - } - } else { - std::string bitmap_key = filament_rgb + extruder_rgb; - auto it = m_mapColorToBitmap.find(bitmap_key); - if (it == m_mapColorToBitmap.end()) { - // Create the bitmap. - wxImage image(24, 16); - parse_color(extruder_rgb, rgb); - image.SetRGB(wxRect(0, 0, 16, 16), rgb[0], rgb[1], rgb[2]); - parse_color(filament_rgb, rgb); - image.SetRGB(wxRect(16, 0, 8, 16), rgb[0], rgb[1], rgb[2]); - m_mapColorToBitmap[filament_rgb] = new wxBitmap(image); - } else { - bitmap = it->second; - } - } - ui->SetItemBitmap(ui_id, *bitmap); - } - ui->Thaw(); -} - -const std::vector& PresetBundle::print_options() -{ - const char *opts[] = { - "layer_height", "first_layer_height", "perimeters", "spiral_vase", "top_solid_layers", "bottom_solid_layers", - "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs", - "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "external_fill_pattern", - "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", - "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "max_print_speed", - "max_volumetric_speed", "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", - "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed", - "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", - "bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", - "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", - "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers", - "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", - "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", - "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", - "support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius", - "extruder_clearance_height", "gcode_comments", "output_filename_format", "post_process", "perimeter_extruder", - "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", - "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", - "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", - "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects", - "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", - "wipe_tower_width", "wipe_tower_per_color_wipe" - }; - static std::vector s_opts; - if (s_opts.empty()) - s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); - return s_opts; -} - -const std::vector& PresetBundle::filament_options() -{ - const char *opts[] = { - "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed", - "extrusion_multiplier", "filament_density", "filament_cost", "temperature", "first_layer_temperature", "bed_temperature", - "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", - "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", - "end_filament_gcode" - }; - static std::vector s_opts; - if (s_opts.empty()) - s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); - return s_opts; -} - -const std::vector& PresetBundle::printer_options() -{ - const char *opts[] = { - "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", - "octoprint_host", "octoprint_apikey", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", - "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", - "nozzle_diameter", "extruder_offset", "retract_length", "retract_lift", "retract_speed", "deretract_speed", - "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "retract_layer_change", "wipe", - "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour", "printer_notes" - }; - static std::vector s_opts; - if (s_opts.empty()) - s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); - return s_opts; -} - -void PresetBundle::set_default_suppressed(bool default_suppressed) -{ - prints.set_default_suppressed(default_suppressed); - filaments.set_default_suppressed(default_suppressed); - printers.set_default_suppressed(default_suppressed); -} - } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp index 110057204..8bad02ce8 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -6,8 +6,6 @@ #include "../../libslic3r/libslic3r.h" #include "../../libslic3r/PrintConfig.hpp" -#include "AppConfig.hpp" - class wxBitmap; class wxChoice; class wxBitmapComboBox; @@ -74,6 +72,13 @@ public: // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection. bool operator<(const Preset &other) const { return this->name < other.name; } + +protected: + friend class PresetCollection; + friend class PresetBundle; + static void load_config_file(DynamicPrintConfig &config, const std::string &path); + static const std::string& suffix_modified(); + static std::string remove_suffix_modified(const std::string &name); }; // Collections of presets of the same type (one of the Print, Filament or Printer type). @@ -195,80 +200,6 @@ private: std::string m_dir_path; }; -// Bundle of Print + Filament + Printer presets. -class PresetBundle -{ -public: - PresetBundle(); - ~PresetBundle(); - - void setup_directories(); - - // Load ini files of all types (print, filament, printer) from the provided directory path. - void load_presets(const std::string &dir_path); - - // Load selections (current print, current filaments, current printer) from config.ini - // This is done just once on application start up. - void load_selections(const AppConfig &config); - // Export selections (current print, current filaments, current printer) into config.ini - void export_selections(AppConfig &config); - - PresetCollection prints; - PresetCollection filaments; - PresetCollection printers; - // Filament preset names for a multi-extruder or multi-material print. - // extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size() - std::vector filament_presets; - - DynamicPrintConfig full_config() const; - - // Load an external config file containing the print, filament and printer presets. - // Instead of a config file, a G-code may be loaded containing the full set of parameters. - // In the future the configuration will likely be read from an AMF file as well. - // If the file is loaded successfully, its print / filament / printer profiles will be activated. - void load_config_file(const std::string &path); - - // Load a config bundle file, into presets and store the loaded presets into separate files - // of the local configuration directory. - // Load settings into the provided settings instance. - // Activate the presets stored in the config bundle. - // Returns the number of presets loaded successfully. - size_t load_configbundle(const std::string &path); - - // Export a config bundle file containing all the presets and the names of the active presets. - void export_configbundle(const std::string &path, const DynamicPrintConfig &settings); - - // Update a filament selection combo box on the platter for an idx_extruder. - void update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui); - // Update the colors preview at the platter extruder combo box. - void update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui); - - static const std::vector& print_options(); - static const std::vector& filament_options(); - static const std::vector& printer_options(); - - // Enable / disable the "- default -" preset. - void set_default_suppressed(bool default_suppressed); - - // Set the filament preset name. As the name could come from the UI selection box, - // an optional "(modified)" suffix will be removed from the filament name. - void set_filament_preset(size_t idx, const std::string &name); - - // Read out the number of extruders from an active printer preset, - // update size and content of filament_presets. - void update_multi_material_filament_presets(); - -private: - bool load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible); - - // Indicator, that the preset is compatible with the selected printer. - wxBitmap *m_bitmapCompatible; - // Indicator, that the preset is NOT compatible with the selected printer. - wxBitmap *m_bitmapIncompatible; - // Caching color bitmaps for the - std::map m_mapColorToBitmap; -}; - } // namespace Slic3r #endif /* slic3r_Preset_hpp_ */ diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp new file mode 100644 index 000000000..4c229edab --- /dev/null +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -0,0 +1,603 @@ +#include "PresetBundle.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../../libslic3r/Utils.hpp" + +#include + +namespace Slic3r { + +PresetBundle::PresetBundle() : + prints(Preset::TYPE_PRINT, print_options()), + filaments(Preset::TYPE_FILAMENT, filament_options()), + printers(Preset::TYPE_PRINTER, printer_options()), + m_bitmapCompatible(new wxBitmap), + m_bitmapIncompatible(new wxBitmap) +{ + ::wxInitAllImageHandlers(); + + // Create the ID config keys, as they are not part of the Static print config classes. + this->prints.preset(0).config.opt_string("print_settings_id", true); + this->filaments.preset(0).config.opt_string("filament_settings_id", true); + this->printers.preset(0).config.opt_string("print_settings_id", true); + // Create the "compatible printers" keys, as they are not part of the Static print config classes. + this->filaments.preset(0).config.optptr("compatible_printers", true); + this->prints.preset(0).config.optptr("compatible_printers", true); + + this->prints .load_bitmap_default("cog.png"); + this->filaments.load_bitmap_default("spool.png"); + this->printers .load_bitmap_default("printer_empty.png"); + + // FIXME select some icons indicating compatibility. + this->load_compatible_bitmaps("cog.png", "cog.png"); +} + +PresetBundle::~PresetBundle() +{ + assert(m_bitmapCompatible != nullptr); + assert(m_bitmapIncompatible != nullptr); + delete m_bitmapCompatible; + m_bitmapCompatible = nullptr; + delete m_bitmapIncompatible; + m_bitmapIncompatible = nullptr; + for (std::pair &bitmap : m_mapColorToBitmap) + delete bitmap.second; +} + +void PresetBundle::setup_directories() +{ + boost::filesystem::path dir = boost::filesystem::canonical(Slic3r::data_dir()); + if (! boost::filesystem::is_directory(dir)) + throw std::runtime_error(std::string("datadir does not exist: ") + Slic3r::data_dir()); + std::initializer_list names = { "print", "filament", "printer" }; + for (const char *name : names) { + boost::filesystem::path subdir = (dir / subdir).make_preferred(); + if (! boost::filesystem::is_directory(subdir) && + ! boost::filesystem::create_directory(subdir)) + throw std::runtime_error(std::string("Slic3r was unable to create its data directory at ") + subdir.string()); + } +} + +void PresetBundle::load_presets(const std::string &dir_path) +{ + this->prints .load_presets(dir_path, "print"); + this->filaments.load_presets(dir_path, "filament"); + this->printers .load_presets(dir_path, "printer"); + this->update_multi_material_filament_presets(); +} + +// Load selections (current print, current filaments, current printer) from config.ini +// This is done just once on application start up. +void PresetBundle::load_selections(const AppConfig &config) +{ + prints.select_preset_by_name(config.get("presets", "print"), true); + filaments.select_preset_by_name(config.get("presets", "filament"), true); + this->set_filament_preset(0, filaments.get_selected_preset().name); + for (int i = 1; i < 1000; ++ i) { + char name[64]; + sprintf(name, "filament_%d", i); + if (! config.has("presets", name)) + break; + this->set_filament_preset(i, name); + } + printers.select_preset_by_name(config.get("presets", "printer"), true); +} + +// Export selections (current print, current filaments, current printer) into config.ini +void PresetBundle::export_selections(AppConfig &config) +{ + config.set("presets", "print", prints .get_selected_preset().name); + config.set("presets", "filament", filaments.get_selected_preset().name); + for (int i = 1; i < 1000; ++ i) { + char name[64]; + sprintf(name, "filament_%d", i); + config.set("presets", name, filament_presets[i]); + } + config.set("presets", "printer", printers .get_selected_preset().name); +} + +bool PresetBundle::load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible) +{ + bool loaded_compatible = m_bitmapCompatible ->LoadFile( + wxString::FromUTF8(Slic3r::var(path_bitmap_compatible).c_str()), wxBITMAP_TYPE_PNG); + bool loaded_incompatible = m_bitmapIncompatible->LoadFile( + wxString::FromUTF8(Slic3r::var(path_bitmap_incompatible).c_str()), wxBITMAP_TYPE_PNG); + if (loaded_compatible) { + prints .set_bitmap_compatible(m_bitmapCompatible); + filaments.set_bitmap_compatible(m_bitmapCompatible); + printers .set_bitmap_compatible(m_bitmapCompatible); + } + if (loaded_incompatible) { + prints .set_bitmap_compatible(m_bitmapIncompatible); + filaments.set_bitmap_compatible(m_bitmapIncompatible); + printers .set_bitmap_compatible(m_bitmapIncompatible); + } + return loaded_compatible && loaded_incompatible; +} + +DynamicPrintConfig PresetBundle::full_config() const +{ + DynamicPrintConfig out; + out.apply(FullPrintConfig()); + out.apply(this->prints.get_edited_preset().config); + out.apply(this->printers.get_edited_preset().config); + + auto *nozzle_diameter = dynamic_cast(out.option("nozzle_diameter")); + size_t num_extruders = nozzle_diameter->values.size(); + + if (num_extruders <= 1) { + out.apply(this->filaments.get_edited_preset().config); + } else { + // Retrieve filament presets and build a single config object for them. + // First collect the filament configurations based on the user selection of this->filament_presets. + std::vector filament_configs; + for (const std::string &filament_preset_name : this->filament_presets) + filament_configs.emplace_back(&this->filaments.find_preset(filament_preset_name, true)->config); + while (filament_configs.size() < num_extruders) + filament_configs.emplace_back(&this->filaments.first_visible().config); + // Option values to set a ConfigOptionVector from. + std::vector filament_opts(num_extruders, nullptr); + // loop through options and apply them to the resulting config. + for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { + // Get a destination option. + ConfigOption *opt_dst = out.option(key, false); + if (opt_dst->is_scalar()) { + // Get an option, do not create if it does not exist. + const ConfigOption *opt_src = filament_configs.front()->option(key); + if (opt_src != nullptr) + opt_dst->set(opt_src); + } else { + // Setting a vector value from all filament_configs. + for (size_t i = 0; i < filament_opts.size(); ++ i) + filament_opts[i] = filament_configs[i]->option(key); + static_cast(opt_dst)->set(filament_opts); + } + } + } + + static const char *keys[] = { "perimeter", "infill", "solid_infill", "support_material", "support_material_interface" }; + for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++ i) { + std::string key = std::string(keys[i]) + "_extruder"; + auto *opt = dynamic_cast(out.option(key, false)); + assert(opt != nullptr); + opt->value = std::min(opt->value, std::min(0, int(num_extruders) - 1)); + } + + return out; +} + +// Load an external config file containing the print, filament and printer presets. +// Instead of a config file, a G-code may be loaded containing the full set of parameters. +// In the future the configuration will likely be read from an AMF file as well. +// If the file is loaded successfully, its print / filament / printer profiles will be activated. +void PresetBundle::load_config_file(const std::string &path) +{ + // 1) Initialize a config from full defaults. + DynamicPrintConfig config; + config.apply(FullPrintConfig()); + + // 2) Try to load the config file. + // Throw exceptions with reasonable messages if something goes wrong. + Preset::load_config_file(config, path); + + // 3) Create a name from the file name. + // Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles. + std::string name = boost::filesystem::path(path).filename().string(); + + // 3) If the loading succeeded, split and load the config into print / filament / printer settings. + // First load the print and printer presets. + for (size_t i_group = 0; i_group < 2; ++ i_group) { + PresetCollection &presets = (i_group == 0) ? this->prints : this->printers; + presets.load_preset(path, name, config).is_external = true; + } + + // Now load the filaments. If there are multiple filament presets, split them and load them. + auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); + auto *filament_diameter = dynamic_cast(config.option("filament_diameter")); + size_t num_extruders = std::min(nozzle_diameter->values.size(), filament_diameter->values.size()); + if (num_extruders <= 1) { + this->filaments.load_preset(path, name, config).is_external = true; + this->filament_presets.clear(); + this->filament_presets.emplace_back(name); + } else { + // Split the filament presets, load each of them separately. + std::vector configs(num_extruders, this->filaments.default_preset().config); + // loop through options and scatter them into configs. + for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { + const ConfigOption *other_opt = config.option(key, false); + if (other_opt == nullptr) + continue; + if (other_opt->is_scalar()) { + for (size_t i = 0; i < configs.size(); ++ i) + configs[i].option(key, false)->set(other_opt); + } else { + for (size_t i = 0; i < configs.size(); ++ i) + static_cast(configs[i].option(key, false))->set_at(other_opt, 0, i); + } + } + // Load the configs into this->filaments and make them active. + filament_presets.clear(); + for (size_t i = 0; i < configs.size(); ++ i) { + char suffix[64]; + if (i == 0) + suffix[0] = 0; + else + sprintf(suffix, " (%d)", i); + // Load all filament presets, but only select the first one in the preset dialog. + this->filaments.load_preset(path, name + suffix, configs[i], i == 0).is_external = true; + filament_presets.emplace_back(name + suffix); + } + } +} + +// Load a config bundle file, into presets and store the loaded presets into separate files +// of the local configuration directory. +size_t PresetBundle::load_configbundle(const std::string &path) +{ + // 1) Read the complete config file into a boost::property_tree. + namespace pt = boost::property_tree; + pt::ptree tree; + boost::nowide::ifstream ifs(path); + pt::read_ini(ifs, tree); + + // 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files. + std::vector loaded_prints; + std::vector loaded_filaments; + std::vector loaded_printers; + std::string active_print; + std::vector active_filaments; + std::string active_printer; + size_t presets_loaded = 0; + for (const auto §ion : tree) { + PresetCollection *presets = nullptr; + std::vector *loaded = nullptr; + std::string preset_name; + if (boost::starts_with(section.first, "print:")) { + presets = &prints; + loaded = &loaded_prints; + preset_name = section.first.substr(6); + } else if (boost::starts_with(section.first, "filament:")) { + presets = &filaments; + loaded = &loaded_filaments; + preset_name = section.first.substr(9); + } else if (boost::starts_with(section.first, "printer:")) { + presets = &printers; + loaded = &loaded_printers; + preset_name = section.first.substr(8); + } else if (section.first == "presets") { + // Load the names of the active presets. + for (auto &kvp : section.second) { + if (kvp.first == "print") { + active_print = kvp.second.data(); + } else if (boost::starts_with(kvp.first, "filament")) { + int idx = 0; + if (kvp.first == "filament" || sscanf(kvp.first.c_str(), "filament_%d", &idx) == 1) { + if (int(active_filaments.size()) <= idx) + active_filaments.resize(idx + 1, std::string()); + active_filaments[idx] = kvp.second.data(); + } + } else if (kvp.first == "printer") { + active_printer = kvp.second.data(); + } + } + } else if (section.first == "settings") { + // Load the settings. + for (auto &kvp : section.second) { + if (kvp.first == "autocenter") { + } + } + } else + // Ignore an unknown section. + continue; + if (presets != nullptr) { + // Load the print, filament or printer preset. + DynamicPrintConfig config(presets->default_preset().config); + for (auto &kvp : section.second) + config.set_deserialize(kvp.first, kvp.second.data()); + // Load the preset into the list of presets, save it to disk. + presets->load_preset(Slic3r::config_path(presets->name(), preset_name), preset_name, config, false).save(); + ++ presets_loaded; + } + } + + // 3) Activate the presets. + if (! active_print.empty()) + prints.select_preset_by_name(active_print, true); + if (! active_printer.empty()) + printers.select_preset_by_name(active_printer, true); + // Activate the first filament preset. + if (! active_filaments.empty() && ! active_filaments.front().empty()) + filaments.select_preset_by_name(active_filaments.front(), true); + + this->update_multi_material_filament_presets(); + for (size_t i = 0; i < std::min(this->filament_presets.size(), active_filaments.size()); ++ i) + this->filament_presets[i] = filaments.first_visible().name; + return presets_loaded; +} + +void PresetBundle::update_multi_material_filament_presets() +{ + // Verify and select the filament presets. + auto *nozzle_diameter = static_cast(printers.get_selected_preset().config.option("nozzle_diameter")); + size_t num_extruders = nozzle_diameter->values.size(); + // Verify validity of the current filament presets. + for (size_t i = 0; i < std::min(this->filament_presets.size(), num_extruders); ++ i) + this->filament_presets[i] = this->filaments.find_preset(this->filament_presets[i], true)->name; + // Append the rest of filament presets. + if (this->filament_presets.size() < num_extruders) + this->filament_presets.resize(num_extruders, this->filaments.first_visible().name); +} + +void PresetBundle::export_configbundle(const std::string &path, const DynamicPrintConfig &settings) +{ + boost::nowide::ofstream c; + c.open(path, std::ios::out | std::ios::trunc); + + // Put a comment at the first line including the time stamp and Slic3r version. + c << "# " << Slic3r::header_slic3r_generated() << std::endl; + + // Export the print, filament and printer profiles. + for (size_t i_group = 0; i_group < 3; ++ i_group) { + const PresetCollection &presets = (i_group == 0) ? this->prints : (i_group == 1) ? this->filaments : this->printers; + for (const Preset &preset : presets()) { + if (preset.is_default || preset.is_external) + // Only export the common presets, not external files or the default preset. + continue; + c << "[" << presets.name() << ":" << preset.name << "]" << std::endl; + for (const std::string &opt_key : preset.config.keys()) + c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl; + } + } + + // Export the names of the active presets. + c << "[presets]" << std::endl; + c << "print = " << this->prints.get_selected_preset().name << std::endl; + c << "printer = " << this->printers.get_selected_preset().name << std::endl; + for (size_t i = 0; i < this->filament_presets.size(); ++ i) { + char suffix[64]; + if (i > 0) + sprintf(suffix, "_%d", i); + else + suffix[0] = 0; + c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl; + } + + // Export the following setting values from the provided setting repository. + static const char *settings_keys[] = { "autocenter" }; + c << "[presets]" << std::endl; + c << "print = " << this->prints.get_selected_preset().name << std::endl; + for (size_t i = 0; i < sizeof(settings_keys) / sizeof(settings_keys[0]); ++ i) + c << settings_keys[i] << " = " << settings.serialize(settings_keys[i]) << std::endl; + + c.close(); +} + +// Set the filament preset name. As the name could come from the UI selection box, +// an optional "(modified)" suffix will be removed from the filament name. +void PresetBundle::set_filament_preset(size_t idx, const std::string &name) +{ + if (idx >= filament_presets.size()) + filament_presets.resize(idx + 1, filaments.default_preset().name); + filament_presets[idx] = Preset::remove_suffix_modified(name); +} + +static inline int hex_digit_to_int(const char c) +{ + return + (c >= '0' && c <= '9') ? int(c - '0') : + (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : + (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; +} + +static inline bool parse_color(const std::string &scolor, unsigned char *rgb_out) +{ + rgb_out[0] = rgb_out[1] = rgb_out[2] = 0; + const char *c = scolor.data() + 1; + if (scolor.size() != 7 || scolor.front() != '#') + return false; + for (size_t i = 0; i < 3; ++ i) { + int digit1 = hex_digit_to_int(*c ++); + int digit2 = hex_digit_to_int(*c ++); + if (digit1 == -1 || digit2 == -1) + return false; + rgb_out[i] = (unsigned char)(digit1 * 16 + digit2); + } + return true; +} + +void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui) +{ + if (ui == nullptr) + return; + + unsigned char rgb[3]; + std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); + if (! parse_color(extruder_color, rgb)) + // Extruder color is not defined. + extruder_color.clear(); + + // Fill in the list from scratch. + ui->Freeze(); + ui->Clear(); + for (size_t i = this->filaments().front().is_visible ? 0 : 1; i < this->filaments().size(); ++ i) { + const Preset &preset = this->filaments.preset(i); + if (! preset.is_visible) + continue; + bool selected = this->filament_presets[idx_extruder] == preset.name; + // Assign an extruder color to the selected item if the extruder color is defined. + std::string filament_rgb = preset.config.opt_string("filament_colour", 0); + std::string extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb; + wxBitmap *bitmap = nullptr; + if (filament_rgb == extruder_rgb) { + auto it = m_mapColorToBitmap.find(filament_rgb); + if (it == m_mapColorToBitmap.end()) { + // Create the bitmap. + parse_color(filament_rgb, rgb); + wxImage image(24, 16); + image.SetRGB(wxRect(0, 0, 24, 16), rgb[0], rgb[1], rgb[2]); + m_mapColorToBitmap[filament_rgb] = bitmap = new wxBitmap(image); + } else { + bitmap = it->second; + } + } else { + std::string bitmap_key = filament_rgb + extruder_rgb; + auto it = m_mapColorToBitmap.find(bitmap_key); + if (it == m_mapColorToBitmap.end()) { + // Create the bitmap. + wxImage image(24, 16); + parse_color(extruder_rgb, rgb); + image.SetRGB(wxRect(0, 0, 16, 16), rgb[0], rgb[1], rgb[2]); + parse_color(filament_rgb, rgb); + image.SetRGB(wxRect(16, 0, 8, 16), rgb[0], rgb[1], rgb[2]); + m_mapColorToBitmap[filament_rgb] = bitmap = new wxBitmap(image); + } else { + bitmap = it->second; + } + } + + ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), (bitmap == 0) ? wxNullBitmap : *bitmap); + if (selected) + ui->SetSelection(ui->GetCount() - 1); + } + ui->Thaw(); +} + +// Update the colors preview at the platter extruder combo box. +void PresetBundle::update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui) +{ + this->update_platter_filament_ui(idx_extruder, ui); + return; + + unsigned char rgb[3]; + std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); + if (! parse_color(extruder_color, rgb)) + // Extruder color is not defined. + extruder_color.clear(); + + ui->Freeze(); + for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { + std::string preset_name = ui->GetString(ui_id).utf8_str().data(); + size_t filament_preset_id = size_t(ui->GetClientData(ui_id)); + const Preset *filament_preset = filaments.find_preset(preset_name, false); + assert(filament_preset != nullptr); + // Assign an extruder color to the selected item if the extruder color is defined. + std::string filament_rgb = filament_preset->config.opt_string("filament_colour", 0); + std::string extruder_rgb = (int(ui_id) == ui->GetSelection() && ! extruder_color.empty()) ? extruder_color : filament_rgb; + wxBitmap *bitmap = nullptr; + if (filament_rgb == extruder_rgb) { + auto it = m_mapColorToBitmap.find(filament_rgb); + if (it == m_mapColorToBitmap.end()) { + // Create the bitmap. + parse_color(filament_rgb, rgb); + wxImage image(24, 16); + image.SetRGB(wxRect(0, 0, 24, 16), rgb[0], rgb[1], rgb[2]); + m_mapColorToBitmap[filament_rgb] = new wxBitmap(image); + } else { + bitmap = it->second; + } + } else { + std::string bitmap_key = filament_rgb + extruder_rgb; + auto it = m_mapColorToBitmap.find(bitmap_key); + if (it == m_mapColorToBitmap.end()) { + // Create the bitmap. + wxImage image(24, 16); + parse_color(extruder_rgb, rgb); + image.SetRGB(wxRect(0, 0, 16, 16), rgb[0], rgb[1], rgb[2]); + parse_color(filament_rgb, rgb); + image.SetRGB(wxRect(16, 0, 8, 16), rgb[0], rgb[1], rgb[2]); + m_mapColorToBitmap[filament_rgb] = new wxBitmap(image); + } else { + bitmap = it->second; + } + } + ui->SetItemBitmap(ui_id, *bitmap); + } + ui->Thaw(); +} + +const std::vector& PresetBundle::print_options() +{ + const char *opts[] = { + "layer_height", "first_layer_height", "perimeters", "spiral_vase", "top_solid_layers", "bottom_solid_layers", + "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs", + "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "external_fill_pattern", + "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", + "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "max_print_speed", + "max_volumetric_speed", "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", + "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed", + "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", + "bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", + "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", + "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers", + "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", + "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", + "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", + "support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius", + "extruder_clearance_height", "gcode_comments", "output_filename_format", "post_process", "perimeter_extruder", + "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", + "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", + "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", + "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects", + "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", + "wipe_tower_width", "wipe_tower_per_color_wipe" + }; + static std::vector s_opts; + if (s_opts.empty()) + s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); + return s_opts; +} + +const std::vector& PresetBundle::filament_options() +{ + const char *opts[] = { + "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed", + "extrusion_multiplier", "filament_density", "filament_cost", "temperature", "first_layer_temperature", "bed_temperature", + "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", + "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", + "end_filament_gcode" + }; + static std::vector s_opts; + if (s_opts.empty()) + s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); + return s_opts; +} + +const std::vector& PresetBundle::printer_options() +{ + const char *opts[] = { + "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", + "octoprint_host", "octoprint_apikey", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", + "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", + "nozzle_diameter", "extruder_offset", "retract_length", "retract_lift", "retract_speed", "deretract_speed", + "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "retract_layer_change", "wipe", + "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour", "printer_notes" + }; + static std::vector s_opts; + if (s_opts.empty()) + s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); + return s_opts; +} + +void PresetBundle::set_default_suppressed(bool default_suppressed) +{ + prints.set_default_suppressed(default_suppressed); + filaments.set_default_suppressed(default_suppressed); + printers.set_default_suppressed(default_suppressed); +} + +} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/xs/src/slic3r/GUI/PresetBundle.hpp new file mode 100644 index 000000000..b00e148f1 --- /dev/null +++ b/xs/src/slic3r/GUI/PresetBundle.hpp @@ -0,0 +1,85 @@ +#ifndef slic3r_PresetBundle_hpp_ +#define slic3r_PresetBundle_hpp_ + +#include "AppConfig.hpp" +#include "Preset.hpp" + +namespace Slic3r { + +// Bundle of Print + Filament + Printer presets. +class PresetBundle +{ +public: + PresetBundle(); + ~PresetBundle(); + + void setup_directories(); + + // Load ini files of all types (print, filament, printer) from the provided directory path. + void load_presets(const std::string &dir_path); + + // Load selections (current print, current filaments, current printer) from config.ini + // This is done just once on application start up. + void load_selections(const AppConfig &config); + // Export selections (current print, current filaments, current printer) into config.ini + void export_selections(AppConfig &config); + + PresetCollection prints; + PresetCollection filaments; + PresetCollection printers; + // Filament preset names for a multi-extruder or multi-material print. + // extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size() + std::vector filament_presets; + + DynamicPrintConfig full_config() const; + + // Load an external config file containing the print, filament and printer presets. + // Instead of a config file, a G-code may be loaded containing the full set of parameters. + // In the future the configuration will likely be read from an AMF file as well. + // If the file is loaded successfully, its print / filament / printer profiles will be activated. + void load_config_file(const std::string &path); + + // Load a config bundle file, into presets and store the loaded presets into separate files + // of the local configuration directory. + // Load settings into the provided settings instance. + // Activate the presets stored in the config bundle. + // Returns the number of presets loaded successfully. + size_t load_configbundle(const std::string &path); + + // Export a config bundle file containing all the presets and the names of the active presets. + void export_configbundle(const std::string &path, const DynamicPrintConfig &settings); + + // Update a filament selection combo box on the platter for an idx_extruder. + void update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui); + // Update the colors preview at the platter extruder combo box. + void update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui); + + static const std::vector& print_options(); + static const std::vector& filament_options(); + static const std::vector& printer_options(); + + // Enable / disable the "- default -" preset. + void set_default_suppressed(bool default_suppressed); + + // Set the filament preset name. As the name could come from the UI selection box, + // an optional "(modified)" suffix will be removed from the filament name. + void set_filament_preset(size_t idx, const std::string &name); + + // Read out the number of extruders from an active printer preset, + // update size and content of filament_presets. + void update_multi_material_filament_presets(); + +private: + bool load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible); + + // Indicator, that the preset is compatible with the selected printer. + wxBitmap *m_bitmapCompatible; + // Indicator, that the preset is NOT compatible with the selected printer. + wxBitmap *m_bitmapIncompatible; + // Caching color bitmaps for the + std::map m_mapColorToBitmap; +}; + +} // namespace Slic3r + +#endif /* slic3r_PresetBundle_hpp_ */ diff --git a/xs/xsp/GUI_AppConfig.xsp b/xs/xsp/GUI_AppConfig.xsp new file mode 100644 index 000000000..c91711635 --- /dev/null +++ b/xs/xsp/GUI_AppConfig.xsp @@ -0,0 +1,29 @@ + +%module{Slic3r::XS}; + +%{ +#include +#include "slic3r/GUI/AppConfig.hpp" +%} + +%name{Slic3r::GUI::AppConfig} class AppConfig { + AppConfig(); + ~AppConfig(); + + void reset(); + void set_defaults(); + + void load(); + void save(); + bool exists(); + + std::string get(char *name); + void set(char *name, char *value); + bool has(char *section); + + std::string get_last_dir(); + void update_config_dir(char *dir); + void update_skein_dir(char *dir); + std::string get_last_output_dir(const char *alt = ""); + void update_last_output_dir(char *dir); +}; diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp index 79392c27c..16dc3399e 100644 --- a/xs/xsp/GUI_Preset.xsp +++ b/xs/xsp/GUI_Preset.xsp @@ -3,6 +3,7 @@ %{ #include #include "slic3r/GUI/Preset.hpp" +#include "slic3r/GUI/PresetBundle.hpp" %} %name{Slic3r::GUI::Preset} class Preset { From 337f6c5808133df1c32ee6b7cb4f3a9047a97d4c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 30 Oct 2017 18:55:31 +0100 Subject: [PATCH 10/83] Deleted the config-bundle-to-config.pl tool, it will be replaced with direct loading of a config bundle as a config file the same way it has been done for the config from a G-code. --- utils/config-bundle-to-config.pl | 58 -------------------------------- 1 file changed, 58 deletions(-) delete mode 100755 utils/config-bundle-to-config.pl diff --git a/utils/config-bundle-to-config.pl b/utils/config-bundle-to-config.pl deleted file mode 100755 index e1d7f6143..000000000 --- a/utils/config-bundle-to-config.pl +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/perl -# This script extracts a full active config from a config bundle. -# (Often users reporting issues don't attach plain configs, but -# bundles...) - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Getopt::Long qw(:config no_auto_abbrev); -use Slic3r; -use Slic3r::Test; -$|++; - -my %opt = (); -{ - my %options = ( - 'help' => sub { usage() }, - 'output=s' => \$opt{output}, - ); - GetOptions(%options) or usage(1); - $ARGV[0] or usage(1); -} - -($ARGV[0] && $opt{output}) or usage(1); - -{ - my $bundle_ini = Slic3r::Config->read_ini($ARGV[0]) - or die "Failed to read $ARGV[0]\n"; - - my $config_ini = { _ => {} }; - foreach my $section (qw(print filament printer)) { - my $preset_name = $bundle_ini->{presets}{$section}; - $preset_name =~ s/\.ini$//; - my $preset = $bundle_ini->{"$section:$preset_name"} - or die "Failed to find preset $preset_name in bundle\n"; - $config_ini->{_}{$_} = $preset->{$_} for keys %$preset; - } - - Slic3r::Config->write_ini($opt{output}, $config_ini); -} - - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: config-bundle-to-config.pl --output config.ini bundle.ini -EOF - exit ($exit_code || 0); -} - -__END__ From 95c284c76431fcd3e21764b206ba6100c3179c1e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 1 Nov 2017 19:30:05 +0100 Subject: [PATCH 11/83] Next step of Perl to C++ configuration layer conversion. --- xs/CMakeLists.txt | 2 + xs/src/libslic3r/Config.cpp | 25 ++- xs/src/libslic3r/Config.hpp | 109 +++++++++--- xs/src/libslic3r/PrintConfig.cpp | 272 +++++------------------------ xs/src/libslic3r/Utils.hpp | 2 +- xs/src/libslic3r/utils.cpp | 8 - xs/src/slic3r/GUI/AppConfig.cpp | 2 +- xs/src/slic3r/GUI/AppConfig.hpp | 7 +- xs/src/slic3r/GUI/Preset.cpp | 162 +++++++++++++---- xs/src/slic3r/GUI/Preset.hpp | 26 ++- xs/src/slic3r/GUI/PresetBundle.cpp | 175 +++++++++---------- xs/src/slic3r/GUI/PresetBundle.hpp | 8 +- xs/src/xsinit.h | 2 + xs/xsp/GUI_AppConfig.xsp | 1 + xs/xsp/GUI_Preset.xsp | 7 +- 15 files changed, 414 insertions(+), 394 deletions(-) diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 562399b47..dac9993c3 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -170,6 +170,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/GLShader.hpp ${LIBDIR}/slic3r/GUI/Preset.cpp ${LIBDIR}/slic3r/GUI/Preset.hpp + ${LIBDIR}/slic3r/GUI/PresetBundle.cpp + ${LIBDIR}/slic3r/GUI/PresetBundle.hpp ${LIBDIR}/slic3r/GUI/GUI.cpp ${LIBDIR}/slic3r/GUI/GUI.hpp ) diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index 3871253fd..cb8dd98f7 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #if defined(_WIN32) && !defined(setenv) && defined(_putenv_s) @@ -329,11 +328,15 @@ void ConfigBase::load(const std::string &file) void ConfigBase::load_from_ini(const std::string &file) { - namespace pt = boost::property_tree; - pt::ptree tree; + boost::property_tree::ptree tree; boost::nowide::ifstream ifs(file); - pt::read_ini(ifs, tree); - for (const pt::ptree::value_type &v : tree) { + boost::property_tree::read_ini(ifs, tree); + this->load(tree); +} + +void ConfigBase::load(const boost::property_tree::ptree &tree) +{ + for (const boost::property_tree::ptree::value_type &v : tree) { try { t_config_option_key opt_key = v.first; this->set_deserialize(opt_key, v.second.get_value()); @@ -428,6 +431,18 @@ void ConfigBase::save(const std::string &file) const c.close(); } +bool DynamicConfig::operator==(const DynamicConfig &rhs) const +{ + t_options_map::const_iterator it1 = this->options.begin(); + t_options_map::const_iterator it1_end = this->options.end(); + t_options_map::const_iterator it2 = rhs.options.begin(); + t_options_map::const_iterator it2_end = rhs.options.end(); + for (; it1 != it1_end && it2 != it2_end; ++ it1, ++ it2) + if (*it1->second != *it2->second) + return false; + return it1 == it1_end && it2 == it2_end; +} + ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) { t_options_map::iterator it = options.find(opt_key); diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 5cf24d8ed..2228b8e7c 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -13,6 +13,8 @@ #include "libslic3r.h" #include "Point.hpp" +#include + namespace Slic3r { // Name of the configuration option. @@ -62,7 +64,7 @@ enum ConfigOptionType { // A generic value of a configuration option. class ConfigOption { public: - virtual ~ConfigOption() {}; + virtual ~ConfigOption() {} virtual ConfigOptionType type() const = 0; virtual std::string serialize() const = 0; @@ -94,7 +96,7 @@ public: throw std::runtime_error("ConfigOptionSingle: Assigning an incompatible type"); assert(dynamic_cast*>(rhs)); this->value = static_cast*>(rhs)->value; - }; + } bool operator==(const ConfigOption &rhs) const override { @@ -122,6 +124,9 @@ public: // This function is useful to split values from multiple extrder / filament settings into separate configurations. virtual void set_at(const ConfigOption *rhs, size_t i, size_t j) = 0; + virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0; + + protected: // Used to verify type compatibility when assigning to / from a scalar ConfigOption. ConfigOptionType scalar_type() const { return static_cast(this->type() - coVectorType); } @@ -132,6 +137,9 @@ template class ConfigOptionVector : public ConfigOptionVectorBase { public: + ConfigOptionVector() {} + explicit ConfigOptionVector(size_t n, const T &value) : values(n, value) {} + explicit ConfigOptionVector(std::initializer_list il) : values(std::move(il)) {} std::vector values; void set(const ConfigOption *rhs) override @@ -194,6 +202,31 @@ public: const T& get_at(size_t i) const { return const_cast*>(this)->get_at(i); } + // Resize this vector by duplicating the last value. + // If the current vector is empty, the default value is used instead. + void resize(size_t n, const ConfigOption *opt_default = nullptr) override + { + assert(opt_default == nullptr || opt_default->is_vector()); + assert(opt_default == nullptr || dynamic_cast>(opt_default)); + assert(! this->values.empty() || opt_default != nullptr); + if (n == 0) + this->values.clear(); + else if (n < this->values.size()) + this->values.erase(this->values.begin() + n, this->values.end()); + else if (n > this->values.size()) + if (this->values.empty()) { + if (opt_default == nullptr) { + throw std::runtime_error("ConfigOptionVector::resize(): No default value provided."); + if (opt_default->type() != this->type()) + throw std::runtime_error("ConfigOptionVector::resize(): Extending with an incompatible type."); + this->values.resize(n, static_cast*>(opt_default)->values.front()); + } else { + // Resize by duplicating the last value. + this->values.resize(n, this->values.back()); + } + } + } + bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) @@ -209,8 +242,8 @@ public: class ConfigOptionFloat : public ConfigOptionSingle { public: - ConfigOptionFloat() : ConfigOptionSingle(0) {}; - explicit ConfigOptionFloat(double _value) : ConfigOptionSingle(_value) {}; + ConfigOptionFloat() : ConfigOptionSingle(0) {} + explicit ConfigOptionFloat(double _value) : ConfigOptionSingle(_value) {} ConfigOptionType type() const override { return coFloat; } double getFloat() const override { return this->value; } @@ -242,6 +275,10 @@ public: class ConfigOptionFloats : public ConfigOptionVector { public: + ConfigOptionFloats() : ConfigOptionVector() {} + explicit ConfigOptionFloats(size_t n, double value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionFloats(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} + ConfigOptionType type() const override { return coFloats; } ConfigOption* clone() const override { return new ConfigOptionFloats(*this); } bool operator==(const ConfigOptionFloats &rhs) const { return this->values == rhs.values; } @@ -254,7 +291,7 @@ public: ss << *it; } return ss.str(); - }; + } std::vector vserialize() const override { @@ -293,13 +330,13 @@ public: class ConfigOptionInt : public ConfigOptionSingle { public: - ConfigOptionInt() : ConfigOptionSingle(0) {}; - explicit ConfigOptionInt(int value) : ConfigOptionSingle(value) {}; - explicit ConfigOptionInt(double _value) : ConfigOptionSingle(int(floor(_value + 0.5))) {}; + ConfigOptionInt() : ConfigOptionSingle(0) {} + explicit ConfigOptionInt(int value) : ConfigOptionSingle(value) {} + explicit ConfigOptionInt(double _value) : ConfigOptionSingle(int(floor(_value + 0.5))) {} ConfigOptionType type() const override { return coInt; } - int getInt() const override { return this->value; }; - void setInt(int val) { this->value = val; }; + int getInt() const override { return this->value; } + void setInt(int val) { this->value = val; } ConfigOption* clone() const override { return new ConfigOptionInt(*this); } bool operator==(const ConfigOptionInt &rhs) const { return this->value == rhs.value; } @@ -328,6 +365,10 @@ public: class ConfigOptionInts : public ConfigOptionVector { public: + ConfigOptionInts() : ConfigOptionVector() {} + explicit ConfigOptionInts(size_t n, int value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionInts(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} + ConfigOptionType type() const override { return coInts; } ConfigOption* clone() const override { return new ConfigOptionInts(*this); } ConfigOptionInts& operator=(const ConfigOption *opt) { this->set(opt); return *this; } @@ -373,8 +414,8 @@ public: class ConfigOptionString : public ConfigOptionSingle { public: - ConfigOptionString() : ConfigOptionSingle("") {}; - explicit ConfigOptionString(std::string _value) : ConfigOptionSingle(_value) {}; + ConfigOptionString() : ConfigOptionSingle("") {} + explicit ConfigOptionString(const std::string &value) : ConfigOptionSingle(value) {} ConfigOptionType type() const override { return coString; } ConfigOption* clone() const override { return new ConfigOptionString(*this); } @@ -397,6 +438,10 @@ public: class ConfigOptionStrings : public ConfigOptionVector { public: + ConfigOptionStrings() : ConfigOptionVector() {} + explicit ConfigOptionStrings(size_t n, const std::string &value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionStrings(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} + ConfigOptionType type() const override { return coStrings; } ConfigOption* clone() const override { return new ConfigOptionStrings(*this); } ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; } @@ -423,8 +468,8 @@ public: class ConfigOptionPercent : public ConfigOptionFloat { public: - ConfigOptionPercent() : ConfigOptionFloat(0) {}; - explicit ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {}; + ConfigOptionPercent() : ConfigOptionFloat(0) {} + explicit ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {} ConfigOptionType type() const override { return coPercent; } ConfigOption* clone() const override { return new ConfigOptionPercent(*this); } @@ -454,6 +499,10 @@ public: class ConfigOptionPercents : public ConfigOptionFloats { public: + ConfigOptionPercents() : ConfigOptionFloats() {} + explicit ConfigOptionPercents(size_t n, double value) : ConfigOptionFloats(n, value) {} + explicit ConfigOptionPercents(std::initializer_list il) : ConfigOptionFloats(std::move(il)) {} + ConfigOptionType type() const override { return coPercents; } ConfigOption* clone() const override { return new ConfigOptionPercents(*this); } ConfigOptionPercents& operator=(const ConfigOption *opt) { this->set(opt); return *this; } @@ -504,9 +553,9 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent { public: bool percent; - ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {}; - explicit ConfigOptionFloatOrPercent(double _value, bool _percent) : ConfigOptionPercent(_value), percent(_percent) {}; - + ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {} + explicit ConfigOptionFloatOrPercent(double _value, bool _percent) : ConfigOptionPercent(_value), percent(_percent) {} + ConfigOptionType type() const override { return coFloatOrPercent; } ConfigOption* clone() const override { return new ConfigOptionFloatOrPercent(*this); } ConfigOptionFloatOrPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; } @@ -544,8 +593,8 @@ public: class ConfigOptionPoint : public ConfigOptionSingle { public: - ConfigOptionPoint() : ConfigOptionSingle(Pointf(0,0)) {}; - explicit ConfigOptionPoint(const Pointf &value) : ConfigOptionSingle(value) {}; + ConfigOptionPoint() : ConfigOptionSingle(Pointf(0,0)) {} + explicit ConfigOptionPoint(const Pointf &value) : ConfigOptionSingle(value) {} ConfigOptionType type() const override { return coPoint; } ConfigOption* clone() const override { return new ConfigOptionPoint(*this); } @@ -576,6 +625,10 @@ public: class ConfigOptionPoints : public ConfigOptionVector { public: + ConfigOptionPoints() : ConfigOptionVector() {} + explicit ConfigOptionPoints(size_t n, const Pointf &value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionPoints(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} + ConfigOptionType type() const override { return coPoints; } ConfigOption* clone() const override { return new ConfigOptionPoints(*this); } ConfigOptionPoints& operator=(const ConfigOption *opt) { this->set(opt); return *this; } @@ -629,11 +682,11 @@ public: class ConfigOptionBool : public ConfigOptionSingle { public: - ConfigOptionBool() : ConfigOptionSingle(false) {}; - explicit ConfigOptionBool(bool _value) : ConfigOptionSingle(_value) {}; + ConfigOptionBool() : ConfigOptionSingle(false) {} + explicit ConfigOptionBool(bool _value) : ConfigOptionSingle(_value) {} ConfigOptionType type() const override { return coBool; } - bool getBool() const override { return this->value; }; + bool getBool() const override { return this->value; } ConfigOption* clone() const override { return new ConfigOptionBool(*this); } ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; } bool operator==(const ConfigOptionBool &rhs) const { return this->value == rhs.value; } @@ -654,6 +707,10 @@ public: class ConfigOptionBools : public ConfigOptionVector { public: + ConfigOptionBools() : ConfigOptionVector() {} + explicit ConfigOptionBools(size_t n, bool value) : ConfigOptionVector(n, (unsigned char)value) {} + explicit ConfigOptionBools(std::initializer_list il) { values.reserve(il.size()); for (bool b : il) values.emplace_back((unsigned char)b); } + ConfigOptionType type() const override { return coBools; } ConfigOption* clone() const override { return new ConfigOptionBools(*this); } ConfigOptionBools& operator=(const ConfigOption *opt) { this->set(opt); return *this; } @@ -711,8 +768,8 @@ class ConfigOptionEnum : public ConfigOptionSingle { public: // by default, use the first value (0) of the T enum type - ConfigOptionEnum() : ConfigOptionSingle(static_cast(0)) {}; - explicit ConfigOptionEnum(T _value) : ConfigOptionSingle(_value) {}; + ConfigOptionEnum() : ConfigOptionSingle(static_cast(0)) {} + explicit ConfigOptionEnum(T _value) : ConfigOptionSingle(_value) {} ConfigOptionType type() const override { return coEnum; } ConfigOption* clone() const override { return new ConfigOptionEnum(*this); } @@ -954,6 +1011,7 @@ public: void load(const std::string &file); void load_from_ini(const std::string &file); void load_from_gcode(const std::string &file); + void load(const boost::property_tree::ptree &tree); void save(const std::string &file) const; private: @@ -986,6 +1044,9 @@ public: return *this; } + bool operator==(const DynamicConfig &rhs) const; + bool operator!=(const DynamicConfig &rhs) const { return ! (*this == rhs); } + void swap(DynamicConfig &other) { std::swap(this->options, other.options); diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 8986bb59d..fa5ea4a8d 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -23,14 +23,7 @@ PrintConfigDef::PrintConfigDef() def = this->add("bed_shape", coPoints); def->label = "Bed shape"; - { - ConfigOptionPoints* opt = new ConfigOptionPoints(); - opt->values.push_back(Pointf(0,0)); - opt->values.push_back(Pointf(200,0)); - opt->values.push_back(Pointf(200,200)); - opt->values.push_back(Pointf(0,200)); - def->default_value = opt; - } + def->default_value = new ConfigOptionPoints { Pointf(0,0), Pointf(200,0), Pointf(200,200), Pointf(0,200) }; def = this->add("bed_temperature", coInts); def->label = "Other layers"; @@ -39,11 +32,7 @@ PrintConfigDef::PrintConfigDef() def->full_label = "Bed temperature"; def->min = 0; def->max = 300; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 0 }; def = this->add("before_layer_gcode", coString); def->label = "Before layer change G-code"; @@ -87,11 +76,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "bridge-fan-speed=i@"; def->min = 0; def->max = 100; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(100); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 100 }; def = this->add("bridge_flow_ratio", coFloat); def->label = "Bridge flow ratio"; @@ -139,11 +124,7 @@ PrintConfigDef::PrintConfigDef() def->label = "Enable auto cooling"; def->tooltip = "This flag enables the automatic cooling logic that adjusts print speed and fan speed according to layer printing time."; def->cli = "cooling!"; - { - ConfigOptionBools* opt = new ConfigOptionBools(); - opt->values.push_back(true); - def->default_value = opt; - } + def->default_value = new ConfigOptionBools { true }; def = this->add("default_acceleration", coFloat); def->label = "Default"; @@ -160,11 +141,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "disable-fan-first-layers=i@"; def->min = 0; def->max = 1000; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(3); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 3 }; def = this->add("dont_support_bridges", coBool); def->label = "Don't support bridges"; @@ -207,11 +184,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 120; - { - ConfigOptionStrings* opt = new ConfigOptionStrings(); - opt->values.push_back("; Filament-specific end gcode \n;END gcode for filament\n"); - def->default_value = opt; - } + def->default_value = new ConfigOptionStrings { "; Filament-specific end gcode \n;END gcode for filament\n" }; def = this->add("ensure_vertical_shell_thickness", coBool); def->label = "Ensure vertical shell thickness"; @@ -305,24 +278,15 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "This is only used in the Slic3r interface as a visual help."; def->cli = "extruder-color=s@"; def->gui_type = "color"; - { - ConfigOptionStrings* opt = new ConfigOptionStrings(); - // Empty string means no color assigned yet. -// opt->values.push_back("#FFFFFF"); - opt->values.push_back(""); - def->default_value = opt; - } + // Empty string means no color assigned yet. + def->default_value = new ConfigOptionStrings { "" }; def = this->add("extruder_offset", coPoints); def->label = "Extruder offset"; def->tooltip = "If your firmware doesn't handle the extruder displacement you need the G-code to take it into account. This option lets you specify the displacement of each extruder with respect to the first one. It expects positive coordinates (they will be subtracted from the XY coordinate)."; def->sidetext = "mm"; def->cli = "extruder-offset=s@"; - { - ConfigOptionPoints* opt = new ConfigOptionPoints(); - opt->values.push_back(Pointf(0,0)); - def->default_value = opt; - } + def->default_value = new ConfigOptionPoints { Pointf(0,0) }; def = this->add("extrusion_axis", coString); def->label = "Extrusion axis"; @@ -334,11 +298,7 @@ PrintConfigDef::PrintConfigDef() def->label = "Extrusion multiplier"; def->tooltip = "This factor changes the amount of flow proportionally. You may need to tweak this setting to get nice surface finish and correct single wall widths. Usual values are between 0.9 and 1.1. If you think you need to change this more, check filament diameter and your firmware E steps."; def->cli = "extrusion-multiplier=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(1); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 1. }; def = this->add("extrusion_width", coFloatOrPercent); def->label = "Default extrusion width"; @@ -352,11 +312,7 @@ PrintConfigDef::PrintConfigDef() def->label = "Keep fan always on"; def->tooltip = "If this is enabled, fan will never be disabled and will be kept running at least at its minimum speed. Useful for PLA, harmful for ABS."; def->cli = "fan-always-on!"; - { - ConfigOptionBools* opt = new ConfigOptionBools(); - opt->values.push_back(false); - def->default_value = opt; - } + def->default_value = new ConfigOptionBools { false }; def = this->add("fan_below_layer_time", coInts); def->label = "Enable fan if layer print time is below"; @@ -366,22 +322,14 @@ PrintConfigDef::PrintConfigDef() def->width = 60; def->min = 0; def->max = 1000; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(60); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 60 }; def = this->add("filament_colour", coStrings); def->label = "Color"; def->tooltip = "This is only used in the Slic3r interface as a visual help."; def->cli = "filament-color=s@"; def->gui_type = "color"; - { - ConfigOptionStrings* opt = new ConfigOptionStrings(); - opt->values.push_back("#FFFFFF"); - def->default_value = opt; - } + def->default_value = new ConfigOptionStrings { "#29b2b2" }; def = this->add("filament_notes", coStrings); def->label = "Filament notes"; @@ -390,11 +338,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 130; - { - ConfigOptionStrings* opt = new ConfigOptionStrings(); - opt->values.push_back(""); - def->default_value = opt; - } + def->default_value = new ConfigOptionStrings { "" }; def = this->add("filament_max_volumetric_speed", coFloats); def->label = "Max volumetric speed"; @@ -402,11 +346,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "mm³/s"; def->cli = "filament-max-volumetric-speed=f@"; def->min = 0; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0.f); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("filament_diameter", coFloats); def->label = "Diameter"; @@ -414,11 +354,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "mm"; def->cli = "filament-diameter=f@"; def->min = 0; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(3); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 3. }; def = this->add("filament_density", coFloats); def->label = "Density"; @@ -426,11 +362,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "g/cm^3"; def->cli = "filament-density=f@"; def->min = 0; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("filament_type", coStrings); def->label = "Filament type"; @@ -447,21 +379,13 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("EDGE"); def->enum_values.push_back("NGEN"); def->enum_values.push_back("PVA"); - { - ConfigOptionStrings* opt = new ConfigOptionStrings(); - opt->values.push_back("PLA"); - def->default_value = opt; - } + def->default_value = new ConfigOptionStrings { "PLA" }; def = this->add("filament_soluble", coBools); def->label = "Soluble material"; def->tooltip = "Soluble material is most likely used for a soluble support."; def->cli = "filament-soluble!"; - { - ConfigOptionBools* opt = new ConfigOptionBools(); - opt->values.push_back(false); - def->default_value = opt; - } + def->default_value = new ConfigOptionBools { false }; def = this->add("filament_cost", coFloats); def->label = "Cost"; @@ -469,15 +393,11 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "money/kg"; def->cli = "filament-cost=f@"; def->min = 0; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } - - def = this->add("filament_settings_id", coString); - def->default_value = new ConfigOptionString(""); + def->default_value = new ConfigOptionFloats { 0. }; + def = this->add("filament_settings_id", coStrings); + def->default_value = new ConfigOptionStrings { "" }; + def = this->add("fill_angle", coFloat); def->label = "Fill angle"; def->category = "Infill"; @@ -574,11 +494,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "first-layer-bed-temperature=i@"; def->max = 0; def->max = 300; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 0 }; def = this->add("first_layer_extrusion_width", coFloatOrPercent); def->label = "First layer"; @@ -612,11 +528,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "first-layer-temperature=i@"; def->min = 0; def->max = 500; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(200); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 200 }; def = this->add("gap_fill_speed", coFloat); def->label = "Gap fill"; @@ -757,11 +669,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "max-fan-speed=i@"; def->min = 0; def->max = 100; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(100); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 100 }; def = this->add("max_layer_height", coFloats); def->label = "Max"; @@ -769,11 +677,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "mm"; def->cli = "max-layer-height=f@"; def->min = 0; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("max_print_speed", coFloat); def->label = "Max print speed"; @@ -816,11 +720,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "min-fan-speed=i@"; def->min = 0; def->max = 100; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(35); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 35 }; def = this->add("min_layer_height", coFloats); def->label = "Min"; @@ -828,11 +728,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "mm"; def->cli = "min-layer-height=f@"; def->min = 0; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0.07); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0.07 }; def = this->add("min_print_speed", coFloats); def->label = "Min print speed"; @@ -840,11 +736,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "mm/s"; def->cli = "min-print-speed=f@"; def->min = 0; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(10.); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 10. }; def = this->add("min_skirt_length", coFloat); def->label = "Minimum extrusion length"; @@ -868,11 +760,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "This is the diameter of your extruder nozzle (for example: 0.5, 0.35 etc.)"; def->sidetext = "mm"; def->cli = "nozzle-diameter=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0.5); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0.5 }; def = this->add("octoprint_apikey", coString); def->label = "API Key"; @@ -1003,32 +891,20 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "Retraction is not triggered when travel moves are shorter than this length."; def->sidetext = "mm"; def->cli = "retract-before-travel=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(2); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 2. }; def = this->add("retract_before_wipe", coPercents); def->label = "Retract amount before wipe"; def->tooltip = "With bowden extruders, it may be wise to do some amount of quick retract before doing the wipe movement."; def->sidetext = "%"; def->cli = "retract-before-wipe=s@"; - { - ConfigOptionPercents* opt = new ConfigOptionPercents(); - opt->values.push_back(0.f); - def->default_value = opt; - } + def->default_value = new ConfigOptionPercents { 0. }; def = this->add("retract_layer_change", coBools); def->label = "Retract on layer change"; def->tooltip = "This flag enforces a retraction whenever a Z move is done."; def->cli = "retract-layer-change!"; - { - ConfigOptionBools* opt = new ConfigOptionBools(); - opt->values.push_back(false); - def->default_value = opt; - } + def->default_value = new ConfigOptionBools { false }; def = this->add("retract_length", coFloats); def->label = "Length"; @@ -1036,11 +912,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "When retraction is triggered, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder)."; def->sidetext = "mm (zero to disable)"; def->cli = "retract-length=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(2); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 2. }; def = this->add("retract_length_toolchange", coFloats); def->label = "Length"; @@ -1048,22 +920,14 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "When retraction is triggered before changing tool, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder)."; def->sidetext = "mm (zero to disable)"; def->cli = "retract-length-toolchange=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(10); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 10. }; def = this->add("retract_lift", coFloats); def->label = "Lift Z"; def->tooltip = "If you set this to a positive value, Z is quickly raised every time a retraction is triggered. When using multiple extruders, only the setting for the first extruder will be considered."; def->sidetext = "mm"; def->cli = "retract-lift=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_lift_above", coFloats); def->label = "Above Z"; @@ -1071,11 +935,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "If you set this to a positive value, Z lift will only take place above the specified absolute Z. You can tune this setting for skipping lift on the first layers."; def->sidetext = "mm"; def->cli = "retract-lift-above=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_lift_below", coFloats); def->label = "Below Z"; @@ -1083,33 +943,21 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "If you set this to a positive value, Z lift will only take place below the specified absolute Z. You can tune this setting for limiting lift to the first layers."; def->sidetext = "mm"; def->cli = "retract-lift-below=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_restart_extra", coFloats); def->label = "Extra length on restart"; def->tooltip = "When the retraction is compensated after the travel move, the extruder will push this additional amount of filament. This setting is rarely needed."; def->sidetext = "mm"; def->cli = "retract-restart-extra=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_restart_extra_toolchange", coFloats); def->label = "Extra length on restart"; def->tooltip = "When the retraction is compensated after changing tool, the extruder will push this additional amount of filament."; def->sidetext = "mm"; def->cli = "retract-restart-extra-toolchange=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_speed", coFloats); def->label = "Retraction Speed"; @@ -1117,11 +965,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "The speed for retractions (it only applies to the extruder motor)."; def->sidetext = "mm/s"; def->cli = "retract-speed=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(40); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 40. }; def = this->add("deretract_speed", coFloats); def->label = "Deretraction Speed"; @@ -1129,11 +973,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "The speed for loading of a filament into extruder after retraction (it only applies to the extruder motor). If left to zero, the retraction speed is used."; def->sidetext = "mm/s"; def->cli = "retract-speed=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("seam_position", coEnum); def->label = "Seam position"; @@ -1227,11 +1067,7 @@ PrintConfigDef::PrintConfigDef() def->width = 60; def->min = 0; def->max = 1000; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(5); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 5 }; def = this->add("small_perimeter_speed", coFloatOrPercent); def->label = "Small perimeters"; @@ -1327,11 +1163,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 120; - { - ConfigOptionStrings* opt = new ConfigOptionStrings(); - opt->values.push_back("; Filament gcode\n"); - def->default_value = opt; - } + def->default_value = new ConfigOptionStrings { "; Filament gcode\n" }; def = this->add("single_extruder_multi_material", coBool); def->label = "Single Extruder Multi Material"; @@ -1522,11 +1354,7 @@ PrintConfigDef::PrintConfigDef() def->full_label = "Temperature"; def->max = 0; def->max = 500; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(200); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 200 }; def = this->add("thin_walls", coBool); def->label = "Detect thin walls"; @@ -1619,11 +1447,7 @@ PrintConfigDef::PrintConfigDef() def->label = "Wipe while retracting"; def->tooltip = "This flag will move the nozzle while retracting to minimize the possible blob on leaky extruders."; def->cli = "wipe!"; - { - ConfigOptionBools* opt = new ConfigOptionBools(); - opt->values.push_back(false); - def->default_value = opt; - } + def->default_value = new ConfigOptionBools { false }; def = this->add("wipe_tower", coBool); def->label = "Enable"; @@ -1798,7 +1622,7 @@ std::string DynamicPrintConfig::validate() { // Full print config is initialized from the defaults. FullPrintConfig fpc; - fpc.apply(*this); + fpc.apply(*this, true); // Verify this print options through the FullPrintConfig. return fpc.validate(); } diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index ac6021ff9..9e4043818 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -34,7 +34,7 @@ extern std::string normalize_utf8_nfc(const char *src); extern std::string timestamp_str(); // Standard "generated by Slic3r version xxx timestamp xxx" header string, // to be placed at the top of Slic3r generated files. -inline std::string header_slic3r_generated() { return std::string("generated by Slic3r " SLIC3R_VERSION " on ") + timestamp_str(); } +inline std::string header_slic3r_generated() { return std::string("generated by " SLIC3R_FORK_NAME " " SLIC3R_VERSION " " ) + timestamp_str(); } // Compute the next highest power of 2 of 32-bit v // http://graphics.stanford.edu/~seander/bithacks.html diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 4dcd3855a..e10e136d1 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -237,21 +237,13 @@ std::string normalize_utf8_nfc(const char *src) std::string timestamp_str() { -#if 1 - std::time_t now; - time(&now); - char buf[sizeof "0000-00-00 00:00:00"]; - strftime(buf, sizeof(buf), "%F %T", gmtime(&now)); -#else const auto now = boost::posix_time::second_clock::local_time(); const auto date = now.date(); char buf[2048]; sprintf(buf, "on %04d-%02d-%02d at %02d:%02d:%02d", - SLIC3R_VERSION, // Local date in an ANSII format. int(now.date().year()), int(now.date().month()), int(now.date().day()), int(now.time_of_day().hours()), int(now.time_of_day().minutes()), int(now.time_of_day().seconds())); -#endif return buf; } diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp index b0a94b486..b61ce89d2 100644 --- a/xs/src/slic3r/GUI/AppConfig.cpp +++ b/xs/src/slic3r/GUI/AppConfig.cpp @@ -143,7 +143,7 @@ void AppConfig::update_last_output_dir(const std::string &dir) std::string AppConfig::config_path() { - return boost::filesystem::canonical(boost::filesystem::path(Slic3r::data_dir()) / "config.ini").make_preferred().string(); + return (boost::filesystem::path(Slic3r::data_dir()) / "slic3r.ini").make_preferred().string(); } bool AppConfig::exists() diff --git a/xs/src/slic3r/GUI/AppConfig.hpp b/xs/src/slic3r/GUI/AppConfig.hpp index d64680011..c6d7766a4 100644 --- a/xs/src/slic3r/GUI/AppConfig.hpp +++ b/xs/src/slic3r/GUI/AppConfig.hpp @@ -16,9 +16,9 @@ public: // Override missing or keys with their defaults. void set_defaults(); - // Load the config.ini from a user profile directory (or a datadir, if configured). + // Load the slic3r.ini from a user profile directory (or a datadir, if configured). void load(); - // Store the config.ini into a user profile directory (or a datadir, if configured). + // Store the slic3r.ini into a user profile directory (or a datadir, if configured). void save(); // Does this config need to be saved? @@ -62,6 +62,9 @@ public: bool has(const std::string &key) const { return this->has("", key); } + void clear_section(const std::string §ion) + { m_storage[section].clear(); } + // return recent/skein_directory or recent/config_directory or empty string. std::string get_last_dir() const; void update_config_dir(const std::string &dir); diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index fcc20cd9c..d02653a4a 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -28,6 +28,39 @@ namespace Slic3r { +ConfigFileType guess_config_file_type(const boost::property_tree::ptree &tree) +{ + size_t app_config = 0; + size_t bundle = 0; + size_t config = 0; + for (const boost::property_tree::ptree::value_type &v : tree) { + if (v.second.empty()) { + if (v.first == "background_processing" || + v.first == "last_output_path" || + v.first == "no_controller" || + v.first == "no_defaults") + ++ app_config; + else if (v.first == "nozzle_diameter" || + v.first == "filament_diameter") + ++ config; + } else if (boost::algorithm::starts_with(v.first, "print:") || + boost::algorithm::starts_with(v.first, "filament:") || + boost::algorithm::starts_with(v.first, "printer:") || + v.first == "settings") + ++ bundle; + else if (v.first == "presets") { + ++ app_config; + ++ bundle; + } else if (v.first == "recent") { + for (auto &kvp : v.second) + if (kvp.first == "config_directory" || kvp.first == "skein_directory") + ++ app_config; + } + } + return (app_config > bundle && app_config > config) ? CONFIG_FILE_TYPE_APP_CONFIG : + (bundle > config) ? CONFIG_FILE_TYPE_CONFIG_BUNDLE : CONFIG_FILE_TYPE_CONFIG; +} + // Suffix to be added to a modified preset name in the combo box. static std::string g_suffix_modified = " (modified)"; const std::string& Preset::suffix_modified() @@ -43,40 +76,26 @@ std::string Preset::remove_suffix_modified(const std::string &name) name; } -// Load keys from a config file or a G-code. -// Throw exceptions with reasonable messages if something goes wrong. -void Preset::load_config_file(DynamicPrintConfig &config, const std::string &path) +void Preset::set_num_extruders(DynamicPrintConfig &config, unsigned int num_extruders) { - try { - if (boost::algorithm::iends_with(path, ".gcode") || boost::algorithm::iends_with(path, ".g")) - config.load_from_gcode(path); - else - config.load(path); - } catch (const std::ifstream::failure&) { - throw std::runtime_error(std::string("The selected preset does not exist anymore: ") + path); - } catch (const std::runtime_error&) { - throw std::runtime_error(std::string("Failed loading the preset file: ") + path); - } - - // Update new extruder fields at the printer profile. - auto keys = config.keys(); const auto &defaults = FullPrintConfig::defaults(); - if (std::find(keys.begin(), keys.end(), "nozzle_diameter") != keys.end()) { - // Loaded the Printer settings. Verify, that all extruder dependent values have enough values. - auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); - size_t num_extruders = nozzle_diameter->values.size(); - auto *deretract_speed = dynamic_cast(config.option("deretract_speed")); - deretract_speed->values.resize(num_extruders, deretract_speed->values.empty() ? - defaults.deretract_speed.values.front() : deretract_speed->values.front()); - auto *extruder_colour = dynamic_cast(config.option("extruder_colour")); - extruder_colour->values.resize(num_extruders, extruder_colour->values.empty() ? - defaults.extruder_colour.values.front() : extruder_colour->values.front()); - auto *retract_before_wipe = dynamic_cast(config.option("retract_before_wipe")); - retract_before_wipe->values.resize(num_extruders, retract_before_wipe->values.empty() ? - defaults.retract_before_wipe.values.front() : retract_before_wipe->values.front()); + for (const std::string &key : Preset::nozzle_options()) { + auto *opt = config.option(key, false); + assert(opt != nullptr); + assert(opt->is_vector()); + static_cast(opt)->resize(num_extruders, defaults.option(key)); } } +// Update new extruder fields at the printer profile. +void Preset::normalize(DynamicPrintConfig &config) +{ + auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); + if (nozzle_diameter != nullptr) + // Loaded the Printer settings. Verify, that all extruder dependent values have enough values. + set_num_extruders(config, (unsigned int)nozzle_diameter->values.size()); +} + // Load a config file, return a C++ class Slic3r::DynamicPrintConfig with $keys initialized from the config file. // In case of a "default" config item, return the default values. DynamicPrintConfig& Preset::load(const std::vector &keys) @@ -84,9 +103,17 @@ DynamicPrintConfig& Preset::load(const std::vector &keys) // Set the configuration from the defaults. Slic3r::FullPrintConfig defaults; this->config.apply_only(defaults, keys.empty() ? defaults.keys() : keys); - if (! this->is_default) + if (! this->is_default) { // Load the preset file, apply preset values on top of defaults. - load_config_file(this->config, this->file); + try { + this->config.load(this->file); + Preset::normalize(this->config); + } catch (const std::ifstream::failure&) { + throw std::runtime_error(std::string("The selected preset does not exist anymore: ") + this->file); + } catch (const std::runtime_error&) { + throw std::runtime_error(std::string("Failed loading the preset file: ") + this->file); + } + } this->loaded = true; return this->config; } @@ -111,6 +138,74 @@ bool Preset::enable_compatible(const std::string &active_printer) return this->is_visible; } +const std::vector& Preset::print_options() +{ + static std::vector s_opts { + "layer_height", "first_layer_height", "perimeters", "spiral_vase", "top_solid_layers", "bottom_solid_layers", + "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs", + "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "external_fill_pattern", + "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", + "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "max_print_speed", + "max_volumetric_speed", "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", + "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed", + "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", + "bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", + "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", + "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers", + "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", + "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", + "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", + "support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius", + "extruder_clearance_height", "gcode_comments", "output_filename_format", "post_process", "perimeter_extruder", + "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", + "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", + "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", + "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects", + "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", + "wipe_tower_width", "wipe_tower_per_color_wipe" + }; + return s_opts; +} + +const std::vector& Preset::filament_options() +{ + static std::vector s_opts { + "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed", + "extrusion_multiplier", "filament_density", "filament_cost", "temperature", "first_layer_temperature", "bed_temperature", + "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", + "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", + "end_filament_gcode" + }; + return s_opts; +} + +const std::vector& Preset::printer_options() +{ + static std::vector s_opts; + if (s_opts.empty()) { + s_opts = { + "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", + "octoprint_host", "octoprint_apikey", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", + "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", + "printer_notes" + }; + s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end()); + } + return s_opts; +} + +const std::vector& Preset::nozzle_options() +{ + // ConfigOptionFloats, ConfigOptionPercents, ConfigOptionBools, ConfigOptionStrings + static std::vector s_opts { + "nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset", + "retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_speed", "deretract_speed", + "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "wipe", + "retract_layer_change", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour" + }; + return s_opts; +} + PresetCollection::PresetCollection(Preset::Type type, const std::vector &keys) : m_type(type), m_edited_preset(type, "", false), @@ -203,6 +298,7 @@ void PresetCollection::save_current_preset(const std::string &new_name) } m_edited_preset = m_presets[m_idx_selected]; m_presets[m_idx_selected].save(); + m_presets.front().is_visible = ! m_default_suppressed || m_idx_selected > 0; } void PresetCollection::delete_current_preset() @@ -254,7 +350,7 @@ void PresetCollection::set_default_suppressed(bool default_suppressed) { if (m_default_suppressed != default_suppressed) { m_default_suppressed = default_suppressed; - m_presets.front().is_visible = ! default_suppressed || m_presets.size() > 1; + m_presets.front().is_visible = ! default_suppressed || (m_presets.size() > 1 && m_idx_selected > 0); } } @@ -320,6 +416,7 @@ bool PresetCollection::update_dirty_ui(wxItemContainer *ui) bool was_dirty = this->get_selected_preset().is_dirty; bool is_dirty = current_is_dirty(); this->get_selected_preset().is_dirty = is_dirty; + this->get_edited_preset().is_dirty = is_dirty; // 2) Update the labels. for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { std::string old_label = ui->GetString(ui_id).utf8_str().data(); @@ -345,6 +442,7 @@ Preset& PresetCollection::select_preset(size_t idx) idx = first_visible_idx(); m_idx_selected = idx; m_edited_preset = m_presets[idx]; + m_presets.front().is_visible = ! m_default_suppressed || m_idx_selected > 0; return m_presets[idx]; } diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp index 8bad02ce8..85280eccf 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -13,6 +13,16 @@ class wxItemContainer; namespace Slic3r { +enum ConfigFileType +{ + CONFIG_FILE_TYPE_UNKNOWN, + CONFIG_FILE_TYPE_APP_CONFIG, + CONFIG_FILE_TYPE_CONFIG, + CONFIG_FILE_TYPE_CONFIG_BUNDLE, +}; + +extern ConfigFileType guess_config_file_type(const boost::property_tree::ptree &tree); + class Preset { public: @@ -70,13 +80,25 @@ public: // Mark this preset as visible if it is compatible with active_printer. bool enable_compatible(const std::string &active_printer); + // Resize the extruder specific fields, initialize them with the content of the 1st extruder. + void set_num_extruders(unsigned int n) { set_num_extruders(this->config, n); } + // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection. bool operator<(const Preset &other) const { return this->name < other.name; } + static const std::vector& print_options(); + static const std::vector& filament_options(); + // Printer options contain the nozzle options. + static const std::vector& printer_options(); + // Nozzle options of the printer options. + static const std::vector& nozzle_options(); + protected: friend class PresetCollection; friend class PresetBundle; - static void load_config_file(DynamicPrintConfig &config, const std::string &path); + static void normalize(DynamicPrintConfig &config); + // Resize the extruder specific vectors () + static void set_num_extruders(DynamicPrintConfig &config, unsigned int n); static const std::string& suffix_modified(); static std::string remove_suffix_modified(const std::string &name); }; @@ -117,7 +139,7 @@ public: // Enable / disable the "- default -" preset. void set_default_suppressed(bool default_suppressed); - bool is_default_suppressed() const { return m_default_suppressed || m_presets.size() <= 1; } + bool is_default_suppressed() const { return m_default_suppressed; } // Select a preset. If an invalid index is provided, the first visible preset is selected. Preset& select_preset(size_t idx); diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 4c229edab..4abb7a88f 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -23,9 +24,9 @@ namespace Slic3r { PresetBundle::PresetBundle() : - prints(Preset::TYPE_PRINT, print_options()), - filaments(Preset::TYPE_FILAMENT, filament_options()), - printers(Preset::TYPE_PRINTER, printer_options()), + prints(Preset::TYPE_PRINT, Preset::print_options()), + filaments(Preset::TYPE_FILAMENT, Preset::filament_options()), + printers(Preset::TYPE_PRINTER, Preset::printer_options()), m_bitmapCompatible(new wxBitmap), m_bitmapIncompatible(new wxBitmap) { @@ -66,7 +67,7 @@ void PresetBundle::setup_directories() throw std::runtime_error(std::string("datadir does not exist: ") + Slic3r::data_dir()); std::initializer_list names = { "print", "filament", "printer" }; for (const char *name : names) { - boost::filesystem::path subdir = (dir / subdir).make_preferred(); + boost::filesystem::path subdir = (dir / name).make_preferred(); if (! boost::filesystem::is_directory(subdir) && ! boost::filesystem::create_directory(subdir)) throw std::runtime_error(std::string("Slic3r was unable to create its data directory at ") + subdir.string()); @@ -87,28 +88,33 @@ void PresetBundle::load_selections(const AppConfig &config) { prints.select_preset_by_name(config.get("presets", "print"), true); filaments.select_preset_by_name(config.get("presets", "filament"), true); + printers.select_preset_by_name(config.get("presets", "printer"), true); + auto *nozzle_diameter = dynamic_cast(printers.get_selected_preset().config.option("nozzle_diameter")); + size_t num_extruders = nozzle_diameter->values.size(); this->set_filament_preset(0, filaments.get_selected_preset().name); - for (int i = 1; i < 1000; ++ i) { + for (unsigned int i = 1; i < (unsigned int)num_extruders; ++ i) { char name[64]; sprintf(name, "filament_%d", i); if (! config.has("presets", name)) break; - this->set_filament_preset(i, name); + this->set_filament_preset(i, config.get("presets", name)); } - printers.select_preset_by_name(config.get("presets", "printer"), true); } // Export selections (current print, current filaments, current printer) into config.ini void PresetBundle::export_selections(AppConfig &config) { - config.set("presets", "print", prints .get_selected_preset().name); - config.set("presets", "filament", filaments.get_selected_preset().name); - for (int i = 1; i < 1000; ++ i) { + assert(filament_presets.size() >= 1); + assert(filament_presets.size() > 1 || filaments.get_selected_preset().name == filament_presets.front()); + config.clear_section("presets"); + config.set("presets", "print", prints.get_selected_preset().name); + config.set("presets", "filament", filament_presets.front()); + for (int i = 1; i < filament_presets.size(); ++i) { char name[64]; sprintf(name, "filament_%d", i); config.set("presets", name, filament_presets[i]); } - config.set("presets", "printer", printers .get_selected_preset().name); + config.set("presets", "printer", printers.get_selected_preset().name); } bool PresetBundle::load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible) @@ -175,7 +181,7 @@ DynamicPrintConfig PresetBundle::full_config() const std::string key = std::string(keys[i]) + "_extruder"; auto *opt = dynamic_cast(out.option(key, false)); assert(opt != nullptr); - opt->value = std::min(opt->value, std::min(0, int(num_extruders) - 1)); + opt->value = boost::algorithm::clamp(opt->value, 0, int(num_extruders)); } return out; @@ -186,16 +192,44 @@ DynamicPrintConfig PresetBundle::full_config() const // In the future the configuration will likely be read from an AMF file as well. // If the file is loaded successfully, its print / filament / printer profiles will be activated. void PresetBundle::load_config_file(const std::string &path) +{ + // 1) Try to load the config file into a boost property tree. + boost::property_tree::ptree tree; + try { + boost::nowide::ifstream ifs(path); + boost::property_tree::read_ini(ifs, tree); + } catch (const std::ifstream::failure&) { + throw std::runtime_error(std::string("The config file cannot be loaded: ") + path); + } catch (const std::runtime_error&) { + throw std::runtime_error(std::string("Failed loading the preset file: ") + path); + } + + // 2) Continue based on the type of the configuration file. + ConfigFileType config_file_type = guess_config_file_type(tree); + switch (config_file_type) { + case CONFIG_FILE_TYPE_UNKNOWN: + throw std::runtime_error(std::string("Unknown configuration file type: ") + path); + case CONFIG_FILE_TYPE_APP_CONFIG: + throw std::runtime_error(std::string("Invalid configuration file: ") + path + ". This is an application config file."); + case CONFIG_FILE_TYPE_CONFIG: + load_config_file_config(path, tree); + break; + case CONFIG_FILE_TYPE_CONFIG_BUNDLE: + load_config_file_config_bundle(path, tree); + break; + } +} + +// Load a config file from a boost property_tree. This is a private method called from load_config_file. +void PresetBundle::load_config_file_config(const std::string &path, const boost::property_tree::ptree &tree) { // 1) Initialize a config from full defaults. DynamicPrintConfig config; config.apply(FullPrintConfig()); + config.load(tree); + Preset::normalize(config); - // 2) Try to load the config file. - // Throw exceptions with reasonable messages if something goes wrong. - Preset::load_config_file(config, path); - - // 3) Create a name from the file name. + // 2) Create a name from the file name. // Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles. std::string name = boost::filesystem::path(path).filename().string(); @@ -245,6 +279,32 @@ void PresetBundle::load_config_file(const std::string &path) } } +// Load the active configuration of a config bundle from a boost property_tree. This is a private method called from load_config_file. +void PresetBundle::load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree) +{ + // 1) Load the config bundle into a temp data. + PresetBundle tmp_bundle; + tmp_bundle.load_configbundle(path); + + // 2) Extract active configs from the config bundle, copy them and activate them in this bundle. + if (tmp_bundle.prints.get_selected_preset().is_default) + this->prints.select_preset(0); + else { + std::string new_name = tmp_bundle.prints.get_selected_preset().name; + Preset *existing = this->prints.find_preset(new_name, false); + if (existing == nullptr) { + // Save under the new_name. + } else if (existing->config == tmp_bundle.prints.get_selected_preset().config) { + // Don't save as the config exists in the current bundle and its content is the same. + new_name.clear(); + } else { + // Generate a new unique name. + } + if (! new_name.empty()) + this->prints.load_preset(path, new_name, tmp_bundle.prints.get_selected_preset().config); + } +} + // Load a config bundle file, into presets and store the loaded presets into separate files // of the local configuration directory. size_t PresetBundle::load_configbundle(const std::string &path) @@ -333,17 +393,18 @@ size_t PresetBundle::load_configbundle(const std::string &path) void PresetBundle::update_multi_material_filament_presets() { // Verify and select the filament presets. - auto *nozzle_diameter = static_cast(printers.get_selected_preset().config.option("nozzle_diameter")); + auto *nozzle_diameter = static_cast(printers.get_edited_preset().config.option("nozzle_diameter")); size_t num_extruders = nozzle_diameter->values.size(); // Verify validity of the current filament presets. + printf("PresetBundle::update_multi_material_filament_presets, old: %d, new: %d\n", int(this->filament_presets.size()), int(num_extruders)); for (size_t i = 0; i < std::min(this->filament_presets.size(), num_extruders); ++ i) this->filament_presets[i] = this->filaments.find_preset(this->filament_presets[i], true)->name; // Append the rest of filament presets. - if (this->filament_presets.size() < num_extruders) - this->filament_presets.resize(num_extruders, this->filaments.first_visible().name); +// if (this->filament_presets.size() < num_extruders) + this->filament_presets.resize(num_extruders, this->filament_presets.empty() ? this->filaments.first_visible().name : this->filament_presets.back()); } -void PresetBundle::export_configbundle(const std::string &path, const DynamicPrintConfig &settings) +void PresetBundle::export_configbundle(const std::string &path) //, const DynamicPrintConfig &settings { boost::nowide::ofstream c; c.open(path, std::ios::out | std::ios::trunc); @@ -358,14 +419,14 @@ void PresetBundle::export_configbundle(const std::string &path, const DynamicPri if (preset.is_default || preset.is_external) // Only export the common presets, not external files or the default preset. continue; - c << "[" << presets.name() << ":" << preset.name << "]" << std::endl; + c << std::endl << "[" << presets.name() << ":" << preset.name << "]" << std::endl; for (const std::string &opt_key : preset.config.keys()) c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl; } } // Export the names of the active presets. - c << "[presets]" << std::endl; + c << std::endl << "[presets]" << std::endl; c << "print = " << this->prints.get_selected_preset().name << std::endl; c << "printer = " << this->printers.get_selected_preset().name << std::endl; for (size_t i = 0; i < this->filament_presets.size(); ++ i) { @@ -377,12 +438,13 @@ void PresetBundle::export_configbundle(const std::string &path, const DynamicPri c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl; } +#if 0 // Export the following setting values from the provided setting repository. static const char *settings_keys[] = { "autocenter" }; - c << "[presets]" << std::endl; - c << "print = " << this->prints.get_selected_preset().name << std::endl; + c << "[settings]" << std::endl; for (size_t i = 0; i < sizeof(settings_keys) / sizeof(settings_keys[0]); ++ i) c << settings_keys[i] << " = " << settings.serialize(settings_keys[i]) << std::endl; +#endif c.close(); } @@ -530,69 +592,6 @@ void PresetBundle::update_platter_filament_ui_colors(unsigned int idx_extruder, ui->Thaw(); } -const std::vector& PresetBundle::print_options() -{ - const char *opts[] = { - "layer_height", "first_layer_height", "perimeters", "spiral_vase", "top_solid_layers", "bottom_solid_layers", - "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs", - "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "external_fill_pattern", - "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", - "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "max_print_speed", - "max_volumetric_speed", "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", - "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed", - "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", - "bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", - "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", - "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers", - "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", - "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", - "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", - "support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius", - "extruder_clearance_height", "gcode_comments", "output_filename_format", "post_process", "perimeter_extruder", - "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", - "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", - "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", - "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects", - "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", - "wipe_tower_width", "wipe_tower_per_color_wipe" - }; - static std::vector s_opts; - if (s_opts.empty()) - s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); - return s_opts; -} - -const std::vector& PresetBundle::filament_options() -{ - const char *opts[] = { - "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed", - "extrusion_multiplier", "filament_density", "filament_cost", "temperature", "first_layer_temperature", "bed_temperature", - "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", - "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", - "end_filament_gcode" - }; - static std::vector s_opts; - if (s_opts.empty()) - s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); - return s_opts; -} - -const std::vector& PresetBundle::printer_options() -{ - const char *opts[] = { - "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", - "octoprint_host", "octoprint_apikey", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", - "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", - "nozzle_diameter", "extruder_offset", "retract_length", "retract_lift", "retract_speed", "deretract_speed", - "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "retract_layer_change", "wipe", - "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour", "printer_notes" - }; - static std::vector s_opts; - if (s_opts.empty()) - s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); - return s_opts; -} - void PresetBundle::set_default_suppressed(bool default_suppressed) { prints.set_default_suppressed(default_suppressed); diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/xs/src/slic3r/GUI/PresetBundle.hpp index b00e148f1..7add3055d 100644 --- a/xs/src/slic3r/GUI/PresetBundle.hpp +++ b/xs/src/slic3r/GUI/PresetBundle.hpp @@ -47,17 +47,13 @@ public: size_t load_configbundle(const std::string &path); // Export a config bundle file containing all the presets and the names of the active presets. - void export_configbundle(const std::string &path, const DynamicPrintConfig &settings); + void export_configbundle(const std::string &path); // , const DynamicPrintConfig &settings); // Update a filament selection combo box on the platter for an idx_extruder. void update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui); // Update the colors preview at the platter extruder combo box. void update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui); - static const std::vector& print_options(); - static const std::vector& filament_options(); - static const std::vector& printer_options(); - // Enable / disable the "- default -" preset. void set_default_suppressed(bool default_suppressed); @@ -70,6 +66,8 @@ public: void update_multi_material_filament_presets(); private: + void load_config_file_config(const std::string &path, const boost::property_tree::ptree &tree); + void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree); bool load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible); // Indicator, that the preset is compatible with the selected printer. diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index dcf54bdbc..49981b74b 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -54,6 +54,8 @@ extern "C" { #ifdef _MSC_VER // Undef some of the macros set by Perl , which cause compilation errors on Win32 #undef connect + #undef link + #undef unlink #undef seek #undef send #undef write diff --git a/xs/xsp/GUI_AppConfig.xsp b/xs/xsp/GUI_AppConfig.xsp index c91711635..d220c3580 100644 --- a/xs/xsp/GUI_AppConfig.xsp +++ b/xs/xsp/GUI_AppConfig.xsp @@ -16,6 +16,7 @@ void load(); void save(); bool exists(); + bool dirty(); std::string get(char *name); void set(char *name, char *value); diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp index 16dc3399e..34184a588 100644 --- a/xs/xsp/GUI_Preset.xsp +++ b/xs/xsp/GUI_Preset.xsp @@ -19,8 +19,9 @@ bool loaded() %code%{ RETVAL = THIS->loaded; %}; - Ref config_ref() %code%{ RETVAL = &THIS->config; %}; - Clone config() %code%{ RETVAL = &THIS->config; %}; + Ref config() %code%{ RETVAL = &THIS->config; %}; + + void set_num_extruders(int num_extruders); }; %name{Slic3r::GUI::PresetCollection} class PresetCollection { @@ -94,7 +95,9 @@ PresetCollection::presets_hash() void setup_directories(); void load_presets(const char *dir_path); + void load_config_file(const char *path); size_t load_configbundle(const char *path); + void export_configbundle(char *path); void set_default_suppressed(bool default_suppressed); void load_selections (AppConfig *config) %code%{ THIS->load_selections(*config); %}; From e8b6d92d4d5852e5731d21afb90362285b2dd6f2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 2 Nov 2017 16:21:34 +0100 Subject: [PATCH 12/83] Looks like the reworked C++ preferences start to work again. --- lib/Slic3r.pm | 20 +-- lib/Slic3r/Config.pm | 65 +-------- lib/Slic3r/GUI.pm | 156 ++++------------------ lib/Slic3r/GUI/Controller.pm | 2 +- lib/Slic3r/GUI/Controller/PrinterPanel.pm | 2 +- lib/Slic3r/GUI/MainFrame.pm | 112 ++++++++-------- lib/Slic3r/GUI/OptionsGroup/Field.pm | 4 + lib/Slic3r/GUI/Plater.pm | 87 +++++------- lib/Slic3r/GUI/Plater/2D.pm | 7 +- lib/Slic3r/GUI/Preferences.pm | 20 +-- lib/Slic3r/GUI/Tab.pm | 127 ++++++------------ slic3r.pl | 13 +- t/threads.t | 7 +- xs/src/libslic3r/Config.hpp | 6 +- xs/src/libslic3r/Utils.hpp | 1 - xs/src/libslic3r/utils.cpp | 7 +- xs/src/slic3r/GUI/AppConfig.cpp | 12 +- xs/src/slic3r/GUI/GUI.cpp | 81 +++++++++++ xs/src/slic3r/GUI/GUI.hpp | 4 + xs/src/slic3r/GUI/Preset.cpp | 83 +++++++----- xs/src/slic3r/GUI/Preset.hpp | 1 + xs/src/slic3r/GUI/PresetBundle.cpp | 66 ++++++--- xs/src/slic3r/GUI/PresetBundle.hpp | 9 +- xs/xsp/GUI.xsp | 3 + xs/xsp/GUI_AppConfig.xsp | 18 ++- xs/xsp/GUI_Preset.xsp | 68 ++++++++-- 26 files changed, 469 insertions(+), 512 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index dc870edf3..53b2e3663 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -23,25 +23,14 @@ sub debugf { our $loglevel = 0; # load threads before Moo as required by it -our $have_threads; BEGIN { # Test, whether the perl was compiled with ithreads support and ithreads actually work. use Config; - $have_threads = $Config{useithreads} && eval "use threads; use threads::shared; use Thread::Queue; 1"; - warn "threads.pm >= 1.96 is required, please update\n" if $have_threads && $threads::VERSION < 1.96; - - ### temporarily disable threads if using the broken Moo version use Moo; - $have_threads = 0 if $Moo::VERSION == 1.003000; - - # Disable multi threading completely by an environment value. - # This is useful for debugging as the Perl debugger does not work - # in multi-threaded context at all. - # A good interactive perl debugger is the ActiveState Komodo IDE - # or the EPIC http://www.epic-ide.org/ - $have_threads = 0 if (defined($ENV{'SLIC3R_SINGLETHREADED'}) && $ENV{'SLIC3R_SINGLETHREADED'} == 1); - print "Threading disabled\n" if !$have_threads; - + my $have_threads = $Config{useithreads} && eval "use threads; use threads::shared; use Thread::Queue; 1"; + die "Slic3r Prusa Edition requires working Perl threads.\n" if ! $have_threads; + die "threads.pm >= 1.96 is required, please update\n" if $threads::VERSION < 1.96; + die "Perl threading is broken with this Moo version: " . $Moo::VERSION . "\n" if $Moo::VERSION == 1.003000; $debug = 1 if (defined($ENV{'SLIC3R_DEBUGOUT'}) && $ENV{'SLIC3R_DEBUGOUT'} == 1); print "Debugging output enabled\n" if $debug; } @@ -164,6 +153,7 @@ sub thread_cleanup { *Slic3r::Surface::Collection::DESTROY = sub {}; *Slic3r::Print::SupportMaterial2::DESTROY = sub {}; *Slic3r::TriangleMesh::DESTROY = sub {}; + *Slic3r::GUI::AppConfig::DESTROY = sub {}; *Slic3r::GUI::PresetBundle::DESTROY = sub {}; return undef; # this prevents a "Scalars leaked" warning } diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index c5e49df6b..a9c822b96 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -12,14 +12,14 @@ use List::Util qw(first max); # The C++ counterpart is a constant singleton. our $Options = print_config_def(); -# overwrite the hard-coded readonly value (this information is not available in XS) -$Options->{threads}{readonly} = !$Slic3r::have_threads; - -# generate accessors +# Generate accessors. { no strict 'refs'; for my $opt_key (keys %$Options) { - *{$opt_key} = sub { $_[0]->get($opt_key) }; + *{$opt_key} = sub { + #print "Slic3r::Config::accessor $opt_key\n"; + $_[0]->get($opt_key) + }; } } @@ -64,61 +64,6 @@ sub new_from_cli { return $self; } -# CLASS METHODS: - -# Write a "Windows" style ini file with categories enclosed in squre brackets. -# Used by config-bundle-to-config.pl and to save slic3r.ini. -sub write_ini { - my $class = shift; - my ($file, $ini) = @_; - - Slic3r::open(\my $fh, '>', $file); - binmode $fh, ':utf8'; - my $localtime = localtime; - printf $fh "# generated by Slic3r $Slic3r::VERSION on %s\n", "$localtime"; - # make sure the _ category is the first one written - foreach my $category (sort { ($a eq '_') ? -1 : ($a cmp $b) } keys %$ini) { - printf $fh "\n[%s]\n", $category if $category ne '_'; - foreach my $key (sort keys %{$ini->{$category}}) { - printf $fh "%s = %s\n", $key, $ini->{$category}{$key}; - } - } - close $fh; -} - -# Parse a "Windows" style ini file with categories enclosed in squre brackets. -# Returns a hash of hashes over strings. -# {category}{name}=value -# Non-categorized entries are stored under a category '_'. -# Used by config-bundle-to-config.pl and to read slic3r.ini. -sub read_ini { - my $class = shift; - my ($file) = @_; - - local $/ = "\n"; - Slic3r::open(\my $fh, '<', $file) - or die "Unable to open $file: $!\n"; - binmode $fh, ':utf8'; - - my $ini = { _ => {} }; - my $category = '_'; - while (<$fh>) { - s/\R+$//; - next if /^\s+/; - next if /^$/; - next if /^\s*#/; - if (/^\[(.+?)\]$/) { - $category = $1; - next; - } - /^(\w+) *= *(.*)/ or die "Unreadable configuration file (invalid data at line $.)\n"; - $ini->{$category}{$1} = $2; - } - close $fh; - - return $ini; -} - package Slic3r::Config::Static; use parent 'Slic3r::Config'; diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index a75b0d83b..baba3baa7 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -53,25 +53,9 @@ use constant FILE_WILDCARDS => { use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf prusa)}; # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. -our $no_controller; our $no_plater; -our $autosave; our @cb; -our $Settings = { - _ => { - version_check => 1, - autocenter => 1, - # Disable background processing by default as it is not stable. - background_processing => 0, - # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. - # By default, Prusa has the controller hidden. - no_controller => 1, - # If set, the "- default -" selections of print/filament/printer are suppressed, if there is a valid preset available. - no_defaults => 1, - }, -}; - our $small_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); $small_font->SetPointSize(11) if &Wx::wxMAC; our $small_bold_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); @@ -89,91 +73,47 @@ sub OnInit { Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION; $self->{notifier} = Slic3r::GUI::Notifier->new; + $self->{app_config} = Slic3r::GUI::AppConfig->new; $self->{preset_bundle} = Slic3r::GUI::PresetBundle->new; # just checking for existence of Slic3r::data_dir is not enough: it may be an empty directory # supplied as argument to --datadir; in that case we should still run the wizard - my $enc_datadir = Slic3r::encode_path(Slic3r::data_dir); - Slic3r::debugf "Data directory: %s\n", $enc_datadir; - my $run_wizard = (-d $enc_datadir && -e "$enc_datadir/slic3r.ini") ? 0 : 1; - foreach my $dir ($enc_datadir, "$enc_datadir/print", "$enc_datadir/filament", "$enc_datadir/printer") { - next if -d $dir; - if (!mkdir $dir) { - my $error = "Slic3r was unable to create its data directory at $dir ($!)."; - warn "$error\n"; - fatal_error(undef, $error); - } + eval { $self->{preset_bundle}->setup_directories() }; + if ($@) { + warn $@ . "\n"; + fatal_error(undef, $@); } - + my $run_wizard = ! $self->{app_config}->exists; # load settings - my $last_version; - if (-f "$enc_datadir/slic3r.ini") { - my $ini = eval { Slic3r::Config->read_ini(Slic3r::data_dir . "/slic3r.ini") }; - $Settings = $ini if $ini; - $last_version = $Settings->{_}{version}; - $Settings->{_}{autocenter} //= 1; - $Settings->{_}{background_processing} //= 1; - # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. - $Settings->{_}{no_controller} //= 1; - # If set, the "- default -" selections of print/filament/printer are suppressed, if there is a valid preset available. - $Settings->{_}{no_defaults} //= 1; - } - $Settings->{_}{version} = $Slic3r::VERSION; - $self->save_settings; + $self->{app_config}->load if ! $run_wizard; + $self->{app_config}->set('version', $Slic3r::VERSION); + $self->{app_config}->save; # Suppress the '- default -' presets. - $self->{preset_bundle}->set_default_suppressed($Slic3r::GUI::Settings->{_}{no_defaults} ? 1 : 0); - eval { $self->{preset_bundle}->load_presets(Slic3r::data_dir) }; + $self->{preset_bundle}->set_default_suppressed($self->{app_config}->get('no_defaults') ? 1 : 0); + eval { + $self->{preset_bundle}->load_presets(Slic3r::data_dir); + $self->{preset_bundle}->load_selections($self->{app_config}); + $run_wizard = 1 if $self->{preset_bundle}->has_defauls_only; + }; # application frame Wx::Image::AddHandler(Wx::PNGHandler->new); $self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new( # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. - no_controller => $no_controller // $Settings->{_}{no_controller}, + no_controller => $self->{app_config}->get('no_controller'), no_plater => $no_plater, ); $self->SetTopWindow($frame); - - # load init bundle - #FIXME this is undocumented and the use case is unclear. -# { -# my @dirs = ($FindBin::Bin); -# if (&Wx::wxMAC) { -# push @dirs, qw(); -# } elsif (&Wx::wxMSW) { -# push @dirs, qw(); -# } -# my $init_bundle = first { -e $_ } map "$_/.init_bundle.ini", @dirs; -# if ($init_bundle) { -# Slic3r::debugf "Loading config bundle from %s\n", $init_bundle; -# $self->{mainframe}->load_configbundle($init_bundle, 1); -# $run_wizard = 0; -# } -# } - - if (!$run_wizard && (!defined $last_version || $last_version ne $Slic3r::VERSION)) { - # user was running another Slic3r version on this computer - if (!defined $last_version || $last_version =~ /^0\./) { - show_info($self->{mainframe}, "Hello! Support material was improved since the " - . "last version of Slic3r you used. It is strongly recommended to revert " - . "your support material settings to the factory defaults and start from " - . "those. Enjoy and provide feedback!", "Support Material"); - } - if (!defined $last_version || $last_version =~ /^(?:0|1\.[01])\./) { - show_info($self->{mainframe}, "Hello! In this version a new Bed Shape option was " - . "added. If the bed coordinates in the plater preview screen look wrong, go " - . "to Print Settings and click the \"Set\" button next to \"Bed Shape\".", "Bed Shape"); - } - } if ($run_wizard) { $self->{mainframe}->config_wizard; - eval { $self->{preset_bundle}->load_presets(Slic3r::data_dir) }; } EVT_IDLE($frame, sub { while (my $cb = shift @cb) { $cb->(); } + $self->{app_config}->save if $self->{app_config}->dirty; }); return 1; @@ -265,11 +205,6 @@ sub notify { $self->{notifier}->notify($message); } -sub save_settings { - my ($self) = @_; - Slic3r::Config->write_ini(Slic3r::data_dir . "/slic3r.ini", $Settings); -} - # Called after the Preferences dialog is closed and the program settings are saved. # Update the UI based on the current preferences. sub update_ui_from_settings { @@ -277,22 +212,11 @@ sub update_ui_from_settings { $self->{mainframe}->update_ui_from_settings; } -sub output_path { - my ($self, $dir) = @_; - - return ($Settings->{_}{last_output_path} && $Settings->{_}{remember_output_path}) - ? $Settings->{_}{last_output_path} - : $dir; -} - sub open_model { my ($self, $window) = @_; - my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} - || $Slic3r::GUI::Settings->{recent}{config_directory} - || ''; - - my $dialog = Wx::FileDialog->new($window // $self->GetTopWindow, 'Choose one or more files (STL/OBJ/AMF/PRUSA):', $dir, "", + my $dialog = Wx::FileDialog->new($window // $self->GetTopWindow, 'Choose one or more files (STL/OBJ/AMF/PRUSA):', + $self->{app_config}->get_last_dir, "", MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST); if ($dialog->ShowModal != wxID_OK) { $dialog->Destroy; @@ -308,31 +232,6 @@ sub CallAfter { push @cb, $cb; } -sub scan_serial_ports { - my ($self) = @_; - - my @ports = (); - - if ($^O eq 'MSWin32') { - # Windows - if (eval "use Win32::TieRegistry; 1") { - my $ts = Win32::TieRegistry->new("HKEY_LOCAL_MACHINE\\HARDWARE\\DEVICEMAP\\SERIALCOMM", - { Access => 'KEY_READ' }); - if ($ts) { - # when no serial ports are available, the registry key doesn't exist and - # TieRegistry->new returns undef - $ts->Tie(\my %reg); - push @ports, sort values %reg; - } - } - } else { - # UNIX and OS X - push @ports, glob '/dev/{ttyUSB,ttyACM,tty.,cu.,rfcomm}*'; - } - - return grep !/Bluetooth|FireFly/, @ports; -} - sub append_menu_item { my ($self, $menu, $string, $description, $cb, $id, $icon, $kind) = @_; @@ -369,25 +268,24 @@ sub set_menu_item_icon { sub save_window_pos { my ($self, $window, $name) = @_; - $Settings->{_}{"${name}_pos"} = join ',', $window->GetScreenPositionXY; - $Settings->{_}{"${name}_size"} = join ',', $window->GetSizeWH; - $Settings->{_}{"${name}_maximized"} = $window->IsMaximized; - $self->save_settings; + $self->{app_config}->set("${name}_pos", join ',', $window->GetScreenPositionXY); + $self->{app_config}->set("${name}_size", join ',', $window->GetSizeWH); + $self->{app_config}->set("${name}_maximized", $window->IsMaximized); + $self->{app_config}->save; } sub restore_window_pos { my ($self, $window, $name) = @_; - - if (defined $Settings->{_}{"${name}_pos"}) { - my $size = [ split ',', $Settings->{_}{"${name}_size"}, 2 ]; + if ($self->{app_config}->has("${name}_pos")) { + my $size = [ split ',', $self->{app_config}->get("${name}_size"), 2 ]; $window->SetSize($size); my $display = Wx::Display->new->GetClientArea(); - my $pos = [ split ',', $Settings->{_}{"${name}_pos"}, 2 ]; + my $pos = [ split ',', $self->{app_config}->get("${name}_pos"), 2 ]; if (($pos->[0] + $size->[0]/2) < $display->GetRight && ($pos->[1] + $size->[1]/2) < $display->GetBottom) { $window->Move($pos); } - $window->Maximize(1) if $Settings->{_}{"${name}_maximized"}; + $window->Maximize(1) if $self->{app_config}->get("${name}_maximized"); } } diff --git a/lib/Slic3r/GUI/Controller.pm b/lib/Slic3r/GUI/Controller.pm index c4a22cace..6f2636d20 100644 --- a/lib/Slic3r/GUI/Controller.pm +++ b/lib/Slic3r/GUI/Controller.pm @@ -121,7 +121,7 @@ sub OnActivate { } if (!%active) { # enable printers whose port is available - my %ports = map { $_ => 1 } wxTheApp->scan_serial_ports; + my %ports = map { $_ => 1 } Slic3r::GUI::scan_serial_ports; $active{$_} = 1 for grep exists $ports{$presets{$_}->serial_port}, keys %presets; } diff --git a/lib/Slic3r/GUI/Controller/PrinterPanel.pm b/lib/Slic3r/GUI/Controller/PrinterPanel.pm index 1bf46c456..794ea4e4e 100644 --- a/lib/Slic3r/GUI/Controller/PrinterPanel.pm +++ b/lib/Slic3r/GUI/Controller/PrinterPanel.pm @@ -348,7 +348,7 @@ sub update_serial_ports { my $cb = $self->{serial_port_combobox}; my $current = $cb->GetValue; $cb->Clear; - $cb->Append($_) for wxTheApp->scan_serial_ports; + $cb->Append($_) for Slic3r::GUI::scan_serial_ports; $cb->SetValue($current); } diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index a0a47c547..c7f3f1cf9 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -76,6 +76,9 @@ sub new { } # save window size wxTheApp->save_window_pos($self, "main_frame"); + # Save the slic3r.ini. Usually the ini file is saved from "on idle" callback, + # but in rare cases it may not have been called yet. + wxTheApp->{app_config}->save; # propagate event $event->Skip; }); @@ -140,9 +143,11 @@ sub _init_tabpanel { my ($group, $name) = @_; $self->{options_tabs}{$group}->select_preset($name); }); - # load initial config - $self->{plater}->on_config_change(wxTheApp->{preset_bundle}->full_config); + my $full_config = wxTheApp->{preset_bundle}->full_config; + $self->{plater}->on_config_change($full_config); + # Show a correct number of filament fields. + $self->{plater}->on_extruders_change(int(@{$full_config->nozzle_diameter})); } } @@ -340,14 +345,15 @@ sub quick_slice { my $progress_dialog; eval { # validate configuration - my $config = $self->config; + my $config = wxTheApp->{preset_bundle}->full_config(); $config->validate; # select input file my $input_file; - my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || ''; if (!$params{reslice}) { - my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/PRUSA):', $dir, "", &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/PRUSA):', + wxTheApp->{app_config}->get_last_dir, "", + &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); if ($dialog->ShowModal != wxID_OK) { $dialog->Destroy; return; @@ -369,8 +375,7 @@ sub quick_slice { $input_file = $qs_last_input_file; } my $input_file_basename = basename($input_file); - $Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_file); - wxTheApp->save_settings; + wxTheApp->{app_config}->update_skein_dir(dirname($input_file)); my $print_center; { @@ -392,11 +397,9 @@ sub quick_slice { $sprint->apply_config($config); $sprint->set_model($model); - { - my $extra = $self->extra_variables; - $sprint->placeholder_parser->set($_, $extra->{$_}) for keys %$extra; - } - + # Copy the names of active presets into the placeholder parser. + wxTheApp->{preset_bundle}->export_selections_pp($sprint->placeholder_parser); + # select output file my $output_file; if ($params{reslice}) { @@ -405,7 +408,7 @@ sub quick_slice { $output_file = $sprint->output_filepath; $output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/ if $params{export_svg}; my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:', - wxTheApp->output_path(dirname($output_file)), + wxTheApp->{app_config}->get_last_output_dir(dirname($output_file)), basename($output_file), $params{export_svg} ? &Slic3r::GUI::FILE_WILDCARDS->{svg} : &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if ($dlg->ShowModal != wxID_OK) { $dlg->Destroy; @@ -413,8 +416,7 @@ sub quick_slice { } $output_file = $dlg->GetPath; $qs_last_output_file = $output_file unless $params{export_svg}; - $Slic3r::GUI::Settings->{_}{last_output_path} = dirname($output_file); - wxTheApp->save_settings; + wxTheApp->{app_config}->update_last_output_dir(dirname($output_file)); $dlg->Destroy; } @@ -457,8 +459,9 @@ sub repair_stl { my $input_file; { - my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || ''; - my $dialog = Wx::FileDialog->new($self, 'Select the STL file to repair:', $dir, "", &Slic3r::GUI::FILE_WILDCARDS->{stl}, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + my $dialog = Wx::FileDialog->new($self, 'Select the STL file to repair:', + wxTheApp->{app_config}->get_last_dir, "", + &Slic3r::GUI::FILE_WILDCARDS->{stl}, wxFD_OPEN | wxFD_FILE_MUST_EXIST); if ($dialog->ShowModal != wxID_OK) { $dialog->Destroy; return; @@ -487,15 +490,6 @@ sub repair_stl { Slic3r::GUI::show_info($self, "Your file was repaired.", "Repair"); } -# Extra variables for the placeholder parser generating a G-code. -sub extra_variables { - my $self = shift; - my %extra_variables = (); - $extra_variables{"${_}_preset"} = wxTheApp->{preset_bundle}->{$_}->get_current_preset_name - for qw(print filament printer); - return { %extra_variables }; -} - sub export_config { my $self = shift; # Generate a cummulative configuration for the selected print, filaments and printer. @@ -504,15 +498,14 @@ sub export_config { eval { $config->validate; }; Slic3r::GUI::catch_error($self) and return; # Ask user for the file name for the config file. - my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || ''; - my $filename = $last_config ? basename($last_config) : "config.ini"; - my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', $dir, $filename, + my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', + $last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir, + $last_config ? basename($last_config) : "config.ini", &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef; $dlg->Destroy; if (defined $file) { - $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); - wxTheApp->save_settings; + wxTheApp->{app_config}->update_config_dir(dirname($file)); $last_config = $file; $config->save($file); } @@ -523,9 +516,10 @@ sub load_config_file { my ($self, $file) = @_; if (!$file) { return unless $self->check_unsaved_changes; - my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || ''; - my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini", - 'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST); + my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', + $last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir, + "config.ini", + 'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST); return unless $dlg->ShowModal == wxID_OK; $file = $dlg->GetPaths; $dlg->Destroy; @@ -534,49 +528,51 @@ sub load_config_file { # Dont proceed further if the config file cannot be loaded. return if Slic3r::GUI::catch_error($self); $_->load_current_preset for (values %{$self->{options_tabs}}); - $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); - wxTheApp->save_settings; + wxTheApp->{app_config}->update_config_dir(dirname($file)); $last_config = $file; } sub export_configbundle { - my $self = shift; + my ($self) = @_; + return unless $self->check_unsaved_changes; # validate current configuration in case it's dirty - eval { $self->config->validate; }; + eval { wxTheApp->{preset_bundle}->full_config->validate; }; Slic3r::GUI::catch_error($self) and return; # Ask user for a file name. - my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || ''; - my $filename = "Slic3r_config_bundle.ini"; - my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:', $dir, $filename, + my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:', + $last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir, + "Slic3r_config_bundle.ini", &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef; $dlg->Destroy; if (defined $file) { # Export the config bundle. - $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); - wxTheApp->save_settings; - eval { $self->{presets}->export_configbundle($file); }; + wxTheApp->{app_config}->update_config_dir(dirname($file)); + eval { wxTheApp->{preset_bundle}->export_configbundle($file); }; Slic3r::GUI::catch_error($self) and return; } } +# Loading a config bundle with an external file name used to be used +# to auto-install a config bundle on a fresh user account, +# but that behavior was not documented and likely buggy. sub load_configbundle { - my ($self, $file, $skip_no_id) = @_; - + my ($self, $file) = @_; + return unless $self->check_unsaved_changes; if (!$file) { - my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || ''; - my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini", - &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', + $last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir, + "config.ini", + &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST); return unless $dlg->ShowModal == wxID_OK; $file = $dlg->GetPaths; $dlg->Destroy; } - $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); - wxTheApp->save_settings; + wxTheApp->{app_config}->update_config_dir(dirname($file)); my $presets_imported = 0; - eval { $presets_imported = $self->{presets}->load_configbundle($file); }; + eval { $presets_imported = wxTheApp->{preset_bundle}->load_configbundle($file); }; Slic3r::GUI::catch_error($self) and return; # Load the currently selected preset into the GUI, update the preset selection box. @@ -597,16 +593,18 @@ sub load_config { } sub config_wizard { - my $self = shift; - + my ($self) = @_; + # Exit wizard if there are unsaved changes and the user cancels the action. return unless $self->check_unsaved_changes; if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) { for my $tab (values %{$self->{options_tabs}}) { - # Select the first visible preset. - $tab->select_preset(undef); + # Select the first visible preset, force. + $tab->select_preset(undef, 1); } + # Load the config over the previously selected defaults. $self->load_config($config); for my $tab (values %{$self->{options_tabs}}) { + # Save the settings under a new name, select the name. $tab->save_preset('My Settings'); } } @@ -666,7 +664,7 @@ sub _set_menu_item_icon { # Update the UI based on the current preferences. sub update_ui_from_settings { my ($self) = @_; - $self->{menu_item_reslice_now}->Enable(! $Slic3r::GUI::Settings->{_}{background_processing}); + $self->{menu_item_reslice_now}->Enable(! wxTheApp->{app_config}->get("background_processing")); $self->{plater}->update_ui_from_settings if ($self->{plater}); } diff --git a/lib/Slic3r/GUI/OptionsGroup/Field.pm b/lib/Slic3r/GUI/OptionsGroup/Field.pm index 531e13a5d..4ef2ce2ca 100644 --- a/lib/Slic3r/GUI/OptionsGroup/Field.pm +++ b/lib/Slic3r/GUI/OptionsGroup/Field.pm @@ -124,6 +124,10 @@ sub BUILD { }); } +sub get_value { + my ($self) = @_; + return $self->wxWindow->GetValue ? 1 : 0; +} package Slic3r::GUI::OptionsGroup::Field::SpinCtrl; use Moo; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 91329e45e..06adbe525 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -63,12 +63,7 @@ sub new { $self->{print}->set_status_cb(sub { my ($percent, $message) = @_; - - if ($Slic3r::have_threads) { - Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROGRESS_BAR_EVENT, shared_clone([$percent, $message]))); - } else { - $self->on_progress_event($percent, $message); - } + Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROGRESS_BAR_EVENT, shared_clone([$percent, $message]))); }); # Initialize preview notebook @@ -119,7 +114,7 @@ sub new { $self->GetFrame->{options_tabs}{print}->load_config($cfg); }); $self->{canvas3D}->set_on_model_update(sub { - if ($Slic3r::GUI::Settings->{_}{background_processing}) { + if (wxTheApp->{app_config}->get("background_processing")) { $self->schedule_background_process; } else { # Hide the print info box, it is no more valid. @@ -330,7 +325,7 @@ sub new { $self->on_process_completed($event->GetData); }); - if ($Slic3r::have_threads) { + { my $timer_id = Wx::NewId(); $self->{apply_config_timer} = Wx::Timer->new($self, $timer_id); EVT_TIMER($self, $timer_id, sub { @@ -448,7 +443,6 @@ sub new { $self->{"print_info_$field"}->SetFont($Slic3r::GUI::small_font); $grid_sizer->Add($self->{"print_info_$field"}, 0); } - } my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL); @@ -510,13 +504,14 @@ sub _on_select_preset { wxTheApp->{preset_bundle}->set_filament_preset($idx, $choice->GetStringSelection); } if ($group eq 'filament' && @{$self->{preset_choosers}{filament}} > 1) { - wxTheApp->save_settings; wxTheApp->{preset_bundle}->update_platter_filament_ui_colors($idx, $choice); } else { # call GetSelection() in scalar context as it's context-aware $self->{on_select_preset}->($group, $choice->GetStringSelection) if $self->{on_select_preset}; } + # Synchronize config.ini with the current selections. + wxTheApp->{preset_bundle}->export_selections(wxTheApp->{app_config}); # get new config and generate on_config_change() event for updating plater and other things $self->on_config_change(wxTheApp->{preset_bundle}->full_config); } @@ -548,8 +543,8 @@ sub GetFrame { sub update_ui_from_settings { my ($self) = @_; - if (defined($self->{btn_reslice}) && $self->{buttons_sizer}->IsShown($self->{btn_reslice}) != (! $Slic3r::GUI::Settings->{_}{background_processing})) { - $self->{buttons_sizer}->Show($self->{btn_reslice}, ! $Slic3r::GUI::Settings->{_}{background_processing}); + if (defined($self->{btn_reslice}) && $self->{buttons_sizer}->IsShown($self->{btn_reslice}) != (! wxTheApp->{app_config}->get("background_processing"))) { + $self->{buttons_sizer}->Show($self->{btn_reslice}, ! wxTheApp->{app_config}->get("background_processing")); $self->{buttons_sizer}->Layout; } } @@ -587,6 +582,8 @@ sub update_presets { $choice_idx += 1; } } + # Synchronize config.ini with the current selections. + wxTheApp->{preset_bundle}->export_selections(wxTheApp->{app_config}); } sub add { @@ -655,8 +652,7 @@ sub load_files { } # Note the current directory for the file open dialog. - $Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_files->[-1]); - wxTheApp->save_settings; + wxTheApp->{app_config}->update_skein_dir(dirname($input_files->[-1])); $process_dialog->Destroy; $self->statusbar->SetStatusText("Loaded " . join(',', @loaded_files)); @@ -704,7 +700,7 @@ sub load_model_objects { } # if user turned autocentering off, automatic arranging would disappoint them - if (!$Slic3r::GUI::Settings->{_}{autocenter}) { + if (! wxTheApp->{app_config}->get("autocenter")) { $need_arrange = 0; } @@ -817,7 +813,7 @@ sub increase { # only autoarrange if user has autocentering enabled $self->stop_background_process; - if ($Slic3r::GUI::Settings->{_}{autocenter}) { + if (wxTheApp->{app_config}->get("autocenter")) { $self->arrange; } else { $self->update; @@ -1130,7 +1126,7 @@ sub async_apply_config { # Hide the slicing results if the current slicing status is no more valid. $self->{"print_info_box_show"}->(0) if $invalidated; - if ($Slic3r::GUI::Settings->{_}{background_processing}) { + if (wxTheApp->{app_config}->get("background_processing")) { if ($invalidated) { # kill current thread if any $self->stop_background_process; @@ -1152,7 +1148,6 @@ sub async_apply_config { sub start_background_process { my ($self) = @_; - return if !$Slic3r::have_threads; return if !@{$self->{objects}}; return if $self->{process_thread}; @@ -1171,11 +1166,8 @@ sub start_background_process { return; } - # apply extra variables - { - my $extra = $self->GetFrame->extra_variables; - $self->{print}->placeholder_parser->set($_, $extra->{$_}) for keys %$extra; - } + # Copy the names of active presets into the placeholder parser. + wxTheApp->{preset_bundle}->export_selections_pp($self->{print}->placeholder_parser); # start thread @_ = (); @@ -1246,7 +1238,7 @@ sub reslice { # explicitly cancel a previous thread and start a new one. my ($self) = @_; # Don't reslice if export of G-code or sending to OctoPrint is running. - if ($Slic3r::have_threads && ! defined($self->{export_gcode_output_file}) && ! defined($self->{send_gcode_file})) { + if (! defined($self->{export_gcode_output_file}) && ! defined($self->{send_gcode_file})) { # Stop the background processing threads, stop the async update timer. $self->stop_background_process; # Rather perform one additional unnecessary update of the print object instead of skipping a pending async update. @@ -1289,52 +1281,41 @@ sub export_gcode { $self->{print}->apply_config($config); $self->{print}->validate; }; - if (!$Slic3r::have_threads) { - Slic3r::GUI::catch_error($self) and return; - } + Slic3r::GUI::catch_error($self) and return; # select output file if ($output_file) { $self->{export_gcode_output_file} = $self->{print}->output_filepath($output_file); } else { my $default_output_file = $self->{print}->output_filepath($main::opt{output} // ''); - my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', wxTheApp->output_path(dirname($default_output_file)), + my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', + wxTheApp->{app_config}->get_last_output_dir(dirname($default_output_file)), basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if ($dlg->ShowModal != wxID_OK) { $dlg->Destroy; return; } my $path = $dlg->GetPath; - $Slic3r::GUI::Settings->{_}{last_output_path} = dirname($path); - wxTheApp->save_settings; + wxTheApp->{app_config}->update_last_output_dir(dirname($path)); $self->{export_gcode_output_file} = $path; $dlg->Destroy; } $self->statusbar->StartBusy; - if ($Slic3r::have_threads) { - $self->statusbar->SetCancelCallback(sub { - $self->stop_background_process; - $self->statusbar->SetStatusText("Export cancelled"); - $self->{export_gcode_output_file} = undef; - $self->{send_gcode_file} = undef; - - # this updates buttons status - $self->object_list_changed; - }); + $self->statusbar->SetCancelCallback(sub { + $self->stop_background_process; + $self->statusbar->SetStatusText("Export cancelled"); + $self->{export_gcode_output_file} = undef; + $self->{send_gcode_file} = undef; - # start background process, whose completion event handler - # will detect $self->{export_gcode_output_file} and proceed with export - $self->start_background_process; - } else { - eval { - $self->{print}->process; - $self->{print}->export_gcode(output_file => $self->{export_gcode_output_file}); - }; - my $result = !Slic3r::GUI::catch_error($self); - $self->on_export_completed($result); - } + # this updates buttons status + $self->object_list_changed; + }); + + # start background process, whose completion event handler + # will detect $self->{export_gcode_output_file} and proceed with export + $self->start_background_process; # this updates buttons status $self->object_list_changed; @@ -1577,7 +1558,7 @@ sub reset_thumbnail { sub update { my ($self, $force_autocenter) = @_; - if ($Slic3r::GUI::Settings->{_}{autocenter} || $force_autocenter) { + if (wxTheApp->{app_config}->get("autocenter") || $force_autocenter) { $self->{model}->center_instances_around_point($self->bed_centerf); } @@ -1604,9 +1585,7 @@ sub update { # and some reasonable default has to be selected for the additional extruders. sub on_extruders_change { my ($self, $num_extruders) = @_; - my $choices = $self->{preset_choosers}{filament}; - wxTheApp->{preset_bundle}->update_multi_material_filament_presets; while (int(@$choices) < $num_extruders) { # copy strings from first choice diff --git a/lib/Slic3r/GUI/Plater/2D.pm b/lib/Slic3r/GUI/Plater/2D.pm index 089b25947..a1e077591 100644 --- a/lib/Slic3r/GUI/Plater/2D.pm +++ b/lib/Slic3r/GUI/Plater/2D.pm @@ -9,7 +9,7 @@ use utf8; use List::Util qw(min max first); use Slic3r::Geometry qw(X Y scale unscale convex_hull); use Slic3r::Geometry::Clipper qw(offset JT_ROUND intersection_pl); -use Wx qw(:misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL); +use Wx qw(wxTheApp :misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL); use Wx::Event qw(EVT_MOUSE_EVENTS EVT_PAINT EVT_ERASE_BACKGROUND EVT_SIZE); use base 'Wx::Panel'; @@ -102,7 +102,7 @@ sub repaint { } # draw print center - if (@{$self->{objects}} && $Slic3r::GUI::Settings->{_}{autocenter}) { + if (@{$self->{objects}} && wxTheApp->{app_config}->get("autocenter")) { my $center = $self->unscaled_point_to_pixel($self->{print_center}); $dc->SetPen($self->{print_center_pen}); $dc->DrawLine($center->[X], 0, $center->[X], $size[Y]); @@ -197,7 +197,6 @@ sub repaint { sub mouse_event { my ($self, $event) = @_; - my $pos = $event->GetPosition; my $point = $self->point_to_model_units([ $pos->x, $pos->y ]); #]] if ($event->ButtonDown) { @@ -257,7 +256,7 @@ sub mouse_event { } sub update_bed_size { - my $self = shift; + my ($self) = @_; # when the canvas is not rendered yet, its GetSize() method returns 0,0 my $canvas_size = $self->GetSize; diff --git a/lib/Slic3r/GUI/Preferences.pm b/lib/Slic3r/GUI/Preferences.pm index 4b6c665b7..ca953e215 100644 --- a/lib/Slic3r/GUI/Preferences.pm +++ b/lib/Slic3r/GUI/Preferences.pm @@ -10,6 +10,7 @@ sub new { my $self = $class->SUPER::new($parent, -1, "Preferences", wxDefaultPosition, wxDefaultSize); $self->{values} = {}; + my $app_config = wxTheApp->{app_config}; my $optgroup; $optgroup = Slic3r::GUI::OptionsGroup->new( parent => $self, @@ -25,7 +26,7 @@ sub new { # type => 'bool', # label => 'Check for updates', # tooltip => 'If this is enabled, Slic3r will check for updates daily and display a reminder if a newer version is available.', -# default => $Slic3r::GUI::Settings->{_}{version_check} // 1, +# default => $app_config->get("version_check") // 1, # readonly => !wxTheApp->have_version_check, # )); $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( @@ -33,36 +34,35 @@ sub new { type => 'bool', label => 'Remember output directory', tooltip => 'If this is enabled, Slic3r will prompt the last output directory instead of the one containing the input files.', - default => $Slic3r::GUI::Settings->{_}{remember_output_path}, + default => $app_config->get("remember_output_path"), )); $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'autocenter', type => 'bool', label => 'Auto-center parts', tooltip => 'If this is enabled, Slic3r will auto-center objects around the print bed center.', - default => $Slic3r::GUI::Settings->{_}{autocenter}, + default => $app_config->get("autocenter"), )); $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'background_processing', type => 'bool', label => 'Background processing', tooltip => 'If this is enabled, Slic3r will pre-process objects as soon as they\'re loaded in order to save time when exporting G-code.', - default => $Slic3r::GUI::Settings->{_}{background_processing}, - readonly => !$Slic3r::have_threads, + default => $app_config->get("background_processing"), )); $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'no_controller', type => 'bool', label => 'Disable USB/serial connection', tooltip => 'Disable communication with the printer over a serial / USB cable. This simplifies the user interface in case the printer is never attached to the computer.', - default => $Slic3r::GUI::Settings->{_}{no_controller}, + default => $app_config->get("no_controller"), )); $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'no_defaults', type => 'bool', label => 'Suppress "- default -" presets', tooltip => 'Suppress "- default -" presets in the Print / Filament / Printer selections once there are any other valid presets available.', - default => $Slic3r::GUI::Settings->{_}{no_defaults}, + default => $app_config->get("no_defaults"), )); my $sizer = Wx::BoxSizer->new(wxVERTICAL); @@ -79,15 +79,15 @@ sub new { } sub _accept { - my $self = shift; + my ($self) = @_; if (defined($self->{values}{no_controller}) || defined($self->{values}{no_defaults})) { Slic3r::GUI::warning_catcher($self)->("You need to restart Slic3r to make the changes effective."); } - $Slic3r::GUI::Settings->{_}{$_} = $self->{values}{$_} for keys %{$self->{values}}; - wxTheApp->save_settings; + my $app_config = wxTheApp->{app_config}; + $app_config->set($_, $self->{values}{$_}) for keys %{$self->{values}}; $self->EndModal(wxID_OK); $self->Close; # needed on Linux diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 1375bb92b..2718d4f5e 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -125,15 +125,9 @@ sub save_preset { $self->{treectrl}->SetFocus; if (!defined $name) { - my $preset = $self->{presets}->get_edited_preset; + my $preset = $self->{presets}->get_selected_preset; my $default_name = $preset->default ? 'Untitled' : $preset->name; $default_name =~ s/\.[iI][nN][iI]$//; - - my @prsts = @{$self->{presets}}; - print "Num of presets: ". int(@prsts) . "\n"; - for my $pr (@prsts) { - print "Name: " . $pr->name . " default " . $pr->default . "\n"; - } my $dlg = Slic3r::GUI::SavePresetWindow->new($self, title => lc($self->title), default => $default_name, @@ -156,16 +150,16 @@ sub delete_preset { my ($self) = @_; my $current_preset = $self->{presets}->get_selected_preset; # Don't let the user delete the '- default -' configuration. + my $msg = 'Are you sure you want to ' . ($current_preset->external ? 'remove' : 'delete') . ' the selected preset?'; + my $title = ($current_preset->external ? 'Remove' : 'Delete') . ' Preset'; return if $current_preset->default || - wxID_YES != Wx::MessageDialog->new($self, "Are you sure you want to delete the selected preset?", 'Delete Preset', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)->ShowModal; + wxID_YES != Wx::MessageDialog->new($self, $msg, $title, wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)->ShowModal; # Delete the file and select some other reasonable preset. # The 'external' presets will only be removed from the preset list, their files will not be deleted. eval { $self->{presets}->delete_current_preset; }; Slic3r::GUI::catch_error($self) and return; - # Delete the item from the UI component and activate another preset. - $self->{presets}->update_tab_ui($self->{presets_choice}); - # Update the selection boxes at the patter. - $self->_on_presets_changed; + # Load the newly selected preset into the UI, update selection combo boxes with their dirty flags. + $self->load_current_preset; } # Register the on_value_change callback. @@ -203,7 +197,6 @@ sub _update {} # to uddate number of "filament" selection boxes when the number of extruders change. sub _on_presets_changed { my ($self) = @_; - print "Tab::_on_presets_changed\n"; $self->{on_presets_changed}->($self->{presets}) if $self->{on_presets_changed}; } @@ -237,38 +230,34 @@ sub may_discard_current_preset_if_dirty } # Called by the UI combo box when the user switches profiles. -# Select a preset by a name. If ! defined(name), then the first visible preset is selected. +# Select a preset by a name. If ! defined(name), then the default preset is selected. # If the current profile is modified, user is asked to save the changes. sub select_preset { my ($self, $name, $force) = @_; - print "select_preset 1\n"; - if (! $self->may_discard_current_preset_if_dirty) { + $force //= 0; + if (! $force && ! $self->may_discard_current_preset_if_dirty) { $self->{presets}->update_tab_ui($self->{presets_choice}); # Trigger the on_presets_changed event so that we also restore the previous value in the plater selector. $self->_on_presets_changed; return; } - print "select_preset 2\n"; - $self->{presets}->select_preset_by_name(defined $name ? $name : ""); - print "select_preset 3\n"; + if (defined $name) { + $self->{presets}->select_preset_by_name($name); + } else { + $self->{presets}->select_preset(0); + } # Initialize the UI from the current preset. $self->load_current_preset; - print "select_preset 4\n"; - # Save the current application settings with the newly selected preset name. - wxTheApp->save_settings; - print "select_preset 5\n"; } # Initialize the UI from the current preset. sub load_current_preset { my ($self) = @_; - print "load_current_preset 1\n"; $self->{presets}->update_tab_ui($self->{presets_choice}); - print "load_current_preset 2\n"; my $preset = $self->{presets}->get_current_preset; eval { local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); - my $method = ($preset->default || $preset->external) ? 'Disable' : 'Enable'; + my $method = $preset->default ? 'Disable' : 'Enable'; $self->{btn_delete_preset}->$method; $self->_update; # For the printer profile, generate the extruder pages. @@ -281,8 +270,9 @@ sub load_current_preset { # preset dirty again # (not sure this is true anymore now that update_dirty is idempotent) wxTheApp->CallAfter(sub { - $self->_on_presets_changed; $self->update_dirty; + #the following is called by update_dirty + #$self->_on_presets_changed; }); } @@ -404,7 +394,7 @@ sub build { my $self = shift; $self->{presets} = wxTheApp->{preset_bundle}->print; - $self->{config} = $self->{presets}->get_edited_preset->config_ref; + $self->{config} = $self->{presets}->get_edited_preset->config; { my $page = $self->add_options_page('Layers and perimeters', 'layers.png'); @@ -608,7 +598,7 @@ sub build { $optgroup->append_single_option_line('clip_multipart_objects'); $optgroup->append_single_option_line('elefant_foot_compensation'); $optgroup->append_single_option_line('xy_size_compensation'); -# $optgroup->append_single_option_line('threads') if $Slic3r::have_threads; +# $optgroup->append_single_option_line('threads'); $optgroup->append_single_option_line('resolution'); } } @@ -675,7 +665,7 @@ sub _update { my ($self) = @_; $self->Freeze; - my $config = $self->{presets}->get_edited_preset->config_ref; + my $config = $self->{config}; if ($config->spiral_vase && !($config->perimeters == 1 && $config->top_solid_layers == 0 && $config->fill_density == 0)) { my $dialog = Wx::MessageDialog->new($self, @@ -882,7 +872,7 @@ sub build { my $self = shift; $self->{presets} = wxTheApp->{preset_bundle}->filament; - $self->{config} = $self->{presets}->get_edited_preset->config_ref; + $self->{config} = $self->{presets}->get_edited_preset->config; { my $page = $self->add_options_page('Filament', 'spool.png'); @@ -1061,8 +1051,9 @@ sub build { my ($self, %params) = @_; $self->{presets} = wxTheApp->{preset_bundle}->printer; - $self->{config} = $self->{presets}->get_edited_preset->config_ref; - + $self->{config} = $self->{presets}->get_edited_preset->config; + $self->{extruders_count} = scalar @{$self->{config}->nozzle_diameter}; + my $bed_shape_widget = sub { my ($parent) = @_; @@ -1086,9 +1077,7 @@ sub build { return $sizer; }; - - $self->{extruders_count} = 1; - + { my $page = $self->add_options_page('General', 'printer_empty.png'); { @@ -1117,13 +1106,16 @@ sub build { $optgroup->append_single_option_line('single_extruder_multi_material'); } $optgroup->on_change(sub { - my ($opt_id) = @_; - if ($opt_id eq 'extruders_count') { - wxTheApp->CallAfter(sub { + my ($opt_key, $value) = @_; + wxTheApp->CallAfter(sub { + if ($opt_key eq 'extruders_count') { $self->_extruders_count_changed($optgroup->get_value('extruders_count')); - }); - $self->update_dirty; - } + $self->update_dirty; + } else { + $self->update_dirty; + $self->_on_value_change($opt_key, $value); + } + }); }); } if (!$params{no_controller}) @@ -1326,43 +1318,23 @@ sub build { sub _update_serial_ports { my ($self) = @_; - $self->get_field('serial_port')->set_values([ wxTheApp->scan_serial_ports ]); + $self->get_field('serial_port')->set_values([ Slic3r::GUI::scan_serial_ports ]); } sub _extruders_count_changed { my ($self, $extruders_count) = @_; - $self->{extruders_count} = $extruders_count; + wxTheApp->{preset_bundle}->printer->get_edited_preset->set_num_extruders($extruders_count); + wxTheApp->{preset_bundle}->update_multi_material_filament_presets; $self->_build_extruder_pages; $self->_on_value_change('extruders_count', $extruders_count); } -sub _extruder_options { - qw(nozzle_diameter min_layer_height max_layer_height extruder_offset - retract_length retract_lift retract_lift_above retract_lift_below retract_speed deretract_speed - retract_before_wipe retract_restart_extra retract_before_travel wipe - retract_layer_change retract_length_toolchange retract_restart_extra_toolchange extruder_colour) } - sub _build_extruder_pages { - my $self = shift; - + my ($self) = @_; my $default_config = Slic3r::Config::Full->new; - + foreach my $extruder_idx (@{$self->{extruder_pages}} .. $self->{extruders_count}-1) { - # extend options - foreach my $opt_key ($self->_extruder_options) { - my $values = $self->{config}->get($opt_key); - if (!defined $values) { - $values = [ $default_config->get_at($opt_key, 0) ]; - } else { - # use last extruder's settings for the new one - my $last_value = $values->[-1]; - $values->[$extruder_idx] //= $last_value; - } - $self->{config}->set($opt_key, $values) - or die "Unable to extend $opt_key"; - } - # build page my $page = $self->{extruder_pages}[$extruder_idx] = $self->add_options_page("Extruder " . ($extruder_idx + 1), 'funnel.png'); { @@ -1412,14 +1384,6 @@ sub _build_extruder_pages { splice @{$self->{extruder_pages}}, $self->{extruders_count}; } - # remove extra config values - foreach my $opt_key ($self->_extruder_options) { - my $values = $self->{config}->get($opt_key); - splice @$values, $self->{extruders_count} if $self->{extruders_count} <= $#$values; - $self->{config}->set($opt_key, $values) - or die "Unable to truncate $opt_key"; - } - # rebuild page list my @pages_without_extruders = (grep $_->{title} !~ /^Extruder \d+/, @{$self->{pages}}); my $page_notes = pop @pages_without_extruders; @@ -1513,15 +1477,12 @@ sub _update { # this gets executed after preset is loaded and before GUI fields are updated sub on_preset_loaded { - my $self = shift; - + my ($self) = @_; # update the extruders count field - { - # update the GUI field according to the number of nozzle diameters supplied - my $extruders_count = scalar @{ $self->{config}->nozzle_diameter }; - $self->set_value('extruders_count', $extruders_count); - $self->_extruders_count_changed($extruders_count); - } + my $extruders_count = scalar @{ $self->{config}->nozzle_diameter }; + $self->set_value('extruders_count', $extruders_count); + # update the GUI field according to the number of nozzle diameters supplied + $self->_extruders_count_changed($extruders_count); } # Single Tab page containing a {vsizer} of {optgroups} diff --git a/slic3r.pl b/slic3r.pl index 9864c0714..87c8fe40c 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -241,16 +241,7 @@ if (@ARGV) { # slicing from command line sub usage { my ($exit_code) = @_; - my $config = Slic3r::Config::new_from_defaults->as_hash; - - my $j = ''; - if ($Slic3r::have_threads) { - $j = <<"EOF"; - -j, --threads Number of threads to use (1+, default: $config->{threads}) -EOF - } - print <<"EOF"; Slic3r $Slic3r::VERSION is a STL-to-GCODE translator for RepRap 3D printers written by Alessandro Ranellucci - http://slic3r.org/ @@ -275,8 +266,8 @@ Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ... them as _upper.stl and _lower.stl --split Split the shells contained in given STL file into several STL files --info Output information about the supplied file(s) and exit - -$j + -j, --threads Number of threads to use (1+, default: $config->{threads}) + GUI options: --gui Forces the GUI launch instead of command line slicing (if you supply a model file, it will be loaded into the plater) diff --git a/t/threads.t b/t/threads.t index 7fcd86f0e..7fede3328 100644 --- a/t/threads.t +++ b/t/threads.t @@ -1,4 +1,4 @@ -use Test::More; +use Test::More tests => 2; use strict; use warnings; @@ -12,11 +12,6 @@ use List::Util qw(first); use Slic3r; use Slic3r::Test; -if (!$Slic3r::have_threads) { - plan skip_all => "this perl is not compiled with threads"; -} -plan tests => 2; - { my $print = Slic3r::Test::init_print('20mm_cube'); { diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 2228b8e7c..30fc00b68 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -207,15 +207,15 @@ public: void resize(size_t n, const ConfigOption *opt_default = nullptr) override { assert(opt_default == nullptr || opt_default->is_vector()); - assert(opt_default == nullptr || dynamic_cast>(opt_default)); +// assert(opt_default == nullptr || dynamic_cast>(opt_default)); assert(! this->values.empty() || opt_default != nullptr); if (n == 0) this->values.clear(); else if (n < this->values.size()) this->values.erase(this->values.begin() + n, this->values.end()); - else if (n > this->values.size()) + else if (n > this->values.size()) { if (this->values.empty()) { - if (opt_default == nullptr) { + if (opt_default == nullptr) throw std::runtime_error("ConfigOptionVector::resize(): No default value provided."); if (opt_default->type() != this->type()) throw std::runtime_error("ConfigOptionVector::resize(): Extending with an incompatible type."); diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 9e4043818..308a2a118 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -25,7 +25,6 @@ std::string config_path(const std::string &file_name); // The suffix ".ini" will be added if it is missing in the name. std::string config_path(const std::string §ion, const std::string &name); -extern std::locale locale_utf8; extern std::string encode_path(const char *src); extern std::string decode_path(const char *src); extern std::string normalize_utf8_nfc(const char *src); diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index e10e136d1..f4c03ef50 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -103,14 +103,14 @@ const std::string& data_dir() std::string config_path(const std::string &file_name) { - auto file = boost::filesystem::canonical(boost::filesystem::path(g_data_dir) / file_name).make_preferred(); + auto file = (boost::filesystem::path(g_data_dir) / file_name).make_preferred(); return file.string(); } std::string config_path(const std::string §ion, const std::string &name) { auto file_name = boost::algorithm::iends_with(name, ".ini") ? name : name + ".ini"; - auto file = boost::filesystem::canonical(boost::filesystem::path(g_data_dir) / file_name).make_preferred(); + auto file = (boost::filesystem::path(g_data_dir) / section / file_name).make_preferred(); return file.string(); } @@ -228,10 +228,9 @@ std::string decode_path(const char *src) #endif /* WIN32 */ } -std::locale locale_utf8(boost::locale::generator().generate("")); - std::string normalize_utf8_nfc(const char *src) { + static std::locale locale_utf8(boost::locale::generator().generate("")); return boost::locale::normalize(src, boost::locale::norm_nfc, locale_utf8); } diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp index b61ce89d2..f978fcbb4 100644 --- a/xs/src/slic3r/GUI/AppConfig.cpp +++ b/xs/src/slic3r/GUI/AppConfig.cpp @@ -27,9 +27,7 @@ void AppConfig::reset() // Override missing or keys with their defaults. void AppConfig::set_defaults() { - // 2) Reset to defaults. - if (get("version_check").empty()) - set("version_check", "1"); + // Reset the empty fields to defaults. if (get("autocenter").empty()) set("autocenter", "1"); // Disable background processing by default as it is not stable. @@ -38,10 +36,13 @@ void AppConfig::set_defaults() // If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. // By default, Prusa has the controller hidden. if (get("no_controller").empty()) - set("no_controller", "0"); + set("no_controller", "1"); // If set, the "- default -" selections of print/filament/printer are suppressed, if there is a valid preset available. if (get("no_defaults").empty()) set("no_defaults", "1"); + // Version check is enabled by default in the config, but it is not implemented yet. + if (get("version_check").empty()) + set("version_check", "1"); } void AppConfig::load() @@ -114,13 +115,11 @@ std::string AppConfig::get_last_dir() const void AppConfig::update_config_dir(const std::string &dir) { this->set("recent", "config_directory", dir); - this->save(); } void AppConfig::update_skein_dir(const std::string &dir) { this->set("recent", "skein_directory", dir); - this->save(); } std::string AppConfig::get_last_output_dir(const std::string &alt) const @@ -138,7 +137,6 @@ std::string AppConfig::get_last_output_dir(const std::string &alt) const void AppConfig::update_last_output_dir(const std::string &dir) { this->set("", "last_output_path", dir); - this->save(); } std::string AppConfig::config_path() diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 17da135a0..12bf48014 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -1,9 +1,14 @@ #include "GUI.hpp" +#include + +#include + #if __APPLE__ #import #elif _WIN32 #include +#include "boost/nowide/convert.hpp" #pragma comment(lib, "user32.lib") #endif @@ -34,6 +39,82 @@ void enable_screensaver() #endif } +std::vector scan_serial_ports() +{ + std::vector out; +#ifdef _WIN32 + // 1) Open the registry key SERIALCOM. + HKEY hKey; + LONG lRes = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_READ, &hKey); + assert(lRes == ERROR_SUCCESS); + if (lRes == ERROR_SUCCESS) { + // 2) Get number of values of SERIALCOM key. + DWORD cValues; // number of values for key + { + TCHAR achKey[255]; // buffer for subkey name + DWORD cbName; // size of name string + TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name + DWORD cchClassName = MAX_PATH; // size of class string + DWORD cSubKeys=0; // number of subkeys + DWORD cbMaxSubKey; // longest subkey size + DWORD cchMaxClass; // longest class string + DWORD cchMaxValue; // longest value name + DWORD cbMaxValueData; // longest value data + DWORD cbSecurityDescriptor; // size of security descriptor + FILETIME ftLastWriteTime; // last write time + // Get the class name and the value count. + lRes = RegQueryInfoKey( + hKey, // key handle + achClass, // buffer for class name + &cchClassName, // size of class string + NULL, // reserved + &cSubKeys, // number of subkeys + &cbMaxSubKey, // longest subkey size + &cchMaxClass, // longest class string + &cValues, // number of values for this key + &cchMaxValue, // longest value name + &cbMaxValueData, // longest value data + &cbSecurityDescriptor, // security descriptor + &ftLastWriteTime); // last write time + assert(lRes == ERROR_SUCCESS); + } + // 3) Read the SERIALCOM values. + { + DWORD dwIndex = 0; + for (int i = 0; i < cValues; ++ i, ++ dwIndex) { + wchar_t valueName[2048]; + DWORD valNameLen = 2048; + DWORD dataType; + wchar_t data[2048]; + DWORD dataSize = 4096; + lRes = ::RegEnumValueW(hKey, dwIndex, valueName, &valNameLen, nullptr, &dataType, (BYTE*)&data, &dataSize); + if (lRes == ERROR_SUCCESS && dataType == REG_SZ && valueName[0] != 0) + out.emplace_back(boost::nowide::narrow(data)); + } + } + ::RegCloseKey(hKey); + } +#else + // UNIX and OS X + boost::filesystem::recursive_directory_iterator end; + std::initializer_list prefixes { "ttyUSB" , "ttyACM", "tty.", "cu.", "rfcomm" }; + for (boost::filesystem::recursive_directory_iterator it_path(boost::filesystem::path("/dev")); + it_path != end; ++ it_path) + for (const char *prefix : prefixes) + if (boost::starts_with(it_path->string(), std::string("/dev/") + prefix)) { + out.emplace_back(path); + break; + } +#endif + + out.erase(std::remove_if(out.begin(), out.end(), + [](const std::string &key){ + return boost::starts_with(key, "Bluetooth") || boost::starts_with(key, "FireFly"); + }), + out.end()); + return out; +} + bool debugged() { #ifdef _WIN32 diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 85656c0a3..955a1cd8d 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -1,10 +1,14 @@ #ifndef slic3r_GUI_hpp_ #define slic3r_GUI_hpp_ +#include +#include + namespace Slic3r { namespace GUI { void disable_screensaver(); void enable_screensaver(); +std::vector scan_serial_ports(); bool debugged(); void break_to_debugger(); diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index d02653a4a..5b42b27a1 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -1,3 +1,6 @@ +//#undef NDEBUG +#include + #include "Preset.hpp" #include @@ -16,16 +19,9 @@ #include #include +#include "../../libslic3r/libslic3r.h" #include "../../libslic3r/Utils.hpp" -#if 0 -#define DEBUG -#define _DEBUG -#undef NDEBUG -#endif - -#include - namespace Slic3r { ConfigFileType guess_config_file_type(const boost::property_tree::ptree &tree) @@ -94,6 +90,19 @@ void Preset::normalize(DynamicPrintConfig &config) if (nozzle_diameter != nullptr) // Loaded the Printer settings. Verify, that all extruder dependent values have enough values. set_num_extruders(config, (unsigned int)nozzle_diameter->values.size()); + if (config.option("filament_diameter") != nullptr) { + // This config contains single or multiple filament presets. + // Ensure that the filament preset vector options contain the correct number of values. + size_t n = (nozzle_diameter == nullptr) ? 1 : nozzle_diameter->values.size(); + const auto &defaults = FullPrintConfig::defaults(); + for (const std::string &key : Preset::filament_options()) { + auto *opt = config.option(key, false); + assert(opt != nullptr); + assert(opt->is_vector()); + if (opt != nullptr && opt->is_vector()) + static_cast(opt)->resize(n, defaults.option(key)); + } + } } // Load a config file, return a C++ class Slic3r::DynamicPrintConfig with $keys initialized from the config file. @@ -247,27 +256,29 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri } } std::sort(m_presets.begin() + 1, m_presets.end()); - m_presets.front().is_visible = ! m_default_suppressed || m_presets.size() > 1; this->select_preset(first_visible_idx()); } // Load a preset from an already parsed config file, insert it into the sorted sequence of presets // and select it, losing previous modifications. Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select) +{ + DynamicPrintConfig cfg(this->default_preset().config); + cfg.apply(config, true); + return this->load_preset(path, name, std::move(cfg)); +} + +Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select) { Preset key(m_type, name); auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key); - if (it != m_presets.end() && it->name == name) { - // The preset with the same name was found. - it->is_dirty = false; - } else { + if (it == m_presets.end() || it->name != name) it = m_presets.emplace(it, Preset(m_type, name, false)); - } Preset &preset = *it; preset.file = path; - preset.config = this->default_preset().config; + preset.config = std::move(config); preset.loaded = true; - this->get_selected_preset().is_dirty = false; + preset.is_dirty = false; if (select) this->select_preset_by_name(name, true); return preset; @@ -275,6 +286,8 @@ Preset& PresetCollection::load_preset(const std::string &path, const std::string void PresetCollection::save_current_preset(const std::string &new_name) { + // 1) Find the preset with a new_name or create a new one, + // initialize it with the edited config. Preset key(m_type, new_name, false); auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key); if (it != m_presets.end() && it->name == key.name) { @@ -285,37 +298,39 @@ void PresetCollection::save_current_preset(const std::string &new_name) return; // Overwriting an existing preset. preset.config = std::move(m_edited_preset.config); - m_idx_selected = it - m_presets.begin(); } else { // Creating a new preset. - m_idx_selected = m_presets.insert(it, m_edited_preset) - m_presets.begin(); - Preset &preset = m_presets[m_idx_selected]; + Preset &preset = *m_presets.insert(it, m_edited_preset); std::string file_name = new_name; if (! boost::iends_with(file_name, ".ini")) file_name += ".ini"; preset.name = new_name; preset.file = (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string(); } - m_edited_preset = m_presets[m_idx_selected]; - m_presets[m_idx_selected].save(); - m_presets.front().is_visible = ! m_default_suppressed || m_idx_selected > 0; + // 2) Activate the saved preset. + this->select_preset_by_name(new_name, true); + // 2) Store the active preset to disk. + this->get_selected_preset().save(); } void PresetCollection::delete_current_preset() { const Preset &selected = this->get_selected_preset(); - if (selected.is_default || selected.is_external) + if (selected.is_default) return; - // Erase the preset file. - boost::nowide::remove(selected.file.c_str()); + if (! selected.is_external) { + // Erase the preset file. + boost::nowide::remove(selected.file.c_str()); + } // Remove the preset from the list. m_presets.erase(m_presets.begin() + m_idx_selected); // Find the next visible preset. - m_presets.front().is_visible = ! m_default_suppressed || m_presets.size() > 1; - for (; m_idx_selected < m_presets.size() && ! m_presets[m_idx_selected].is_visible; ++ m_idx_selected) ; - if (m_idx_selected == m_presets.size()) - m_idx_selected = this->first_visible_idx(); - m_edited_preset = m_presets[m_idx_selected]; + size_t new_selected_idx = m_idx_selected; + if (new_selected_idx < m_presets.size()) + for (; new_selected_idx < m_presets.size() && ! m_presets[new_selected_idx].is_visible; ++ new_selected_idx) ; + if (new_selected_idx == m_presets.size()) + for (--new_selected_idx; new_selected_idx > 0 && !m_presets[new_selected_idx].is_visible; --new_selected_idx); + this->select_preset(new_selected_idx); } bool PresetCollection::load_bitmap_default(const std::string &file_name) @@ -337,7 +352,7 @@ Preset* PresetCollection::find_preset(const std::string &name, bool first_visibl // Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible. size_t PresetCollection::first_visible_idx() const { - size_t idx = 0; + size_t idx = m_default_suppressed ? 1 : 0; for (; idx < this->m_presets.size(); ++ idx) if (m_presets[idx].is_visible) break; @@ -438,11 +453,13 @@ bool PresetCollection::update_dirty_ui(wxChoice *ui) Preset& PresetCollection::select_preset(size_t idx) { + for (Preset &preset : m_presets) + preset.is_dirty = false; if (idx >= m_presets.size()) idx = first_visible_idx(); m_idx_selected = idx; m_edited_preset = m_presets[idx]; - m_presets.front().is_visible = ! m_default_suppressed || m_idx_selected > 0; + m_presets.front().is_visible = ! m_default_suppressed || m_idx_selected == 0; return m_presets[idx]; } @@ -458,7 +475,7 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b idx = it - m_presets.begin(); else { // Find the first visible preset. - for (size_t i = 0; i < m_presets.size(); ++ i) + for (size_t i = m_default_suppressed ? 1 : 0; i < m_presets.size(); ++ i) if (m_presets[i].is_visible) { idx = i; break; diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp index 85280eccf..a9bc8812c 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -121,6 +121,7 @@ public: // Load a preset from an already parsed config file, insert it into the sorted sequence of presets // and select it, losing previous modifications. Preset& load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select = true); + Preset& load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select = true); // Save the preset under a new name. If the name is different from the old one, // a new preset is stored into the list of presets. diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 4abb7a88f..4cfeb5fad 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -1,3 +1,6 @@ +//#undef NDEBUGc +#include + #include "PresetBundle.hpp" #include @@ -17,10 +20,10 @@ #include #include +#include "../../libslic3r/libslic3r.h" +#include "../../libslic3r/PlaceholderParser.hpp" #include "../../libslic3r/Utils.hpp" -#include - namespace Slic3r { PresetBundle::PresetBundle() : @@ -117,6 +120,15 @@ void PresetBundle::export_selections(AppConfig &config) config.set("presets", "printer", printers.get_selected_preset().name); } +void PresetBundle::export_selections(PlaceholderParser &pp) +{ + assert(filament_presets.size() >= 1); + assert(filament_presets.size() > 1 || filaments.get_selected_preset().name == filament_presets.front()); + pp.set("print_preset", prints.get_selected_preset().name); + pp.set("filament_preset", filament_presets); + pp.set("printer_preset", printers.get_selected_preset().name); +} + bool PresetBundle::load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible) { bool loaded_compatible = m_bitmapCompatible ->LoadFile( @@ -193,6 +205,15 @@ DynamicPrintConfig PresetBundle::full_config() const // If the file is loaded successfully, its print / filament / printer profiles will be activated. void PresetBundle::load_config_file(const std::string &path) { + if (boost::iends_with(path, ".gcode") || boost::iends_with(path, ".g")) { + DynamicPrintConfig config; + config.apply(FullPrintConfig::defaults()); + config.load_from_gcode(path); + Preset::normalize(config); + load_config_file_config(path, std::move(config)); + return; + } + // 1) Try to load the config file into a boost property tree. boost::property_tree::ptree tree; try { @@ -211,36 +232,37 @@ void PresetBundle::load_config_file(const std::string &path) throw std::runtime_error(std::string("Unknown configuration file type: ") + path); case CONFIG_FILE_TYPE_APP_CONFIG: throw std::runtime_error(std::string("Invalid configuration file: ") + path + ". This is an application config file."); - case CONFIG_FILE_TYPE_CONFIG: - load_config_file_config(path, tree); - break; + case CONFIG_FILE_TYPE_CONFIG: + { + // Initialize a config from full defaults. + DynamicPrintConfig config; + config.apply(FullPrintConfig::defaults()); + config.load(tree); + Preset::normalize(config); + load_config_file_config(path, std::move(config)); + break; + } case CONFIG_FILE_TYPE_CONFIG_BUNDLE: - load_config_file_config_bundle(path, tree); + load_config_file_config_bundle(path, tree); break; } } // Load a config file from a boost property_tree. This is a private method called from load_config_file. -void PresetBundle::load_config_file_config(const std::string &path, const boost::property_tree::ptree &tree) +void PresetBundle::load_config_file_config(const std::string &path, const DynamicPrintConfig &config) { - // 1) Initialize a config from full defaults. - DynamicPrintConfig config; - config.apply(FullPrintConfig()); - config.load(tree); - Preset::normalize(config); - - // 2) Create a name from the file name. + // 1) Create a name from the file name. // Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles. std::string name = boost::filesystem::path(path).filename().string(); - // 3) If the loading succeeded, split and load the config into print / filament / printer settings. + // 2) If the loading succeeded, split and load the config into print / filament / printer settings. // First load the print and printer presets. for (size_t i_group = 0; i_group < 2; ++ i_group) { PresetCollection &presets = (i_group == 0) ? this->prints : this->printers; presets.load_preset(path, name, config).is_external = true; } - // Now load the filaments. If there are multiple filament presets, split them and load them. + // 3) Now load the filaments. If there are multiple filament presets, split them and load them. auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); auto *filament_diameter = dynamic_cast(config.option("filament_diameter")); size_t num_extruders = std::min(nozzle_diameter->values.size(), filament_diameter->values.size()); @@ -253,7 +275,7 @@ void PresetBundle::load_config_file_config(const std::string &path, const boost: std::vector configs(num_extruders, this->filaments.default_preset().config); // loop through options and scatter them into configs. for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { - const ConfigOption *other_opt = config.option(key, false); + const ConfigOption *other_opt = config.option(key); if (other_opt == nullptr) continue; if (other_opt->is_scalar()) { @@ -273,7 +295,7 @@ void PresetBundle::load_config_file_config(const std::string &path, const boost: else sprintf(suffix, " (%d)", i); // Load all filament presets, but only select the first one in the preset dialog. - this->filaments.load_preset(path, name + suffix, configs[i], i == 0).is_external = true; + this->filaments.load_preset(path, name + suffix, std::move(configs[i]), i == 0).is_external = true; filament_presets.emplace_back(name + suffix); } } @@ -301,7 +323,7 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const // Generate a new unique name. } if (! new_name.empty()) - this->prints.load_preset(path, new_name, tmp_bundle.prints.get_selected_preset().config); + this->prints.load_preset(path, new_name, std::move(tmp_bundle.prints.get_selected_preset().config)); } } @@ -369,8 +391,9 @@ size_t PresetBundle::load_configbundle(const std::string &path) DynamicPrintConfig config(presets->default_preset().config); for (auto &kvp : section.second) config.set_deserialize(kvp.first, kvp.second.data()); + Preset::normalize(config); // Load the preset into the list of presets, save it to disk. - presets->load_preset(Slic3r::config_path(presets->name(), preset_name), preset_name, config, false).save(); + presets->load_preset(Slic3r::config_path(presets->name(), preset_name), preset_name, std::move(config), false).save(); ++ presets_loaded; } } @@ -386,7 +409,7 @@ size_t PresetBundle::load_configbundle(const std::string &path) this->update_multi_material_filament_presets(); for (size_t i = 0; i < std::min(this->filament_presets.size(), active_filaments.size()); ++ i) - this->filament_presets[i] = filaments.first_visible().name; + this->filament_presets[i] = filaments.find_preset(active_filaments[i], true)->name; return presets_loaded; } @@ -396,7 +419,6 @@ void PresetBundle::update_multi_material_filament_presets() auto *nozzle_diameter = static_cast(printers.get_edited_preset().config.option("nozzle_diameter")); size_t num_extruders = nozzle_diameter->values.size(); // Verify validity of the current filament presets. - printf("PresetBundle::update_multi_material_filament_presets, old: %d, new: %d\n", int(this->filament_presets.size()), int(num_extruders)); for (size_t i = 0; i < std::min(this->filament_presets.size(), num_extruders); ++ i) this->filament_presets[i] = this->filaments.find_preset(this->filament_presets[i], true)->name; // Append the rest of filament presets. diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/xs/src/slic3r/GUI/PresetBundle.hpp index 7add3055d..b1077c11c 100644 --- a/xs/src/slic3r/GUI/PresetBundle.hpp +++ b/xs/src/slic3r/GUI/PresetBundle.hpp @@ -6,6 +6,8 @@ namespace Slic3r { +class PlaceholderParser; + // Bundle of Print + Filament + Printer presets. class PresetBundle { @@ -23,6 +25,8 @@ public: void load_selections(const AppConfig &config); // Export selections (current print, current filaments, current printer) into config.ini void export_selections(AppConfig &config); + // Export selections (current print, current filaments, current printer) into a placeholder parser. + void export_selections(PlaceholderParser &pp); PresetCollection prints; PresetCollection filaments; @@ -31,6 +35,9 @@ public: // extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size() std::vector filament_presets; + bool has_defauls_only() const + { return prints.size() <= 1 && filaments.size() <= 1 && printers.size() <= 1; } + DynamicPrintConfig full_config() const; // Load an external config file containing the print, filament and printer presets. @@ -66,7 +73,7 @@ public: void update_multi_material_filament_presets(); private: - void load_config_file_config(const std::string &path, const boost::property_tree::ptree &tree); + void load_config_file_config(const std::string &path, const DynamicPrintConfig &config); void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree); bool load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible); diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index 875c49e26..ce3c178a1 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -14,6 +14,9 @@ void disable_screensaver() void enable_screensaver() %code{% Slic3r::GUI::enable_screensaver(); %}; +std::vector scan_serial_ports() + %code{% RETVAL=Slic3r::GUI::scan_serial_ports(); %}; + bool debugged() %code{% RETVAL=Slic3r::GUI::debugged(); %}; diff --git a/xs/xsp/GUI_AppConfig.xsp b/xs/xsp/GUI_AppConfig.xsp index d220c3580..8162b9a5e 100644 --- a/xs/xsp/GUI_AppConfig.xsp +++ b/xs/xsp/GUI_AppConfig.xsp @@ -13,8 +13,22 @@ void reset(); void set_defaults(); - void load(); - void save(); + void load() + %code%{ + try { + THIS->load(); + } catch (std::exception& e) { + croak("Loading an application config file failed:\n%s\n", e.what()); + } + %}; + void save() + %code%{ + try { + THIS->save(); + } catch (std::exception& e) { + croak("Saving an application config file failed:\n%s\n", e.what()); + } + %}; bool exists(); bool dirty(); diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp index 34184a588..e76b5c704 100644 --- a/xs/xsp/GUI_Preset.xsp +++ b/xs/xsp/GUI_Preset.xsp @@ -52,10 +52,25 @@ bool update_dirty_ui(SV *ui) %code%{ RETVAL = THIS->update_dirty_ui((wxChoice*)wxPli_sv_2_object(aTHX_ ui, "Wx::Choice")); %}; + void select_preset(int idx); bool select_preset_by_name(char *name) %code%{ RETVAL = THIS->select_preset_by_name(name, true); %}; - void save_current_preset(char *new_name); - void delete_current_preset(); + void save_current_preset(char *new_name) + %code%{ + try { + THIS->save_current_preset(new_name); + } catch (std::exception& e) { + croak("Error saving a preset %s:\n%s\n", new_name, e.what()); + } + %}; + void delete_current_preset() + %code%{ + try { + THIS->delete_current_preset(); + } catch (std::exception& e) { + croak("Error deleting a preset file %s:\n%s\n", THIS->get_selected_preset().file.c_str(), e.what()); + } + %}; %{ @@ -88,24 +103,61 @@ PresetCollection::presets_hash() %} }; - %name{Slic3r::GUI::PresetBundle} class PresetBundle { PresetBundle(); ~PresetBundle(); - void setup_directories(); - void load_presets(const char *dir_path); - void load_config_file(const char *path); - size_t load_configbundle(const char *path); - void export_configbundle(char *path); + void setup_directories() + %code%{ + try { + THIS->setup_directories(); + } catch (std::exception& e) { + croak("Cannot create configuration directories:\n%s\n", e.what()); + } + %}; + void load_presets(const char *dir_path) + %code%{ + try { + THIS->load_presets(dir_path); + } catch (std::exception& e) { + croak("Loading of Slic3r presets from %s failed:\n%s\n", dir_path, e.what()); + } + %}; + void load_config_file(const char *path) + %code%{ + try { + THIS->load_config_file(path); + } catch (std::exception& e) { + croak("Loading a configuration file %s failed:\n%s\n", path, e.what()); + } + %}; + size_t load_configbundle(const char *path) + %code%{ + try { + RETVAL = THIS->load_configbundle(path); + } catch (std::exception& e) { + croak("Loading of a config bundle %s failed:\n%s\n", path, e.what()); + } + %}; + void export_configbundle(char *path) + %code%{ + try { + THIS->export_configbundle(path); + } catch (std::exception& e) { + croak("Export of a config bundle %s failed:\n%s\n", path, e.what()); + } + %}; + void set_default_suppressed(bool default_suppressed); void load_selections (AppConfig *config) %code%{ THIS->load_selections(*config); %}; void export_selections(AppConfig *config) %code%{ THIS->export_selections(*config); %}; + void export_selections_pp(PlaceholderParser *pp) %code%{ THIS->export_selections(*pp); %}; Ref print() %code%{ RETVAL = &THIS->prints; %}; Ref filament() %code%{ RETVAL = &THIS->filaments; %}; Ref printer() %code%{ RETVAL = &THIS->printers; %}; + bool has_defauls_only(); std::vector filament_presets() %code%{ RETVAL = THIS->filament_presets; %}; void set_filament_preset(int idx, const char *name); From df5628422c9e765cf9ad3779d059868a3239b41d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 2 Nov 2017 16:29:03 +0100 Subject: [PATCH 13/83] Fixed serial port enumeration on non-Win32 systems. --- xs/src/slic3r/GUI/GUI.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 12bf48014..91de87423 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -3,6 +3,7 @@ #include #include +#include #if __APPLE__ #import @@ -96,9 +97,9 @@ std::vector scan_serial_ports() } #else // UNIX and OS X - boost::filesystem::recursive_directory_iterator end; + boost::filesystem::directory_iterator end; std::initializer_list prefixes { "ttyUSB" , "ttyACM", "tty.", "cu.", "rfcomm" }; - for (boost::filesystem::recursive_directory_iterator it_path(boost::filesystem::path("/dev")); + for (boost::filesystem::directory_iterator it_path(boost::filesystem::path("/dev")); it_path != end; ++ it_path) for (const char *prefix : prefixes) if (boost::starts_with(it_path->string(), std::string("/dev/") + prefix)) { From 7551006102ff8ca0b45d66d3f9ac980cb9032e01 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 2 Nov 2017 16:35:46 +0100 Subject: [PATCH 14/83] Another fix or Unices of the previous commit. --- xs/src/slic3r/GUI/GUI.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 91de87423..90cb0a93c 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -97,15 +97,16 @@ std::vector scan_serial_ports() } #else // UNIX and OS X - boost::filesystem::directory_iterator end; std::initializer_list prefixes { "ttyUSB" , "ttyACM", "tty.", "cu.", "rfcomm" }; - for (boost::filesystem::directory_iterator it_path(boost::filesystem::path("/dev")); - it_path != end; ++ it_path) - for (const char *prefix : prefixes) - if (boost::starts_with(it_path->string(), std::string("/dev/") + prefix)) { - out.emplace_back(path); + for (auto &file : boost::filesystem::directory_iterator(boost::filesystem::path("/dev"))) { + std::string name = it->path().filename().string(); + for (const char *prefix : prefixes) { + if (boost::starts_with(name, prefix)) { + out.emplace_back(it->path().string()); break; } + } + } #endif out.erase(std::remove_if(out.begin(), out.end(), From 4ab4a9afe8e642a5a5547240f6de22db0d6dfae4 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 2 Nov 2017 16:40:25 +0100 Subject: [PATCH 15/83] Fighting with boost::filesystem::directory_iterator --- xs/src/slic3r/GUI/GUI.cpp | 6 +++--- xs/src/slic3r/GUI/Preset.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 90cb0a93c..63cc7749d 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -98,11 +98,11 @@ std::vector scan_serial_ports() #else // UNIX and OS X std::initializer_list prefixes { "ttyUSB" , "ttyACM", "tty.", "cu.", "rfcomm" }; - for (auto &file : boost::filesystem::directory_iterator(boost::filesystem::path("/dev"))) { - std::string name = it->path().filename().string(); + for (auto &dir_entry : boost::filesystem::directory_iterator(boost::filesystem::path("/dev"))) { + std::string name = dir_entry.path().filename().string(); for (const char *prefix : prefixes) { if (boost::starts_with(name, prefix)) { - out.emplace_back(it->path().string()); + out.emplace_back(dir_entry.path().string()); break; } } diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 5b42b27a1..05d1c3927 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -241,14 +241,14 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri m_dir_path = dir.string(); m_presets.erase(m_presets.begin()+1, m_presets.end()); t_config_option_keys keys = this->default_preset().config.keys(); - for (auto &file : boost::filesystem::directory_iterator(dir)) - if (boost::filesystem::is_regular_file(file.status()) && boost::algorithm::iends_with(file.path().filename().string(), ".ini")) { - std::string name = file.path().filename().string(); + for (auto &dir_entry : boost::filesystem::directory_iterator(dir)) + if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) { + std::string name = dir_entry.path().filename().string(); // Remove the .ini suffix. name.erase(name.size() - 4); try { Preset preset(m_type, name, false); - preset.file = file.path().string(); + preset.file = dir_entry.path().string(); preset.load(keys); m_presets.emplace_back(preset); } catch (const boost::filesystem::filesystem_error &err) { From ee84e1773d76f2e993703f4f3d05d13c03c0b2b5 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 2 Nov 2017 16:50:08 +0100 Subject: [PATCH 16/83] Fixed an incorrect comment separator in generated g-code. --- xs/src/libslic3r/GCode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index b66861a4a..93f324a1d 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -462,7 +462,7 @@ bool GCode::_do_export(Print &print, FILE *file) m_enable_extrusion_role_markers = (bool)m_pressure_equalizer; // Write information on the generator. - fprintf(file, "# %s\n\n", Slic3r::header_slic3r_generated().c_str()); + fprintf(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str()); // Write notes (content of the Print Settings tab -> Notes) { std::list lines; From dd9e1aff7023dab9e33f4710bd808387f103e998 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 2 Nov 2017 17:31:24 +0100 Subject: [PATCH 17/83] When loading Slic3r.ini of older Slic3rs, remove the .ini suffix from the preset names. This fixes https://github.com/prusa3d/Slic3r/issues/367 --- xs/src/slic3r/GUI/PresetBundle.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 4cfeb5fad..fc50f98e9 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -85,13 +85,21 @@ void PresetBundle::load_presets(const std::string &dir_path) this->update_multi_material_filament_presets(); } +static inline std::string remove_ini_suffix(const std::string &name) +{ + std::string out = name; + if (boost::iends_with(out, ".ini")) + out.erase(out.end() - 4, out.end()); + return out; +} + // Load selections (current print, current filaments, current printer) from config.ini // This is done just once on application start up. void PresetBundle::load_selections(const AppConfig &config) { - prints.select_preset_by_name(config.get("presets", "print"), true); - filaments.select_preset_by_name(config.get("presets", "filament"), true); - printers.select_preset_by_name(config.get("presets", "printer"), true); + prints.select_preset_by_name(remove_ini_suffix(config.get("presets", "print")), true); + filaments.select_preset_by_name(remove_ini_suffix(config.get("presets", "filament")), true); + printers.select_preset_by_name(remove_ini_suffix(config.get("presets", "printer")), true); auto *nozzle_diameter = dynamic_cast(printers.get_selected_preset().config.option("nozzle_diameter")); size_t num_extruders = nozzle_diameter->values.size(); this->set_filament_preset(0, filaments.get_selected_preset().name); @@ -100,7 +108,7 @@ void PresetBundle::load_selections(const AppConfig &config) sprintf(name, "filament_%d", i); if (! config.has("presets", name)) break; - this->set_filament_preset(i, config.get("presets", name)); + this->set_filament_preset(i, remove_ini_suffix(config.get("presets", name))); } } From b11d9708edd7ad4041c2a91b865430047628a6f8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 2 Nov 2017 21:51:06 +0100 Subject: [PATCH 18/83] Updated Controller after the presets C++ port. --- lib/Slic3r/GUI/Controller.pm | 53 ++++++++++++++---------------------- lib/Slic3r/GUI/MainFrame.pm | 4 +-- lib/Slic3r/GUI/Plater.pm | 2 +- xs/xsp/Config.xsp | 2 ++ xs/xsp/GUI_Preset.xsp | 13 --------- 5 files changed, 26 insertions(+), 48 deletions(-) diff --git a/lib/Slic3r/GUI/Controller.pm b/lib/Slic3r/GUI/Controller.pm index 6f2636d20..6aa7b34cb 100644 --- a/lib/Slic3r/GUI/Controller.pm +++ b/lib/Slic3r/GUI/Controller.pm @@ -10,6 +10,7 @@ use utf8; use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button :icon :dialog); use Wx::Event qw(EVT_CLOSE EVT_LEFT_DOWN EVT_MENU); use base qw(Wx::ScrolledWindow Class::Accessor); +use List::Util qw(first); __PACKAGE__->mk_accessors(qw(_selected_printer_preset)); @@ -38,21 +39,19 @@ sub new { if $btn->can('SetToolTipString'); EVT_LEFT_DOWN($btn, sub { - my $menu = Wx::Menu->new; - my $presets = wxTheApp->{preset_bundle}->printer->presets_hash; - + my $menu = Wx::Menu->new; + my @panels = $self->print_panels; # remove printers that already exist - my @panels = $self->print_panels; - delete $presets->{$_} for map $_->printer_name, @panels; - - foreach my $preset_name (sort keys %{$presets}) { - my $config = Slic3r::Config->load($presets->{$preset_name}); - next if !$config->serial_port; - + # update configs of currently loaded print panels + foreach my $preset (@{wxTheApp->{preset_bundle}->printer}) { + my $preset_name = $preset->name; + next if ! $preset->config->serial_port || + defined first { defined $_ && $_->printer_name eq $preset_name } @panels; + my $myconfig = $preset->config->clone_only(\@ConfigOptions); my $id = &Wx::NewId(); $menu->Append($id, $preset_name); EVT_MENU($menu, $id, sub { - $self->add_printer($preset_name, $config); + $self->add_printer($preset_name, $myconfig); }); } $self->PopupMenu($menu, $btn->GetPosition); @@ -98,12 +97,8 @@ sub OnActivate { my ($self) = @_; # get all available presets - my %presets = (); - { - my $all = wxTheApp->{preset_bundle}->printer->presets_hash; - my %configs = map { my $name = $_; $name => Slic3r::Config->load($all->{$name}) } keys %{$all}; - %presets = map { $_ => $configs{$_} } grep $configs{$_}->serial_port, keys %{$all}; - } + my %presets = map { $_->name => $_->config->clone_only(\@ConfigOptions) } + grep { $_->config->serial_port } @{wxTheApp->{preset_bundle}->printer}; # decide which ones we want to keep my %active = (); @@ -177,25 +172,19 @@ sub print_panels { map $_->GetWindow, $self->{sizer}->GetChildren; } -# Called by -# Slic3r::GUI::Tab::Print::_on_presets_changed -# Slic3r::GUI::Tab::Filament::_on_presets_changed -# Slic3r::GUI::Tab::Printer::_on_presets_changed -# when the presets are loaded or the user select another preset. +# Called by Slic3r::GUI::Tab::Printer::_on_presets_changed +# when the presets are loaded or the user selects another preset. sub update_presets { - my ($self, $group, $presets, $default_suppressed, $selected, $is_dirty) = @_; - + my ($self, $presets) = @_; # update configs of currently loaded print panels + my @presets = @$presets; foreach my $panel ($self->print_panels) { - foreach my $preset (@$presets) { - if ($panel->printer_name eq $preset->name) { - my $config = $preset->config(\@ConfigOptions); - $panel->config->apply($config); - } - } + my $preset = $presets->find_preset($panel->printer_name, 0); + $panel->config($preset->config->clone_only(\@ConfigOptions)) + if defined $preset; } - - $self->_selected_printer_preset($presets->[$selected]->name); + + $self->_selected_printer_preset($presets->get_selected_preset->name); } 1; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index c7f3f1cf9..836c3e9d0 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -128,8 +128,8 @@ sub _init_tabpanel { # Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs. $self->{plater}->update_presets($tab_name, @_); $self->{plater}->on_config_change($tab->{presets}->get_current_preset->config); - if ($self->{controller}) { - $self->{controller}->update_presets($tab_name, @_); + if ($self->{controller} && $tab_name eq 'printer') { + $self->{controller}->update_presets(@_); } } }); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 06adbe525..6b853e410 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1535,7 +1535,7 @@ sub export_amf { sub _get_export_file { my ($self, $format) = @_; my $suffix = $format eq 'STL' ? '.stl' : '.amf.xml'; - my $output_file = $self->{print}->output_filepath($main::opt{output}); + my $output_file = $self->{print}->output_filepath($main::opt{output} // ''); $output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/; my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file), basename($output_file), &Slic3r::GUI::MODEL_WILDCARD, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index f78b3bf60..6adfc49a2 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -11,6 +11,8 @@ static DynamicPrintConfig* new_from_defaults(); static DynamicPrintConfig* new_from_defaults_keys(std::vector keys); DynamicPrintConfig* clone() %code{% RETVAL = new DynamicPrintConfig(*THIS); %}; + DynamicPrintConfig* clone_only(std::vector keys) + %code{% RETVAL = new DynamicPrintConfig(); RETVAL->apply_only(*THIS, keys, true); %}; bool has(t_config_option_key opt_key); SV* as_hash() %code{% RETVAL = ConfigBase__as_hash(THIS); %}; diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp index e76b5c704..a084d7f2a 100644 --- a/xs/xsp/GUI_Preset.xsp +++ b/xs/xsp/GUI_Preset.xsp @@ -87,19 +87,6 @@ PresetCollection::arrayref() OUTPUT: RETVAL -SV* -PresetCollection::presets_hash() - CODE: - HV* hv = newHV(); - for (size_t i = 1; i < THIS->size(); ++ i) { - const Slic3r::Preset &preset = THIS->preset(i); - if (! preset.is_default && ! preset.is_external) - (void)hv_store(hv, preset.name.c_str(), - int(preset.name.size()), newSVpvn_utf8(preset.file.c_str(), preset.file.size(), true), 0); - } - RETVAL = (SV*)newRV_noinc((SV*)hv); - OUTPUT: - RETVAL - %} }; From e6ecb77d9add74311123ef70c36dd832dd72eaa0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 3 Nov 2017 19:14:33 +0100 Subject: [PATCH 19/83] Fixed loading of configs and configs from g-codes. --- xs/src/slic3r/GUI/Preset.cpp | 4 ++-- xs/src/slic3r/GUI/PresetBundle.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 05d1c3927..596e07c8e 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -115,7 +115,7 @@ DynamicPrintConfig& Preset::load(const std::vector &keys) if (! this->is_default) { // Load the preset file, apply preset values on top of defaults. try { - this->config.load(this->file); + this->config.load_from_ini(this->file); Preset::normalize(this->config); } catch (const std::ifstream::failure&) { throw std::runtime_error(std::string("The selected preset does not exist anymore: ") + this->file); @@ -264,7 +264,7 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select) { DynamicPrintConfig cfg(this->default_preset().config); - cfg.apply(config, true); + cfg.apply_only(config, cfg.keys(), true); return this->load_preset(path, name, std::move(cfg)); } diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index fc50f98e9..915bec6aa 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -295,7 +295,7 @@ void PresetBundle::load_config_file_config(const std::string &path, const Dynami } } // Load the configs into this->filaments and make them active. - filament_presets.clear(); + this->filament_presets.clear(); for (size_t i = 0; i < configs.size(); ++ i) { char suffix[64]; if (i == 0) @@ -304,7 +304,7 @@ void PresetBundle::load_config_file_config(const std::string &path, const Dynami sprintf(suffix, " (%d)", i); // Load all filament presets, but only select the first one in the preset dialog. this->filaments.load_preset(path, name + suffix, std::move(configs[i]), i == 0).is_external = true; - filament_presets.emplace_back(name + suffix); + this->filament_presets.emplace_back(name + suffix); } } } From 71b58e24a9671f6a6d9f1e0703505090fd755266 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 6 Nov 2017 15:43:56 +0100 Subject: [PATCH 20/83] Fixed a regression bug in the "first layer extrusion width" G-code comment. https://github.com/prusa3d/Slic3r/issues/566 --- xs/src/libslic3r/GCode.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 93f324a1d..f46d35657 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -480,6 +480,7 @@ bool GCode::_do_export(Print &print, FILE *file) { const PrintObject *first_object = print.objects.front(); const double layer_height = first_object->config.layer_height.value; + const double first_layer_height = first_object->config.first_layer_height.get_abs_value(layer_height); for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) { auto region = print.regions[region_id]; fprintf(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width); @@ -490,7 +491,7 @@ bool GCode::_do_export(Print &print, FILE *file) if (print.has_support_material()) fprintf(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width); if (print.config.first_layer_extrusion_width.value > 0) - fprintf(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, true, -1., *first_object).width); + fprintf(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width); fprintf(file, "\n"); } } From e4614f301cee9ec8b4e13121e3963e6571a29da8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 9 Nov 2017 10:05:37 +0100 Subject: [PATCH 21/83] ConfigBase with option template to do the type conversion of resolved ConfigOption. --- xs/src/libslic3r/Config.hpp | 142 +++++++++++++++++++++--------------- 1 file changed, 85 insertions(+), 57 deletions(-) diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 30fc00b68..1d2a5c17d 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -245,10 +245,11 @@ public: ConfigOptionFloat() : ConfigOptionSingle(0) {} explicit ConfigOptionFloat(double _value) : ConfigOptionSingle(_value) {} - ConfigOptionType type() const override { return coFloat; } - double getFloat() const override { return this->value; } - ConfigOption* clone() const override { return new ConfigOptionFloat(*this); } - bool operator==(const ConfigOptionFloat &rhs) const { return this->value == rhs.value; } + static ConfigOptionType static_type() { return coFloat; } + ConfigOptionType type() const override { return static_type(); } + double getFloat() const override { return this->value; } + ConfigOption* clone() const override { return new ConfigOptionFloat(*this); } + bool operator==(const ConfigOptionFloat &rhs) const { return this->value == rhs.value; } std::string serialize() const override { @@ -279,9 +280,10 @@ public: explicit ConfigOptionFloats(size_t n, double value) : ConfigOptionVector(n, value) {} explicit ConfigOptionFloats(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} - ConfigOptionType type() const override { return coFloats; } - ConfigOption* clone() const override { return new ConfigOptionFloats(*this); } - bool operator==(const ConfigOptionFloats &rhs) const { return this->values == rhs.values; } + static ConfigOptionType static_type() { return coFloats; } + ConfigOptionType type() const override { return static_type(); } + ConfigOption* clone() const override { return new ConfigOptionFloats(*this); } + bool operator==(const ConfigOptionFloats &rhs) const { return this->values == rhs.values; } std::string serialize() const override { @@ -334,11 +336,12 @@ public: explicit ConfigOptionInt(int value) : ConfigOptionSingle(value) {} explicit ConfigOptionInt(double _value) : ConfigOptionSingle(int(floor(_value + 0.5))) {} - ConfigOptionType type() const override { return coInt; } - int getInt() const override { return this->value; } - void setInt(int val) { this->value = val; } - ConfigOption* clone() const override { return new ConfigOptionInt(*this); } - bool operator==(const ConfigOptionInt &rhs) const { return this->value == rhs.value; } + static ConfigOptionType static_type() { return coInt; } + ConfigOptionType type() const override { return static_type(); } + int getInt() const override { return this->value; } + void setInt(int val) { this->value = val; } + ConfigOption* clone() const override { return new ConfigOptionInt(*this); } + bool operator==(const ConfigOptionInt &rhs) const { return this->value == rhs.value; } std::string serialize() const override { @@ -369,10 +372,11 @@ public: explicit ConfigOptionInts(size_t n, int value) : ConfigOptionVector(n, value) {} explicit ConfigOptionInts(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} - ConfigOptionType type() const override { return coInts; } - ConfigOption* clone() const override { return new ConfigOptionInts(*this); } - ConfigOptionInts& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionInts &rhs) const { return this->values == rhs.values; } + static ConfigOptionType static_type() { return coInts; } + ConfigOptionType type() const override { return static_type(); } + ConfigOption* clone() const override { return new ConfigOptionInts(*this); } + ConfigOptionInts& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionInts &rhs) const { return this->values == rhs.values; } std::string serialize() const override { std::ostringstream ss; @@ -417,10 +421,11 @@ public: ConfigOptionString() : ConfigOptionSingle("") {} explicit ConfigOptionString(const std::string &value) : ConfigOptionSingle(value) {} - ConfigOptionType type() const override { return coString; } - ConfigOption* clone() const override { return new ConfigOptionString(*this); } - ConfigOptionString& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionString &rhs) const { return this->value == rhs.value; } + static ConfigOptionType static_type() { return coString; } + ConfigOptionType type() const override { return static_type(); } + ConfigOption* clone() const override { return new ConfigOptionString(*this); } + ConfigOptionString& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionString &rhs) const { return this->value == rhs.value; } std::string serialize() const override { @@ -442,10 +447,11 @@ public: explicit ConfigOptionStrings(size_t n, const std::string &value) : ConfigOptionVector(n, value) {} explicit ConfigOptionStrings(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} - ConfigOptionType type() const override { return coStrings; } - ConfigOption* clone() const override { return new ConfigOptionStrings(*this); } - ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionStrings &rhs) const { return this->values == rhs.values; } + static ConfigOptionType static_type() { return coStrings; } + ConfigOptionType type() const override { return static_type(); } + ConfigOption* clone() const override { return new ConfigOptionStrings(*this); } + ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionStrings &rhs) const { return this->values == rhs.values; } std::string serialize() const override { @@ -471,11 +477,12 @@ public: ConfigOptionPercent() : ConfigOptionFloat(0) {} explicit ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {} - ConfigOptionType type() const override { return coPercent; } - ConfigOption* clone() const override { return new ConfigOptionPercent(*this); } - ConfigOptionPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionPercent &rhs) const { return this->value == rhs.value; } - double get_abs_value(double ratio_over) const { return ratio_over * this->value / 100; } + static ConfigOptionType static_type() { return coPercent; } + ConfigOptionType type() const override { return static_type(); } + ConfigOption* clone() const override { return new ConfigOptionPercent(*this); } + ConfigOptionPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionPercent &rhs) const { return this->value == rhs.value; } + double get_abs_value(double ratio_over) const { return ratio_over * this->value / 100; } std::string serialize() const override { @@ -503,10 +510,11 @@ public: explicit ConfigOptionPercents(size_t n, double value) : ConfigOptionFloats(n, value) {} explicit ConfigOptionPercents(std::initializer_list il) : ConfigOptionFloats(std::move(il)) {} - ConfigOptionType type() const override { return coPercents; } - ConfigOption* clone() const override { return new ConfigOptionPercents(*this); } - ConfigOptionPercents& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionPercents &rhs) const { return this->values == rhs.values; } + static ConfigOptionType static_type() { return coPercents; } + ConfigOptionType type() const override { return static_type(); } + ConfigOption* clone() const override { return new ConfigOptionPercents(*this); } + ConfigOptionPercents& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionPercents &rhs) const { return this->values == rhs.values; } std::string serialize() const override { @@ -556,7 +564,8 @@ public: ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {} explicit ConfigOptionFloatOrPercent(double _value, bool _percent) : ConfigOptionPercent(_value), percent(_percent) {} - ConfigOptionType type() const override { return coFloatOrPercent; } + static ConfigOptionType static_type() { return coFloatOrPercent; } + ConfigOptionType type() const override { return static_type(); } ConfigOption* clone() const override { return new ConfigOptionFloatOrPercent(*this); } ConfigOptionFloatOrPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; } bool operator==(const ConfigOptionFloatOrPercent &rhs) const @@ -596,10 +605,11 @@ public: ConfigOptionPoint() : ConfigOptionSingle(Pointf(0,0)) {} explicit ConfigOptionPoint(const Pointf &value) : ConfigOptionSingle(value) {} - ConfigOptionType type() const override { return coPoint; } - ConfigOption* clone() const override { return new ConfigOptionPoint(*this); } - ConfigOptionPoint& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionPoint &rhs) const { return this->value == rhs.value; } + static ConfigOptionType static_type() { return coPoint; } + ConfigOptionType type() const override { return static_type(); } + ConfigOption* clone() const override { return new ConfigOptionPoint(*this); } + ConfigOptionPoint& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionPoint &rhs) const { return this->value == rhs.value; } std::string serialize() const override { @@ -629,10 +639,11 @@ public: explicit ConfigOptionPoints(size_t n, const Pointf &value) : ConfigOptionVector(n, value) {} explicit ConfigOptionPoints(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} - ConfigOptionType type() const override { return coPoints; } - ConfigOption* clone() const override { return new ConfigOptionPoints(*this); } - ConfigOptionPoints& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionPoints &rhs) const { return this->values == rhs.values; } + static ConfigOptionType static_type() { return coPoints; } + ConfigOptionType type() const override { return static_type(); } + ConfigOption* clone() const override { return new ConfigOptionPoints(*this); } + ConfigOptionPoints& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionPoints &rhs) const { return this->values == rhs.values; } std::string serialize() const override { @@ -685,11 +696,12 @@ public: ConfigOptionBool() : ConfigOptionSingle(false) {} explicit ConfigOptionBool(bool _value) : ConfigOptionSingle(_value) {} - ConfigOptionType type() const override { return coBool; } - bool getBool() const override { return this->value; } - ConfigOption* clone() const override { return new ConfigOptionBool(*this); } - ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionBool &rhs) const { return this->value == rhs.value; } + static ConfigOptionType static_type() { return coBool; } + ConfigOptionType type() const override { return static_type(); } + bool getBool() const override { return this->value; } + ConfigOption* clone() const override { return new ConfigOptionBool(*this); } + ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionBool &rhs) const { return this->value == rhs.value; } std::string serialize() const override { @@ -711,10 +723,11 @@ public: explicit ConfigOptionBools(size_t n, bool value) : ConfigOptionVector(n, (unsigned char)value) {} explicit ConfigOptionBools(std::initializer_list il) { values.reserve(il.size()); for (bool b : il) values.emplace_back((unsigned char)b); } - ConfigOptionType type() const override { return coBools; } - ConfigOption* clone() const override { return new ConfigOptionBools(*this); } - ConfigOptionBools& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionBools &rhs) const { return this->values == rhs.values; } + static ConfigOptionType static_type() { return coBools; } + ConfigOptionType type() const override { return static_type(); } + ConfigOption* clone() const override { return new ConfigOptionBools(*this); } + ConfigOptionBools& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionBools &rhs) const { return this->values == rhs.values; } bool& get_at(size_t i) { assert(! this->values.empty()); @@ -771,10 +784,11 @@ public: ConfigOptionEnum() : ConfigOptionSingle(static_cast(0)) {} explicit ConfigOptionEnum(T _value) : ConfigOptionSingle(_value) {} - ConfigOptionType type() const override { return coEnum; } - ConfigOption* clone() const override { return new ConfigOptionEnum(*this); } - ConfigOptionEnum& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionEnum &rhs) const { return this->value == rhs.value; } + static ConfigOptionType static_type() { return coEnum; } + ConfigOptionType type() const override { return static_type(); } + ConfigOption* clone() const override { return new ConfigOptionEnum(*this); } + ConfigOptionEnum& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionEnum &rhs) const { return this->value == rhs.value; } std::string serialize() const override { @@ -833,8 +847,9 @@ public: const t_config_enum_values* keys_map; - ConfigOptionType type() const override { return coEnum; } - ConfigOption* clone() const override { return new ConfigOptionEnumGeneric(*this); } + static ConfigOptionType static_type() { return coEnum; } + ConfigOptionType type() const override { return static_type(); } + ConfigOption* clone() const override { return new ConfigOptionEnumGeneric(*this); } ConfigOptionEnumGeneric& operator=(const ConfigOption *opt) { this->set(opt); return *this; } bool operator==(const ConfigOptionEnumGeneric &rhs) const { return this->value == rhs.value; } @@ -990,6 +1005,16 @@ public: { return const_cast(this)->option(opt_key, false); } ConfigOption* option(const t_config_option_key &opt_key, bool create = false) { return this->optptr(opt_key, create); } + template + TYPE* option(const t_config_option_key &opt_key, bool create = false) + { + ConfigOption *opt = this->optptr(opt_key, create); + assert(opt == nullptr || opt->type() == typename TYPE::static_type()); + return (opt == nullptr || opt->type() == typename TYPE::static_type()) ? nullptr : static_cast(opt); + } + template + const TYPE* option(const t_config_option_key &opt_key) const + { return const_cast(this)->option(opt_key, false); } // Apply all keys of other ConfigBase defined by this->def() to this ConfigBase. // An UnknownOptionException is thrown in case some option keys of other are not defined by this->def(), // or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set. @@ -1091,6 +1116,9 @@ public: int& opt_int(const t_config_option_key &opt_key, unsigned int idx) { return dynamic_cast(this->option(opt_key))->get_at(idx); } const int opt_int(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx); } + bool opt_bool(const t_config_option_key &opt_key) const { return dynamic_cast(this->option(opt_key))->value != 0; } + bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx) != 0; } + protected: DynamicConfig() {} From 5fb54ed1f30ffa57201022b513199ff303afed13 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 9 Nov 2017 10:06:25 +0100 Subject: [PATCH 22/83] Code beautification: PrintConfig tooltips were split to multiple lines. --- xs/src/libslic3r/PrintConfig.cpp | 458 +++++++++++++++++++++++-------- 1 file changed, 338 insertions(+), 120 deletions(-) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index fa5ea4a8d..e3bbb9eab 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -17,7 +17,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("avoid_crossing_perimeters", coBool); def->label = "Avoid crossing perimeters"; - def->tooltip = "Optimize travel moves in order to minimize the crossing of perimeters. This is mostly useful with Bowden extruders which suffer from oozing. This feature slows down both the print and the G-code generation."; + def->tooltip = "Optimize travel moves in order to minimize the crossing of perimeters. " + "This is mostly useful with Bowden extruders which suffer from oozing. " + "This feature slows down both the print and the G-code generation."; def->cli = "avoid-crossing-perimeters!"; def->default_value = new ConfigOptionBool(false); @@ -27,7 +29,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("bed_temperature", coInts); def->label = "Other layers"; - def->tooltip = "Bed temperature for layers after the first one. Set this to zero to disable bed temperature control commands in the output."; + def->tooltip = "Bed temperature for layers after the first one. " + "Set this to zero to disable bed temperature control commands in the output."; def->cli = "bed-temperature=i@"; def->full_label = "Bed temperature"; def->min = 0; @@ -36,7 +39,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("before_layer_gcode", coString); def->label = "Before layer change G-code"; - def->tooltip = "This custom code is inserted at every layer change, right before the Z move. Note that you can use placeholder variables for all Slic3r settings as well as [layer_num] and [layer_z]."; + def->tooltip = "This custom code is inserted at every layer change, right before the Z move. " + "Note that you can use placeholder variables for all Slic3r settings as well " + "as [layer_num] and [layer_z]."; def->cli = "before-layer-gcode=s"; def->multiline = true; def->full_width = true; @@ -54,7 +59,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("bridge_acceleration", coFloat); def->label = "Bridge"; - def->tooltip = "This is the acceleration your printer will use for bridges. Set zero to disable acceleration control for bridges."; + def->tooltip = "This is the acceleration your printer will use for bridges. " + "Set zero to disable acceleration control for bridges."; def->sidetext = "mm/s²"; def->cli = "bridge-acceleration=f"; def->min = 0; @@ -63,7 +69,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("bridge_angle", coFloat); def->label = "Bridging angle"; def->category = "Infill"; - def->tooltip = "Bridging angle override. If left to zero, the bridging angle will be calculated automatically. Otherwise the provided angle will be used for all bridges. Use 180° for zero angle."; + def->tooltip = "Bridging angle override. If left to zero, the bridging angle will be calculated " + "automatically. Otherwise the provided angle will be used for all bridges. " + "Use 180° for zero angle."; def->sidetext = "°"; def->cli = "bridge-angle=f"; def->min = 0; @@ -81,7 +89,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("bridge_flow_ratio", coFloat); def->label = "Bridge flow ratio"; def->category = "Advanced"; - def->tooltip = "This factor affects the amount of plastic for bridging. You can decrease it slightly to pull the extrudates and prevent sagging, although default settings are usually good and you should experiment with cooling (use a fan) before tweaking this."; + def->tooltip = "This factor affects the amount of plastic for bridging. " + "You can decrease it slightly to pull the extrudates and prevent sagging, " + "although default settings are usually good and you should experiment " + "with cooling (use a fan) before tweaking this."; def->cli = "bridge-flow-ratio=f"; def->min = 0; def->default_value = new ConfigOptionFloat(1); @@ -106,7 +117,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("clip_multipart_objects", coBool); def->label = "Clip multi-part objects"; - def->tooltip = "When printing multi-material objects, this settings will make slic3r to clip the overlapping object parts one by the other (2nd part will be clipped by the 1st, 3rd part will be clipped by the 1st and 2nd etc)."; + def->tooltip = "When printing multi-material objects, this settings will make slic3r " + "to clip the overlapping object parts one by the other " + "(2nd part will be clipped by the 1st, 3rd part will be clipped by the 1st and 2nd etc)."; def->cli = "clip-multipart-objects!"; def->default_value = new ConfigOptionBool(false); @@ -116,19 +129,25 @@ PrintConfigDef::PrintConfigDef() def = this->add("complete_objects", coBool); def->label = "Complete individual objects"; - def->tooltip = "When printing multiple objects or copies, this feature will complete each object before moving onto next one (and starting it from its bottom layer). This feature is useful to avoid the risk of ruined prints. Slic3r should warn and prevent you from extruder collisions, but beware."; + def->tooltip = "When printing multiple objects or copies, this feature will complete " + "each object before moving onto next one (and starting it from its bottom layer). " + "This feature is useful to avoid the risk of ruined prints. " + "Slic3r should warn and prevent you from extruder collisions, but beware."; def->cli = "complete-objects!"; def->default_value = new ConfigOptionBool(false); def = this->add("cooling", coBools); def->label = "Enable auto cooling"; - def->tooltip = "This flag enables the automatic cooling logic that adjusts print speed and fan speed according to layer printing time."; + def->tooltip = "This flag enables the automatic cooling logic that adjusts print speed " + "and fan speed according to layer printing time."; def->cli = "cooling!"; def->default_value = new ConfigOptionBools { true }; def = this->add("default_acceleration", coFloat); def->label = "Default"; - def->tooltip = "This is the acceleration your printer will be reset to after the role-specific acceleration values are used (perimeter/infill). Set zero to prevent resetting acceleration at all."; + def->tooltip = "This is the acceleration your printer will be reset to after " + "the role-specific acceleration values are used (perimeter/infill). " + "Set zero to prevent resetting acceleration at all."; def->sidetext = "mm/s²"; def->cli = "default-acceleration=f"; def->min = 0; @@ -136,7 +155,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("disable_fan_first_layers", coInts); def->label = "Disable fan for the first"; - def->tooltip = "You can set this to a positive value to disable fan at all during the first layers, so that it does not make adhesion worse."; + def->tooltip = "You can set this to a positive value to disable fan at all " + "during the first layers, so that it does not make adhesion worse."; def->sidetext = "layers"; def->cli = "disable-fan-first-layers=i@"; def->min = 0; @@ -146,7 +166,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("dont_support_bridges", coBool); def->label = "Don't support bridges"; def->category = "Support material"; - def->tooltip = "Experimental option for preventing support material from being generated under bridged areas."; + def->tooltip = "Experimental option for preventing support material from being generated " + "under bridged areas."; def->cli = "dont-support-bridges!"; def->default_value = new ConfigOptionBool(true); @@ -162,7 +183,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("elefant_foot_compensation", coFloat); def->label = "Elefant foot compensation"; def->category = "Advanced"; - def->tooltip = "The first layer will be shrunk in the XY plane by the configured value to compensate for the 1st layer squish aka an Elefant Foot effect."; + def->tooltip = "The first layer will be shrunk in the XY plane by the configured value " + "to compensate for the 1st layer squish aka an Elefant Foot effect."; def->sidetext = "mm"; def->cli = "elefant-foot-compensation=f"; def->min = 0; @@ -170,7 +192,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("end_gcode", coString); def->label = "End G-code"; - def->tooltip = "This end procedure is inserted at the end of the output file. Note that you can use placeholder variables for all Slic3r settings."; + def->tooltip = "This end procedure is inserted at the end of the output file. " + "Note that you can use placeholder variables for all Slic3r settings."; def->cli = "end-gcode=s"; def->multiline = true; def->full_width = true; @@ -179,7 +202,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("end_filament_gcode", coStrings); def->label = "End G-code"; - def->tooltip = "This end procedure is inserted at the end of the output file, before the printer end gcode. Note that you can use placeholder variables for all Slic3r settings. If you have multiple extruders, the gcode is processed in extruder order."; + def->tooltip = "This end procedure is inserted at the end of the output file, before the printer end gcode. " + "Note that you can use placeholder variables for all Slic3r settings. " + "If you have multiple extruders, the gcode is processed in extruder order."; def->cli = "end-filament-gcode=s@"; def->multiline = true; def->full_width = true; @@ -189,14 +214,16 @@ PrintConfigDef::PrintConfigDef() def = this->add("ensure_vertical_shell_thickness", coBool); def->label = "Ensure vertical shell thickness"; def->category = "Layers and Perimeters"; - def->tooltip = "Add solid infill near sloping surfaces to guarantee the vertical shell thickness (top+bottom solid layers)."; + def->tooltip = "Add solid infill near sloping surfaces to guarantee the vertical shell thickness " + "(top+bottom solid layers)."; def->cli = "ensure-vertical-shell-thickness!"; def->default_value = new ConfigOptionBool(false); def = this->add("external_fill_pattern", coEnum); def->label = "Top/bottom fill pattern"; def->category = "Infill"; - def->tooltip = "Fill pattern for top/bottom infill. This only affects the external visible layer, and not its adjacent solid shells."; + def->tooltip = "Fill pattern for top/bottom infill. This only affects the external visible layer, " + "and not its adjacent solid shells."; def->cli = "external-fill-pattern|solid-fill-pattern=s"; def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("rectilinear"); @@ -215,7 +242,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("external_perimeter_extrusion_width", coFloatOrPercent); def->label = "External perimeters"; def->category = "Extrusion Width"; - def->tooltip = "Set this to a non-zero value to set a manual extrusion width for external perimeters. If left zero, an automatic value will be used that maximizes accuracy of the external visible surfaces. If expressed as percentage (for example 200%) it will be computed over layer height."; + def->tooltip = "Set this to a non-zero value to set a manual extrusion width for external perimeters. " + "If left zero, an automatic value will be used that maximizes accuracy " + "of the external visible surfaces. If expressed as percentage (for example 200%) " + "it will be computed over layer height."; def->sidetext = "mm or % (leave 0 for default)"; def->cli = "external-perimeter-extrusion-width=s"; def->default_value = new ConfigOptionFloatOrPercent(0, false); @@ -223,7 +253,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("external_perimeter_speed", coFloatOrPercent); def->label = "External perimeters"; def->category = "Speed"; - def->tooltip = "This separate setting will affect the speed of external perimeters (the visible ones). If expressed as percentage (for example: 80%) it will be calculated on the perimeters speed setting above. Set to zero for auto."; + def->tooltip = "This separate setting will affect the speed of external perimeters (the visible ones). " + "If expressed as percentage (for example: 80%) it will be calculated " + "on the perimeters speed setting above. Set to zero for auto."; def->sidetext = "mm/s or %"; def->cli = "external-perimeter-speed=s"; def->ratio_over = "perimeter_speed"; @@ -233,14 +265,17 @@ PrintConfigDef::PrintConfigDef() def = this->add("external_perimeters_first", coBool); def->label = "External perimeters first"; def->category = "Layers and Perimeters"; - def->tooltip = "Print contour perimeters from the outermost one to the innermost one instead of the default inverse order."; + def->tooltip = "Print contour perimeters from the outermost one to the innermost one " + "instead of the default inverse order."; def->cli = "external-perimeters-first!"; def->default_value = new ConfigOptionBool(false); def = this->add("extra_perimeters", coBool); def->label = "Extra perimeters if needed"; def->category = "Layers and Perimeters"; - def->tooltip = "Add more perimeters when needed for avoiding gaps in sloping walls. Slic3r keeps adding perimeters, until more than 70% of the loop immediately above is supported."; + def->tooltip = "Add more perimeters when needed for avoiding gaps in sloping walls. " + "Slic3r keeps adding perimeters, until more than 70% of the loop immediately above " + "is supported."; def->cli = "extra-perimeters!"; def->default_value = new ConfigOptionBool(true); @@ -248,7 +283,8 @@ PrintConfigDef::PrintConfigDef() def->gui_type = "i_enum_open"; def->label = "Extruder"; def->category = "Extruders"; - def->tooltip = "The extruder to use (unless more specific extruder settings are specified). This value overrides perimeter and infill extruders, but not the support extruders."; + def->tooltip = "The extruder to use (unless more specific extruder settings are specified). " + "This value overrides perimeter and infill extruders, but not the support extruders."; def->cli = "extruder=i"; def->min = 0; // 0 = inherit defaults def->enum_labels.push_back("default"); // override label for item 0 @@ -259,7 +295,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("extruder_clearance_height", coFloat); def->label = "Height"; - def->tooltip = "Set this to the vertical distance between your nozzle tip and (usually) the X carriage rods. In other words, this is the height of the clearance cylinder around your extruder, and it represents the maximum depth the extruder can peek before colliding with other printed objects."; + def->tooltip = "Set this to the vertical distance between your nozzle tip and (usually) the X carriage rods. " + "In other words, this is the height of the clearance cylinder around your extruder, " + "and it represents the maximum depth the extruder can peek before colliding with " + "other printed objects."; def->sidetext = "mm"; def->cli = "extruder-clearance-height=f"; def->min = 0; @@ -267,7 +306,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("extruder_clearance_radius", coFloat); def->label = "Radius"; - def->tooltip = "Set this to the clearance radius around your extruder. If the extruder is not centered, choose the largest value for safety. This setting is used to check for collisions and to display the graphical preview in the plater."; + def->tooltip = "Set this to the clearance radius around your extruder. " + "If the extruder is not centered, choose the largest value for safety. " + "This setting is used to check for collisions and to display the graphical preview " + "in the plater."; def->sidetext = "mm"; def->cli = "extruder-clearance-radius=f"; def->min = 0; @@ -283,40 +325,51 @@ PrintConfigDef::PrintConfigDef() def = this->add("extruder_offset", coPoints); def->label = "Extruder offset"; - def->tooltip = "If your firmware doesn't handle the extruder displacement you need the G-code to take it into account. This option lets you specify the displacement of each extruder with respect to the first one. It expects positive coordinates (they will be subtracted from the XY coordinate)."; + def->tooltip = "If your firmware doesn't handle the extruder displacement you need the G-code " + "to take it into account. This option lets you specify the displacement of each extruder " + "with respect to the first one. It expects positive coordinates (they will be subtracted " + "from the XY coordinate)."; def->sidetext = "mm"; def->cli = "extruder-offset=s@"; def->default_value = new ConfigOptionPoints { Pointf(0,0) }; def = this->add("extrusion_axis", coString); def->label = "Extrusion axis"; - def->tooltip = "Use this option to set the axis letter associated to your printer's extruder (usually E but some printers use A)."; + def->tooltip = "Use this option to set the axis letter associated to your printer's extruder " + "(usually E but some printers use A)."; def->cli = "extrusion-axis=s"; def->default_value = new ConfigOptionString("E"); def = this->add("extrusion_multiplier", coFloats); def->label = "Extrusion multiplier"; - def->tooltip = "This factor changes the amount of flow proportionally. You may need to tweak this setting to get nice surface finish and correct single wall widths. Usual values are between 0.9 and 1.1. If you think you need to change this more, check filament diameter and your firmware E steps."; + def->tooltip = "This factor changes the amount of flow proportionally. You may need to tweak " + "this setting to get nice surface finish and correct single wall widths. " + "Usual values are between 0.9 and 1.1. If you think you need to change this more, " + "check filament diameter and your firmware E steps."; def->cli = "extrusion-multiplier=f@"; def->default_value = new ConfigOptionFloats { 1. }; def = this->add("extrusion_width", coFloatOrPercent); def->label = "Default extrusion width"; def->category = "Extrusion Width"; - def->tooltip = "Set this to a non-zero value to set a manual extrusion width. If left to zero, Slic3r calculates a width automatically. If expressed as percentage (for example: 230%) it will be computed over layer height."; + def->tooltip = "Set this to a non-zero value to set a manual extrusion width. If left to zero, " + "Slic3r calculates a width automatically. If expressed as percentage (for example: 230%) " + "it will be computed over layer height."; def->sidetext = "mm or % (leave 0 for auto)"; def->cli = "extrusion-width=s"; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("fan_always_on", coBools); def->label = "Keep fan always on"; - def->tooltip = "If this is enabled, fan will never be disabled and will be kept running at least at its minimum speed. Useful for PLA, harmful for ABS."; + def->tooltip = "If this is enabled, fan will never be disabled and will be kept running at least " + "at its minimum speed. Useful for PLA, harmful for ABS."; def->cli = "fan-always-on!"; def->default_value = new ConfigOptionBools { false }; def = this->add("fan_below_layer_time", coInts); def->label = "Enable fan if layer print time is below"; - def->tooltip = "If layer print time is estimated below this number of seconds, fan will be enabled and its speed will be calculated by interpolating the minimum and maximum speeds."; + def->tooltip = "If layer print time is estimated below this number of seconds, fan will be enabled " + "and its speed will be calculated by interpolating the minimum and maximum speeds."; def->sidetext = "approximate seconds"; def->cli = "fan-below-layer-time=i@"; def->width = 60; @@ -342,7 +395,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("filament_max_volumetric_speed", coFloats); def->label = "Max volumetric speed"; - def->tooltip = "Maximum volumetric speed allowed for this filament. Limits the maximum volumetric speed of a print to the minimum of print and filament volumetric speed. Set to zero for no limit."; + def->tooltip = "Maximum volumetric speed allowed for this filament. Limits the maximum volumetric " + "speed of a print to the minimum of print and filament volumetric speed. " + "Set to zero for no limit."; def->sidetext = "mm³/s"; def->cli = "filament-max-volumetric-speed=f@"; def->min = 0; @@ -350,7 +405,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("filament_diameter", coFloats); def->label = "Diameter"; - def->tooltip = "Enter your filament diameter here. Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average."; + def->tooltip = "Enter your filament diameter here. Good precision is required, so use a caliper " + "and do multiple measurements along the filament, then compute the average."; def->sidetext = "mm"; def->cli = "filament-diameter=f@"; def->min = 0; @@ -358,7 +414,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("filament_density", coFloats); def->label = "Density"; - def->tooltip = "Enter your filament density here. This is only for statistical information. A decent way is to weigh a known length of filament and compute the ratio of the length to volume. Better is to calculate the volume directly through displacement."; + def->tooltip = "Enter your filament density here. This is only for statistical information. " + "A decent way is to weigh a known length of filament and compute the ratio " + "of the length to volume. Better is to calculate the volume directly through displacement."; def->sidetext = "g/cm^3"; def->cli = "filament-density=f@"; def->min = 0; @@ -366,7 +424,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("filament_type", coStrings); def->label = "Filament type"; - def->tooltip = "If you want to process the output G-code through custom scripts, just list their absolute paths here. Separate multiple scripts with a semicolon. Scripts will be passed the absolute path to the G-code file as the first argument, and they can access the Slic3r config settings by reading environment variables."; + def->tooltip = "If you want to process the output G-code through custom scripts, just list their " + "absolute paths here. Separate multiple scripts with a semicolon. Scripts will be passed " + "the absolute path to the G-code file as the first argument, and they can access " + "the Slic3r config settings by reading environment variables."; def->cli = "filament_type=s@"; def->gui_type = "f_enum_open"; def->gui_flags = "show_value"; @@ -401,7 +462,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("fill_angle", coFloat); def->label = "Fill angle"; def->category = "Infill"; - def->tooltip = "Default base angle for infill orientation. Cross-hatching will be applied to this. Bridges will be infilled using the best direction Slic3r can detect, so this setting does not affect them."; + def->tooltip = "Default base angle for infill orientation. Cross-hatching will be applied to this. " + "Bridges will be infilled using the best direction Slic3r can detect, so this setting " + "does not affect them."; def->sidetext = "°"; def->cli = "fill-angle=f"; def->min = 0; @@ -482,7 +545,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("first_layer_acceleration", coFloat); def->label = "First layer"; - def->tooltip = "This is the acceleration your printer will use for first layer. Set zero to disable acceleration control for first layer."; + def->tooltip = "This is the acceleration your printer will use for first layer. Set zero " + "to disable acceleration control for first layer."; def->sidetext = "mm/s²"; def->cli = "first-layer-acceleration=f"; def->min = 0; @@ -490,7 +554,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("first_layer_bed_temperature", coInts); def->label = "First layer"; - def->tooltip = "Heated build plate temperature for the first layer. Set this to zero to disable bed temperature control commands in the output."; + def->tooltip = "Heated build plate temperature for the first layer. Set this to zero to disable " + "bed temperature control commands in the output."; def->cli = "first-layer-bed-temperature=i@"; def->max = 0; def->max = 300; @@ -499,7 +564,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("first_layer_extrusion_width", coFloatOrPercent); def->label = "First layer"; def->category = "Extrusion Width"; - def->tooltip = "Set this to a non-zero value to set a manual extrusion width for first layer. You can use this to force fatter extrudates for better adhesion. If expressed as percentage (for example 120%) it will be computed over first layer height. If set to zero, it will use the Default Extrusion Width."; + def->tooltip = "Set this to a non-zero value to set a manual extrusion width for first layer. " + "You can use this to force fatter extrudates for better adhesion. If expressed " + "as percentage (for example 120%) it will be computed over first layer height. " + "If set to zero, it will use the Default Extrusion Width."; def->sidetext = "mm or % (leave 0 for default)"; def->cli = "first-layer-extrusion-width=s"; def->ratio_over = "first_layer_height"; @@ -508,7 +576,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("first_layer_height", coFloatOrPercent); def->label = "First layer height"; def->category = "Layers and Perimeters"; - def->tooltip = "When printing with very low layer heights, you might still want to print a thicker bottom layer to improve adhesion and tolerance for non perfect build plates. This can be expressed as an absolute value or as a percentage (for example: 150%) over the default layer height."; + def->tooltip = "When printing with very low layer heights, you might still want to print a thicker " + "bottom layer to improve adhesion and tolerance for non perfect build plates. " + "This can be expressed as an absolute value or as a percentage (for example: 150%) " + "over the default layer height."; def->sidetext = "mm or %"; def->cli = "first-layer-height=s"; def->ratio_over = "layer_height"; @@ -516,7 +587,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("first_layer_speed", coFloatOrPercent); def->label = "First layer speed"; - def->tooltip = "If expressed as absolute value in mm/s, this speed will be applied to all the print moves of the first layer, regardless of their type. If expressed as a percentage (for example: 40%) it will scale the default speeds."; + def->tooltip = "If expressed as absolute value in mm/s, this speed will be applied to all the print moves " + "of the first layer, regardless of their type. If expressed as a percentage " + "(for example: 40%) it will scale the default speeds."; def->sidetext = "mm/s or %"; def->cli = "first-layer-speed=s"; def->min = 0; @@ -524,7 +597,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("first_layer_temperature", coInts); def->label = "First layer"; - def->tooltip = "Extruder temperature for first layer. If you want to control temperature manually during print, set this to zero to disable temperature control commands in the output file."; + def->tooltip = "Extruder temperature for first layer. If you want to control temperature manually " + "during print, set this to zero to disable temperature control commands in the output file."; def->cli = "first-layer-temperature=i@"; def->min = 0; def->max = 500; @@ -533,7 +607,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("gap_fill_speed", coFloat); def->label = "Gap fill"; def->category = "Speed"; - def->tooltip = "Speed for filling small gaps using short zigzag moves. Keep this reasonably low to avoid too much shaking and resonance issues. Set zero to disable gaps filling."; + def->tooltip = "Speed for filling small gaps using short zigzag moves. Keep this reasonably low " + "to avoid too much shaking and resonance issues. Set zero to disable gaps filling."; def->sidetext = "mm/s"; def->cli = "gap-fill-speed=f"; def->min = 0; @@ -541,13 +616,17 @@ PrintConfigDef::PrintConfigDef() def = this->add("gcode_comments", coBool); def->label = "Verbose G-code"; - def->tooltip = "Enable this to get a commented G-code file, with each line explained by a descriptive text. If you print from SD card, the additional weight of the file could make your firmware slow down."; + def->tooltip = "Enable this to get a commented G-code file, with each line explained by a descriptive text. " + "If you print from SD card, the additional weight of the file could make your firmware " + "slow down."; def->cli = "gcode-comments!"; def->default_value = new ConfigOptionBool(0); def = this->add("gcode_flavor", coEnum); def->label = "G-code flavor"; - def->tooltip = "Some G/M-code commands, including temperature control and others, are not universal. Set this option to your printer's firmware to get a compatible output. The \"No extrusion\" flavor prevents Slic3r from exporting any extrusion value at all."; + def->tooltip = "Some G/M-code commands, including temperature control and others, are not universal. " + "Set this option to your printer's firmware to get a compatible output. " + "The \"No extrusion\" flavor prevents Slic3r from exporting any extrusion value at all."; def->cli = "gcode-flavor=s"; def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("reprap"); @@ -572,7 +651,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("infill_acceleration", coFloat); def->label = "Infill"; - def->tooltip = "This is the acceleration your printer will use for infill. Set zero to disable acceleration control for infill."; + def->tooltip = "This is the acceleration your printer will use for infill. Set zero to disable " + "acceleration control for infill."; def->sidetext = "mm/s²"; def->cli = "infill-acceleration=f"; def->min = 0; @@ -581,7 +661,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("infill_every_layers", coInt); def->label = "Combine infill every"; def->category = "Infill"; - def->tooltip = "This feature allows to combine infill and speed up your print by extruding thicker infill layers while preserving thin perimeters, thus accuracy."; + def->tooltip = "This feature allows to combine infill and speed up your print by extruding thicker " + "infill layers while preserving thin perimeters, thus accuracy."; def->sidetext = "layers"; def->cli = "infill-every-layers=i"; def->full_label = "Combine infill every n layers"; @@ -599,7 +680,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("infill_extrusion_width", coFloatOrPercent); def->label = "Infill"; def->category = "Extrusion Width"; - def->tooltip = "Set this to a non-zero value to set a manual extrusion width for infill. You may want to use fatter extrudates to speed up the infill and make your parts stronger. If expressed as percentage (for example 90%) it will be computed over layer height."; + def->tooltip = "Set this to a non-zero value to set a manual extrusion width for infill. " + "You may want to use fatter extrudates to speed up the infill and make your parts stronger. " + "If expressed as percentage (for example 90%) it will be computed over layer height."; def->sidetext = "mm or % (leave 0 for default)"; def->cli = "infill-extrusion-width=s"; def->default_value = new ConfigOptionFloatOrPercent(0, false); @@ -613,14 +696,18 @@ PrintConfigDef::PrintConfigDef() def = this->add("infill_only_where_needed", coBool); def->label = "Only infill where needed"; def->category = "Infill"; - def->tooltip = "This option will limit infill to the areas actually needed for supporting ceilings (it will act as internal support material). If enabled, slows down the G-code generation due to the multiple checks involved."; + def->tooltip = "This option will limit infill to the areas actually needed for supporting ceilings " + "(it will act as internal support material). If enabled, slows down the G-code generation " + "due to the multiple checks involved."; def->cli = "infill-only-where-needed!"; def->default_value = new ConfigOptionBool(false); def = this->add("infill_overlap", coFloatOrPercent); def->label = "Infill/perimeters overlap"; def->category = "Advanced"; - def->tooltip = "This setting applies an additional overlap between infill and perimeters for better bonding. Theoretically this shouldn't be needed, but backlash might cause gaps. If expressed as percentage (example: 15%) it is calculated over perimeter extrusion width."; + def->tooltip = "This setting applies an additional overlap between infill and perimeters for better bonding. " + "Theoretically this shouldn't be needed, but backlash might cause gaps. If expressed " + "as percentage (example: 15%) it is calculated over perimeter extrusion width."; def->sidetext = "mm or %"; def->cli = "infill-overlap=s"; def->ratio_over = "perimeter_extrusion_width"; @@ -639,14 +726,18 @@ PrintConfigDef::PrintConfigDef() def = this->add("interface_shells", coBool); def->label = "Interface shells"; - def->tooltip = "Force the generation of solid shells between adjacent materials/volumes. Useful for multi-extruder prints with translucent materials or manual soluble support material."; + def->tooltip = "Force the generation of solid shells between adjacent materials/volumes. " + "Useful for multi-extruder prints with translucent materials or manual soluble " + "support material."; def->cli = "interface-shells!"; def->category = "Layers and Perimeters"; def->default_value = new ConfigOptionBool(false); def = this->add("layer_gcode", coString); def->label = "After layer change G-code"; - def->tooltip = "This custom code is inserted at every layer change, right after the Z move and before the extruder moves to the first layer point. Note that you can use placeholder variables for all Slic3r settings as well as [layer_num] and [layer_z]."; + def->tooltip = "This custom code is inserted at every layer change, right after the Z move " + "and before the extruder moves to the first layer point. Note that you can use " + "placeholder variables for all Slic3r settings as well as [layer_num] and [layer_z]."; def->cli = "after-layer-gcode|layer-gcode=s"; def->multiline = true; def->full_width = true; @@ -656,7 +747,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("layer_height", coFloat); def->label = "Layer height"; def->category = "Layers and Perimeters"; - def->tooltip = "This setting controls the height (and thus the total number) of the slices/layers. Thinner layers give better accuracy but take more time to print."; + def->tooltip = "This setting controls the height (and thus the total number) of the slices/layers. " + "Thinner layers give better accuracy but take more time to print."; def->sidetext = "mm"; def->cli = "layer-height=f"; def->min = 0; @@ -673,7 +765,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("max_layer_height", coFloats); def->label = "Max"; - def->tooltip = "This is the highest printable layer height for this extruder, used to cap the variable layer height and support layer height. Maximum recommended layer height is 75% of the extrusion width to achieve reasonable inter-layer adhesion. If set to 0, layer height is limited to 75% of the nozzle diameter."; + def->tooltip = "This is the highest printable layer height for this extruder, used to cap " + "the variable layer height and support layer height. Maximum recommended layer height " + "is 75% of the extrusion width to achieve reasonable inter-layer adhesion. " + "If set to 0, layer height is limited to 75% of the nozzle diameter."; def->sidetext = "mm"; def->cli = "max-layer-height=f@"; def->min = 0; @@ -681,7 +776,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("max_print_speed", coFloat); def->label = "Max print speed"; - def->tooltip = "When setting other speed settings to 0 Slic3r will autocalculate the optimal speed in order to keep constant extruder pressure. This experimental setting is used to set the highest print speed you want to allow."; + def->tooltip = "When setting other speed settings to 0 Slic3r will autocalculate the optimal speed " + "in order to keep constant extruder pressure. This experimental setting is used " + "to set the highest print speed you want to allow."; def->sidetext = "mm/s"; def->cli = "max-print-speed=f"; def->min = 1; @@ -689,7 +786,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("max_volumetric_speed", coFloat); def->label = "Max volumetric speed"; - def->tooltip = "This experimental setting is used to set the maximum volumetric speed your extruder supports."; + def->tooltip = "This experimental setting is used to set the maximum volumetric speed your " + "extruder supports."; def->sidetext = "mm³/s"; def->cli = "max-volumetric-speed=f"; def->min = 0; @@ -697,8 +795,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("max_volumetric_extrusion_rate_slope_positive", coFloat); def->label = "Max volumetric slope positive"; - def->tooltip = "This experimental setting is used to limit the speed of change in extrusion rate. A value of 1.8 mm³/s² ensures, that a change from the extrusion rate " - "of 1.8 mm³/s (0.45mm extrusion width, 0.2mm extrusion height, feedrate 20 mm/s) to 5.4 mm³/s (feedrate 60 mm/s) will take at least 2 seconds."; + def->tooltip = "This experimental setting is used to limit the speed of change in extrusion rate. " + "A value of 1.8 mm³/s² ensures, that a change from the extrusion rate " + "of 1.8 mm³/s (0.45mm extrusion width, 0.2mm extrusion height, feedrate 20 mm/s) " + "to 5.4 mm³/s (feedrate 60 mm/s) will take at least 2 seconds."; def->sidetext = "mm³/s²"; def->cli = "max-volumetric-extrusion-rate-slope-positive=f"; def->min = 0; @@ -706,8 +806,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("max_volumetric_extrusion_rate_slope_negative", coFloat); def->label = "Max volumetric slope negative"; - def->tooltip = "This experimental setting is used to limit the speed of change in extrusion rate. A value of 1.8 mm³/s² ensures, that a change from the extrusion rate " - "of 1.8 mm³/s (0.45mm extrusion width, 0.2mm extrusion height, feedrate 20 mm/s) to 5.4 mm³/s (feedrate 60 mm/s) will take at least 2 seconds."; + def->tooltip = "This experimental setting is used to limit the speed of change in extrusion rate. " + "A value of 1.8 mm³/s² ensures, that a change from the extrusion rate " + "of 1.8 mm³/s (0.45mm extrusion width, 0.2mm extrusion height, feedrate 20 mm/s) " + "to 5.4 mm³/s (feedrate 60 mm/s) will take at least 2 seconds."; def->sidetext = "mm³/s²"; def->cli = "max-volumetric-extrusion-rate-slope-negative=f"; def->min = 0; @@ -724,7 +826,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("min_layer_height", coFloats); def->label = "Min"; - def->tooltip = "This is the lowest printable layer height for this extruder and limits the resolution for variable layer height. Typical values are between 0.05 mm and 0.1 mm."; + def->tooltip = "This is the lowest printable layer height for this extruder and limits " + "the resolution for variable layer height. Typical values are between 0.05 mm and 0.1 mm."; def->sidetext = "mm"; def->cli = "min-layer-height=f@"; def->min = 0; @@ -740,7 +843,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("min_skirt_length", coFloat); def->label = "Minimum extrusion length"; - def->tooltip = "Generate no less than the number of skirt loops required to consume the specified amount of filament on the bottom layer. For multi-extruder machines, this minimum applies to each extruder."; + def->tooltip = "Generate no less than the number of skirt loops required to consume " + "the specified amount of filament on the bottom layer. For multi-extruder machines, " + "this minimum applies to each extruder."; def->sidetext = "mm"; def->cli = "min-skirt-length=f"; def->min = 0; @@ -748,7 +853,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("notes", coString); def->label = "Configuration notes"; - def->tooltip = "You can put here your personal notes. This text will be added to the G-code header comments."; + def->tooltip = "You can put here your personal notes. This text will be added to the G-code " + "header comments."; def->cli = "notes=s"; def->multiline = true; def->full_width = true; @@ -764,31 +870,39 @@ PrintConfigDef::PrintConfigDef() def = this->add("octoprint_apikey", coString); def->label = "API Key"; - def->tooltip = "Slic3r can upload G-code files to OctoPrint. This field should contain the API Key required for authentication."; + def->tooltip = "Slic3r can upload G-code files to OctoPrint. This field should contain " + "the API Key required for authentication."; def->cli = "octoprint-apikey=s"; def->default_value = new ConfigOptionString(""); def = this->add("octoprint_host", coString); def->label = "Host or IP"; - def->tooltip = "Slic3r can upload G-code files to OctoPrint. This field should contain the hostname or IP address of the OctoPrint instance."; + def->tooltip = "Slic3r can upload G-code files to OctoPrint. This field should contain " + "the hostname or IP address of the OctoPrint instance."; def->cli = "octoprint-host=s"; def->default_value = new ConfigOptionString(""); def = this->add("only_retract_when_crossing_perimeters", coBool); def->label = "Only retract when crossing perimeters"; - def->tooltip = "Disables retraction when the travel path does not exceed the upper layer's perimeters (and thus any ooze will be probably invisible)."; + def->tooltip = "Disables retraction when the travel path does not exceed the upper layer's perimeters " + "(and thus any ooze will be probably invisible)."; def->cli = "only-retract-when-crossing-perimeters!"; def->default_value = new ConfigOptionBool(true); def = this->add("ooze_prevention", coBool); def->label = "Enable"; - def->tooltip = "This option will drop the temperature of the inactive extruders to prevent oozing. It will enable a tall skirt automatically and move extruders outside such skirt when changing temperatures."; + def->tooltip = "This option will drop the temperature of the inactive extruders to prevent oozing. " + "It will enable a tall skirt automatically and move extruders outside such " + "skirt when changing temperatures."; def->cli = "ooze-prevention!"; def->default_value = new ConfigOptionBool(false); def = this->add("output_filename_format", coString); def->label = "Output filename format"; - def->tooltip = "You can use all configuration options as variables inside this template. For example: [layer_height], [fill_density] etc. You can also use [timestamp], [year], [month], [day], [hour], [minute], [second], [version], [input_filename], [input_filename_base]."; + def->tooltip = "You can use all configuration options as variables inside this template. " + "For example: [layer_height], [fill_density] etc. You can also use [timestamp], " + "[year], [month], [day], [hour], [minute], [second], [version], [input_filename], " + "[input_filename_base]."; def->cli = "output-filename-format=s"; def->full_width = true; def->default_value = new ConfigOptionString("[input_filename_base].gcode"); @@ -796,13 +910,16 @@ PrintConfigDef::PrintConfigDef() def = this->add("overhangs", coBool); def->label = "Detect bridging perimeters"; def->category = "Layers and Perimeters"; - def->tooltip = "Experimental option to adjust flow for overhangs (bridge flow will be used), to apply bridge speed to them and enable fan."; + def->tooltip = "Experimental option to adjust flow for overhangs (bridge flow will be used), " + "to apply bridge speed to them and enable fan."; def->cli = "overhangs!"; def->default_value = new ConfigOptionBool(true); def = this->add("perimeter_acceleration", coFloat); def->label = "Perimeters"; - def->tooltip = "This is the acceleration your printer will use for perimeters. A high value like 9000 usually gives good results if your hardware is up to the job. Set zero to disable acceleration control for perimeters."; + def->tooltip = "This is the acceleration your printer will use for perimeters. " + "A high value like 9000 usually gives good results if your hardware is up to the job. " + "Set zero to disable acceleration control for perimeters."; def->sidetext = "mm/s²"; def->cli = "perimeter-acceleration=f"; def->default_value = new ConfigOptionFloat(0); @@ -819,7 +936,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("perimeter_extrusion_width", coFloatOrPercent); def->label = "Perimeters"; def->category = "Extrusion Width"; - def->tooltip = "Set this to a non-zero value to set a manual extrusion width for perimeters. You may want to use thinner extrudates to get more accurate surfaces. If expressed as percentage (for example 200%) it will be computed over layer height."; + def->tooltip = "Set this to a non-zero value to set a manual extrusion width for perimeters. " + "You may want to use thinner extrudates to get more accurate surfaces. " + "If expressed as percentage (for example 200%) it will be computed over layer height."; def->sidetext = "mm or % (leave 0 for default)"; def->cli = "perimeter-extrusion-width=s"; def->aliases.push_back("perimeters_extrusion_width"); @@ -838,7 +957,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("perimeters", coInt); def->label = "Perimeters"; def->category = "Layers and Perimeters"; - def->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."; + def->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."; def->sidetext = "(minimum)"; def->cli = "perimeters=i"; def->aliases.push_back("perimeter_offsets"); @@ -847,7 +969,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("post_process", coStrings); def->label = "Post-processing scripts"; - def->tooltip = "If you want to process the output G-code through custom scripts, just list their absolute paths here. Separate multiple scripts with a semicolon. Scripts will be passed the absolute path to the G-code file as the first argument, and they can access the Slic3r config settings by reading environment variables."; + def->tooltip = "If you want to process the output G-code through custom scripts, " + "just list their absolute paths here. Separate multiple scripts with a semicolon. " + "Scripts will be passed the absolute path to the G-code file as the first argument, " + "and they can access the Slic3r config settings by reading environment variables."; def->cli = "post-process=s@"; def->gui_flags = "serialized"; def->multiline = true; @@ -872,7 +997,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("raft_layers", coInt); def->label = "Raft layers"; def->category = "Support material"; - def->tooltip = "The object will be raised by this number of layers, and support material will be generated under it."; + def->tooltip = "The object will be raised by this number of layers, and support material " + "will be generated under it."; def->sidetext = "layers"; def->cli = "raft-layers=i"; def->min = 0; @@ -880,7 +1006,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("resolution", coFloat); def->label = "Resolution"; - def->tooltip = "Minimum detail resolution, used to simplify the input file for speeding up the slicing job and reducing memory usage. High-resolution models often carry more detail than printers can render. Set to zero to disable any simplification and use full resolution from input."; + def->tooltip = "Minimum detail resolution, used to simplify the input file for speeding up " + "the slicing job and reducing memory usage. High-resolution models often carry " + "more detail than printers can render. Set to zero to disable any simplification " + "and use full resolution from input."; def->sidetext = "mm"; def->cli = "resolution=f"; def->min = 0; @@ -895,7 +1024,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("retract_before_wipe", coPercents); def->label = "Retract amount before wipe"; - def->tooltip = "With bowden extruders, it may be wise to do some amount of quick retract before doing the wipe movement."; + def->tooltip = "With bowden extruders, it may be wise to do some amount of quick retract " + "before doing the wipe movement."; def->sidetext = "%"; def->cli = "retract-before-wipe=s@"; def->default_value = new ConfigOptionPercents { 0. }; @@ -909,7 +1039,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("retract_length", coFloats); def->label = "Length"; def->full_label = "Retraction Length"; - def->tooltip = "When retraction is triggered, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder)."; + def->tooltip = "When retraction is triggered, filament is pulled back by the specified amount " + "(the length is measured on raw filament, before it enters the extruder)."; def->sidetext = "mm (zero to disable)"; def->cli = "retract-length=f@"; def->default_value = new ConfigOptionFloats { 2. }; @@ -917,14 +1048,18 @@ PrintConfigDef::PrintConfigDef() def = this->add("retract_length_toolchange", coFloats); def->label = "Length"; def->full_label = "Retraction Length (Toolchange)"; - def->tooltip = "When retraction is triggered before changing tool, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder)."; + def->tooltip = "When retraction is triggered before changing tool, filament is pulled back " + "by the specified amount (the length is measured on raw filament, before it enters " + "the extruder)."; def->sidetext = "mm (zero to disable)"; def->cli = "retract-length-toolchange=f@"; def->default_value = new ConfigOptionFloats { 10. }; def = this->add("retract_lift", coFloats); def->label = "Lift Z"; - def->tooltip = "If you set this to a positive value, Z is quickly raised every time a retraction is triggered. When using multiple extruders, only the setting for the first extruder will be considered."; + def->tooltip = "If you set this to a positive value, Z is quickly raised every time a retraction " + "is triggered. When using multiple extruders, only the setting for the first extruder " + "will be considered."; def->sidetext = "mm"; def->cli = "retract-lift=f@"; def->default_value = new ConfigOptionFloats { 0. }; @@ -932,7 +1067,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("retract_lift_above", coFloats); def->label = "Above Z"; def->full_label = "Only lift Z above"; - def->tooltip = "If you set this to a positive value, Z lift will only take place above the specified absolute Z. You can tune this setting for skipping lift on the first layers."; + def->tooltip = "If you set this to a positive value, Z lift will only take place above the specified " + "absolute Z. You can tune this setting for skipping lift on the first layers."; def->sidetext = "mm"; def->cli = "retract-lift-above=f@"; def->default_value = new ConfigOptionFloats { 0. }; @@ -940,21 +1076,25 @@ PrintConfigDef::PrintConfigDef() def = this->add("retract_lift_below", coFloats); def->label = "Below Z"; def->full_label = "Only lift Z below"; - def->tooltip = "If you set this to a positive value, Z lift will only take place below the specified absolute Z. You can tune this setting for limiting lift to the first layers."; + def->tooltip = "If you set this to a positive value, Z lift will only take place below " + "the specified absolute Z. You can tune this setting for limiting lift " + "to the first layers."; def->sidetext = "mm"; def->cli = "retract-lift-below=f@"; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_restart_extra", coFloats); def->label = "Extra length on restart"; - def->tooltip = "When the retraction is compensated after the travel move, the extruder will push this additional amount of filament. This setting is rarely needed."; + def->tooltip = "When the retraction is compensated after the travel move, the extruder will push " + "this additional amount of filament. This setting is rarely needed."; def->sidetext = "mm"; def->cli = "retract-restart-extra=f@"; def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_restart_extra_toolchange", coFloats); def->label = "Extra length on restart"; - def->tooltip = "When the retraction is compensated after changing tool, the extruder will push this additional amount of filament."; + def->tooltip = "When the retraction is compensated after changing tool, the extruder will push " + "this additional amount of filament."; def->sidetext = "mm"; def->cli = "retract-restart-extra-toolchange=f@"; def->default_value = new ConfigOptionFloats { 0. }; @@ -970,7 +1110,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("deretract_speed", coFloats); def->label = "Deretraction Speed"; def->full_label = "Deretraction Speed"; - def->tooltip = "The speed for loading of a filament into extruder after retraction (it only applies to the extruder motor). If left to zero, the retraction speed is used."; + def->tooltip = "The speed for loading of a filament into extruder after retraction " + "(it only applies to the extruder motor). If left to zero, the retraction speed is used."; def->sidetext = "mm/s"; def->cli = "retract-speed=f@"; def->default_value = new ConfigOptionFloats { 0. }; @@ -1038,7 +1179,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("skirt_distance", coFloat); def->label = "Distance from object"; - def->tooltip = "Distance between skirt and object(s). Set this to zero to attach the skirt to the object(s) and get a brim for better adhesion."; + def->tooltip = "Distance between skirt and object(s). Set this to zero to attach the skirt " + "to the object(s) and get a brim for better adhesion."; def->sidetext = "mm"; def->cli = "skirt-distance=f"; def->min = 0; @@ -1046,7 +1188,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("skirt_height", coInt); def->label = "Skirt height"; - def->tooltip = "Height of skirt expressed in layers. Set this to a tall value to use skirt as a shield against drafts."; + def->tooltip = "Height of skirt expressed in layers. Set this to a tall value to use skirt " + "as a shield against drafts."; def->sidetext = "layers"; def->cli = "skirt-height=i"; def->default_value = new ConfigOptionInt(1); @@ -1054,14 +1197,17 @@ PrintConfigDef::PrintConfigDef() def = this->add("skirts", coInt); def->label = "Loops (minimum)"; def->full_label = "Skirt Loops"; - def->tooltip = "Number of loops for the skirt. If the Minimum Extrusion Length option is set, the number of loops might be greater than the one configured here. Set this to zero to disable skirt completely."; + def->tooltip = "Number of loops for the skirt. If the Minimum Extrusion Length option is set, " + "the number of loops might be greater than the one configured here. Set this to zero " + "to disable skirt completely."; def->cli = "skirts=i"; def->min = 0; def->default_value = new ConfigOptionInt(1); def = this->add("slowdown_below_layer_time", coInts); def->label = "Slow down if layer print time is below"; - def->tooltip = "If layer print time is estimated below this number of seconds, print moves speed will be scaled down to extend duration to this value."; + def->tooltip = "If layer print time is estimated below this number of seconds, print moves " + "speed will be scaled down to extend duration to this value."; def->sidetext = "approximate seconds"; def->cli = "slowdown-below-layer-time=i@"; def->width = 60; @@ -1072,7 +1218,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("small_perimeter_speed", coFloatOrPercent); def->label = "Small perimeters"; def->category = "Speed"; - def->tooltip = "This separate setting will affect the speed of perimeters having radius <= 6.5mm (usually holes). If expressed as percentage (for example: 80%) it will be calculated on the perimeters speed setting above. Set to zero for auto."; + def->tooltip = "This separate setting will affect the speed of perimeters having radius <= 6.5mm " + "(usually holes). If expressed as percentage (for example: 80%) it will be calculated " + "on the perimeters speed setting above. Set to zero for auto."; def->sidetext = "mm/s or %"; def->cli = "small-perimeter-speed=s"; def->ratio_over = "perimeter_speed"; @@ -1099,7 +1247,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("solid_infill_every_layers", coInt); def->label = "Solid infill every"; def->category = "Infill"; - def->tooltip = "This feature allows to force a solid layer every given number of layers. Zero to disable. You can set this to any value (for example 9999); Slic3r will automatically choose the maximum possible number of layers to combine according to nozzle diameter and layer height."; + def->tooltip = "This feature allows to force a solid layer every given number of layers. " + "Zero to disable. You can set this to any value (for example 9999); " + "Slic3r will automatically choose the maximum possible number of layers " + "to combine according to nozzle diameter and layer height."; def->sidetext = "layers"; def->cli = "solid-infill-every-layers=i"; def->min = 0; @@ -1108,7 +1259,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("solid_infill_extrusion_width", coFloatOrPercent); def->label = "Solid infill"; def->category = "Extrusion Width"; - def->tooltip = "Set this to a non-zero value to set a manual extrusion width for infill for solid surfaces. If expressed as percentage (for example 90%) it will be computed over layer height."; + def->tooltip = "Set this to a non-zero value to set a manual extrusion width for infill " + "for solid surfaces. If expressed as percentage (for example 90%) it will be computed " + "over layer height."; def->sidetext = "mm or % (leave 0 for default)"; def->cli = "solid-infill-extrusion-width=s"; def->default_value = new ConfigOptionFloatOrPercent(0, false); @@ -1116,7 +1269,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("solid_infill_speed", coFloatOrPercent); def->label = "Solid infill"; def->category = "Speed"; - def->tooltip = "Speed for printing solid regions (top/bottom/internal horizontal shells). This can be expressed as a percentage (for example: 80%) over the default infill speed above. Set to zero for auto."; + def->tooltip = "Speed for printing solid regions (top/bottom/internal horizontal shells). " + "This can be expressed as a percentage (for example: 80%) over the default " + "infill speed above. Set to zero for auto."; def->sidetext = "mm/s or %"; def->cli = "solid-infill-speed=s"; def->ratio_over = "infill_speed"; @@ -1134,13 +1289,18 @@ PrintConfigDef::PrintConfigDef() def = this->add("spiral_vase", coBool); def->label = "Spiral vase"; - def->tooltip = "This feature will raise Z gradually while printing a single-walled object in order to remove any visible seam. This option requires a single perimeter, no infill, no top solid layers and no support material. You can still set any number of bottom solid layers as well as skirt/brim loops. It won't work when printing more than an object."; + def->tooltip = "This feature will raise Z gradually while printing a single-walled object " + "in order to remove any visible seam. This option requires a single perimeter, " + "no infill, no top solid layers and no support material. You can still set " + "any number of bottom solid layers as well as skirt/brim loops. " + "It won't work when printing more than an object."; def->cli = "spiral-vase!"; def->default_value = new ConfigOptionBool(false); def = this->add("standby_temperature_delta", coInt); def->label = "Temperature variation"; - def->tooltip = "Temperature difference to be applied when an extruder is not active. Enables a full-height \"sacrificial\" skirt on which the nozzles are periodically wiped."; + def->tooltip = "Temperature difference to be applied when an extruder is not active. " + "Enables a full-height \"sacrificial\" skirt on which the nozzles are periodically wiped."; def->sidetext = "∆°C"; def->cli = "standby-temperature-delta=i"; def->min = -500; @@ -1149,7 +1309,13 @@ PrintConfigDef::PrintConfigDef() def = this->add("start_gcode", coString); def->label = "Start G-code"; - def->tooltip = "This start procedure is inserted at the beginning, after bed has reached the target temperature and extruder just started heating, and before extruder has finished heating. If Slic3r detects M104 or M190 in your custom codes, such commands will not be prepended automatically so you're free to customize the order of heating commands and other custom actions. Note that you can use placeholder variables for all Slic3r settings, so you can put a \"M109 S[first_layer_temperature]\" command wherever you want."; + def->tooltip = "This start procedure is inserted at the beginning, after bed has reached " + "the target temperature and extruder just started heating, and before extruder " + "has finished heating. If Slic3r detects M104 or M190 in your custom codes, " + "such commands will not be prepended automatically so you're free to customize " + "the order of heating commands and other custom actions. Note that you can use " + "placeholder variables for all Slic3r settings, so you can put " + "a \"M109 S[first_layer_temperature]\" command wherever you want."; def->cli = "start-gcode=s"; def->multiline = true; def->full_width = true; @@ -1158,7 +1324,14 @@ PrintConfigDef::PrintConfigDef() def = this->add("start_filament_gcode", coStrings); def->label = "Start G-code"; - def->tooltip = "This start procedure is inserted at the beginning, after any printer start gcode. This is used to override settings for a specific filament. If Slic3r detects M104, M109, M140 or M190 in your custom codes, such commands will not be prepended automatically so you're free to customize the order of heating commands and other custom actions. Note that you can use placeholder variables for all Slic3r settings, so you can put a \"M109 S[first_layer_temperature]\" command wherever you want. If you have multiple extruders, the gcode is processed in extruder order."; + def->tooltip = "This start procedure is inserted at the beginning, after any printer start gcode. " + "This is used to override settings for a specific filament. If Slic3r detects " + "M104, M109, M140 or M190 in your custom codes, such commands will " + "not be prepended automatically so you're free to customize the order " + "of heating commands and other custom actions. Note that you can use placeholder variables " + "for all Slic3r settings, so you can put a \"M109 S[first_layer_temperature]\" command " + "wherever you want. If you have multiple extruders, the gcode is processed " + "in extruder order."; def->cli = "start-filament-gcode=s@"; def->multiline = true; def->full_width = true; @@ -1181,7 +1354,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("support_material_xy_spacing", coFloatOrPercent); def->label = "XY separation between an object and its support"; def->category = "Support material"; - def->tooltip = "XY separation between an object and its support. If expressed as percentage (for example 50%), it will be calculated over external perimeter width."; + def->tooltip = "XY separation between an object and its support. If expressed as percentage " + "(for example 50%), it will be calculated over external perimeter width."; def->sidetext = "mm or %"; def->cli = "support-material-xy-spacing=s"; def->ratio_over = "external_perimeter_extrusion_width"; @@ -1210,7 +1384,9 @@ PrintConfigDef::PrintConfigDef() def->gui_type = "f_enum_open"; def->label = "Contact Z distance"; def->category = "Support material"; - def->tooltip = "The vertical distance between object and support material interface. Setting this to 0 will also prevent Slic3r from using bridge flow and speed for the first object layer."; + def->tooltip = "The vertical distance between object and support material interface. " + "Setting this to 0 will also prevent Slic3r from using bridge flow and speed " + "for the first object layer."; def->sidetext = "mm"; def->cli = "support-material-contact-distance=f"; def->min = 0; @@ -1223,7 +1399,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("support_material_enforce_layers", coInt); def->label = "Enforce support for the first"; def->category = "Support material"; - def->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."; + def->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."; def->sidetext = "layers"; def->cli = "support-material-enforce-layers=f"; def->full_label = "Enforce support for the first n layers"; @@ -1233,7 +1412,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("support_material_extruder", coInt); def->label = "Support material/raft/skirt extruder"; def->category = "Extruders"; - def->tooltip = "The extruder to use when printing support material, raft and skirt (1+, 0 to use the current extruder to minimize tool changes)."; + def->tooltip = "The extruder to use when printing support material, raft and skirt " + "(1+, 0 to use the current extruder to minimize tool changes)."; def->cli = "support-material-extruder=i"; def->min = 0; def->default_value = new ConfigOptionInt(1); @@ -1241,7 +1421,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("support_material_extrusion_width", coFloatOrPercent); def->label = "Support material"; def->category = "Extrusion Width"; - def->tooltip = "Set this to a non-zero value to set a manual extrusion width for support material. If expressed as percentage (for example 90%) it will be computed over layer height."; + def->tooltip = "Set this to a non-zero value to set a manual extrusion width for support material. " + "If expressed as percentage (for example 90%) it will be computed over layer height."; def->sidetext = "mm or % (leave 0 for default)"; def->cli = "support-material-extrusion-width=s"; def->default_value = new ConfigOptionFloatOrPercent(0, false); @@ -1256,7 +1437,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("support_material_interface_extruder", coInt); def->label = "Support material/raft interface extruder"; def->category = "Extruders"; - def->tooltip = "The extruder to use when printing support material interface (1+, 0 to use the current extruder to minimize tool changes). This affects raft too."; + def->tooltip = "The extruder to use when printing support material interface " + "(1+, 0 to use the current extruder to minimize tool changes). This affects raft too."; def->cli = "support-material-interface-extruder=i"; def->min = 0; def->default_value = new ConfigOptionInt(1); @@ -1282,7 +1464,8 @@ PrintConfigDef::PrintConfigDef() def = this->add("support_material_interface_speed", coFloatOrPercent); def->label = "Support material interface"; def->category = "Support material"; - def->tooltip = "Speed for printing support material interface layers. If expressed as percentage (for example 50%) it will be calculated over support material speed."; + def->tooltip = "Speed for printing support material interface layers. If expressed as percentage " + "(for example 50%) it will be calculated over support material speed."; def->sidetext = "mm/s or %"; def->cli = "support-material-interface-speed=s"; def->ratio_over = "support_material_speed"; @@ -1326,14 +1509,19 @@ PrintConfigDef::PrintConfigDef() def = this->add("support_material_synchronize_layers", coBool); def->label = "Synchronize with object layers"; def->category = "Support material"; - def->tooltip = "Synchronize support layers with the object print layers. This is useful with multi-material printers, where the extruder switch is expensive."; + def->tooltip = "Synchronize support layers with the object print layers. This is useful " + "with multi-material printers, where the extruder switch is expensive."; def->cli = "support-material-synchronize-layers!"; def->default_value = new ConfigOptionBool(false); def = this->add("support_material_threshold", coInt); def->label = "Overhang threshold"; def->category = "Support material"; - def->tooltip = "Support material will not be generated for overhangs whose slope angle (90° = vertical) is above the given threshold. In other words, this value represent the most horizontal slope (measured from the horizontal plane) that you can print without support material. Set to zero for automatic detection (recommended)."; + def->tooltip = "Support material will not be generated for overhangs whose slope angle " + "(90° = vertical) is above the given threshold. In other words, this value " + "represent the most horizontal slope (measured from the horizontal plane) " + "that you can print without support material. Set to zero for automatic detection " + "(recommended)."; def->sidetext = "°"; def->cli = "support-material-threshold=i"; def->min = 0; @@ -1343,13 +1531,15 @@ PrintConfigDef::PrintConfigDef() def = this->add("support_material_with_sheath", coBool); def->label = "With sheath around the support"; def->category = "Support material"; - def->tooltip = "Add a sheath (a single perimeter line) around the base support. This makes the support more reliable, but also more difficult to remove."; + def->tooltip = "Add a sheath (a single perimeter line) around the base support. This makes " + "the support more reliable, but also more difficult to remove."; def->cli = "support-material-with-sheath!"; def->default_value = new ConfigOptionBool(true); def = this->add("temperature", coInts); def->label = "Other layers"; - def->tooltip = "Extruder temperature for layers after the first one. Set this to zero to disable temperature control commands in the output."; + def->tooltip = "Extruder temperature for layers after the first one. Set this to zero to disable " + "temperature control commands in the output."; def->cli = "temperature=i@"; def->full_label = "Temperature"; def->max = 0; @@ -1359,13 +1549,15 @@ PrintConfigDef::PrintConfigDef() def = this->add("thin_walls", coBool); def->label = "Detect thin walls"; def->category = "Layers and Perimeters"; - def->tooltip = "Detect single-width walls (parts where two extrusions don't fit and we need to collapse them into a single trace)."; + def->tooltip = "Detect single-width walls (parts where two extrusions don't fit and we need " + "to collapse them into a single trace)."; def->cli = "thin-walls!"; def->default_value = new ConfigOptionBool(true); def = this->add("threads", coInt); def->label = "Threads"; - def->tooltip = "Threads are used to parallelize long-running tasks. Optimal threads number is slightly above the number of available cores/processors."; + def->tooltip = "Threads are used to parallelize long-running tasks. Optimal threads number " + "is slightly above the number of available cores/processors."; def->cli = "threads|j=i"; def->readonly = true; def->min = 1; @@ -1376,7 +1568,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("toolchange_gcode", coString); def->label = "Tool change G-code"; - def->tooltip = "This custom code is inserted right before every extruder change. Note that you can use placeholder variables for all Slic3r settings as well as [previous_extruder] and [next_extruder]."; + def->tooltip = "This custom code is inserted right before every extruder change. " + "Note that you can use placeholder variables for all Slic3r settings as well " + "as [previous_extruder] and [next_extruder]."; def->cli = "toolchange-gcode=s"; def->multiline = true; def->full_width = true; @@ -1386,7 +1580,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("top_infill_extrusion_width", coFloatOrPercent); def->label = "Top solid infill"; def->category = "Extrusion Width"; - def->tooltip = "Set this to a non-zero value to set a manual extrusion width for infill for top surfaces. You may want to use thinner extrudates to fill all narrow regions and get a smoother finish. If expressed as percentage (for example 90%) it will be computed over layer height."; + def->tooltip = "Set this to a non-zero value to set a manual extrusion width " + "for infill for top surfaces. You may want to use thinner extrudates " + "to fill all narrow regions and get a smoother finish. If expressed " + "as percentage (for example 90%) it will be computed over layer height."; def->sidetext = "mm or % (leave 0 for default)"; def->cli = "top-infill-extrusion-width=s"; def->default_value = new ConfigOptionFloatOrPercent(0, false); @@ -1394,7 +1591,11 @@ PrintConfigDef::PrintConfigDef() def = this->add("top_solid_infill_speed", coFloatOrPercent); def->label = "Top solid infill"; def->category = "Speed"; - def->tooltip = "Speed for printing top solid layers (it only applies to the uppermost external layers and not to their internal solid layers). You may want to slow down this to get a nicer surface finish. This can be expressed as a percentage (for example: 80%) over the solid infill speed above. Set to zero for auto."; + def->tooltip = "Speed for printing top solid layers (it only applies to the uppermost " + "external layers and not to their internal solid layers). You may want " + "to slow down this to get a nicer surface finish. This can be expressed " + "as a percentage (for example: 80%) over the solid infill speed above. " + "Set to zero for auto."; def->sidetext = "mm/s or %"; def->cli = "top-solid-infill-speed=s"; def->ratio_over = "solid_infill_speed"; @@ -1421,37 +1622,47 @@ PrintConfigDef::PrintConfigDef() def = this->add("use_firmware_retraction", coBool); def->label = "Use firmware retraction"; - def->tooltip = "This experimental setting uses G10 and G11 commands to have the firmware handle the retraction. This is only supported in recent Marlin."; + def->tooltip = "This experimental setting uses G10 and G11 commands to have the firmware " + "handle the retraction. This is only supported in recent Marlin."; def->cli = "use-firmware-retraction!"; def->default_value = new ConfigOptionBool(false); def = this->add("use_relative_e_distances", coBool); def->label = "Use relative E distances"; - def->tooltip = "If your firmware requires relative E values, check this, otherwise leave it unchecked. Most firmwares use absolute values."; + def->tooltip = "If your firmware requires relative E values, check this, " + "otherwise leave it unchecked. Most firmwares use absolute values."; def->cli = "use-relative-e-distances!"; def->default_value = new ConfigOptionBool(false); def = this->add("use_volumetric_e", coBool); def->label = "Use volumetric E"; - def->tooltip = "This experimental setting uses outputs the E values in cubic millimeters instead of linear millimeters. If your firmware doesn't already know filament diameter(s), you can put commands like 'M200 D[filament_diameter_0] T0' in your start G-code in order to turn volumetric mode on and use the filament diameter associated to the filament selected in Slic3r. This is only supported in recent Marlin."; + def->tooltip = "This experimental setting uses outputs the E values in cubic millimeters " + "instead of linear millimeters. If your firmware doesn't already know " + "filament diameter(s), you can put commands like 'M200 D[filament_diameter_0] T0' " + "in your start G-code in order to turn volumetric mode on and use the filament " + "diameter associated to the filament selected in Slic3r. This is only supported " + "in recent Marlin."; def->cli = "use-volumetric-e!"; def->default_value = new ConfigOptionBool(false); def = this->add("variable_layer_height", coBool); def->label = "Enable variable layer height feature"; - def->tooltip = "Some printers or printer setups may have difficulties printing with a variable layer height. Enabled by default."; + def->tooltip = "Some printers or printer setups may have difficulties printing " + "with a variable layer height. Enabled by default."; def->cli = "variable-layer-height!"; def->default_value = new ConfigOptionBool(true); def = this->add("wipe", coBools); def->label = "Wipe while retracting"; - def->tooltip = "This flag will move the nozzle while retracting to minimize the possible blob on leaky extruders."; + def->tooltip = "This flag will move the nozzle while retracting to minimize the possible blob " + "on leaky extruders."; def->cli = "wipe!"; def->default_value = new ConfigOptionBools { false }; def = this->add("wipe_tower", coBool); def->label = "Enable"; - def->tooltip = "Multi material printers may need to prime or purge extruders on tool changes. Extrude the excess material into the wipe tower."; + def->tooltip = "Multi material printers may need to prime or purge extruders on tool changes. " + "Extrude the excess material into the wipe tower."; def->cli = "wipe-tower!"; def->default_value = new ConfigOptionBool(false); @@ -1478,7 +1689,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("wipe_tower_per_color_wipe", coFloat); def->label = "Per color change depth"; - def->tooltip = "Depth of a wipe color per color change. For N colors, there will be maximum (N-1) tool switches performed, therefore the total depth of the wipe tower will be (N-1) times this value."; + def->tooltip = "Depth of a wipe color per color change. For N colors, there will be " + "maximum (N-1) tool switches performed, therefore the total depth " + "of the wipe tower will be (N-1) times this value."; def->sidetext = "mm"; def->cli = "wipe-tower-per-color-wipe=f"; def->default_value = new ConfigOptionFloat(15.); @@ -1486,14 +1699,19 @@ PrintConfigDef::PrintConfigDef() def = this->add("xy_size_compensation", coFloat); def->label = "XY Size Compensation"; def->category = "Advanced"; - def->tooltip = "The object will be grown/shrunk in the XY plane by the configured value (negative = inwards, positive = outwards). This might be useful for fine-tuning hole sizes."; + def->tooltip = "The object will be grown/shrunk in the XY plane by the configured value " + "(negative = inwards, positive = outwards). This might be useful " + "for fine-tuning hole sizes."; def->sidetext = "mm"; def->cli = "xy-size-compensation=f"; def->default_value = new ConfigOptionFloat(0); def = this->add("z_offset", coFloat); def->label = "Z offset"; - def->tooltip = "This value will be added (or subtracted) from all the Z coordinates in the output G-code. It is used to compensate for bad Z endstop position: for example, if your endstop zero actually leaves the nozzle 0.3mm far from the print bed, set this to -0.3 (or fix your endstop)."; + def->tooltip = "This value will be added (or subtracted) from all the Z coordinates " + "in the output G-code. It is used to compensate for bad Z endstop position: " + "for example, if your endstop zero actually leaves the nozzle 0.3mm far " + "from the print bed, set this to -0.3 (or fix your endstop)."; def->sidetext = "mm"; def->cli = "z-offset=f"; def->default_value = new ConfigOptionFloat(0); From 9a0100d6deb4748e778a2136d6f3150e88643aa7 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 9 Nov 2017 10:48:06 +0100 Subject: [PATCH 23/83] Initial definition of PresetHints C++ class and Perl binding, ported the cooling logic hints to C++. Removed Perl Flow::new_from_spacing bindings. Some Fill C++11 beautification. Fix of a support_material_1st_layer_flow, brim_flow and skirt_flow logic to use the extrusion_width if both first_layer_extrusion_width and support_material_extrusion_width are undefined. Documented the extrusion width logic in the config tooltips, including the default values. --- xs/CMakeLists.txt | 3 + xs/lib/Slic3r/XS.pm | 8 - xs/src/libslic3r/Fill/Fill.cpp | 7 +- xs/src/libslic3r/Flow.cpp | 3 +- xs/src/libslic3r/Flow.hpp | 3 + xs/src/libslic3r/Print.cpp | 10 +- xs/src/libslic3r/PrintConfig.cpp | 31 ++-- xs/src/perlglue.cpp | 1 + xs/src/slic3r/GUI/PresetHints.cpp | 254 ++++++++++++++++++++++++++++++ xs/src/slic3r/GUI/PresetHints.hpp | 25 +++ xs/xsp/Flow.xsp | 12 -- 11 files changed, 316 insertions(+), 41 deletions(-) create mode 100644 xs/src/slic3r/GUI/PresetHints.cpp create mode 100644 xs/src/slic3r/GUI/PresetHints.hpp diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index dac9993c3..07442f248 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -26,6 +26,7 @@ include_directories(${LIBDIR}/libslic3r) if(WIN32) # BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking. add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB) + # -D_ITERATOR_DEBUG_LEVEL) endif() add_definitions(-DwxUSE_UNICODE -D_UNICODE -DUNICODE) @@ -172,6 +173,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/Preset.hpp ${LIBDIR}/slic3r/GUI/PresetBundle.cpp ${LIBDIR}/slic3r/GUI/PresetBundle.hpp + ${LIBDIR}/slic3r/GUI/PresetHints.cpp + ${LIBDIR}/slic3r/GUI/PresetHints.hpp ${LIBDIR}/slic3r/GUI/GUI.cpp ${LIBDIR}/slic3r/GUI/GUI.hpp ) diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index 0e995acf2..26c8befe2 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -179,14 +179,6 @@ sub new_from_width { ); } -sub new_from_spacing { - my ($class, %args) = @_; - - return $class->_new_from_spacing( - @args{qw(spacing nozzle_diameter layer_height bridge)}, - ); -} - package Slic3r::Surface; sub new { diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/xs/src/libslic3r/Fill/Fill.cpp index ffae7fa5b..5333fcfec 100644 --- a/xs/src/libslic3r/Fill/Fill.cpp +++ b/xs/src/libslic3r/Fill/Fill.cpp @@ -149,8 +149,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) // ); } - for (Surfaces::const_iterator surface_it = surfaces.begin(); surface_it != surfaces.end(); ++ surface_it) { - const Surface &surface = *surface_it; + for (const Surface &surface : surfaces) { if (surface.surface_type == stInternalVoid) continue; InfillPattern fill_pattern = layerm.region()->config.fill_pattern.value; @@ -262,10 +261,10 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) // Unpacks the collection, creates multiple collections per path. // The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection. // Why the paths are unpacked? - for (ExtrusionEntitiesPtr::iterator thin_fill = layerm.thin_fills.entities.begin(); thin_fill != layerm.thin_fills.entities.end(); ++ thin_fill) { + for (const ExtrusionEntity *thin_fill : layerm.thin_fills.entities) { ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection()); out.entities.push_back(&collection); - collection.entities.push_back((*thin_fill)->clone()); + collection.entities.push_back(thin_fill->clone()); } } diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp index 25a44ea8f..c48452c2b 100644 --- a/xs/src/libslic3r/Flow.cpp +++ b/xs/src/libslic3r/Flow.cpp @@ -154,10 +154,11 @@ Flow support_material_flow(const PrintObject *object, float layer_height) Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) { + const auto &width = (object->print()->config.first_layer_extrusion_width.value > 0) ? object->print()->config.first_layer_extrusion_width : object->config.support_material_extrusion_width; return Flow::new_from_config_width( frSupportMaterial, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. - (object->print()->config.first_layer_extrusion_width.value > 0) ? object->print()->config.first_layer_extrusion_width : object->config.support_material_extrusion_width, + (width.value > 0) ? width : object->config.extrusion_width, float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)), (layer_height > 0.f) ? layer_height : float(object->config.first_layer_height.get_abs_value(object->config.layer_height.value)), false); diff --git a/xs/src/libslic3r/Flow.hpp b/xs/src/libslic3r/Flow.hpp index a058cc8b7..516490b35 100644 --- a/xs/src/libslic3r/Flow.hpp +++ b/xs/src/libslic3r/Flow.hpp @@ -52,6 +52,9 @@ public: coord_t scaled_spacing(const Flow &other) const { return coord_t(scale_(this->spacing(other))); }; static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio); + // Create a flow from the spacing of extrusion lines. + // This method is used exclusively to calculate new flow of 100% infill, where the extrusion width was allowed to scale + // to fit a region with integer number of lines. static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); }; diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 4b47deab2..4ad08192a 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -707,7 +707,10 @@ double Print::skirt_first_layer_height() const Flow Print::brim_flow() const { ConfigOptionFloatOrPercent width = this->config.first_layer_extrusion_width; - if (width.value == 0) width = this->regions.front()->config.perimeter_extrusion_width; + if (width.value == 0) + width = this->regions.front()->config.perimeter_extrusion_width; + if (width.value == 0) + width = this->objects.front()->config.extrusion_width; /* We currently use a random region's perimeter extruder. While this works for most cases, we should probably consider all of the perimeter @@ -726,7 +729,10 @@ Flow Print::brim_flow() const Flow Print::skirt_flow() const { ConfigOptionFloatOrPercent width = this->config.first_layer_extrusion_width; - if (width.value == 0) width = this->regions.front()->config.perimeter_extrusion_width; + if (width.value == 0) + width = this->regions.front()->config.perimeter_extrusion_width; + if (width.value == 0) + width = this->objects.front()->config.extrusion_width; /* We currently use a random object's support material extruder. While this works for most cases, we should probably consider all of the support material diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index e3bbb9eab..54d51c237 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -243,9 +243,8 @@ PrintConfigDef::PrintConfigDef() def->label = "External perimeters"; def->category = "Extrusion Width"; def->tooltip = "Set this to a non-zero value to set a manual extrusion width for external perimeters. " - "If left zero, an automatic value will be used that maximizes accuracy " - "of the external visible surfaces. If expressed as percentage (for example 200%) " - "it will be computed over layer height."; + "If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. " + "If expressed as percentage (for example 200%), it will be computed over layer height."; def->sidetext = "mm or % (leave 0 for default)"; def->cli = "external-perimeter-extrusion-width=s"; def->default_value = new ConfigOptionFloatOrPercent(0, false); @@ -352,9 +351,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("extrusion_width", coFloatOrPercent); def->label = "Default extrusion width"; def->category = "Extrusion Width"; - def->tooltip = "Set this to a non-zero value to set a manual extrusion width. If left to zero, " - "Slic3r calculates a width automatically. If expressed as percentage (for example: 230%) " - "it will be computed over layer height."; + def->tooltip = "Set this to a non-zero value to allow a manual extrusion width. " + "If left to zero, Slic3r derives extrusion widths from the nozzle diameter " + "(see the tooltips for perimeter extrusion width, infill extrusion width etc). " + "If expressed as percentage (for example: 230%), it will be computed over layer height."; def->sidetext = "mm or % (leave 0 for auto)"; def->cli = "extrusion-width=s"; def->default_value = new ConfigOptionFloatOrPercent(0, false); @@ -567,7 +567,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "Set this to a non-zero value to set a manual extrusion width for first layer. " "You can use this to force fatter extrudates for better adhesion. If expressed " "as percentage (for example 120%) it will be computed over first layer height. " - "If set to zero, it will use the Default Extrusion Width."; + "If set to zero, it will use the default extrusion width."; def->sidetext = "mm or % (leave 0 for default)"; def->cli = "first-layer-extrusion-width=s"; def->ratio_over = "first_layer_height"; @@ -681,6 +681,7 @@ PrintConfigDef::PrintConfigDef() def->label = "Infill"; def->category = "Extrusion Width"; def->tooltip = "Set this to a non-zero value to set a manual extrusion width for infill. " + "If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. " "You may want to use fatter extrudates to speed up the infill and make your parts stronger. " "If expressed as percentage (for example 90%) it will be computed over layer height."; def->sidetext = "mm or % (leave 0 for default)"; @@ -938,6 +939,7 @@ PrintConfigDef::PrintConfigDef() def->category = "Extrusion Width"; def->tooltip = "Set this to a non-zero value to set a manual extrusion width for perimeters. " "You may want to use thinner extrudates to get more accurate surfaces. " + "If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. " "If expressed as percentage (for example 200%) it will be computed over layer height."; def->sidetext = "mm or % (leave 0 for default)"; def->cli = "perimeter-extrusion-width=s"; @@ -1259,9 +1261,9 @@ PrintConfigDef::PrintConfigDef() def = this->add("solid_infill_extrusion_width", coFloatOrPercent); def->label = "Solid infill"; def->category = "Extrusion Width"; - def->tooltip = "Set this to a non-zero value to set a manual extrusion width for infill " - "for solid surfaces. If expressed as percentage (for example 90%) it will be computed " - "over layer height."; + def->tooltip = "Set this to a non-zero value to set a manual extrusion width for infill for solid surfaces. " + "If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. " + "If expressed as percentage (for example 90%) it will be computed over layer height."; def->sidetext = "mm or % (leave 0 for default)"; def->cli = "solid-infill-extrusion-width=s"; def->default_value = new ConfigOptionFloatOrPercent(0, false); @@ -1422,6 +1424,7 @@ PrintConfigDef::PrintConfigDef() def->label = "Support material"; def->category = "Extrusion Width"; def->tooltip = "Set this to a non-zero value to set a manual extrusion width for support material. " + "If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. " "If expressed as percentage (for example 90%) it will be computed over layer height."; def->sidetext = "mm or % (leave 0 for default)"; def->cli = "support-material-extrusion-width=s"; @@ -1580,10 +1583,10 @@ PrintConfigDef::PrintConfigDef() def = this->add("top_infill_extrusion_width", coFloatOrPercent); def->label = "Top solid infill"; def->category = "Extrusion Width"; - def->tooltip = "Set this to a non-zero value to set a manual extrusion width " - "for infill for top surfaces. You may want to use thinner extrudates " - "to fill all narrow regions and get a smoother finish. If expressed " - "as percentage (for example 90%) it will be computed over layer height."; + def->tooltip = "Set this to a non-zero value to set a manual extrusion width for infill for top surfaces. " + "You may want to use thinner extrudates to fill all narrow regions and get a smoother finish. " + "If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. " + "If expressed as percentage (for example 90%) it will be computed over layer height."; def->sidetext = "mm or % (leave 0 for default)"; def->cli = "top-infill-extrusion-width=s"; def->default_value = new ConfigOptionFloatOrPercent(0, false); diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index fbc8df8d2..948fcfd93 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -61,6 +61,7 @@ REGISTER_CLASS(GLVolumeCollection, "GUI::_3DScene::GLVolume::Collection"); REGISTER_CLASS(Preset, "GUI::Preset"); REGISTER_CLASS(PresetCollection, "GUI::PresetCollection"); REGISTER_CLASS(PresetBundle, "GUI::PresetBundle"); +REGISTER_CLASS(PresetHints, "GUI::PresetHints"); SV* ConfigBase__as_hash(ConfigBase* THIS) { diff --git a/xs/src/slic3r/GUI/PresetHints.cpp b/xs/src/slic3r/GUI/PresetHints.cpp new file mode 100644 index 000000000..082286ede --- /dev/null +++ b/xs/src/slic3r/GUI/PresetHints.cpp @@ -0,0 +1,254 @@ +//#undef NDEBUGc +#include + +#include "PresetBundle.hpp" +#include "PresetHints.hpp" +#include "Flow.hpp" + +#include + +#include "../../libslic3r/libslic3r.h" + +namespace Slic3r { + +std::string PresetHints::cooling_description(const Preset &preset) +{ + std::string out; + char buf[4096]; + if (preset.config.opt_bool("cooling", 0)) { + int slowdown_below_layer_time = preset.config.opt_int("slowdown_below_layer_time", 0); + int min_fan_speed = preset.config.opt_int("min_fan_speed", 0); + int max_fan_speed = preset.config.opt_int("max_fan_speed", 0); + int min_print_speed = int(preset.config.opt_float("min_print_speed", 0) + 0.5); + int fan_below_layer_time = preset.config.opt_int("fan_below_layer_time", 0); + sprintf(buf, "If estimated layer time is below ~%ds, fan will run at %d%% and print speed will be reduced so that no less than %ds are spent on that layer (however, speed will never be reduced below %dmm/s).", + slowdown_below_layer_time, max_fan_speed, slowdown_below_layer_time, min_print_speed); + out += buf; + if (fan_below_layer_time > slowdown_below_layer_time) { + sprintf(buf, "\nIf estimated layer time is greater, but still below ~%ds, fan will run at a proportionally decreasing speed between %d%% and %d%%.", + fan_below_layer_time, max_fan_speed, min_fan_speed); + out += buf; + } + out += "\nDuring the other layers, fan "; + } else { + out = "Fan "; + } + if (preset.config.opt_bool("fan_always_on", 0)) { + int disable_fan_first_layers = preset.config.opt_int("disable_fan_first_layers", 0); + int min_fan_speed = preset.config.opt_int("min_fan_speed", 0); + sprintf(buf, "will always run at %d% ", min_fan_speed); + out += buf; + if (disable_fan_first_layers > 1) { + sprintf(buf, "except for the first %d layers", disable_fan_first_layers); + out += buf; + } + else if (disable_fan_first_layers == 1) + out += "except for the first layer"; + } else + out += "will be turned off."; + + return out; +} + +std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle &preset_bundle) +{ + // Find out, to which nozzle index is the current filament profile assigned. + unsigned int idx_nozzle = 0; + for (; idx_nozzle < (unsigned int)preset_bundle.filaments.size(); ++ idx_nozzle) + if (preset_bundle.filament_presets[idx_nozzle] == preset_bundle.filaments.get_selected_preset().name) + break; + if (idx_nozzle == (unsigned int)preset_bundle.filaments.size()) + // The current filament preset is not active for any extruder. + idx_nozzle = (unsigned int)-1; + + const DynamicPrintConfig &print_config = preset_bundle.prints .get_edited_preset().config; + const DynamicPrintConfig &filament_config = preset_bundle.filaments.get_edited_preset().config; + const DynamicPrintConfig &printer_config = preset_bundle.printers .get_edited_preset().config; + + // Current printer values. + double nozzle_diameter = printer_config.opt_float("nozzle_diameter", idx_nozzle); + + // Print config values + double layer_height = print_config.get_abs_value("layer_height", nozzle_diameter); + double first_layer_height = print_config.get_abs_value("first_layer_height", layer_height); + double support_material_speed = print_config.opt_float("support_material_speed"); + double support_material_interface_speed = print_config.get_abs_value("support_material_interface_speed", support_material_speed); + double bridge_speed = print_config.opt_float("bridge_speed"); + double bridge_flow_ratio = print_config.opt_float("bridge_flow_ratio"); + double perimeter_speed = print_config.opt_float("perimeter_speed"); + double external_perimeter_speed = print_config.get_abs_value("external_perimeter_speed", perimeter_speed); + double gap_fill_speed = print_config.opt_float("gap_fill_speed"); + double infill_speed = print_config.opt_float("infill_speed"); + double small_perimeter_speed = print_config.get_abs_value("small_perimeter_speed", perimeter_speed); + double solid_infill_speed = print_config.get_abs_value("solid_infill_speed", infill_speed); + double top_solid_infill_speed = print_config.get_abs_value("top_solid_infill_speed", solid_infill_speed); + // Maximum print speed when auto-speed is enabled by setting any of the above speed values to zero. + double max_print_speed = print_config.opt_float("max_print_speed"); + // Maximum volumetric speed allowed for the print profile. + double max_volumetric_speed = print_config.opt_float("max_volumetric_speed"); + + const auto &extrusion_width = print_config.get_abs_value("extrusion_width"); + const auto &support_material_extrusion_width = *print_config.option("support_material_extrusion_width"); + const auto &external_perimeter_extrusion_width = *print_config.option("external_perimeter_extrusion_width"); + const auto &infill_extrusion_width = *print_config.option("infill_extrusion_width"); + const auto &solid_infill_extrusion_width = *print_config.option("solid_infill_extrusion_width"); + const auto &perimeter_extrusion_width = *print_config.option("perimeter_extrusion_width"); + const auto &top_infill_extrusion_width = *print_config.option("top_infill_extrusion_width"); + const auto &first_layer_extrusion_width = *print_config.option("first_layer_extrusion_width"); + + int perimeter_extruder = print_config.opt_int("perimeter_extruder"); + int infill_extruder = print_config.opt_int("infill_extruder"); + int solid_infill_extruder = print_config.opt_int("solid_infill_extruder"); + int support_material_extruder = print_config.opt_int("support_material_extruder"); + int support_material_interface_extruder = print_config.opt_int("support_material_interface_extruder"); + + // Current filament values + double filament_diameter = filament_config.opt_float("filament_diameter", 0); + double extrusion_multiplier = filament_config.opt_float("extrusion_multiplier", 0); + double filament_max_volumetric_speed = filament_config.opt_float("filament_max_volumetric_speed", 0); + + + auto external_perimeter_flow = Flow::new_from_config_width(frExternalPerimeter, external_perimeter_extrusion_width, (float)nozzle_diameter, (float)layer_height, 0); + auto perimeter_flow = Flow::new_from_config_width(frPerimeter, perimeter_extrusion_width, (float)nozzle_diameter, (float)layer_height, 0); + auto infill_flow = Flow::new_from_config_width(frInfill, infill_extrusion_width, (float)nozzle_diameter, (float)layer_height, 0); + auto solid_infill_flow = Flow::new_from_config_width(frInfill, solid_infill_extrusion_width, (float)nozzle_diameter, (float)layer_height, 0); + auto top_solid_infill_flow = Flow::new_from_config_width(frInfill, top_infill_extrusion_width, (float)nozzle_diameter, (float)layer_height, 0); +// auto support_material_flow = Flow::new_from_config_width(frSupportMaterial, , +// (float)nozzle_diameter, (float)layer_height, 0); + auto support_material_interface_flow = Flow::new_from_config_width(frSupportMaterialInterface, *print_config.option("support_material_extrusion_width"), + (float)nozzle_diameter, (float)layer_height, 0); + + std::string out; + out="Hu"; + return out; +} + +#if 0 +static create_flow(FlowRole role, ConfigOptionFloatOrPercent &width, double layer_height, bool bridge, bool first_layer, double width) const +{ + ConfigOptionFloatOrPercent config_width; + if (width != -1) { + // use the supplied custom width, if any + config_width.value = width; + config_width.percent = false; + } else { + // otherwise, get extrusion width from configuration + // (might be an absolute value, or a percent value, or zero for auto) + if (first_layer && this->_print->config.first_layer_extrusion_width.value > 0) { + config_width = this->_print->config.first_layer_extrusion_width; + } else if (role == frExternalPerimeter) { + config_width = this->config.external_perimeter_extrusion_width; + } else if (role == frPerimeter) { + config_width = this->config.perimeter_extrusion_width; + } else if (role == frInfill) { + config_width = this->config.infill_extrusion_width; + } else if (role == frSolidInfill) { + config_width = this->config.solid_infill_extrusion_width; + } else if (role == frTopSolidInfill) { + config_width = this->config.top_infill_extrusion_width; + } else { + CONFESS("Unknown role"); + } + } + if (config_width.value == 0) { + config_width = object.config.extrusion_width; + } + + // get the configured nozzle_diameter for the extruder associated + // to the flow role requested + size_t extruder = 0; // 1-based + if (role == frPerimeter || role == frExternalPerimeter) { + extruder = this->config.perimeter_extruder; + } else if (role == frInfill) { + extruder = this->config.infill_extruder; + } else if (role == frSolidInfill || role == frTopSolidInfill) { + extruder = this->config.solid_infill_extruder; + } else { + CONFESS("Unknown role $role"); + } + double nozzle_diameter = this->_print->config.nozzle_diameter.get_at(extruder-1); + + return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? (float)this->config.bridge_flow_ratio : 0.0); +} + + if (first_layer && this->_print->config.first_layer_extrusion_width.value > 0) { + config_width = this->_print->config.first_layer_extrusion_width; + auto flow = Flow::new_from_config_width(frExternalPerimeter, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio); + + + +Flow Print::skirt_flow() const +{ + ConfigOptionFloatOrPercent width = this->config.first_layer_extrusion_width; + if (width.value == 0) width = this->regions.front()->config.perimeter_extrusion_width; + + /* We currently use a random object's support material extruder. + While this works for most cases, we should probably consider all of the support material + extruders and take the one with, say, the smallest index; + The same logic should be applied to the code that selects the extruder during G-code + generation as well. */ + return Flow::new_from_config_width( + frPerimeter, + width, + this->config.nozzle_diameter.get_at(this->objects.front()->config.support_material_extruder-1), + this->skirt_first_layer_height(), + 0 + ); +} + +Flow Print::brim_flow() const +{ + ConfigOptionFloatOrPercent width = this->config.first_layer_extrusion_width; + if (width.value == 0) width = this->regions.front()->config.perimeter_extrusion_width; + + /* We currently use a random region's perimeter extruder. + While this works for most cases, we should probably consider all of the perimeter + extruders and take the one with, say, the smallest index. + The same logic should be applied to the code that selects the extruder during G-code + generation as well. */ + return Flow::new_from_config_width( + frPerimeter, + width, + this->config.nozzle_diameter.get_at(this->regions.front()->config.perimeter_extruder-1), + this->skirt_first_layer_height(), + 0 + ); +} + +Flow support_material_flow(const PrintObject *object, float layer_height) +{ + return Flow::new_from_config_width( + frSupportMaterial, + // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. + (support_material_extrusion_width.value > 0) ? support_material_extrusion_width : extrusion_width, + // if object->config.support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. + float(nozzle_diameter.get_at(support_material_extruder-1)), + (layer_height > 0.f) ? layer_height : float(layer_height.value), + false); +} + +Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) +{ + return Flow::new_from_config_width( + frSupportMaterial, + // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. + (first_layer_extrusion_width.value > 0) ? first_layer_extrusion_width : support_material_extrusion_width, + float(nozzle_diameter.get_at(object->config.support_material_extruder-1)), + (layer_height > 0.f) ? layer_height : float(first_layer_height)), + false); +} + +Flow support_material_interface_flow(const PrintObject *object, float layer_height) +{ + return Flow::new_from_config_width( + frSupportMaterialInterface, + // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. + (support_material_extrusion_width > 0) ? support_material_extrusion_width : extrusion_width, + // if object->config.support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. + float(nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)), + layer_height, + false); +} +#endif + +}; // namespace Slic3r diff --git a/xs/src/slic3r/GUI/PresetHints.hpp b/xs/src/slic3r/GUI/PresetHints.hpp new file mode 100644 index 000000000..589cc2f98 --- /dev/null +++ b/xs/src/slic3r/GUI/PresetHints.hpp @@ -0,0 +1,25 @@ +#ifndef slic3r_PresetHints_hpp_ +#define slic3r_PresetHints_hpp_ + +#include + +#include "PresetBundle.hpp" + +namespace Slic3r { + +// GUI utility functions to produce hint messages from the current profile. +class PresetHints +{ +public: + // Produce a textual description of the cooling logic of a currently active filament. + static std::string cooling_description(const Preset &preset); + // Produce a textual description of the maximum flow achived for the current configuration + // (the current printer, filament and print settigns). + // This description will be useful for getting a gut feeling for the maximum volumetric + // print speed achievable with the extruder. + static std::string maximum_volumetric_flow_description(const PresetBundle &preset_bundle); +}; + +} // namespace Slic3r + +#endif /* slic3r_PresetHints_hpp_ */ diff --git a/xs/xsp/Flow.xsp b/xs/xsp/Flow.xsp index d09f0b351..d9b7a45c0 100644 --- a/xs/xsp/Flow.xsp +++ b/xs/xsp/Flow.xsp @@ -46,18 +46,6 @@ _new_from_width(CLASS, role, width, nozzle_diameter, height, bridge_flow_ratio) OUTPUT: RETVAL -Flow* -_new_from_spacing(CLASS, spacing, nozzle_diameter, height, bridge) - char* CLASS; - float spacing; - float nozzle_diameter; - float height; - bool bridge; - CODE: - RETVAL = new Flow(Flow::new_from_spacing(spacing, nozzle_diameter, height, bridge)); - OUTPUT: - RETVAL - %} }; From b23b9ea1d208f67fac6c2c1e9b5fc154d686c2eb Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 9 Nov 2017 15:10:20 +0100 Subject: [PATCH 24/83] Implemented volumetric flow rate hints, removed some C++11 conditioned compilation. Slic3r now requires C++11. --- xs/src/libslic3r/Config.hpp | 18 +- xs/src/libslic3r/ExPolygon.hpp | 3 +- xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp | 11 +- xs/src/libslic3r/Fill/FillHoneycomb.cpp | 9 - xs/src/libslic3r/Fill/FillRectilinear.cpp | 11 +- xs/src/libslic3r/Flow.cpp | 34 --- xs/src/libslic3r/Polygon.hpp | 2 - xs/src/slic3r/GUI/PresetHints.cpp | 295 ++++++++++------------ xs/xsp/my.map | 2 + xs/xsp/typemap.xspt | 2 + 10 files changed, 151 insertions(+), 236 deletions(-) diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 1d2a5c17d..33f28e4a8 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -1010,7 +1010,7 @@ public: { ConfigOption *opt = this->optptr(opt_key, create); assert(opt == nullptr || opt->type() == typename TYPE::static_type()); - return (opt == nullptr || opt->type() == typename TYPE::static_type()) ? nullptr : static_cast(opt); + return (opt == nullptr || opt->type() != typename TYPE::static_type()) ? nullptr : static_cast(opt); } template const TYPE* option(const t_config_option_key &opt_key) const @@ -1101,23 +1101,23 @@ public: // Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. t_config_option_keys keys() const override; - std::string& opt_string(const t_config_option_key &opt_key, bool create = false) { return dynamic_cast(this->option(opt_key, create))->value; } + std::string& opt_string(const t_config_option_key &opt_key, bool create = false) { return this->option(opt_key, create)->value; } const std::string& opt_string(const t_config_option_key &opt_key) const { return const_cast(this)->opt_string(opt_key); } - std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) { return dynamic_cast(this->option(opt_key))->get_at(idx); } + std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) { return this->option(opt_key)->get_at(idx); } const std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) const { return const_cast(this)->opt_string(opt_key, idx); } - double& opt_float(const t_config_option_key &opt_key) { return dynamic_cast(this->option(opt_key))->value; } + double& opt_float(const t_config_option_key &opt_key) { return this->option(opt_key)->value; } const double opt_float(const t_config_option_key &opt_key) const { return dynamic_cast(this->option(opt_key))->value; } - double& opt_float(const t_config_option_key &opt_key, unsigned int idx) { return dynamic_cast(this->option(opt_key))->get_at(idx); } + double& opt_float(const t_config_option_key &opt_key, unsigned int idx) { return this->option(opt_key)->get_at(idx); } const double opt_float(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx); } - int& opt_int(const t_config_option_key &opt_key) { return dynamic_cast(this->option(opt_key))->value; } + int& opt_int(const t_config_option_key &opt_key) { return this->option(opt_key)->value; } const int opt_int(const t_config_option_key &opt_key) const { return dynamic_cast(this->option(opt_key))->value; } - int& opt_int(const t_config_option_key &opt_key, unsigned int idx) { return dynamic_cast(this->option(opt_key))->get_at(idx); } + int& opt_int(const t_config_option_key &opt_key, unsigned int idx) { return this->option(opt_key)->get_at(idx); } const int opt_int(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx); } - bool opt_bool(const t_config_option_key &opt_key) const { return dynamic_cast(this->option(opt_key))->value != 0; } - bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx) != 0; } + bool opt_bool(const t_config_option_key &opt_key) const { return this->option(opt_key)->value != 0; } + bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option(opt_key)->get_at(idx) != 0; } protected: DynamicConfig() {} diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp index af34694b7..f4782ba55 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -149,7 +149,6 @@ inline Polylines to_polylines(const ExPolygons &src) return polylines; } -#if SLIC3R_CPPVER >= 11 inline Polylines to_polylines(ExPolygon &&src) { Polylines polylines; @@ -166,6 +165,7 @@ inline Polylines to_polylines(ExPolygon &&src) assert(idx == polylines.size()); return polylines; } + inline Polylines to_polylines(ExPolygons &&src) { Polylines polylines; @@ -184,7 +184,6 @@ inline Polylines to_polylines(ExPolygons &&src) assert(idx == polylines.size()); return polylines; } -#endif inline Polygons to_polygons(const ExPolygon &src) { diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp index d03c1f208..aa9774784 100644 --- a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -176,11 +176,7 @@ void Fill3DHoneycomb::_fill_surface_single( } } Polylines chained = PolylineCollection::chained_path_from( -#if SLIC3R_CPPVER >= 11 std::move(polylines), -#else - polylines, -#endif PolylineCollection::leftmost_point(polylines), false); // reverse allowed bool first = true; for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) { @@ -199,12 +195,7 @@ void Fill3DHoneycomb::_fill_surface_single( } } // The lines cannot be connected. -#if SLIC3R_CPPVER >= 11 - polylines_out.push_back(std::move(*it_polyline)); -#else - polylines_out.push_back(Polyline()); - std::swap(polylines_out.back(), *it_polyline); -#endif + polylines_out.emplace_back(std::move(*it_polyline)); first = false; } } diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.cpp b/xs/src/libslic3r/Fill/FillHoneycomb.cpp index 22dea85da..aa0e0f6b0 100644 --- a/xs/src/libslic3r/Fill/FillHoneycomb.cpp +++ b/xs/src/libslic3r/Fill/FillHoneycomb.cpp @@ -17,12 +17,7 @@ void FillHoneycomb::_fill_surface_single( CacheID cache_id(params.density, this->spacing); Cache::iterator it_m = this->cache.find(cache_id); if (it_m == this->cache.end()) { -#if 0 -// #if SLIC3R_CPPVER > 11 - it_m = this->cache.emplace_hint(it_m); -#else it_m = this->cache.insert(it_m, std::pair(cache_id, CacheData())); -#endif CacheData &m = it_m->second; coord_t min_spacing = scale_(this->spacing); m.distance = min_spacing / params.density; @@ -99,11 +94,7 @@ void FillHoneycomb::_fill_surface_single( // connect paths if (! paths.empty()) { // prevent calling leftmost_point() on empty collections Polylines chained = PolylineCollection::chained_path_from( -#if SLIC3R_CPPVER >= 11 std::move(paths), -#else - paths, -#endif PolylineCollection::leftmost_point(paths), false); assert(paths.empty()); paths.clear(); diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp index 991adc0b3..5ba30ba51 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp @@ -93,11 +93,7 @@ void FillRectilinear::_fill_surface_single( } } Polylines chained = PolylineCollection::chained_path_from( -#if SLIC3R_CPPVER >= 11 std::move(polylines), -#else - polylines, -#endif PolylineCollection::leftmost_point(polylines), false); // reverse allowed bool first = true; for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) { @@ -118,12 +114,7 @@ void FillRectilinear::_fill_surface_single( } } // The lines cannot be connected. -#if SLIC3R_CPPVER >= 11 - polylines_out.push_back(std::move(*it_polyline)); -#else - polylines_out.push_back(Polyline()); - std::swap(polylines_out.back(), *it_polyline); -#endif + polylines_out.emplace_back(std::move(*it_polyline)); first = false; } } diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp index c48452c2b..b60e26dcc 100644 --- a/xs/src/libslic3r/Flow.cpp +++ b/xs/src/libslic3r/Flow.cpp @@ -8,39 +8,6 @@ namespace Slic3r { // This static method returns a sane extrusion width default. static inline float auto_extrusion_width(FlowRole role, float nozzle_diameter, float height) { -#if 0 - // Here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate. - // shape: rectangle with semicircles at the ends - // This "sane" extrusion width gives the following results for a 0.4mm dmr nozzle: - // Layer Calculated Calculated width - // heigh extrusion over nozzle - // width diameter - // 0.40 0.40 1.00 - // 0.35 0.43 1.09 - // 0.30 0.48 1.21 - // 0.25 0.56 1.39 - // 0.20 0.67 1.68 - // 0.15 0.87 2.17 - // 0.10 1.28 3.20 - // 0.05 2.52 6.31 - // - float width = float(0.25 * (nozzle_diameter * nozzle_diameter) * PI / height + height * (1.0 - 0.25 * PI)); - - switch (role) { - case frExternalPerimeter: - case frSupportMaterial: - case frSupportMaterialInterface: - return nozzle_diameter; - case frPerimeter: - case frSolidInfill: - case frTopSolidInfill: - // do not limit width for sparse infill so that we use full native flow for it - return std::min(std::max(width, nozzle_diameter * 1.05f), nozzle_diameter * 1.7f); - case frInfill: - default: - return std::max(width, nozzle_diameter * 1.05f); - } -#else switch (role) { case frSupportMaterial: case frSupportMaterialInterface: @@ -53,7 +20,6 @@ static inline float auto_extrusion_width(FlowRole role, float nozzle_diameter, f case frInfill: return 1.125f * nozzle_diameter; } -#endif } // This constructor builds a Flow object from an extrusion width config setting diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp index 6ea300cee..f36abc185 100644 --- a/xs/src/libslic3r/Polygon.hpp +++ b/xs/src/libslic3r/Polygon.hpp @@ -154,7 +154,6 @@ inline Polylines to_polylines(const Polygons &polys) return polylines; } -#if SLIC3R_CPPVER >= 11 inline Polylines to_polylines(Polygons &&polys) { Polylines polylines; @@ -168,7 +167,6 @@ inline Polylines to_polylines(Polygons &&polys) assert(idx == polylines.size()); return polylines; } -#endif } // Slic3r diff --git a/xs/src/slic3r/GUI/PresetHints.cpp b/xs/src/slic3r/GUI/PresetHints.cpp index 082286ede..13478f980 100644 --- a/xs/src/slic3r/GUI/PresetHints.cpp +++ b/xs/src/slic3r/GUI/PresetHints.cpp @@ -1,4 +1,4 @@ -//#undef NDEBUGc +//#undef NDEBUG #include #include "PresetBundle.hpp" @@ -36,7 +36,7 @@ std::string PresetHints::cooling_description(const Preset &preset) if (preset.config.opt_bool("fan_always_on", 0)) { int disable_fan_first_layers = preset.config.opt_int("disable_fan_first_layers", 0); int min_fan_speed = preset.config.opt_int("min_fan_speed", 0); - sprintf(buf, "will always run at %d% ", min_fan_speed); + sprintf(buf, "will always run at %d%% ", min_fan_speed); out += buf; if (disable_fan_first_layers > 1) { sprintf(buf, "except for the first %d layers", disable_fan_first_layers); @@ -50,26 +50,37 @@ std::string PresetHints::cooling_description(const Preset &preset) return out; } +static const ConfigOptionFloatOrPercent& first_positive(const ConfigOptionFloatOrPercent *v1, const ConfigOptionFloatOrPercent &v2, const ConfigOptionFloatOrPercent &v3) +{ + return (v1 != nullptr && v1->value > 0) ? *v1 : ((v2.value > 0) ? v2 : v3); +} + +static double first_positive(double v1, double v2) +{ + return (v1 > 0.) ? v1 : v2; +} + std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle &preset_bundle) { // Find out, to which nozzle index is the current filament profile assigned. - unsigned int idx_nozzle = 0; - for (; idx_nozzle < (unsigned int)preset_bundle.filaments.size(); ++ idx_nozzle) - if (preset_bundle.filament_presets[idx_nozzle] == preset_bundle.filaments.get_selected_preset().name) + int idx_extruder = 0; + int num_extruders = (int)preset_bundle.filament_presets.size(); + for (; idx_extruder < num_extruders; ++ idx_extruder) + if (preset_bundle.filament_presets[idx_extruder] == preset_bundle.filaments.get_selected_preset().name) break; - if (idx_nozzle == (unsigned int)preset_bundle.filaments.size()) + if (idx_extruder == num_extruders) // The current filament preset is not active for any extruder. - idx_nozzle = (unsigned int)-1; + idx_extruder = -1; const DynamicPrintConfig &print_config = preset_bundle.prints .get_edited_preset().config; const DynamicPrintConfig &filament_config = preset_bundle.filaments.get_edited_preset().config; const DynamicPrintConfig &printer_config = preset_bundle.printers .get_edited_preset().config; // Current printer values. - double nozzle_diameter = printer_config.opt_float("nozzle_diameter", idx_nozzle); + float nozzle_diameter = (float)printer_config.opt_float("nozzle_diameter", idx_extruder); // Print config values - double layer_height = print_config.get_abs_value("layer_height", nozzle_diameter); + double layer_height = print_config.opt_float("layer_height"); double first_layer_height = print_config.get_abs_value("first_layer_height", layer_height); double support_material_speed = print_config.opt_float("support_material_speed"); double support_material_interface_speed = print_config.get_abs_value("support_material_interface_speed", support_material_speed); @@ -87,168 +98,132 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle // Maximum volumetric speed allowed for the print profile. double max_volumetric_speed = print_config.opt_float("max_volumetric_speed"); - const auto &extrusion_width = print_config.get_abs_value("extrusion_width"); - const auto &support_material_extrusion_width = *print_config.option("support_material_extrusion_width"); + const auto &extrusion_width = *print_config.option("extrusion_width"); const auto &external_perimeter_extrusion_width = *print_config.option("external_perimeter_extrusion_width"); - const auto &infill_extrusion_width = *print_config.option("infill_extrusion_width"); - const auto &solid_infill_extrusion_width = *print_config.option("solid_infill_extrusion_width"); - const auto &perimeter_extrusion_width = *print_config.option("perimeter_extrusion_width"); - const auto &top_infill_extrusion_width = *print_config.option("top_infill_extrusion_width"); const auto &first_layer_extrusion_width = *print_config.option("first_layer_extrusion_width"); + const auto &infill_extrusion_width = *print_config.option("infill_extrusion_width"); + const auto &perimeter_extrusion_width = *print_config.option("perimeter_extrusion_width"); + const auto &solid_infill_extrusion_width = *print_config.option("solid_infill_extrusion_width"); + const auto &support_material_extrusion_width = *print_config.option("support_material_extrusion_width"); + const auto &top_infill_extrusion_width = *print_config.option("top_infill_extrusion_width"); - int perimeter_extruder = print_config.opt_int("perimeter_extruder"); - int infill_extruder = print_config.opt_int("infill_extruder"); - int solid_infill_extruder = print_config.opt_int("solid_infill_extruder"); - int support_material_extruder = print_config.opt_int("support_material_extruder"); - int support_material_interface_extruder = print_config.opt_int("support_material_interface_extruder"); + // Index of an extruder assigned to a feature. If set to 0, an active extruder will be used for a multi-material print. + // If different from idx_extruder, it will not be taken into account for this hint. + auto feature_extruder_active = [idx_extruder, num_extruders](int i) { + return i <= 0 || i > num_extruders || idx_extruder == -1 || idx_extruder == i - 1; + }; + bool perimeter_extruder_active = feature_extruder_active(print_config.opt_int("perimeter_extruder")); + bool infill_extruder_active = feature_extruder_active(print_config.opt_int("infill_extruder")); + bool solid_infill_extruder_active = feature_extruder_active(print_config.opt_int("solid_infill_extruder")); + bool support_material_extruder_active = feature_extruder_active(print_config.opt_int("support_material_extruder")); + bool support_material_interface_extruder_active = feature_extruder_active(print_config.opt_int("support_material_interface_extruder")); // Current filament values double filament_diameter = filament_config.opt_float("filament_diameter", 0); + double filament_crossection = M_PI * 0.25 * filament_diameter * filament_diameter; double extrusion_multiplier = filament_config.opt_float("extrusion_multiplier", 0); - double filament_max_volumetric_speed = filament_config.opt_float("filament_max_volumetric_speed", 0); - - - auto external_perimeter_flow = Flow::new_from_config_width(frExternalPerimeter, external_perimeter_extrusion_width, (float)nozzle_diameter, (float)layer_height, 0); - auto perimeter_flow = Flow::new_from_config_width(frPerimeter, perimeter_extrusion_width, (float)nozzle_diameter, (float)layer_height, 0); - auto infill_flow = Flow::new_from_config_width(frInfill, infill_extrusion_width, (float)nozzle_diameter, (float)layer_height, 0); - auto solid_infill_flow = Flow::new_from_config_width(frInfill, solid_infill_extrusion_width, (float)nozzle_diameter, (float)layer_height, 0); - auto top_solid_infill_flow = Flow::new_from_config_width(frInfill, top_infill_extrusion_width, (float)nozzle_diameter, (float)layer_height, 0); -// auto support_material_flow = Flow::new_from_config_width(frSupportMaterial, , -// (float)nozzle_diameter, (float)layer_height, 0); - auto support_material_interface_flow = Flow::new_from_config_width(frSupportMaterialInterface, *print_config.option("support_material_extrusion_width"), - (float)nozzle_diameter, (float)layer_height, 0); + // The following value will be annotated by this hint, so it does not take part in the calculation. +// double filament_max_volumetric_speed = filament_config.opt_float("filament_max_volumetric_speed", 0); std::string out; - out="Hu"; - return out; -} - -#if 0 -static create_flow(FlowRole role, ConfigOptionFloatOrPercent &width, double layer_height, bool bridge, bool first_layer, double width) const -{ - ConfigOptionFloatOrPercent config_width; - if (width != -1) { - // use the supplied custom width, if any - config_width.value = width; - config_width.percent = false; - } else { - // otherwise, get extrusion width from configuration - // (might be an absolute value, or a percent value, or zero for auto) - if (first_layer && this->_print->config.first_layer_extrusion_width.value > 0) { - config_width = this->_print->config.first_layer_extrusion_width; - } else if (role == frExternalPerimeter) { - config_width = this->config.external_perimeter_extrusion_width; - } else if (role == frPerimeter) { - config_width = this->config.perimeter_extrusion_width; - } else if (role == frInfill) { - config_width = this->config.infill_extrusion_width; - } else if (role == frSolidInfill) { - config_width = this->config.solid_infill_extrusion_width; - } else if (role == frTopSolidInfill) { - config_width = this->config.top_infill_extrusion_width; - } else { - CONFESS("Unknown role"); + for (size_t idx_type = (first_layer_extrusion_width.value == 0) ? 1 : 0; idx_type < 3; ++ idx_type) { + // First test the maximum volumetric extrusion speed for non-bridging extrusions. + bool first_layer = idx_type == 0; + bool bridging = idx_type == 2; + const ConfigOptionFloatOrPercent *first_layer_extrusion_width_ptr = (first_layer && first_layer_extrusion_width.value > 0) ? + &first_layer_extrusion_width : nullptr; + const float lh = float(first_layer ? first_layer_height : layer_height); + const float bfr = bridging ? bridge_flow_ratio : 0.f; + double max_flow = 0.; + std::string max_flow_extrusion_type; + if (perimeter_extruder_active) { + double external_perimeter_rate = Flow::new_from_config_width(frExternalPerimeter, + first_positive(first_layer_extrusion_width_ptr, external_perimeter_extrusion_width, extrusion_width), + nozzle_diameter, lh, bfr).mm3_per_mm() * + (bridging ? bridge_speed : + first_positive(std::max(external_perimeter_speed, small_perimeter_speed), max_print_speed)); + if (max_flow < external_perimeter_rate) { + max_flow = external_perimeter_rate; + max_flow_extrusion_type = "external perimeters"; + } + double perimeter_rate = Flow::new_from_config_width(frPerimeter, + first_positive(first_layer_extrusion_width_ptr, perimeter_extrusion_width, extrusion_width), + nozzle_diameter, lh, bfr).mm3_per_mm() * + (bridging ? bridge_speed : + first_positive(std::max(perimeter_speed, small_perimeter_speed), max_print_speed)); + if (max_flow < perimeter_rate) { + max_flow = perimeter_rate; + max_flow_extrusion_type = "perimeters"; + } } + if (! bridging && infill_extruder_active) { + double infill_rate = Flow::new_from_config_width(frInfill, + first_positive(first_layer_extrusion_width_ptr, infill_extrusion_width, extrusion_width), + nozzle_diameter, lh, bfr).mm3_per_mm() * first_positive(infill_speed, max_print_speed); + if (max_flow < infill_rate) { + max_flow = infill_rate; + max_flow_extrusion_type = "infill"; + } + } + if (solid_infill_extruder_active) { + double solid_infill_rate = Flow::new_from_config_width(frInfill, + first_positive(first_layer_extrusion_width_ptr, solid_infill_extrusion_width, extrusion_width), + nozzle_diameter, lh, 0).mm3_per_mm() * + (bridging ? bridge_speed : first_positive(solid_infill_speed, max_print_speed)); + if (max_flow < solid_infill_rate) { + max_flow = solid_infill_rate; + max_flow_extrusion_type = "solid infill"; + } + if (! bridging) { + double top_solid_infill_rate = Flow::new_from_config_width(frInfill, + first_positive(first_layer_extrusion_width_ptr, top_infill_extrusion_width, extrusion_width), + nozzle_diameter, lh, bfr).mm3_per_mm() * first_positive(top_solid_infill_speed, max_print_speed); + if (max_flow < top_solid_infill_rate) { + max_flow = top_solid_infill_rate; + max_flow_extrusion_type = "top solid infill"; + } + } + } + if (support_material_extruder_active) { + double support_material_rate = Flow::new_from_config_width(frSupportMaterial, + first_positive(first_layer_extrusion_width_ptr, support_material_extrusion_width, extrusion_width), + nozzle_diameter, lh, bfr).mm3_per_mm() * + (bridging ? bridge_speed : first_positive(support_material_speed, max_print_speed)); + if (max_flow < support_material_rate) { + max_flow = support_material_rate; + max_flow_extrusion_type = "support"; + } + } + if (support_material_interface_extruder_active) { + double support_material_interface_rate = Flow::new_from_config_width(frSupportMaterialInterface, + first_positive(first_layer_extrusion_width_ptr, support_material_extrusion_width, extrusion_width), + nozzle_diameter, lh, bfr).mm3_per_mm() * + (bridging ? bridge_speed : first_positive(support_material_interface_speed, max_print_speed)); + if (max_flow < support_material_interface_rate) { + max_flow = support_material_interface_rate; + max_flow_extrusion_type = "support interface"; + } + } + + //FIXME handle gap_fill_speed + if (! out.empty()) + out += "\n"; + out += (first_layer ? "First layer volumetric" : (bridging ? "Bridging volumetric" : "Volumetric")); + out += " flow rate is maximized "; + out += ((max_volumetric_speed > 0 && max_volumetric_speed < max_flow) ? + "by the print profile maximum" : + ("when printing " + max_flow_extrusion_type)) + + " with a volumetric rate "; + if (max_volumetric_speed > 0 && max_volumetric_speed < max_flow) + max_flow = max_volumetric_speed; + char buf[2048]; + sprintf(buf, "%3.2f mm³/s", max_flow); + out += buf; + sprintf(buf, " at filament speed %3.2f mm/s.", max_flow / filament_crossection); + out += buf; } - if (config_width.value == 0) { - config_width = object.config.extrusion_width; - } - - // get the configured nozzle_diameter for the extruder associated - // to the flow role requested - size_t extruder = 0; // 1-based - if (role == frPerimeter || role == frExternalPerimeter) { - extruder = this->config.perimeter_extruder; - } else if (role == frInfill) { - extruder = this->config.infill_extruder; - } else if (role == frSolidInfill || role == frTopSolidInfill) { - extruder = this->config.solid_infill_extruder; - } else { - CONFESS("Unknown role $role"); - } - double nozzle_diameter = this->_print->config.nozzle_diameter.get_at(extruder-1); - - return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? (float)this->config.bridge_flow_ratio : 0.0); + + return out; } - if (first_layer && this->_print->config.first_layer_extrusion_width.value > 0) { - config_width = this->_print->config.first_layer_extrusion_width; - auto flow = Flow::new_from_config_width(frExternalPerimeter, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio); - - - -Flow Print::skirt_flow() const -{ - ConfigOptionFloatOrPercent width = this->config.first_layer_extrusion_width; - if (width.value == 0) width = this->regions.front()->config.perimeter_extrusion_width; - - /* We currently use a random object's support material extruder. - While this works for most cases, we should probably consider all of the support material - extruders and take the one with, say, the smallest index; - The same logic should be applied to the code that selects the extruder during G-code - generation as well. */ - return Flow::new_from_config_width( - frPerimeter, - width, - this->config.nozzle_diameter.get_at(this->objects.front()->config.support_material_extruder-1), - this->skirt_first_layer_height(), - 0 - ); -} - -Flow Print::brim_flow() const -{ - ConfigOptionFloatOrPercent width = this->config.first_layer_extrusion_width; - if (width.value == 0) width = this->regions.front()->config.perimeter_extrusion_width; - - /* We currently use a random region's perimeter extruder. - While this works for most cases, we should probably consider all of the perimeter - extruders and take the one with, say, the smallest index. - The same logic should be applied to the code that selects the extruder during G-code - generation as well. */ - return Flow::new_from_config_width( - frPerimeter, - width, - this->config.nozzle_diameter.get_at(this->regions.front()->config.perimeter_extruder-1), - this->skirt_first_layer_height(), - 0 - ); -} - -Flow support_material_flow(const PrintObject *object, float layer_height) -{ - return Flow::new_from_config_width( - frSupportMaterial, - // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. - (support_material_extrusion_width.value > 0) ? support_material_extrusion_width : extrusion_width, - // if object->config.support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. - float(nozzle_diameter.get_at(support_material_extruder-1)), - (layer_height > 0.f) ? layer_height : float(layer_height.value), - false); -} - -Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) -{ - return Flow::new_from_config_width( - frSupportMaterial, - // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. - (first_layer_extrusion_width.value > 0) ? first_layer_extrusion_width : support_material_extrusion_width, - float(nozzle_diameter.get_at(object->config.support_material_extruder-1)), - (layer_height > 0.f) ? layer_height : float(first_layer_height)), - false); -} - -Flow support_material_interface_flow(const PrintObject *object, float layer_height) -{ - return Flow::new_from_config_width( - frSupportMaterialInterface, - // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. - (support_material_extrusion_width > 0) ? support_material_extrusion_width : extrusion_width, - // if object->config.support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. - float(nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)), - layer_height, - false); -} -#endif - }; // namespace Slic3r diff --git a/xs/xsp/my.map b/xs/xsp/my.map index d6af00009..7b59623d4 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -226,6 +226,8 @@ PresetCollection* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T PresetBundle* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T +PresetHints* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T Axis T_UV ExtrusionLoopRole T_UV diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 6a97e7a73..d6c74659c 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -205,6 +205,8 @@ %typemap{Ref}{simple}; %typemap{PresetBundle*}; %typemap{Ref}{simple}; +%typemap{PresetHints*}; +%typemap{Ref}{simple}; %typemap{PrintRegionPtrs*}; %typemap{PrintObjectPtrs*}; From bfce6dba9bd9a6ce66259e5fa766876af36a75d2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 10 Nov 2017 17:27:05 +0100 Subject: [PATCH 25/83] Integrated the "compatible printers" idea by @alexrj with Vojtech's twist: The incompatible presets are hidden in the tabs if show_incompatible_presets is false. If show_incompatible_presets is true, there is a button to show / hide the incompatible presets from the tab selector. --- lib/Slic3r/GUI/MainFrame.pm | 17 +- lib/Slic3r/GUI/Plater.pm | 15 +- lib/Slic3r/GUI/Preferences.pm | 8 + lib/Slic3r/GUI/Tab.pm | 341 ++++++++++++++++++++++------- var/flag-green-icon.png | Bin 0 -> 672 bytes var/flag-red-icon.png | Bin 0 -> 665 bytes xs/src/slic3r/GUI/AppConfig.cpp | 2 + xs/src/slic3r/GUI/Preset.cpp | 82 +++++-- xs/src/slic3r/GUI/Preset.hpp | 28 ++- xs/src/slic3r/GUI/PresetBundle.cpp | 175 +++++++-------- xs/src/slic3r/GUI/PresetBundle.hpp | 11 +- xs/xsp/GUI_Preset.xsp | 29 ++- 12 files changed, 482 insertions(+), 226 deletions(-) create mode 100644 var/flag-green-icon.png create mode 100644 var/flag-red-icon.png diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 836c3e9d0..09a64ca4b 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -127,10 +127,18 @@ sub _init_tabpanel { if ($self->{plater}) { # Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs. $self->{plater}->update_presets($tab_name, @_); - $self->{plater}->on_config_change($tab->{presets}->get_current_preset->config); - if ($self->{controller} && $tab_name eq 'printer') { - $self->{controller}->update_presets(@_); + if ($tab_name eq 'printer') { + # Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors. + wxTheApp->{preset_bundle}->print->update_tab_ui( + $self->{options_tabs}{'print'}->{presets_choice}, + $self->{options_tabs}{'print'}->{show_incompatible_presets}); + wxTheApp->{preset_bundle}->filament->update_tab_ui( + $self->{options_tabs}{'filament'}->{presets_choice}, + $self->{options_tabs}{'filament'}->{show_incompatible_presets}); + # Update the controller printers. + $self->{controller}->update_presets(@_) if $self->{controller}; } + $self->{plater}->on_config_change($tab->{presets}->get_current_preset->config); } }); # Load the currently selected preset into the GUI, update the preset selection box. @@ -666,6 +674,9 @@ sub update_ui_from_settings { my ($self) = @_; $self->{menu_item_reslice_now}->Enable(! wxTheApp->{app_config}->get("background_processing")); $self->{plater}->update_ui_from_settings if ($self->{plater}); + for my $tab_name (qw(print filament printer)) { + $self->{options_tabs}{$tab_name}->update_ui_from_settings; + } } 1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 6b853e410..a6915bb7c 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -504,7 +504,7 @@ sub _on_select_preset { wxTheApp->{preset_bundle}->set_filament_preset($idx, $choice->GetStringSelection); } if ($group eq 'filament' && @{$self->{preset_choosers}{filament}} > 1) { - wxTheApp->{preset_bundle}->update_platter_filament_ui_colors($idx, $choice); + wxTheApp->{preset_bundle}->update_platter_filament_ui($idx, $choice); } else { # call GetSelection() in scalar context as it's context-aware $self->{on_select_preset}->($group, $choice->GetStringSelection) @@ -573,12 +573,17 @@ sub update_presets { $choice_idx += 1; } } elsif ($group eq 'print') { - wxTheApp->{preset_bundle}->print->update_platter_ui($choosers[0]); + wxTheApp->{preset_bundle}->print->update_platter_ui($choosers[0]); } elsif ($group eq 'printer') { - wxTheApp->{preset_bundle}->printer->update_platter_ui($choosers[0]); + # Update the print choosers to only contain the compatible presets, update the dirty flags. + wxTheApp->{preset_bundle}->print->update_platter_ui($self->{preset_choosers}{print}->[0]); + # Update the printer choosers, update the dirty flags. + wxTheApp->{preset_bundle}->printer->update_platter_ui($choosers[0]); + # Update the filament choosers to only contain the compatible presets, update the color preview, + # update the dirty flags. my $choice_idx = 0; foreach my $choice (@{$self->{preset_choosers}{filament}}) { - wxTheApp->{preset_bundle}->update_platter_filament_ui_colors($choice_idx, $choice); + wxTheApp->{preset_bundle}->update_platter_filament_ui($choice_idx, $choice); $choice_idx += 1; } } @@ -1726,7 +1731,7 @@ sub filament_color_box_lmouse_down $colors->[$extruder_idx] = $dialog->GetColourData->GetColour->GetAsString(wxC2S_HTML_SYNTAX); $cfg->set('extruder_colour', $colors); $self->GetFrame->{options_tabs}{printer}->load_config($cfg); - wxTheApp->{preset_bundle}->update_platter_filament_ui_colors($extruder_idx, $combobox); + wxTheApp->{preset_bundle}->update_platter_filament_ui($extruder_idx, $combobox); } $dialog->Destroy(); } diff --git a/lib/Slic3r/GUI/Preferences.pm b/lib/Slic3r/GUI/Preferences.pm index ca953e215..d20ccb53f 100644 --- a/lib/Slic3r/GUI/Preferences.pm +++ b/lib/Slic3r/GUI/Preferences.pm @@ -64,6 +64,14 @@ sub new { tooltip => 'Suppress "- default -" presets in the Print / Filament / Printer selections once there are any other valid presets available.', default => $app_config->get("no_defaults"), )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'show_incompatible_presets', + type => 'bool', + label => 'Show incompatible print and filament presets', + tooltip => 'When checked, the print and filament presets are shown in the preset editor even ' . + 'if they are marked as incompatible with the active printer', + default => $app_config->get("show_incompatible_presets"), + )); my $sizer = Wx::BoxSizer->new(wxVERTICAL); $sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 2718d4f5e..d479eb911 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -20,8 +20,8 @@ use utf8; use File::Basename qw(basename); use List::Util qw(first); use Wx qw(:bookctrl :dialog :keycode :icon :id :misc :panel :sizer :treectrl :window - :button wxTheApp); -use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_KEY_DOWN EVT_CHECKBOX EVT_TREE_SEL_CHANGED); + :button wxTheApp wxCB_READONLY); +use Wx::Event qw(EVT_BUTTON EVT_COMBOBOX EVT_KEY_DOWN EVT_CHECKBOX EVT_TREE_SEL_CHANGED); use base qw(Wx::Panel Class::Accessor); sub new { @@ -37,7 +37,7 @@ sub new { { # choice menu - $self->{presets_choice} = Wx::Choice->new($self, -1, wxDefaultPosition, [270, -1], []); + $self->{presets_choice} = Wx::BitmapComboBox->new($self, -1, "", wxDefaultPosition, [270, -1], [], wxCB_READONLY); $self->{presets_choice}->SetFont($Slic3r::GUI::small_font); # buttons @@ -45,15 +45,25 @@ sub new { wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); $self->{btn_delete_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); + $self->{show_incompatible_presets} = 0; + $self->{bmp_show_incompatible_presets} = Wx::Bitmap->new(Slic3r::var("flag-red-icon.png"), wxBITMAP_TYPE_PNG); + $self->{bmp_hide_incompatible_presets} = Wx::Bitmap->new(Slic3r::var("flag-green-icon.png"), wxBITMAP_TYPE_PNG); + $self->{btn_hide_incompatible_presets} = Wx::BitmapButton->new($self, -1, + $self->{bmp_hide_incompatible_presets}, + wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); $self->{btn_save_preset}->SetToolTipString("Save current " . lc($self->title)); $self->{btn_delete_preset}->SetToolTipString("Delete this preset"); $self->{btn_delete_preset}->Disable; - my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); + my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); $self->{sizer}->Add($hsizer, 0, wxBOTTOM, 3); $hsizer->Add($self->{presets_choice}, 1, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3); + $hsizer->AddSpacer(4); $hsizer->Add($self->{btn_save_preset}, 0, wxALIGN_CENTER_VERTICAL); + $hsizer->AddSpacer(4); $hsizer->Add($self->{btn_delete_preset}, 0, wxALIGN_CENTER_VERTICAL); + $hsizer->AddSpacer(16); + $hsizer->Add($self->{btn_hide_incompatible_presets}, 0, wxALIGN_CENTER_VERTICAL); } # Horizontal sizer to hold the tree and the selected page. @@ -95,12 +105,13 @@ sub new { } }); - EVT_CHOICE($parent, $self->{presets_choice}, sub { + EVT_COMBOBOX($parent, $self->{presets_choice}, sub { $self->select_preset($self->{presets_choice}->GetStringSelection); }); EVT_BUTTON($self, $self->{btn_save_preset}, sub { $self->save_preset }); EVT_BUTTON($self, $self->{btn_delete_preset}, sub { $self->delete_preset }); + EVT_BUTTON($self, $self->{btn_hide_incompatible_presets}, sub { $self->_toggle_show_hide_incompatible }); # Initialize the DynamicPrintConfig by default keys/values. # Possible %params keys: no_controller @@ -140,7 +151,7 @@ sub save_preset { eval { $self->{presets}->save_current_preset($name); }; Slic3r::GUI::catch_error($self) and return; # Add the new item into the UI component, remove dirty flags and activate the saved item. - $self->{presets}->update_tab_ui($self->{presets_choice}); + $self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets}); # Update the selection boxes at the platter. $self->_on_presets_changed; } @@ -162,6 +173,22 @@ sub delete_preset { $self->load_current_preset; } +sub _toggle_show_hide_incompatible { + my ($self) = @_; + $self->{show_incompatible_presets} = ! $self->{show_incompatible_presets}; + $self->_update_show_hide_incompatible_button; + $self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets}); +} + +sub _update_show_hide_incompatible_button { + my ($self) = @_; + $self->{btn_hide_incompatible_presets}->SetBitmap($self->{show_incompatible_presets} ? + $self->{bmp_show_incompatible_presets} : $self->{bmp_hide_incompatible_presets}); + $self->{btn_hide_incompatible_presets}->SetToolTipString($self->{show_incompatible_presets} ? + "Both compatible an incompatible presets are shown. Click to hide presets not compatible with the current printer." : + "Only compatible presets are shown. Click to show both the presets compatible and not compatible with the current printer."); +} + # Register the on_value_change callback. sub on_value_change { my ($self, $cb) = @_; @@ -205,28 +232,33 @@ sub on_preset_loaded {} # If the current preset is dirty, the user is asked whether the changes may be discarded. # if the current preset was not dirty, or the user agreed to discard the changes, 1 is returned. -sub may_discard_current_preset_if_dirty +sub may_discard_current_dirty_preset { - my ($self) = @_; - if ($self->{presets}->current_is_dirty) { - # Display a dialog showing the dirty options in a human readable form. - my $old_preset = $self->{presets}->get_current_preset; - my $name = $old_preset->default ? 'Default preset' : "Preset \"" . $old_preset->name . "\""; - # Collect descriptions of the dirty options. - my @option_names = (); - foreach my $opt_key (@{$self->{presets}->current_dirty_options}) { - my $opt = $Slic3r::Config::Options->{$opt_key}; - my $name = $opt->{full_label} // $opt->{label}; - $name = $opt->{category} . " > $name" if $opt->{category}; - push @option_names, $name; - } - # Show a confirmation dialog with the list of dirty options. - my $changes = join "\n", map "- $_", @option_names; - my $confirm = Wx::MessageDialog->new($self, "$name has unsaved changes:\n$changes\n\nDiscard changes and continue anyway?", - 'Unsaved Changes', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); - return 0 if $confirm->ShowModal == wxID_NO; + my ($self, $presets, $new_printer_name) = @_; + $presets //= $self->{presets}; + # Display a dialog showing the dirty options in a human readable form. + my $old_preset = $presets->get_current_preset; + my $type_name = $presets->name; + my $name = $old_preset->default ? + ('Default ' . $type_name . ' preset') : + ($type_name . " preset \"" . $old_preset->name . "\""); + # Collect descriptions of the dirty options. + my @option_names = (); + foreach my $opt_key (@{$presets->current_dirty_options}) { + my $opt = $Slic3r::Config::Options->{$opt_key}; + my $name = $opt->{full_label} // $opt->{label}; + $name = $opt->{category} . " > $name" if $opt->{category}; + push @option_names, $name; } - return 1; + # Show a confirmation dialog with the list of dirty options. + my $changes = join "\n", map "- $_", @option_names; + my $message = (defined $new_printer_name) ? + "$name is not compatible with printer \"$new_printer_name\"\n and it has unsaved changes:" : + "$name has unsaved changes:"; + my $confirm = Wx::MessageDialog->new($self, + $message . "\n$changes\n\nDiscard changes and continue anyway?", + 'Unsaved Changes', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); + return $confirm->ShowModal == wxID_YES; } # Called by the UI combo box when the user switches profiles. @@ -235,25 +267,60 @@ sub may_discard_current_preset_if_dirty sub select_preset { my ($self, $name, $force) = @_; $force //= 0; - if (! $force && ! $self->may_discard_current_preset_if_dirty) { - $self->{presets}->update_tab_ui($self->{presets_choice}); + my $current_dirty = $self->{presets}->current_is_dirty; + my $canceled = 0; + my $printer_tab = $self->{presets}->name eq 'printer'; + if (! $force && $current_dirty && ! $self->may_discard_current_dirty_preset) { + $canceled = 1; + } elsif ($printer_tab) { + # Before switching the printer to a new one, verify, whether the currently active print and filament + # are compatible with the new printer. + # If they are not compatible and the the current print or filament are dirty, let user decide + # whether to discard the changes or keep the current printer selection. + my $new_printer_name = $name // ''; + my $new_printer_preset = $self->{presets}->find_preset($new_printer_name, 1); + # my $new_nozzle_dmrs = $new_printer_preset->config->get('nozzle_diameter'); + my $print_presets = wxTheApp->{preset_bundle}->print; + if ($print_presets->current_is_dirty && + ! $print_presets->get_edited_preset->is_compatible_with_printer($new_printer_name)) { + if ($self->may_discard_current_dirty_preset($print_presets, $new_printer_name)) { + $canceled = 1; + } else { + $print_presets->discard_current_changes; + } + } + my $filament_presets = wxTheApp->{preset_bundle}->filament; + # if ((@$new_nozzle_dmrs <= 1) && + if (! $canceled && $filament_presets->current_is_dirty && + ! $filament_presets->get_edited_preset->is_compatible_with_printer($new_printer_name)) { + if ($self->may_discard_current_dirty_preset($filament_presets, $new_printer_name)) { + $canceled = 1; + } else { + $filament_presets->discard_current_changes; + } + } + } + if ($canceled) { + $self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets}); # Trigger the on_presets_changed event so that we also restore the previous value in the plater selector. $self->_on_presets_changed; - return; - } - if (defined $name) { - $self->{presets}->select_preset_by_name($name); } else { - $self->{presets}->select_preset(0); + if (defined $name) { + $self->{presets}->select_preset_by_name($name); + } else { + $self->{presets}->select_preset(0); + } + # Mark the print & filament enabled if they are compatible with the currently selected preset. + wxTheApp->{preset_bundle}->update_compatible_with_printer(1) + if $current_dirty || $printer_tab; + # Initialize the UI from the current preset. + $self->load_current_preset; } - # Initialize the UI from the current preset. - $self->load_current_preset; } # Initialize the UI from the current preset. sub load_current_preset { my ($self) = @_; - $self->{presets}->update_tab_ui($self->{presets_choice}); my $preset = $self->{presets}->get_current_preset; eval { local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); @@ -270,9 +337,8 @@ sub load_current_preset { # preset dirty again # (not sure this is true anymore now that update_dirty is idempotent) wxTheApp->CallAfter(sub { - $self->update_dirty; - #the following is called by update_dirty - #$self->_on_presets_changed; + $self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets}); + $self->_on_presets_changed; }); } @@ -344,7 +410,6 @@ sub update_dirty { # This could be used for example by setting a Wipe Tower position by interactive manipulation in the 3D view. sub load_config { my ($self, $config) = @_; - my $modified = 0; foreach my $opt_key (@{$self->{config}->diff($config)}) { $self->{config}->set($opt_key, $config->get($opt_key)); @@ -358,6 +423,22 @@ sub load_config { } } +# To be called by custom widgets, load a value into a config, +# update the preset selection boxes (the dirty flags) +sub _load_key_value { + my ($self, $opt_key, $value) = @_; + $self->{config}->set($opt_key, $value); + # Mark the print & filament enabled if they are compatible with the currently selected preset. + if ($opt_key eq 'compatible_printers') { + wxTheApp->{preset_bundle}->update_compatible_with_printer(0); + $self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets}); + } else { + $self->{presets}->update_dirty_ui($self->{presets_choice}); + } + $self->_on_presets_changed; + $self->_update; +} + # Find a field with an index over all pages of this tab. # This method is used often and everywhere, therefore it shall be quick. sub get_field { @@ -381,6 +462,87 @@ sub set_value { return $changed; } +# Return a callback to create a Tab widget to mark the preferences as compatible / incompatible to the current printer. +sub _compatible_printers_widget { + my ($self) = @_; + + return sub { + my ($parent) = @_; + + my $checkbox = $self->{compatible_printers_checkbox} = Wx::CheckBox->new($parent, -1, "All"); + + my $btn = $self->{compatible_printers_btn} = Wx::Button->new($parent, -1, "Set…", wxDefaultPosition, wxDefaultSize, + wxBU_LEFT | wxBU_EXACTFIT); + $btn->SetFont($Slic3r::GUI::small_font); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("printer_empty.png"), wxBITMAP_TYPE_PNG)); + + my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); + $sizer->Add($checkbox, 0, wxALIGN_CENTER_VERTICAL); + $sizer->Add($btn, 0, wxALIGN_CENTER_VERTICAL); + + EVT_CHECKBOX($self, $checkbox, sub { + my $method = $checkbox->GetValue ? 'Disable' : 'Enable'; + $btn->$method; + # All printers have been made compatible with this preset. + $self->_load_key_value('compatible_printers', []) if $checkbox->GetValue; + }); + + EVT_BUTTON($self, $btn, sub { + # Collect names of non-default non-external printer profiles. + my @presets = map $_->name, grep !$_->default && !$_->external, + @{wxTheApp->{preset_bundle}->printer}; + my $dlg = Wx::MultiChoiceDialog->new($self, + "Select the printers this profile is compatible with.", + "Compatible printers", \@presets); + # Collect and set indices of printers marked as compatible. + my @selections = (); + foreach my $preset_name (@{ $self->{config}->get('compatible_printers') }) { + my $idx = first { $presets[$_] eq $preset_name } 0..$#presets; + push @selections, $idx if defined $idx; + } + $dlg->SetSelections(@selections); + # Show the dialog. + if ($dlg->ShowModal == wxID_OK) { + my $value = [ @presets[$dlg->GetSelections] ]; + if (!@$value) { + $checkbox->SetValue(1); + $btn->Disable; + } + # All printers have been made compatible with this preset. + $self->_load_key_value('compatible_printers', $value); + } + }); + + return $sizer; + }; +} + +sub _reload_compatible_printers_widget { + my ($self) = @_; + my $has_any = int(@{$self->{config}->get('compatible_printers')}) > 0; + my $method = $has_any ? 'Enable' : 'Disable'; + $self->{compatible_printers_checkbox}->SetValue(! $has_any); + $self->{compatible_printers_btn}->$method; +} + +sub update_ui_from_settings { + my ($self) = @_; + # Show the 'show / hide presets' button only for the print and filament tabs, and only if enabled + # in application preferences. + my $show = wxTheApp->{app_config}->get("show_incompatible_presets") && $self->{presets}->name ne 'printer'; + my $method = $show ? 'Show' : 'Hide'; + $self->{btn_hide_incompatible_presets}->$method; + # If the 'show / hide presets' button is hidden, hide the incompatible presets. + if ($show) { + $self->_update_show_hide_incompatible_button; + } else { + if ($self->{show_incompatible_presets}) { + $self->{show_incompatible_presets} = 0; + $self->{presets}->update_tab_ui($self->{presets_choice}, 0); + } + } +} + package Slic3r::GUI::Tab::Print; use base 'Slic3r::GUI::Tab'; @@ -651,12 +813,26 @@ sub build { $optgroup->append_single_option_line($option); } } + + { + my $page = $self->add_options_page('Dependencies', 'wrench.png'); + { + my $optgroup = $page->new_optgroup('Profile dependencies'); + { + my $line = Slic3r::GUI::OptionsGroup::Line->new( + label => 'Compatible printers', + widget => $self->_compatible_printers_widget, + ); + $optgroup->append_line($line); + } + } + } } # Reload current $self->{config} (aka $self->{presets}->edited_preset->config) into the UI fields. sub _reload_config { my ($self) = @_; -# $self->_reload_compatible_printers_widget; + $self->_reload_compatible_printers_widget; $self->SUPER::_reload_config; } @@ -920,7 +1096,7 @@ sub build { full_width => 1, widget => sub { my ($parent) = @_; - return $self->{description_line} = Slic3r::GUI::OptionsGroup::StaticText->new($parent); + return $self->{cooling_description_line} = Slic3r::GUI::OptionsGroup::StaticText->new($parent); }, ); $optgroup->append_line($line); @@ -959,6 +1135,16 @@ sub build { $optgroup = $page->new_optgroup('Print speed override'); $optgroup->append_single_option_line('filament_max_volumetric_speed', 0); + + my $line = Slic3r::GUI::OptionsGroup::Line->new( + label => '', + full_width => 1, + widget => sub { + my ($parent) = @_; + return $self->{volumetric_speed_description_line} = Slic3r::GUI::OptionsGroup::StaticText->new($parent); + }, + ); + $optgroup->append_line($line); } } @@ -996,13 +1182,37 @@ sub build { $optgroup->append_single_option_line($option); } } + + { + my $page = $self->add_options_page('Dependencies', 'wrench.png'); + { + my $optgroup = $page->new_optgroup('Profile dependencies'); + { + my $line = Slic3r::GUI::OptionsGroup::Line->new( + label => 'Compatible printers', + widget => $self->_compatible_printers_widget, + ); + $optgroup->append_line($line); + } + } + } +} + +# Reload current $self->{config} (aka $self->{presets}->edited_preset->config) into the UI fields. +sub _reload_config { + my ($self) = @_; + $self->_reload_compatible_printers_widget; + $self->SUPER::_reload_config; } # Slic3r::GUI::Tab::Filament::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user. sub _update { my ($self) = @_; - $self->_update_description; + $self->{cooling_description_line}->SetText( + Slic3r::GUI::PresetHints::cooling_description($self->{presets}->get_edited_preset)); + $self->{volumetric_speed_description_line}->SetText( + Slic3r::GUI::PresetHints::maximum_volumetric_flow_description(wxTheApp->{preset_bundle})); my $cooling = $self->{config}->cooling->[0]; my $fan_always_on = $cooling || $self->{config}->fan_always_on->[0]; @@ -1012,33 +1222,6 @@ sub _update { for qw(min_fan_speed disable_fan_first_layers); } -sub _update_description { - my ($self) = @_; - my $config = $self->{config}; - my $msg = ""; - my $fan_other_layers = $config->fan_always_on->[0] - ? sprintf "will always run at %d%%%s.", $config->min_fan_speed->[0], - ($config->disable_fan_first_layers->[0] > 1 - ? " except for the first " . $config->disable_fan_first_layers->[0] . " layers" - : $config->disable_fan_first_layers->[0] == 1 - ? " except for the first layer" - : "") - : "will be turned off."; - - if ($config->cooling->[0]) { - $msg = sprintf "If estimated layer time is below ~%ds, fan will run at %d%% and print speed will be reduced so that no less than %ds are spent on that layer (however, speed will never be reduced below %dmm/s).", - $config->slowdown_below_layer_time->[0], $config->max_fan_speed->[0], $config->slowdown_below_layer_time->[0], $config->min_print_speed->[0]; - if ($config->fan_below_layer_time->[0] > $config->slowdown_below_layer_time->[0]) { - $msg .= sprintf "\nIf estimated layer time is greater, but still below ~%ds, fan will run at a proportionally decreasing speed between %d%% and %d%%.", - $config->fan_below_layer_time->[0], $config->max_fan_speed->[0], $config->min_fan_speed->[0]; - } - $msg .= "\nDuring the other layers, fan $fan_other_layers" - } else { - $msg = "Fan $fan_other_layers"; - } - $self->{description_line}->SetText($msg); -} - package Slic3r::GUI::Tab::Printer; use base 'Slic3r::GUI::Tab'; use Wx qw(wxTheApp :sizer :button :bitmap :misc :id :icon :dialog); @@ -1060,19 +1243,14 @@ sub build { my $btn = Wx::Button->new($parent, -1, "Set…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("printer_empty.png"), wxBITMAP_TYPE_PNG)); my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); $sizer->Add($btn); EVT_BUTTON($self, $btn, sub { my $dlg = Slic3r::GUI::BedShapeDialog->new($self, $self->{config}->bed_shape); - if ($dlg->ShowModal == wxID_OK) { - my $value = $dlg->GetValue; - $self->{config}->set('bed_shape', $value); - $self->update_dirty; - $self->_on_value_change('bed_shape', $value); - } + $self->_load_key_value('bed_shape', $dlg->GetValue) if $dlg->ShowModal == wxID_OK; }); return $sizer; @@ -1188,13 +1366,8 @@ sub build { } if (@{$entries}) { my $dlg = Slic3r::GUI::BonjourBrowser->new($self, $entries); - if ($dlg->ShowModal == wxID_OK) { - my $value = $dlg->GetValue . ":" . $dlg->GetPort; - $self->{config}->set('octoprint_host', $value); - $self->update_dirty; - $self->_on_value_change('octoprint_host', $value); - $self->_reload_config; - } + $self->_load_key_value('octoprint_host', $dlg->GetValue . ":" . $dlg->GetPort) + if $dlg->ShowModal == wxID_OK; } else { Wx::MessageDialog->new($self, 'No Bonjour device found', 'Device Browser', wxOK | wxICON_INFORMATION)->ShowModal; } diff --git a/var/flag-green-icon.png b/var/flag-green-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e4bc611f87b454e5969078aa167f48798adfd208 GIT binary patch literal 672 zcmV;R0$=@!P)1R5;6} z(_KtbVHgK+*KHTwb z3U8KEr|M6CKHF5d&eQVNxr*Xd#pN8o{Cp0%jbBO9Pl3P2cIMh>fmjREstPwbsR%UAN&a~0&W97_+{w2 zzK;Pe5g_}Da_b3Y$=5(~fG&DU3P+ZQ@qA?rj`(8?e(XnAv>p1lR%ogzEDpDzrF16) z>>$A2s?J^XXy3bIMu@`YByICjm?Ii^mmGBZV2)~`4mZPIM`5A2nrR066HnGEYA&=F z{ItC{p*~!JTj5FqSfV=gCp=^dW8^+e7-xWNN zxg9h5B6J9LGr&<6$h%weF5k=nw=Wx9Cml;Jj>-7nQg?!GIQQV{9x_4d_iwmJ{{Z)# z5>8sc{Dg*|7C@Q@mHF%nZwj!Ba~VLi89@E#6*A|x)Vtp=Lby^Yqxhx(0000W2MxECPn{*ELdZ1op0xyVpxzDdhz3h^YVPo^S(T97!Wp}{CC5s zOO0&MCbYN{*S_u5sKy(UFm7-Lr)M|odnZ1F`|tp z;QLC~;7ZS9V!0QqC_yL9nVW1CJTMZN4M(R$+ zOTZBsFr_4h`&3JNv0I_O8fwI~`z}0v3|$@I8UFpf@&;uE8s7DoF&~bRfJhmzX*a6< zs}(O~Kx~cpZxH}pS*S{qZRPWYW?G`zW8AQcn3#1@S*BcNV6f!zir{5lPjYZSt>eR~E27PJ0+es0y~s0ch&nN;M*NkCc% zXiQ#beHkC&om4IpK8qPut?)aNVjs<%39&#|d3=N1!OZi|I!ONj#Vr@M%?lVEC`+Fg zAQwL{?FfzVoB-$9WC=Ju7r^r86-w*!nR~wgLM67#gs;7-00000NkvXXu0mjfgI^=` literal 0 HcmV?d00001 diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp index f978fcbb4..3161c5b78 100644 --- a/xs/src/slic3r/GUI/AppConfig.cpp +++ b/xs/src/slic3r/GUI/AppConfig.cpp @@ -40,6 +40,8 @@ void AppConfig::set_defaults() // If set, the "- default -" selections of print/filament/printer are suppressed, if there is a valid preset available. if (get("no_defaults").empty()) set("no_defaults", "1"); + if (get("show_incompatible_presets").empty()) + set("show_incompatible_presets", "0"); // Version check is enabled by default in the config, but it is not implemented yet. if (get("version_check").empty()) set("version_check", "1"); diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 596e07c8e..5a9ceb04b 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -96,6 +96,8 @@ void Preset::normalize(DynamicPrintConfig &config) size_t n = (nozzle_diameter == nullptr) ? 1 : nozzle_diameter->values.size(); const auto &defaults = FullPrintConfig::defaults(); for (const std::string &key : Preset::filament_options()) { + if (key == "compatible_printers") + continue; auto *opt = config.option(key, false); assert(opt != nullptr); assert(opt->is_vector()); @@ -138,13 +140,18 @@ std::string Preset::label() const return this->name + (this->is_dirty ? g_suffix_modified : ""); } -bool Preset::enable_compatible(const std::string &active_printer) +bool Preset::is_compatible_with_printer(const std::string &active_printer) const { - auto *compatible_printers = dynamic_cast(this->config.optptr("compatible_printers")); - this->is_visible = compatible_printers && ! compatible_printers->values.empty() && + auto *compatible_printers = dynamic_cast(this->config.option("compatible_printers")); + return this->is_default || active_printer.empty() || + compatible_printers == nullptr || compatible_printers->values.empty() || std::find(compatible_printers->values.begin(), compatible_printers->values.end(), active_printer) != compatible_printers->values.end(); - return this->is_visible; +} + +bool Preset::update_compatible_with_printer(const std::string &active_printer) +{ + return this->is_compatible = is_compatible_with_printer(active_printer); } const std::vector& Preset::print_options() @@ -171,7 +178,8 @@ const std::vector& Preset::print_options() "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects", "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", - "wipe_tower_width", "wipe_tower_per_color_wipe" + "wipe_tower_width", "wipe_tower_per_color_wipe", + "compatible_printers" }; return s_opts; } @@ -183,7 +191,8 @@ const std::vector& Preset::filament_options() "extrusion_multiplier", "filament_density", "filament_cost", "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", - "end_filament_gcode" + "end_filament_gcode", + "compatible_printers" }; return s_opts; } @@ -361,6 +370,18 @@ size_t PresetCollection::first_visible_idx() const return idx; } +// Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible. +size_t PresetCollection::first_compatible_idx() const +{ + size_t idx = m_default_suppressed ? 1 : 0; + for (; idx < this->m_presets.size(); ++ idx) + if (m_presets[idx].is_compatible) + break; + if (idx == this->m_presets.size()) + idx = 0; + return idx; +} + void PresetCollection::set_default_suppressed(bool default_suppressed) { if (m_default_suppressed != default_suppressed) { @@ -369,13 +390,29 @@ void PresetCollection::set_default_suppressed(bool default_suppressed) } } -void PresetCollection::enable_disable_compatible_to_printer(const std::string &active_printer) +void PresetCollection::update_compatible_with_printer(const std::string &active_printer, bool select_other_if_incompatible) { size_t num_visible = 0; - for (size_t idx_preset = 1; idx_preset < m_presets.size(); ++ idx_preset) - if (m_presets[idx_preset].enable_compatible(active_printer)) + for (size_t idx_preset = 1; idx_preset < m_presets.size(); ++ idx_preset) { + bool selected = idx_preset == m_idx_selected; + Preset &preset_selected = m_presets[idx_preset]; + Preset &preset_edited = selected ? m_edited_preset : preset_selected; + if (preset_edited.update_compatible_with_printer(active_printer)) + // Mark compatible presets as visible. + preset_selected.is_visible = true; + else if (selected && select_other_if_incompatible) { + preset_selected.is_visible = false; + m_idx_selected = (size_t)-1; + } + if (selected) + preset_selected.is_compatible = preset_edited.is_compatible; + if (preset_selected.is_visible) ++ num_visible; - if (num_visible == 0) + } + if (m_idx_selected == (size_t)-1) + // Find some other visible preset. + this->select_preset(first_visible_idx()); + else if (num_visible == 0) // Show the "-- default --" preset. m_presets.front().is_visible = true; } @@ -399,15 +436,18 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) ui->Clear(); for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { const Preset &preset = this->m_presets[i]; - const wxBitmap *bmp = (i == 0 || preset.is_visible) ? m_bitmap_compatible : m_bitmap_incompatible; - ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (bmp == 0) ? wxNullBitmap : *bmp, (void*)i); + if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected)) + continue; + const wxBitmap *bmp = (i == 0 || preset.is_compatible) ? m_bitmap_main_frame : m_bitmap_incompatible; + ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), + (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); if (i == m_idx_selected) ui->SetSelection(ui->GetCount() - 1); } ui->Thaw(); } -void PresetCollection::update_tab_ui(wxChoice *ui) +void PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible) { if (ui == nullptr) return; @@ -415,8 +455,11 @@ void PresetCollection::update_tab_ui(wxChoice *ui) ui->Clear(); for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { const Preset &preset = this->m_presets[i]; - const wxBitmap *bmp = (i == 0 || preset.is_visible) ? m_bitmap_compatible : m_bitmap_incompatible; - ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (void*)&preset); + if (! show_incompatible && ! preset.is_compatible && i != m_idx_selected) + continue; + const wxBitmap *bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible; + ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), + (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); if (i == m_idx_selected) ui->SetSelection(ui->GetCount() - 1); } @@ -425,8 +468,9 @@ void PresetCollection::update_tab_ui(wxChoice *ui) // Update a dirty floag of the current preset, update the labels of the UI component accordingly. // Return true if the dirty flag changed. -bool PresetCollection::update_dirty_ui(wxItemContainer *ui) +bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui) { + wxWindowUpdateLocker noUpdates(ui); // 1) Update the dirty flag of the current preset. bool was_dirty = this->get_selected_preset().is_dirty; bool is_dirty = current_is_dirty(); @@ -445,12 +489,6 @@ bool PresetCollection::update_dirty_ui(wxItemContainer *ui) return was_dirty != is_dirty; } -bool PresetCollection::update_dirty_ui(wxChoice *ui) -{ - wxWindowUpdateLocker noUpdates(ui); - return update_dirty_ui(dynamic_cast(ui)); -} - Preset& PresetCollection::select_preset(size_t idx) { for (Preset &preset : m_presets) diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp index a9bc8812c..f44fd1590 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -49,6 +49,8 @@ public: bool is_visible = true; // Has this preset been modified? bool is_dirty = false; + // Is this preset compatible with the currently active printer? + bool is_compatible = true; // Name of the preset, usually derived form the file name. std::string name; @@ -77,8 +79,9 @@ public: void set_dirty(bool dirty = true) { this->is_dirty = dirty; } void reset_dirty() { this->is_dirty = false; } - // Mark this preset as visible if it is compatible with active_printer. - bool enable_compatible(const std::string &active_printer); + bool is_compatible_with_printer(const std::string &active_printer) const; + // Mark this preset as compatible if it is compatible with active_printer. + bool update_compatible_with_printer(const std::string &active_printer); // Resize the extruder specific fields, initialize them with the content of the 1st extruder. void set_num_extruders(unsigned int n) { set_num_extruders(this->config, n); } @@ -147,6 +150,7 @@ public: // Return the selected preset, without the user modifications applied. Preset& get_selected_preset() { return m_presets[m_idx_selected]; } const Preset& get_selected_preset() const { return m_presets[m_idx_selected]; } + int get_selected_idx() const { return m_idx_selected; } // Return the selected preset including the user modifications. Preset& get_edited_preset() { return m_edited_preset; } const Preset& get_edited_preset() const { return m_edited_preset; } @@ -155,6 +159,7 @@ public: // Return a preset by an index. If the preset is active, a temporary copy is returned. Preset& preset(size_t idx) { return (int(idx) == m_idx_selected) ? m_edited_preset : m_presets[idx]; } const Preset& preset(size_t idx) const { return const_cast(this)->preset(idx); } + void discard_current_changes() { m_edited_preset = m_presets[m_idx_selected]; } // Return a preset by its name. If the preset is active, a temporary copy is returned. // If a preset is not found by its name, null is returned. @@ -163,16 +168,19 @@ public: { return const_cast(this)->find_preset(name, first_visible_if_not_found); } size_t first_visible_idx() const; + size_t first_compatible_idx() const; // Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible. // Return the first visible preset. Certainly at least the '- default -' preset shall be visible. - Preset& first_visible() { return this->preset(this->first_visible_idx()); } - const Preset& first_visible() const { return this->preset(this->first_visible_idx()); } + Preset& first_visible() { return this->preset(this->first_visible_idx()); } + const Preset& first_visible() const { return this->preset(this->first_visible_idx()); } + Preset& first_compatible() { return this->preset(this->first_compatible_idx()); } + const Preset& first_compatible() const { return this->preset(this->first_compatible_idx()); } // Return number of presets including the "- default -" preset. size_t size() const { return this->m_presets.size(); } // For Print / Filament presets, disable those, which are not compatible with the printer. - void enable_disable_compatible_to_printer(const std::string &active_printer); + void update_compatible_with_printer(const std::string &active_printer, bool select_other_if_incompatible); size_t num_visible() const { return std::count_if(m_presets.begin(), m_presets.end(), [](const Preset &preset){return preset.is_visible;}); } @@ -182,13 +190,17 @@ public: std::vector current_dirty_options() { return this->get_selected_preset().config.diff(this->get_edited_preset().config); } // Update the choice UI from the list of presets. - void update_tab_ui(wxChoice *ui); + // If show_incompatible, all presets are shown, otherwise only the compatible presets are shown. + // If an incompatible preset is selected, it is shown as well. + void update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible); + // Update the choice UI from the list of presets. + // Only the compatible presets are shown. + // If an incompatible preset is selected, it is shown as well. void update_platter_ui(wxBitmapComboBox *ui); // Update a dirty floag of the current preset, update the labels of the UI component accordingly. // Return true if the dirty flag changed. - bool update_dirty_ui(wxItemContainer *ui); - bool update_dirty_ui(wxChoice *ui); + bool update_dirty_ui(wxBitmapComboBox *ui); // Select a profile by its name. Return true if the selection changed. // Without force, the selection is only updated if the index changes. diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 915bec6aa..cf253e6f1 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -46,9 +47,7 @@ PresetBundle::PresetBundle() : this->prints .load_bitmap_default("cog.png"); this->filaments.load_bitmap_default("spool.png"); this->printers .load_bitmap_default("printer_empty.png"); - - // FIXME select some icons indicating compatibility. - this->load_compatible_bitmaps("cog.png", "cog.png"); + this->load_compatible_bitmaps(); } PresetBundle::~PresetBundle() @@ -110,6 +109,9 @@ void PresetBundle::load_selections(const AppConfig &config) break; this->set_filament_preset(i, remove_ini_suffix(config.get("presets", name))); } + // Update visibility of presets based on their compatibility with the active printer. + // This will switch the print or filament presets to compatible if the active presets are incompatible. + this->update_compatible_with_printer(false); } // Export selections (current print, current filaments, current printer) into config.ini @@ -137,8 +139,10 @@ void PresetBundle::export_selections(PlaceholderParser &pp) pp.set("printer_preset", printers.get_selected_preset().name); } -bool PresetBundle::load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible) +bool PresetBundle::load_compatible_bitmaps() { + const std::string path_bitmap_compatible = "flag-green-icon.png"; + const std::string path_bitmap_incompatible = "flag-red-icon.png"; bool loaded_compatible = m_bitmapCompatible ->LoadFile( wxString::FromUTF8(Slic3r::var(path_bitmap_compatible).c_str()), wxBITMAP_TYPE_PNG); bool loaded_incompatible = m_bitmapIncompatible->LoadFile( @@ -146,12 +150,12 @@ bool PresetBundle::load_compatible_bitmaps(const std::string &path_bitmap_compat if (loaded_compatible) { prints .set_bitmap_compatible(m_bitmapCompatible); filaments.set_bitmap_compatible(m_bitmapCompatible); - printers .set_bitmap_compatible(m_bitmapCompatible); +// printers .set_bitmap_compatible(m_bitmapCompatible); } if (loaded_incompatible) { - prints .set_bitmap_compatible(m_bitmapIncompatible); - filaments.set_bitmap_compatible(m_bitmapIncompatible); - printers .set_bitmap_compatible(m_bitmapIncompatible); + prints .set_bitmap_incompatible(m_bitmapIncompatible); + filaments.set_bitmap_incompatible(m_bitmapIncompatible); +// printers .set_bitmap_incompatible(m_bitmapIncompatible); } return loaded_compatible && loaded_incompatible; } @@ -180,6 +184,8 @@ DynamicPrintConfig PresetBundle::full_config() const std::vector filament_opts(num_extruders, nullptr); // loop through options and apply them to the resulting config. for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { + if (key == "compatible_printers") + continue; // Get a destination option. ConfigOption *opt_dst = out.option(key, false); if (opt_dst->is_scalar()) { @@ -434,6 +440,20 @@ void PresetBundle::update_multi_material_filament_presets() this->filament_presets.resize(num_extruders, this->filament_presets.empty() ? this->filaments.first_visible().name : this->filament_presets.back()); } +void PresetBundle::update_compatible_with_printer(bool select_other_if_incompatible) +{ + this->prints.update_compatible_with_printer(this->printers.get_selected_preset().name, select_other_if_incompatible); + this->filaments.update_compatible_with_printer(this->printers.get_selected_preset().name, select_other_if_incompatible); + if (select_other_if_incompatible) { + // Verify validity of the current filament presets. + for (std::string &filament_name : this->filament_presets) { + Preset *preset = this->filaments.find_preset(filament_name, false); + if (preset == nullptr || ! preset->is_compatible) + filament_name = this->filaments.first_compatible().name; + } + } +} + void PresetBundle::export_configbundle(const std::string &path) //, const DynamicPrintConfig &settings { boost::nowide::ofstream c; @@ -526,42 +546,66 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma // Fill in the list from scratch. ui->Freeze(); ui->Clear(); - for (size_t i = this->filaments().front().is_visible ? 0 : 1; i < this->filaments().size(); ++ i) { - const Preset &preset = this->filaments.preset(i); - if (! preset.is_visible) + const Preset *selected_preset = this->filaments.find_preset(this->filament_presets[idx_extruder]); + // Show wide icons if the currently selected preset is not compatible with the current printer, + // and draw a red flag in front of the selected preset. + bool wide_icons = selected_preset != nullptr && ! selected_preset->is_compatible && m_bitmapIncompatible != nullptr; + assert(selected_preset != nullptr); + for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++ i) { + const Preset &preset = this->filaments.preset(i); + bool selected = this->filament_presets[idx_extruder] == preset.name; + if (! preset.is_visible || (! preset.is_compatible && ! selected)) continue; - bool selected = this->filament_presets[idx_extruder] == preset.name; // Assign an extruder color to the selected item if the extruder color is defined. std::string filament_rgb = preset.config.opt_string("filament_colour", 0); std::string extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb; - wxBitmap *bitmap = nullptr; - if (filament_rgb == extruder_rgb) { - auto it = m_mapColorToBitmap.find(filament_rgb); - if (it == m_mapColorToBitmap.end()) { - // Create the bitmap. - parse_color(filament_rgb, rgb); - wxImage image(24, 16); - image.SetRGB(wxRect(0, 0, 24, 16), rgb[0], rgb[1], rgb[2]); - m_mapColorToBitmap[filament_rgb] = bitmap = new wxBitmap(image); - } else { - bitmap = it->second; - } - } else { - std::string bitmap_key = filament_rgb + extruder_rgb; - auto it = m_mapColorToBitmap.find(bitmap_key); - if (it == m_mapColorToBitmap.end()) { - // Create the bitmap. - wxImage image(24, 16); - parse_color(extruder_rgb, rgb); - image.SetRGB(wxRect(0, 0, 16, 16), rgb[0], rgb[1], rgb[2]); - parse_color(filament_rgb, rgb); - image.SetRGB(wxRect(16, 0, 8, 16), rgb[0], rgb[1], rgb[2]); - m_mapColorToBitmap[filament_rgb] = bitmap = new wxBitmap(image); - } else { - bitmap = it->second; - } + bool single_bar = filament_rgb == extruder_rgb; + std::string bitmap_key = single_bar ? filament_rgb : filament_rgb + extruder_rgb; + // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left + // to the filament color image. + if (wide_icons) + bitmap_key += preset.is_compatible ? "comp" : "notcomp"; + auto it = m_mapColorToBitmap.find(bitmap_key); + wxBitmap *bitmap = (it == m_mapColorToBitmap.end()) ? nullptr : it->second; + if (bitmap == nullptr) { + // Create the bitmap with color bars. + bitmap = new wxBitmap((wide_icons ? 16 : 0) + 24, 16); + bitmap->UseAlpha(); + wxMemoryDC memDC; + memDC.SelectObject(*bitmap); + memDC.SetBackground(*wxTRANSPARENT_BRUSH); + memDC.Clear(); + if (wide_icons && ! preset.is_compatible) + // Paint the red flag. + memDC.DrawBitmap(*m_bitmapIncompatible, 0, 0, false); + // Paint the color bars. + parse_color(filament_rgb, rgb); + wxImage image(24, 16); + image.InitAlpha(); + unsigned char* imgdata = image.GetData(); + unsigned char* imgalpha = image.GetAlpha(); + for (size_t i = 0; i < image.GetWidth() * image.GetHeight(); ++ i) { + *imgdata ++ = rgb[0]; + *imgdata ++ = rgb[1]; + *imgdata ++ = rgb[2]; + *imgalpha ++ = wxALPHA_OPAQUE; + } + if (! single_bar) { + parse_color(extruder_rgb, rgb); + imgdata = image.GetData(); + for (size_t r = 0; r < 16; ++ r) { + imgdata = image.GetData() + r * image.GetWidth() * 3; + for (size_t c = 0; c < 16; ++ c) { + *imgdata ++ = rgb[0]; + *imgdata ++ = rgb[1]; + *imgdata ++ = rgb[2]; + } + } + } + memDC.DrawBitmap(wxBitmap(image), wide_icons ? 16 : 0, 0, false); + memDC.SelectObject(wxNullBitmap); + m_mapColorToBitmap[bitmap_key] = bitmap; } - ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), (bitmap == 0) ? wxNullBitmap : *bitmap); if (selected) ui->SetSelection(ui->GetCount() - 1); @@ -569,59 +613,6 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma ui->Thaw(); } -// Update the colors preview at the platter extruder combo box. -void PresetBundle::update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui) -{ - this->update_platter_filament_ui(idx_extruder, ui); - return; - - unsigned char rgb[3]; - std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); - if (! parse_color(extruder_color, rgb)) - // Extruder color is not defined. - extruder_color.clear(); - - ui->Freeze(); - for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { - std::string preset_name = ui->GetString(ui_id).utf8_str().data(); - size_t filament_preset_id = size_t(ui->GetClientData(ui_id)); - const Preset *filament_preset = filaments.find_preset(preset_name, false); - assert(filament_preset != nullptr); - // Assign an extruder color to the selected item if the extruder color is defined. - std::string filament_rgb = filament_preset->config.opt_string("filament_colour", 0); - std::string extruder_rgb = (int(ui_id) == ui->GetSelection() && ! extruder_color.empty()) ? extruder_color : filament_rgb; - wxBitmap *bitmap = nullptr; - if (filament_rgb == extruder_rgb) { - auto it = m_mapColorToBitmap.find(filament_rgb); - if (it == m_mapColorToBitmap.end()) { - // Create the bitmap. - parse_color(filament_rgb, rgb); - wxImage image(24, 16); - image.SetRGB(wxRect(0, 0, 24, 16), rgb[0], rgb[1], rgb[2]); - m_mapColorToBitmap[filament_rgb] = new wxBitmap(image); - } else { - bitmap = it->second; - } - } else { - std::string bitmap_key = filament_rgb + extruder_rgb; - auto it = m_mapColorToBitmap.find(bitmap_key); - if (it == m_mapColorToBitmap.end()) { - // Create the bitmap. - wxImage image(24, 16); - parse_color(extruder_rgb, rgb); - image.SetRGB(wxRect(0, 0, 16, 16), rgb[0], rgb[1], rgb[2]); - parse_color(filament_rgb, rgb); - image.SetRGB(wxRect(16, 0, 8, 16), rgb[0], rgb[1], rgb[2]); - m_mapColorToBitmap[filament_rgb] = new wxBitmap(image); - } else { - bitmap = it->second; - } - } - ui->SetItemBitmap(ui_id, *bitmap); - } - ui->Thaw(); -} - void PresetBundle::set_default_suppressed(bool default_suppressed) { prints.set_default_suppressed(default_suppressed); diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/xs/src/slic3r/GUI/PresetBundle.hpp index b1077c11c..571dea0be 100644 --- a/xs/src/slic3r/GUI/PresetBundle.hpp +++ b/xs/src/slic3r/GUI/PresetBundle.hpp @@ -58,8 +58,6 @@ public: // Update a filament selection combo box on the platter for an idx_extruder. void update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui); - // Update the colors preview at the platter extruder combo box. - void update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui); // Enable / disable the "- default -" preset. void set_default_suppressed(bool default_suppressed); @@ -72,10 +70,17 @@ public: // update size and content of filament_presets. void update_multi_material_filament_presets(); + // Update the is_compatible flag of all print and filament presets depending on whether they are marked + // as compatible with the currently selected printer. + // Also updates the is_visible flag of each preset. + // If select_other_if_incompatible is true, then the print or filament preset is switched to some compatible + // preset if the current print or filament preset is not compatible. + void update_compatible_with_printer(bool select_other_if_incompatible); + private: void load_config_file_config(const std::string &path, const DynamicPrintConfig &config); void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree); - bool load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible); + bool load_compatible_bitmaps(); // Indicator, that the preset is compatible with the selected printer. wxBitmap *m_bitmapCompatible; diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp index a084d7f2a..e1f8a72c4 100644 --- a/xs/xsp/GUI_Preset.xsp +++ b/xs/xsp/GUI_Preset.xsp @@ -4,6 +4,7 @@ #include #include "slic3r/GUI/Preset.hpp" #include "slic3r/GUI/PresetBundle.hpp" +#include "slic3r/GUI/PresetHints.hpp" %} %name{Slic3r::GUI::Preset} class Preset { @@ -13,6 +14,7 @@ bool external() %code%{ RETVAL = THIS->is_external; %}; bool visible() %code%{ RETVAL = THIS->is_visible; %}; bool dirty() %code%{ RETVAL = THIS->is_dirty; %}; + bool is_compatible_with_printer(char *active_printer) const; const char* name() %code%{ RETVAL = THIS->name.c_str(); %}; const char* file() %code%{ RETVAL = THIS->file.c_str(); %}; @@ -30,6 +32,7 @@ Ref default_preset() %code%{ RETVAL = &THIS->default_preset(); %}; size_t size() const; size_t num_visible() const; + std::string name() const; Ref get_selected_preset() %code%{ RETVAL = &THIS->get_selected_preset(); %}; Ref get_current_preset() %code%{ RETVAL = &THIS->get_edited_preset(); %}; @@ -41,19 +44,20 @@ bool current_is_dirty(); std::vector current_dirty_options(); - void update_tab_ui(SV *ui) - %code%{ auto cb = (wxChoice*)wxPli_sv_2_object( aTHX_ ui, "Wx::Choice" ); - THIS->update_tab_ui(cb); %}; + void update_tab_ui(SV *ui, bool show_incompatible) + %code%{ auto cb = (wxBitmapComboBox*)wxPli_sv_2_object( aTHX_ ui, "Wx::BitmapComboBox" ); + THIS->update_tab_ui(cb, show_incompatible); %}; void update_platter_ui(SV *ui) %code%{ auto cb = (wxBitmapComboBox*)wxPli_sv_2_object( aTHX_ ui, "Wx::BitmapComboBox" ); THIS->update_platter_ui(cb); %}; bool update_dirty_ui(SV *ui) - %code%{ RETVAL = THIS->update_dirty_ui((wxChoice*)wxPli_sv_2_object(aTHX_ ui, "Wx::Choice")); %}; + %code%{ RETVAL = THIS->update_dirty_ui((wxBitmapComboBox*)wxPli_sv_2_object(aTHX_ ui, "Wx::BitmapComboBox")); %}; void select_preset(int idx); bool select_preset_by_name(char *name) %code%{ RETVAL = THIS->select_preset_by_name(name, true); %}; + void discard_current_changes(); void save_current_preset(char *new_name) %code%{ @@ -150,14 +154,21 @@ PresetCollection::arrayref() void set_filament_preset(int idx, const char *name); void update_multi_material_filament_presets(); + void update_compatible_with_printer(bool select_other_if_incompatible); + Clone full_config() %code%{ RETVAL = THIS->full_config(); %}; void update_platter_filament_ui(int extruder_idx, SV *ui) %code%{ auto cb = (wxBitmapComboBox*)wxPli_sv_2_object(aTHX_ ui, "Wx::BitmapComboBox"); THIS->update_platter_filament_ui(extruder_idx, cb); %}; - - void update_platter_filament_ui_colors(int extruder_idx, SV *ui) - %code%{ auto cb = (wxBitmapComboBox*)wxPli_sv_2_object(aTHX_ ui, "Wx::BitmapComboBox"); - THIS->update_platter_filament_ui_colors(extruder_idx, cb); %}; - +}; + +%name{Slic3r::GUI::PresetHints} class PresetHints { + PresetHints(); + ~PresetHints(); + + static std::string cooling_description(Preset *preset) + %code%{ RETVAL = PresetHints::cooling_description(*preset); %}; + static std::string maximum_volumetric_flow_description(PresetBundle *preset) + %code%{ RETVAL = PresetHints::maximum_volumetric_flow_description(*preset); %}; }; From 46283084154d2783dea3a3d3505f8225695b43b6 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 10 Nov 2017 17:42:30 +0100 Subject: [PATCH 26/83] Oh those compiler differences. --- xs/src/libslic3r/Config.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 33f28e4a8..d37f03c4f 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -1009,8 +1009,8 @@ public: TYPE* option(const t_config_option_key &opt_key, bool create = false) { ConfigOption *opt = this->optptr(opt_key, create); - assert(opt == nullptr || opt->type() == typename TYPE::static_type()); - return (opt == nullptr || opt->type() != typename TYPE::static_type()) ? nullptr : static_cast(opt); + assert(opt == nullptr || opt->type() == TYPE::static_type()); + return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast(opt); } template const TYPE* option(const t_config_option_key &opt_key) const From 200f176951d06cea9f2e33b7d8a325fb103d14d4 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 10 Nov 2017 18:50:14 +0100 Subject: [PATCH 27/83] Fixed compilation on Linux / GTK --- xs/src/slic3r/GUI/PresetBundle.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index cf253e6f1..00e35683a 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -570,14 +570,16 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma if (bitmap == nullptr) { // Create the bitmap with color bars. bitmap = new wxBitmap((wide_icons ? 16 : 0) + 24, 16); +#if defined(__APPLE__) || defined(_MSC_VER) bitmap->UseAlpha(); +#endif wxMemoryDC memDC; memDC.SelectObject(*bitmap); memDC.SetBackground(*wxTRANSPARENT_BRUSH); memDC.Clear(); if (wide_icons && ! preset.is_compatible) // Paint the red flag. - memDC.DrawBitmap(*m_bitmapIncompatible, 0, 0, false); + memDC.DrawBitmap(*m_bitmapIncompatible, 0, 0, true); // Paint the color bars. parse_color(filament_rgb, rgb); wxImage image(24, 16); @@ -602,7 +604,7 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma } } } - memDC.DrawBitmap(wxBitmap(image), wide_icons ? 16 : 0, 0, false); + memDC.DrawBitmap(wxBitmap(image), wide_icons ? 16 : 0, 0, true); memDC.SelectObject(wxNullBitmap); m_mapColorToBitmap[bitmap_key] = bitmap; } From 47f193fe2dd78fd1f5d058ade3ff897ce85cfdb8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 17 Nov 2017 11:15:46 +0100 Subject: [PATCH 28/83] The PlaceholderParser has been rewritten to use a real boost::spirit::qi parser, accessing the DynamicConfig repository directly. This is a first step towards a full fledged expression interpreter. --- t/custom_gcode.t | 2 +- xs/src/libslic3r/Config.cpp | 24 +- xs/src/libslic3r/Config.hpp | 100 ++++- xs/src/libslic3r/GCode.cpp | 30 +- xs/src/libslic3r/PlaceholderParser.cpp | 489 ++++++++++++++++++++----- xs/src/libslic3r/PlaceholderParser.hpp | 26 +- xs/xsp/PlaceholderParser.xsp | 7 +- 7 files changed, 534 insertions(+), 144 deletions(-) diff --git a/t/custom_gcode.t b/t/custom_gcode.t index ed74e750c..a0d96736a 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -48,7 +48,7 @@ use Slic3r::Test; { my $parser = Slic3r::GCode::PlaceholderParser->new; $parser->apply_config(my $config = Slic3r::Config::new_from_defaults); - $parser->set('foo' => '0'); + $parser->set('foo' => 0); is $parser->process('[temperature_[foo]]'), $config->temperature->[0], "nested config options"; diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index cb8dd98f7..d7671c82f 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -234,10 +234,13 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con { t_config_option_key opt_key = opt_key_src; // Try to deserialize the option by its name. - const ConfigOptionDef* optdef = this->def()->get(opt_key); + const ConfigDef *def = this->def(); + if (def == nullptr) + throw NoDefinitionException(); + const ConfigOptionDef *optdef = def->get(opt_key); if (optdef == nullptr) { // If we didn't find an option, look for any other option having this as an alias. - for (const auto &opt : this->def()->options) { + for (const auto &opt : def->options) { for (const t_config_option_key &opt_key2 : opt.second.aliases) { if (opt_key2 == opt_key) { opt_key = opt_key2; @@ -277,10 +280,16 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const return static_cast(raw_opt)->value; if (raw_opt->type() == coFloatOrPercent) { // Get option definition. - const ConfigOptionDef *def = this->def()->get(opt_key); - assert(def != nullptr); + const ConfigDef *def = this->def(); + if (def == nullptr) + throw NoDefinitionException(); + const ConfigOptionDef *opt_def = def->get(opt_key); + assert(opt_def != nullptr); // Compute absolute value over the absolute value of the base option. - return static_cast(raw_opt)->get_abs_value(this->get_abs_value(def->ratio_over)); + //FIXME there are some ratio_over chains, which end with empty ratio_with. + // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly. + return opt_def->ratio_over.empty() ? 0. : + static_cast(raw_opt)->get_abs_value(this->get_abs_value(opt_def->ratio_over)); } throw std::runtime_error("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()"); } @@ -453,7 +462,10 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre // Option was not found and a new option shall not be created. return nullptr; // Try to create a new ConfigOption. - const ConfigOptionDef *optdef = this->def()->get(opt_key); + const ConfigDef *def = this->def(); + if (def == nullptr) + throw NoDefinitionException(); + const ConfigOptionDef *optdef = def->get(opt_key); if (optdef == nullptr) // throw std::runtime_error(std::string("Invalid option name: ") + opt_key); // Let the parent decide what to do if the opt_key is not defined by this->def(). diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index d37f03c4f..8b384c9b5 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -126,6 +126,10 @@ public: virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0; + // Get size of this vector. + virtual size_t size() const = 0; + // Is this vector empty? + virtual bool empty() const = 0; protected: // Used to verify type compatibility when assigning to / from a scalar ConfigOption. @@ -140,6 +144,8 @@ public: ConfigOptionVector() {} explicit ConfigOptionVector(size_t n, const T &value) : values(n, value) {} explicit ConfigOptionVector(std::initializer_list il) : values(std::move(il)) {} + explicit ConfigOptionVector(const std::vector &values) : values(values) {} + explicit ConfigOptionVector(std::vector &&values) : values(std::move(values)) {} std::vector values; void set(const ConfigOption *rhs) override @@ -227,6 +233,9 @@ public: } } + size_t size() const override { return this->values.size(); } + bool empty() const override { return this->values.empty(); } + bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) @@ -445,6 +454,8 @@ class ConfigOptionStrings : public ConfigOptionVector public: ConfigOptionStrings() : ConfigOptionVector() {} explicit ConfigOptionStrings(size_t n, const std::string &value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionStrings(const std::vector &values) : ConfigOptionVector(values) {} + explicit ConfigOptionStrings(std::vector &&values) : ConfigOptionVector(std::move(values)) {} explicit ConfigOptionStrings(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} static ConfigOptionType static_type() { return coStrings; } @@ -1049,23 +1060,71 @@ private: class DynamicConfig : public virtual ConfigBase { public: + DynamicConfig() {} DynamicConfig(const DynamicConfig& other) { *this = other; } DynamicConfig(DynamicConfig&& other) : options(std::move(other.options)) { other.options.clear(); } virtual ~DynamicConfig() { clear(); } - DynamicConfig& operator=(const DynamicConfig &other) + // Copy a content of one DynamicConfig to another DynamicConfig. + // If rhs.def() is not null, then it has to be equal to this->def(). + DynamicConfig& operator=(const DynamicConfig &rhs) { + assert(this->def() == nullptr || this->def() == rhs.def()); this->clear(); - for (const auto &kvp : other.options) + for (const auto &kvp : rhs.options) this->options[kvp.first] = kvp.second->clone(); return *this; } - DynamicConfig& operator=(DynamicConfig &&other) + // Move a content of one DynamicConfig to another DynamicConfig. + // If rhs.def() is not null, then it has to be equal to this->def(). + DynamicConfig& operator=(DynamicConfig &&rhs) { + assert(this->def() == nullptr || this->def() == rhs.def()); this->clear(); - this->options = std::move(other.options); - other.options.clear(); + this->options = std::move(rhs.options); + rhs.options.clear(); + return *this; + } + + // Add a content of one DynamicConfig to another DynamicConfig. + // If rhs.def() is not null, then it has to be equal to this->def(). + DynamicConfig& operator+=(const DynamicConfig &rhs) + { + assert(this->def() == nullptr || this->def() == rhs.def()); + for (const auto &kvp : rhs.options) { + auto it = this->options.find(kvp.first); + if (it == this->options.end()) + this->options[kvp.first] = kvp.second->clone(); + else { + assert(it->second->type() == kvp.second->type()); + if (it->second->type() == kvp.second->type()) + *it->second = *kvp.second; + else { + delete it->second; + it->second = kvp.second->clone(); + } + } + } + return *this; + } + + // Move a content of one DynamicConfig to another DynamicConfig. + // If rhs.def() is not null, then it has to be equal to this->def(). + DynamicConfig& operator+=(DynamicConfig &&rhs) + { + assert(this->def() == nullptr || this->def() == rhs.def()); + for (const auto &kvp : rhs.options) { + auto it = this->options.find(kvp.first); + if (it == this->options.end()) { + this->options[kvp.first] = kvp.second; + } else { + assert(it->second->type() == kvp.second->type()); + delete it->second; + it->second = kvp.second; + } + } + rhs.options.clear(); return *this; } @@ -1094,13 +1153,32 @@ public: return true; } - template T* opt(const t_config_option_key &opt_key, bool create = false) + // Allow DynamicConfig to be instantiated on ints own without a definition. + // If the definition is not defined, the method requiring the definition will throw NoDefinitionException. + const ConfigDef* def() const override { return nullptr; }; + template T* opt(const t_config_option_key &opt_key, bool create = false) { return dynamic_cast(this->option(opt_key, create)); } // Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override; // Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. t_config_option_keys keys() const override; + // Set a value for an opt_key. Returns true if the value did not exist yet. + // This DynamicConfig will take ownership of opt. + // Be careful, as this method does not test the existence of opt_key in this->def(). + bool set_key_value(const std::string &opt_key, ConfigOption *opt) + { + auto it = this->options.find(opt_key); + if (it == this->options.end()) { + this->options[opt_key] = opt; + return true; + } else { + delete it->second; + it->second = opt; + return false; + } + } + std::string& opt_string(const t_config_option_key &opt_key, bool create = false) { return this->option(opt_key, create)->value; } const std::string& opt_string(const t_config_option_key &opt_key) const { return const_cast(this)->opt_string(opt_key); } std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) { return this->option(opt_key)->get_at(idx); } @@ -1119,9 +1197,6 @@ public: bool opt_bool(const t_config_option_key &opt_key) const { return this->option(opt_key)->value != 0; } bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option(opt_key)->get_at(idx) != 0; } -protected: - DynamicConfig() {} - private: typedef std::map t_options_map; t_options_map options; @@ -1150,6 +1225,13 @@ public: const char* what() const noexcept override { return "Unknown config option"; } }; +/// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null). +class NoDefinitionException : public std::exception +{ +public: + const char* what() const noexcept override { return "No config definition"; } +}; + } #endif diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index f46d35657..12444d1a5 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -887,18 +887,22 @@ void GCode::process_layer( // Set new layer - this will change Z and force a retraction if retract_layer_change is enabled. if (! print.config.before_layer_gcode.value.empty()) { - PlaceholderParser pp(m_placeholder_parser); - pp.set("layer_num", m_layer_index + 1); - pp.set("layer_z", print_z); - gcode += pp.process(print.config.before_layer_gcode.value, m_writer.extruder()->id()) + "\n"; + DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1)); + config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + gcode += m_placeholder_parser.process( + print.config.before_layer_gcode.value, m_writer.extruder()->id(), &config) + + "\n"; } gcode += this->change_layer(print_z); // this will increase m_layer_index m_layer = &layer; if (! print.config.layer_gcode.value.empty()) { - PlaceholderParser pp(m_placeholder_parser); - pp.set("layer_num", m_layer_index); - pp.set("layer_z", print_z); - gcode += pp.process(print.config.layer_gcode.value, m_writer.extruder()->id()) + "\n"; + DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + gcode += m_placeholder_parser.process( + print.config.layer_gcode.value, m_writer.extruder()->id(), &config) + + "\n"; } if (! first_layer && ! m_second_layer_things_done) { @@ -2091,10 +2095,12 @@ std::string GCode::set_extruder(unsigned int extruder_id) // append custom toolchange G-code if (m_writer.extruder() != nullptr && !m_config.toolchange_gcode.value.empty()) { - PlaceholderParser pp = m_placeholder_parser; - pp.set("previous_extruder", m_writer.extruder()->id()); - pp.set("next_extruder", extruder_id); - gcode += pp.process(m_config.toolchange_gcode.value, extruder_id) + '\n'; + DynamicConfig config; + config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id())); + config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); + gcode += m_placeholder_parser.process( + m_config.toolchange_gcode.value, extruder_id, &config) + + '\n'; } // if ooze prevention is enabled, park current extruder in the nearest diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index e25ec9526..5967f6a7e 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -21,6 +21,29 @@ #endif #endif +// Spirit v2.5 allows you to suppress automatic generation +// of predefined terminals to speed up complation. With +// BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are +// responsible in creating instances of the terminals that +// you need (e.g. see qi::uint_type uint_ below). +//#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + namespace Slic3r { PlaceholderParser::PlaceholderParser() @@ -61,130 +84,398 @@ void PlaceholderParser::update_timestamp() // are expected to be addressed by the extruder ID, therefore // if a vector configuration value is addressed without an index, // a current extruder ID is used. -void PlaceholderParser::apply_config(const DynamicPrintConfig &config) +void PlaceholderParser::apply_config(const DynamicPrintConfig &rhs) { - for (const t_config_option_key &opt_key : config.keys()) { - const ConfigOptionDef* def = config.def()->get(opt_key); - if (def->multiline || opt_key == "post_process") + const ConfigDef *def = rhs.def(); + for (const t_config_option_key &opt_key : rhs.keys()) { + const ConfigOptionDef *opt_def = def->get(opt_key); + if (opt_def->multiline || opt_key == "post_process") continue; - - const ConfigOption* opt = config.option(opt_key); - const ConfigOptionVectorBase* optv = dynamic_cast(opt); - if (optv != nullptr && opt_key != "bed_shape") { - // set placeholders for options with multiple values - this->set(opt_key, optv->vserialize()); - } else if (const ConfigOptionPoint* optp = dynamic_cast(opt)) { - this->set(opt_key, optp->serialize()); - Pointf val = *optp; - this->set(opt_key + "_X", val.x); - this->set(opt_key + "_Y", val.y); - } else { - // set single-value placeholders - this->set(opt_key, opt->serialize()); - } + const ConfigOption *opt = rhs.option(opt_key); + // Store a copy of the config option. + // Convert FloatOrPercent values to floats first. + //FIXME there are some ratio_over chains, which end with empty ratio_with. + // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly. + this->set(opt_key, (opt->type() == coFloatOrPercent) ? + new ConfigOptionFloat(rhs.get_abs_value(opt_key)) : + opt->clone()); } } void PlaceholderParser::apply_env_variables() { - for (char** env = environ; *env; env++) { + for (char** env = environ; *env; ++ env) { if (strncmp(*env, "SLIC3R_", 7) == 0) { std::stringstream ss(*env); std::string key, value; std::getline(ss, key, '='); ss >> value; - this->set(key, value); } } } -void PlaceholderParser::set(const std::string &key, const std::string &value) +namespace client { - m_single[key] = value; - m_multiple.erase(key); -} + struct MyContext { + const PlaceholderParser *pp = nullptr; + const DynamicConfig *config_override = nullptr; + const size_t current_extruder_id = 0; -void PlaceholderParser::set(const std::string &key, int value) -{ - std::ostringstream ss; - ss << value; - this->set(key, ss.str()); -} - -void PlaceholderParser::set(const std::string &key, unsigned int value) -{ - std::ostringstream ss; - ss << value; - this->set(key, ss.str()); -} - -void PlaceholderParser::set(const std::string &key, double value) -{ - std::ostringstream ss; - ss << value; - this->set(key, ss.str()); -} - -void PlaceholderParser::set(const std::string &key, std::vector values) -{ - m_single.erase(key); - if (values.empty()) - m_multiple.erase(key); - else - m_multiple[key] = values; -} - -std::string PlaceholderParser::process(std::string str, unsigned int current_extruder_id) const -{ - char key[2048]; - - // Replace extruder independent single options, like [foo]. - for (const auto &key_value : m_single) { - sprintf(key, "[%s]", key_value.first.c_str()); - const std::string &replace = key_value.second; - for (size_t i = 0; (i = str.find(key, i)) != std::string::npos;) { - str.replace(i, key_value.first.size() + 2, replace); - i += replace.size(); + const ConfigOption* resolve_symbol(const std::string &opt_key) const + { + const ConfigOption *opt = nullptr; + if (config_override != nullptr) + opt = config_override->option(opt_key); + if (opt == nullptr) + opt = pp->option(opt_key); + return opt; } - } - // Replace extruder dependent single options with the value for the active extruder. - // For example, [temperature] will be replaced with the current extruder temperature. - for (const auto &key_value : m_multiple) { - sprintf(key, "[%s]", key_value.first.c_str()); - const std::string &replace = key_value.second[(current_extruder_id < key_value.second.size()) ? current_extruder_id : 0]; - for (size_t i = 0; (i = str.find(key, i)) != std::string::npos;) { - str.replace(i, key_value.first.size() + 2, replace); - i += replace.size(); - } - } - - // Replace multiple options like [foo_0]. - for (const auto &key_value : m_multiple) { - sprintf(key, "[%s_", key_value.first.c_str()); - const std::vector &values = key_value.second; - for (size_t i = 0; (i = str.find(key, i)) != std::string::npos;) { - size_t k = str.find(']', i + key_value.first.size() + 2); - if (k != std::string::npos) { - // Parse the key index and the closing bracket. - ++ k; - int idx = 0; - if (sscanf(str.c_str() + i + key_value.first.size() + 2, "%d]", &idx) == 1 && idx >= 0) { - if (idx >= int(values.size())) - idx = 0; - str.replace(i, k - i, values[idx]); - i += values[idx].size(); - continue; + template + static void legacy_variable_expansion( + const MyContext *ctx, + boost::iterator_range &opt_key, + std::string &output) + { + std::string opt_key_str(opt_key.begin(), opt_key.end()); + const ConfigOption *opt = ctx->resolve_symbol(opt_key_str); + size_t idx = ctx->current_extruder_id; + if (opt == nullptr) { + // Check whether this is a legacy vector indexing. + idx = opt_key_str.rfind('_'); + if (idx != std::string::npos) { + opt = ctx->resolve_symbol(opt_key_str.substr(0, idx)); + if (opt != nullptr) { + if (! opt->is_vector()) + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt_key.begin(), opt_key.end(), boost::spirit::info("Trying to index a scalar variable"))); + char *endptr = nullptr; + idx = strtol(opt_key_str.c_str() + idx + 1, &endptr, 10); + if (endptr == nullptr || *endptr != 0) + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt_key.begin() + idx + 1, opt_key.end(), boost::spirit::info("Invalid vector index"))); + } } } - // The key does not match the pattern [foo_%d]. Skip just [foo_.] with the hope that there was a missing ']', - // so an opening '[' may be found somewhere before the position k. - i += key_value.first.size() + 3; + if (opt == nullptr) + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt_key.begin(), opt_key.end(), boost::spirit::info("Variable does not exist"))); + if (opt->is_scalar()) + output = opt->serialize(); + else { + const ConfigOptionVectorBase *vec = static_cast(opt); + if (vec->empty()) + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt_key.begin(), opt_key.end(), boost::spirit::info("Indexing an empty vector variable"))); + output = vec->vserialize()[(idx >= vec->size()) ? 0 : idx]; + } } + + template + static void legacy_variable_expansion2( + const MyContext *ctx, + boost::iterator_range &opt_key, + boost::iterator_range &opt_vector_index, + std::string &output) + { + std::string opt_key_str(opt_key.begin(), opt_key.end()); + const ConfigOption *opt = ctx->resolve_symbol(opt_key_str); + if (opt == nullptr) { + // Check whether the opt_key ends with '_'. + if (opt_key_str.back() == '_') + opt_key_str.resize(opt_key_str.size() - 1); + opt = ctx->resolve_symbol(opt_key_str); + } + if (! opt->is_vector()) + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt_key.begin(), opt_key.end(), boost::spirit::info("Trying to index a scalar variable"))); + const ConfigOptionVectorBase *vec = static_cast(opt); + if (vec->empty()) + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt_key.begin(), opt_key.end(), boost::spirit::info("Indexing an empty vector variable"))); + const ConfigOption *opt_index = ctx->resolve_symbol(std::string(opt_vector_index.begin(), opt_vector_index.end())); + if (opt_index == nullptr) + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt_key.begin(), opt_key.end(), boost::spirit::info("Variable does not exist"))); + if (opt_index->type() != coInt) + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt_key.begin(), opt_key.end(), boost::spirit::info("Indexing variable has to be integer"))); + int idx = opt_index->getInt(); + if (idx < 0) + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt_key.begin(), opt_key.end(), boost::spirit::info("Negative vector index"))); + output = vec->vserialize()[(idx >= (int)vec->size()) ? 0 : idx]; + } + }; + + struct expr + { + expr(int i = 0) : type(TYPE_INT) { data.i = i; } + expr(double d) : type(TYPE_DOUBLE) { data.d = d; } + expr(const char *s) : type(TYPE_STRING) { data.s = new std::string(s); } + expr(std::string &s) : type(TYPE_STRING) { data.s = new std::string(s); } + expr(const expr &rhs) : type(rhs.type) { data.s = (rhs.type == TYPE_STRING) ? new std::string(*rhs.data.s) : rhs.data.s; } + expr(expr &&rhs) : type(rhs.type) { data.s = rhs.data.s; rhs.data.s = nullptr; rhs.type = TYPE_EMPTY; } + ~expr() { if (type == TYPE_STRING) delete data.s; data.s = nullptr; } + + expr &operator=(const expr &rhs) + { + type = rhs.type; + data.s = (type == TYPE_STRING) ? new std::string(*rhs.data.s) : rhs.data.s; + return *this; + } + + expr &operator=(expr &&rhs) + { + type = rhs.type; + data.s = rhs.data.s; + rhs.data.s = nullptr; + rhs.type = TYPE_EMPTY; + return *this; + } + + int& i() { return data.i; } + int i() const { return data.i; } + double& d() { return data.d; } + double d() const { return data.d; } + std::string& s() { return *data.s; } + const std::string& s() const { return *data.s; } + + std::string to_string() const + { + std::string out; + switch (type) { + case TYPE_INT: out = boost::to_string(data.i); break; + case TYPE_DOUBLE: out = boost::to_string(data.d); break; + case TYPE_STRING: out = *data.s; break; + } + return out; + } + + union { + int i; + double d; + std::string *s; + } data; + + enum Type { + TYPE_EMPTY = 0, + TYPE_INT, + TYPE_DOUBLE, + TYPE_STRING, + }; + + int type; + }; + + /////////////////////////////////////////////////////////////////////////// + // Our calculator grammar + /////////////////////////////////////////////////////////////////////////// + template + struct calculator : boost::spirit::qi::grammar + { + calculator() : calculator::base_type(start) + { + using boost::spirit::qi::alpha; + using boost::spirit::qi::alnum; + using boost::spirit::qi::eol; + using boost::spirit::qi::eoi; + using boost::spirit::qi::eps; + using boost::spirit::qi::raw; + using boost::spirit::qi::lit; + using boost::spirit::qi::lexeme; + using boost::spirit::qi::on_error; + using boost::spirit::qi::fail; + using boost::spirit::ascii::char_; + using boost::spirit::int_; + using boost::spirit::double_; + using boost::spirit::ascii::string; + using namespace boost::spirit::qi::labels; + + using boost::phoenix::construct; + using boost::phoenix::val; + using boost::phoenix::begin; + + boost::spirit::qi::_val_type _val; + boost::spirit::qi::_1_type _1; + boost::spirit::qi::_2_type _2; + boost::spirit::qi::_r1_type _r1; + boost::spirit::qi::uint_type uint_; + + // Starting symbol of the grammer. + // The leading eps is required by the "expectation point" operator ">". + // Without it, some of the errors would not trigger the error handler. + start = eps > *(text [_val+=_1] + || ((lit('{') > macro [_val+=_1] > '}') + | (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']'))); + start.name("start"); + + // Free-form text up to a first brace, including spaces and newlines. + // The free-form text will be inserted into the processed text without a modification. + text = raw[+(char_ - '[' - '{')]; + text.name("text"); + + // New style of macro expansion. + // The macro expansion may contain numeric or string expressions, ifs and cases. + macro = identifier; + macro.name("macro"); + + // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index]. + legacy_variable_expansion = + (identifier >> &lit(']')) + [ boost::phoenix::bind(&MyContext::legacy_variable_expansion, _r1, _1, _val) ] + | (identifier > lit('[') > identifier > ']') + [ boost::phoenix::bind(&MyContext::legacy_variable_expansion2, _r1, _1, _2, _val) ] + ; + legacy_variable_expansion.name("legacy_variable_expansion"); + + identifier = +// !expr.keywords >> + raw[lexeme[(alpha | '_') >> *(alnum | '_')]] + ; + identifier.name("identifier"); + +/* + bool_expr = + (expression >> '=' >> '=' >> expression) | + (expression >> '!' >> '=' >> expression) | + (expression >> '<' >> '>' >> expression) + ; + + expression = + term //[_val = _1] + >> *( ('+' >> term ) //[_val += _1]) + | ('-' >> term )//[_val -= _1]) + ) + ; + + term = + factor //[_val = _1] + >> *( ('*' >> factor )//[_val *= _1]) + | ('/' >> factor )//[_val /= _1]) + ) + ; + + factor = + int_ //[_val = expr(_1)] + | double_ //[_val = expr(_1)] + | '(' >> expression >> ')' // [_val = std::move(_1)] >> ')' + | ('-' >> factor ) //[_val = -_1]) + | ('+' >> factor ) //[_val = std::move(_1)]) + ; +*/ + +// text %= lexeme[+(char_ - '<')]; + +// text_to_eol %= lexeme[*(char_ - eol) >> (eol | eoi)]; + /* + expression_with_braces = lit('{') >> ( + string("if") >> if_else_output [_val = _1] | + string("switch") >> switch_output [_val = _1] | + expression [_val = boost::to_string(_1)] >> '}' + ); + if_else_output = + bool_expr[_r1 = _1] >> '}' >> text_to_eol[_val = _r1 ? _1 : std::string()] >> + *(lit('{') >> "elsif" >> bool_expr[_r1 = !_r1 && _1] >> '}' >> text_to_eol[_val = _r1 ? _1 : std::string()]) >> + -(lit('{') >> "else" >> '}' >> text_to_eol[_val = _r1 ? std::string() : _1]); +*/ +/* + on_error(start, + phx::ref(std::cout) + << "Error! Expecting " + << boost::spirit::qi::_4 + << " here: '" + << construct(boost::spirit::qi::_3, boost::spirit::qi::_2) + << "'\n" + ); +*/ + } + + // The start of the grammar. + boost::spirit::qi::rule start; + // A free-form text. + boost::spirit::qi::rule text; + // Statements enclosed in curely braces {} + boost::spirit::qi::rule macro; + // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index]. + boost::spirit::qi::rule legacy_variable_expansion; + // Parsed identifier name. + boost::spirit::qi::rule, boost::spirit::ascii::space_type> identifier; + + boost::spirit::qi::rule expression, term, factor; + boost::spirit::qi::rule text_to_eol; + boost::spirit::qi::rule expression_with_braces; + boost::spirit::qi::rule bool_expr; + + boost::spirit::qi::rule if_else_output; + boost::spirit::qi::rule switch_output; + }; +} + +struct printer +{ + typedef boost::spirit::utf8_string string; + + void element(string const& tag, string const& value, int depth) const + { + for (int i = 0; i < (depth*4); ++i) // indent to depth + std::cout << ' '; + std::cout << "tag: " << tag; + if (value != "") + std::cout << ", value: " << value; + std::cout << std::endl; } - - return str; +}; + +void print_info(boost::spirit::info const& what) +{ + using boost::spirit::basic_info_walker; + printer pr; + basic_info_walker walker(pr, what.tag, 0); + boost::apply_visitor(walker, what.value); +} + +std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) const +{ + typedef std::string::const_iterator iterator_type; + typedef client::calculator calculator; + + boost::spirit::ascii::space_type space; // Our skipper + calculator calc; // Our grammar + + std::string::const_iterator iter = templ.begin(); + std::string::const_iterator end = templ.end(); + //std::string result; + std::string result; + bool r = false; + try { + client::MyContext context; + context.pp = this; + context.config_override = config_override; + r = phrase_parse(iter, end, calc(&context), space, result); + } catch (boost::spirit::qi::expectation_failure const& x) { + std::cout << "expected: "; print_info(x.what_); + std::cout << "got: \"" << std::string(x.first, x.last) << '"' << std::endl; + } + + if (r && iter == end) + { +// std::cout << "-------------------------\n"; +// std::cout << "Parsing succeeded\n"; +// std::cout << "result = " << result << std::endl; +// std::cout << "-------------------------\n"; + } + else + { + std::string rest(iter, end); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \" " << rest << "\"\n"; + std::cout << "source: \n" << templ; + std::cout << "-------------------------\n"; + } + return result; } } diff --git a/xs/src/libslic3r/PlaceholderParser.hpp b/xs/src/libslic3r/PlaceholderParser.hpp index a8abf8871..9ec2eedea 100644 --- a/xs/src/libslic3r/PlaceholderParser.hpp +++ b/xs/src/libslic3r/PlaceholderParser.hpp @@ -11,23 +11,27 @@ namespace Slic3r { class PlaceholderParser { -public: - std::map m_single; - std::map> m_multiple; - +public: PlaceholderParser(); + void update_timestamp(); void apply_config(const DynamicPrintConfig &config); void apply_env_variables(); - void set(const std::string &key, const std::string &value); - void set(const std::string &key, int value); - void set(const std::string &key, unsigned int value); - void set(const std::string &key, double value); - void set(const std::string &key, std::vector values); - std::string process(std::string str, unsigned int current_extruder_id) const; + + // Add new ConfigOption values to m_config. + void set(const std::string &key, const std::string &value) { this->set(key, new ConfigOptionString(value)); } + void set(const std::string &key, int value) { this->set(key, new ConfigOptionInt(value)); } + void set(const std::string &key, unsigned int value) { this->set(key, int(value)); } + void set(const std::string &key, double value) { this->set(key, new ConfigOptionFloat(value)); } + void set(const std::string &key, const std::vector &values) { this->set(key, new ConfigOptionStrings(values)); } + void set(const std::string &key, ConfigOption *opt) { m_config.set_key_value(key, opt); } + const ConfigOption* option(const std::string &key) const { return m_config.option(key); } + + // Fill in the template. + std::string process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr) const; private: - bool find_and_replace(std::string &source, std::string const &find, std::string const &replace) const; + DynamicConfig m_config; }; } diff --git a/xs/xsp/PlaceholderParser.xsp b/xs/xsp/PlaceholderParser.xsp index 3f1588ace..08630f9d5 100644 --- a/xs/xsp/PlaceholderParser.xsp +++ b/xs/xsp/PlaceholderParser.xsp @@ -9,15 +9,10 @@ %name{Slic3r::GCode::PlaceholderParser} class PlaceholderParser { PlaceholderParser(); ~PlaceholderParser(); - Clone clone() - %code{% RETVAL = THIS; %}; - void update_timestamp(); - void apply_env_variables(); void apply_config(DynamicPrintConfig *config) %code%{ THIS->apply_config(*config); %}; - void set(std::string key, std::string value); - %name{set_multiple} void set(std::string key, std::vector values); + void set(std::string key, int value); std::string process(std::string str) const %code%{ RETVAL = THIS->process(str, 0); %}; }; From 9205c8aab4ac6f39be53e370132fa86b245f6992 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 17 Nov 2017 18:46:03 +0100 Subject: [PATCH 29/83] Sketch of the PlaceholderParser if/elsif/else macro. --- xs/src/libslic3r/Config.hpp | 3 + xs/src/libslic3r/PlaceholderParser.cpp | 358 ++++++++++++++++++------- 2 files changed, 269 insertions(+), 92 deletions(-) diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 8b384c9b5..c203f3be2 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -82,6 +82,9 @@ public: bool is_vector() const { return ! this->is_scalar(); } }; +typedef ConfigOption* ConfigOptionPtr; +typedef const ConfigOption* ConfigOptionConstPtr; + // Value of a single valued option (bool, int, float, string, point, enum) template class ConfigOptionSingle : public ConfigOption { diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index 5967f6a7e..5b05897da 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -117,6 +117,109 @@ void PlaceholderParser::apply_env_variables() namespace client { + template + struct OptWithPos { + OptWithPos() {} + OptWithPos(ConfigOptionConstPtr opt, boost::iterator_range it_range) : opt(opt), it_range(it_range) {} + ConfigOptionConstPtr opt = nullptr; + boost::iterator_range it_range; + }; + + template + struct expr + { + expr() : type(TYPE_EMPTY) { data.s = nullptr; } + expr(bool b) : type(TYPE_BOOL) { data.b = b; } + expr(int i) : type(TYPE_INT) { data.i = i; } + expr(double d) : type(TYPE_DOUBLE) { data.d = d; } + expr(const char *s) : type(TYPE_STRING) { data.s = new std::string(s); } + expr(const std::string &s) : type(TYPE_STRING) { data.s = new std::string(s); } + expr(const expr &rhs) : type(rhs.type) { data.s = (rhs.type == TYPE_STRING) ? new std::string(*rhs.data.s) : rhs.data.s; } + expr(expr &&rhs) : type(rhs.type) { data.s = rhs.data.s; rhs.data.s = nullptr; rhs.type = TYPE_EMPTY; } + ~expr() { if (type == TYPE_STRING) delete data.s; data.s = nullptr; } + + expr &operator=(const expr &rhs) + { + type = rhs.type; + data.s = (type == TYPE_STRING) ? new std::string(*rhs.data.s) : rhs.data.s; + return *this; + } + + expr &operator=(expr &&rhs) + { + type = rhs.type; + data.s = rhs.data.s; + rhs.data.s = nullptr; + rhs.type = TYPE_EMPTY; + return *this; + } + + bool& b() { return data.b; } + bool b() const { return data.b; } + int& i() { return data.i; } + int i() const { return data.i; } + double& d() { return data.d; } + double d() const { return data.d; } + std::string& s() { return *data.s; } + const std::string& s() const { return *data.s; } + + std::string to_string() const + { + std::string out; + switch (type) { + case TYPE_BOOL: out = boost::to_string(data.b); break; + case TYPE_INT: out = boost::to_string(data.i); break; + case TYPE_DOUBLE: out = boost::to_string(data.d); break; + case TYPE_STRING: out = *data.s; break; + } + return out; + } + + union { + bool b; + int i; + double d; + std::string *s; + } data; + + enum Type { + TYPE_EMPTY = 0, + TYPE_BOOL, + TYPE_INT, + TYPE_DOUBLE, + TYPE_STRING, + }; + + int type; + + // Range of input iterators covering this expression. + // Used for throwing parse exceptions. + boost::iterator_range it_range; + + static void store_iterator_range(expr &self, Iterator &it_start, boost::iterator_range &range_end) + { + self.it_range = boost::iterator_range(it_start, range_end.end()); + } + static void to_string2(expr &self, std::string &out) + { + out = self.to_string(); + } + static void evaluate_boolean(expr &self, bool &out) + { + if (self.type != TYPE_BOOL) + boost::throw_exception(boost::spirit::qi::expectation_failure( + self.it_range.begin(), self.it_range.end(), boost::spirit::info("Not a boolean expression"))); + out = self.b(); + } + static void set_if(bool &cond, bool ¬_yet_consumed, std::string &str_in, std::string &str_out) + { + if (cond && not_yet_consumed) { + str_out = str_in; + not_yet_consumed = false; + } + } + }; + struct MyContext { const PlaceholderParser *pp = nullptr; const DynamicConfig *config_override = nullptr; @@ -207,66 +310,94 @@ namespace client opt_key.begin(), opt_key.end(), boost::spirit::info("Negative vector index"))); output = vec->vserialize()[(idx >= (int)vec->size()) ? 0 : idx]; } - }; - struct expr - { - expr(int i = 0) : type(TYPE_INT) { data.i = i; } - expr(double d) : type(TYPE_DOUBLE) { data.d = d; } - expr(const char *s) : type(TYPE_STRING) { data.s = new std::string(s); } - expr(std::string &s) : type(TYPE_STRING) { data.s = new std::string(s); } - expr(const expr &rhs) : type(rhs.type) { data.s = (rhs.type == TYPE_STRING) ? new std::string(*rhs.data.s) : rhs.data.s; } - expr(expr &&rhs) : type(rhs.type) { data.s = rhs.data.s; rhs.data.s = nullptr; rhs.type = TYPE_EMPTY; } - ~expr() { if (type == TYPE_STRING) delete data.s; data.s = nullptr; } - - expr &operator=(const expr &rhs) - { - type = rhs.type; - data.s = (type == TYPE_STRING) ? new std::string(*rhs.data.s) : rhs.data.s; - return *this; - } - - expr &operator=(expr &&rhs) - { - type = rhs.type; - data.s = rhs.data.s; - rhs.data.s = nullptr; - rhs.type = TYPE_EMPTY; - return *this; - } - - int& i() { return data.i; } - int i() const { return data.i; } - double& d() { return data.d; } - double d() const { return data.d; } - std::string& s() { return *data.s; } - const std::string& s() const { return *data.s; } - - std::string to_string() const + template + static void resolve_variable( + const MyContext *ctx, + boost::iterator_range &opt_key, + OptWithPos &output) { - std::string out; - switch (type) { - case TYPE_INT: out = boost::to_string(data.i); break; - case TYPE_DOUBLE: out = boost::to_string(data.d); break; - case TYPE_STRING: out = *data.s; break; - } - return out; + const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end())); + if (opt == nullptr) + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt_key.begin(), opt_key.end(), boost::spirit::info("Not a variable name"))); + output.opt = opt; + output.it_range = opt_key; } - union { - int i; - double d; - std::string *s; - } data; + template + static void scalar_variable_reference( + const MyContext *ctx, + OptWithPos &opt, + expr &output) + { + if (opt.opt->is_vector()) + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt.it_range.begin(), opt.it_range.end(), boost::spirit::info("Referencing a scalar variable in a vector context"))); + switch (opt.opt->type()) { + case coFloat: output.d() = opt.opt->getFloat(); break; + case coInt: output.i() = opt.opt->getInt(); break; + case coString: output.s() = static_cast(opt.opt)->value; break; + case coPercent: output.d() = opt.opt->getFloat(); break; + case coPoint: output.s() = opt.opt->serialize(); break; + case coBool: output.b() = opt.opt->getBool(); break; + case coFloatOrPercent: + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt.it_range.begin(), opt.it_range.end(), boost::spirit::info("FloatOrPercent variables are not supported"))); + default: + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt.it_range.begin(), opt.it_range.end(), boost::spirit::info("Unknown scalar variable type"))); + } + } - enum Type { - TYPE_EMPTY = 0, - TYPE_INT, - TYPE_DOUBLE, - TYPE_STRING, - }; + template + static void vector_variable_reference( + const MyContext *ctx, + OptWithPos &opt, + expr &expr_index, + Iterator it_end, + expr &output) + { + if (opt.opt->is_scalar()) + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt.it_range.begin(), opt.it_range.end(), boost::spirit::info("Referencing a vector variable in a scalar context"))); + const ConfigOptionVectorBase *vec = static_cast(opt.opt); + if (vec->empty()) + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt.it_range.begin(), opt.it_range.end(), boost::spirit::info("Indexing an empty vector variable"))); + if (expr_index.type != expr::TYPE_INT) + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt.it_range.begin(), opt.it_range.end(), boost::spirit::info("Non-integer index is not allowed to index a vector"))); + size_t idx = (expr_index.i() < 0) ? 0 : (expr_index.i() >= int(vec->size())) ? 0 : expr_index.i(); + switch (opt.opt->type()) { + case coFloats: output.d() = static_cast(opt.opt)->values[idx]; break; + case coInts: output.i() = static_cast(opt.opt)->values[idx]; break; + case coStrings: output.s() = static_cast(opt.opt)->values[idx]; break; + case coPercents: output.d() = static_cast(opt.opt)->values[idx]; break; + case coPoints: output.s() = static_cast(opt.opt)->values[idx].dump_perl(); break; + case coBools: output.b() = static_cast(opt.opt)->values[idx] != 0; break; + default: + boost::throw_exception(boost::spirit::qi::expectation_failure( + opt.it_range.begin(), opt.it_range.end(), boost::spirit::info("Unknown vector variable type"))); + } + output.it_range = boost::iterator_range(opt.it_range.begin(), it_end); + } - int type; + template + static void store_start_position( + boost::iterator_range &range, + Iterator &output) + { + output = range.begin(); + } + + template + static void store_end_position( + boost::iterator_range &range, + Iterator &output) + { + output = range.end(); + } }; /////////////////////////////////////////////////////////////////////////// @@ -282,6 +413,7 @@ namespace client using boost::spirit::qi::eol; using boost::spirit::qi::eoi; using boost::spirit::qi::eps; + using boost::spirit::qi::omit; using boost::spirit::qi::raw; using boost::spirit::qi::lit; using boost::spirit::qi::lexeme; @@ -300,6 +432,7 @@ namespace client boost::spirit::qi::_val_type _val; boost::spirit::qi::_1_type _1; boost::spirit::qi::_2_type _2; + boost::spirit::qi::_r1_type _a1; boost::spirit::qi::_r1_type _r1; boost::spirit::qi::uint_type uint_; @@ -307,7 +440,7 @@ namespace client // The leading eps is required by the "expectation point" operator ">". // Without it, some of the errors would not trigger the error handler. start = eps > *(text [_val+=_1] - || ((lit('{') > macro [_val+=_1] > '}') + || ((lit('{') > macro(_r1) [_val+=_1] > '}') | (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']'))); start.name("start"); @@ -315,12 +448,34 @@ namespace client // The free-form text will be inserted into the processed text without a modification. text = raw[+(char_ - '[' - '{')]; text.name("text"); + text_or_empty = raw[*(char_ - '[' - '{')]; + text_or_empty.name("text"); // New style of macro expansion. // The macro expansion may contain numeric or string expressions, ifs and cases. - macro = identifier; + macro = + string("if") > if_else_output(_r1) [_val = _1] + | string("switch") > switch_output(_r1) [_val = _1] + | expression(_r1) [ boost::phoenix::bind(&expr::to_string2, _1, _val) ]; macro.name("macro"); + // An if expression enclosed in {} (the outmost {} are already parsed by the caller). + if_else_output = + eps[_b=true] > + omit[bool_expr_eval(_r1)[_a=_1]] > '}' > text_or_empty[boost::phoenix::bind(&expr::set_if, _a, _b, _1, _val)] > '{' > + *("elsif" > omit[bool_expr_eval(_r1)[_a=_1]] > '}' > text_or_empty[boost::phoenix::bind(&expr::set_if, _a, _b, _1, _val)]) >> + -("else" > '}' >> text_or_empty[boost::phoenix::bind(&expr::set_if, _b, _b, _1, _val)]) > + "endif"; + // A switch expression enclosed in {} (the outmost {} are already parsed by the caller). +/* + switch_output = + eps[_b=true] > + omit[expr(_r1)[_a=_1]] > '}' > text_or_empty[boost::phoenix::bind(&expr::set_if_equal, _a, _b, _1, _val)] > '{' > + *("elsif" > omit[bool_expr_eval(_r1)[_a=_1]] > '}' > text_or_empty[boost::phoenix::bind(&expr::set_if, _a, _b, _1, _val)]) >> + -("else" > '}' >> text_or_empty[boost::phoenix::bind(&expr::set_if, _b, _b, _1, _val)]) > + "endif"; +*/ + // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index]. legacy_variable_expansion = (identifier >> &lit(']')) @@ -336,50 +491,58 @@ namespace client ; identifier.name("identifier"); -/* bool_expr = - (expression >> '=' >> '=' >> expression) | - (expression >> '!' >> '=' >> expression) | - (expression >> '<' >> '>' >> expression) + expression(_r1) // [ _val = _1 ] + >> *( (lit('=') > '=' > expression(_r1) ) //[_val += _1]) + | (lit('!') > '=' > expression(_r1) )//[_val -= _1]) + | (lit('<') > '>' > expression(_r1) )//[_val -= _1]) + ) ; + bool_expr.name("bool expression"); + + // Evaluate a boolean expression stored as expr into a boolean value. + // Throw if the bool_expr does not produce a expr of boolean type. + bool_expr_eval = bool_expr(_r1) [ boost::phoenix::bind(&expr::evaluate_boolean, _1, _val) ]; expression = - term //[_val = _1] - >> *( ('+' >> term ) //[_val += _1]) - | ('-' >> term )//[_val -= _1]) + term(_r1) //[_val = _1] + >> *( (lit('+') > term(_r1) ) //[_val += _1]) + | (lit('-') > term(_r1) )//[_val -= _1]) ) ; + expression.name("expression"); term = - factor //[_val = _1] - >> *( ('*' >> factor )//[_val *= _1]) - | ('/' >> factor )//[_val /= _1]) + factor(_r1) //[_val = _1] + >> *( (lit('*') > factor(_r1) )//[_val *= _1]) + | (lit('/') > factor(_r1) )//[_val /= _1]) ) ; + term.name("term"); +#define START_LIT(LIT) omit[raw[lit(LIT)][boost::phoenix::bind(&MyContext::store_start_position, _1, _a)]] factor = int_ //[_val = expr(_1)] | double_ //[_val = expr(_1)] - | '(' >> expression >> ')' // [_val = std::move(_1)] >> ')' - | ('-' >> factor ) //[_val = -_1]) - | ('+' >> factor ) //[_val = std::move(_1)]) + | (START_LIT('(') > expression(_r1) > ')') // [_val = std::move(_1)] >> ')' + | (START_LIT('-') > factor(_r1) ) //[_val = -_1]) + | (START_LIT('+') > factor(_r1) ) //[_val = std::move(_1)]) + | scalar_variable_reference(_r1) ; -*/ + factor.name("factor"); +#undef START_LIT -// text %= lexeme[+(char_ - '<')]; + scalar_variable_reference = + (variable_reference(_r1) >>'[' > expression(_r1) > + omit[raw[lit(']')][boost::phoenix::bind(&MyContext::store_end_position, _1, _a)]]) + [ boost::phoenix::bind(&MyContext::vector_variable_reference, _r1, _1, _2, _a, _val) ] + | variable_reference(_r1) + [ boost::phoenix::bind(&MyContext::scalar_variable_reference, _r1, _1, _val) ]; + scalar_variable_reference.name("scalar variable reference"); -// text_to_eol %= lexeme[*(char_ - eol) >> (eol | eoi)]; - /* - expression_with_braces = lit('{') >> ( - string("if") >> if_else_output [_val = _1] | - string("switch") >> switch_output [_val = _1] | - expression [_val = boost::to_string(_1)] >> '}' - ); - if_else_output = - bool_expr[_r1 = _1] >> '}' >> text_to_eol[_val = _r1 ? _1 : std::string()] >> - *(lit('{') >> "elsif" >> bool_expr[_r1 = !_r1 && _1] >> '}' >> text_to_eol[_val = _r1 ? _1 : std::string()]) >> - -(lit('{') >> "else" >> '}' >> text_to_eol[_val = _r1 ? std::string() : _1]); -*/ + variable_reference = identifier + [ boost::phoenix::bind(&MyContext::resolve_variable, _r1, _1, _val) ]; + variable_reference.name("variable reference"); /* on_error(start, phx::ref(std::cout) @@ -396,20 +559,31 @@ namespace client boost::spirit::qi::rule start; // A free-form text. boost::spirit::qi::rule text; + // A free-form text, possibly empty. + boost::spirit::qi::rule text_or_empty; // Statements enclosed in curely braces {} - boost::spirit::qi::rule macro; + boost::spirit::qi::rule macro; // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index]. boost::spirit::qi::rule legacy_variable_expansion; // Parsed identifier name. - boost::spirit::qi::rule, boost::spirit::ascii::space_type> identifier; + boost::spirit::qi::rule(), boost::spirit::ascii::space_type> identifier; + // Math expression consisting of +- operators over terms. + boost::spirit::qi::rule(const MyContext*), boost::spirit::ascii::space_type> expression; + // Boolean expressions over expressions. + boost::spirit::qi::rule(const MyContext*), boost::spirit::ascii::space_type> bool_expr; + // Evaluate boolean expression into bool. + boost::spirit::qi::rule bool_expr_eval; + // Math expression consisting of */ operators over factors. + boost::spirit::qi::rule(const MyContext*), boost::spirit::ascii::space_type> term; + // Number literals, functions, braced expressions, variable references, variable indexing references. + boost::spirit::qi::rule(const MyContext*), boost::spirit::qi::locals, boost::spirit::ascii::space_type> factor; + // Reference of a scalar variable, or reference to a field of a vector variable. + boost::spirit::qi::rule(const MyContext*), boost::spirit::qi::locals, boost::spirit::ascii::space_type> scalar_variable_reference; + // Rule to translate an identifier to a ConfigOption, or to fail. + boost::spirit::qi::rule(const MyContext*), boost::spirit::ascii::space_type> variable_reference; - boost::spirit::qi::rule expression, term, factor; - boost::spirit::qi::rule text_to_eol; - boost::spirit::qi::rule expression_with_braces; - boost::spirit::qi::rule bool_expr; - - boost::spirit::qi::rule if_else_output; - boost::spirit::qi::rule switch_output; + boost::spirit::qi::rule, boost::spirit::ascii::space_type> if_else_output; + boost::spirit::qi::rule, bool, std::string>, boost::spirit::ascii::space_type> switch_output; }; } From 708f416c845728fbd45e2886305c32ec65af85b5 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 26 Nov 2017 09:59:14 +0100 Subject: [PATCH 30/83] PlaceholderParser extended with {if}/{elsif}{else} blocks and + - * / == != <> numeric expressions. --- t/custom_gcode.t | 93 +++- xs/src/boost/nowide/cenv.hpp | 8 +- xs/src/libslic3r/GCode/WipeTower.hpp | 4 +- xs/src/libslic3r/PlaceholderParser.cpp | 576 +++++++++++++++++-------- 4 files changed, 485 insertions(+), 196 deletions(-) diff --git a/t/custom_gcode.t b/t/custom_gcode.t index a0d96736a..62bd7993c 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 15; +use Test::More tests => 39; use strict; use warnings; @@ -51,7 +51,10 @@ use Slic3r::Test; $parser->set('foo' => 0); is $parser->process('[temperature_[foo]]'), $config->temperature->[0], - "nested config options"; + "nested config options (legacy syntax)"; + is $parser->process('{temperature[foo]}'), + $config->temperature->[0], + "array reference"; } { @@ -90,20 +93,39 @@ use Slic3r::Test; ok $gcode =~ /M104 S205 T1/, 'temperature set correctly for second extruder'; } - $config->set('start_gcode', qq! + my @start_gcode = (qq! ;__temp0:[first_layer_temperature_0]__ ;__temp1:[first_layer_temperature_1]__ ;__temp2:[first_layer_temperature_2]__ + !, qq! +;__temp0:{first_layer_temperature[0]}__ +;__temp1:{first_layer_temperature[1]}__ +;__temp2:{first_layer_temperature[2]}__ + !); + my @syntax_description = (' (legacy syntax)', ' (new syntax)'); + for my $i (0, 1) { + $config->set('start_gcode', $start_gcode[$i]); + { + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + my $gcode = Slic3r::Test::gcode($print); + # we use the [infill_extruder] placeholder to make sure this test doesn't + # catch a false positive caused by the unparsed start G-code option itself + # being embedded in the G-code + ok $gcode =~ /temp0:200/, 'temperature placeholder for first extruder correctly populated' . $syntax_description[$i]; + ok $gcode =~ /temp1:205/, 'temperature placeholder for second extruder correctly populated' . $syntax_description[$i]; + ok $gcode =~ /temp2:200/, 'temperature placeholder for unused extruder populated with first value' . $syntax_description[$i]; + } + } + + $config->set('start_gcode', qq! +;substitution:{if infill_extruder==1}extruder1 + {elsif infill_extruder==2}extruder2 + {else}extruder3{endif} !); { my $print = Slic3r::Test::init_print('20mm_cube', config => $config); my $gcode = Slic3r::Test::gcode($print); - # we use the [infill_extruder] placeholder to make sure this test doesn't - # catch a false positive caused by the unparsed start G-code option itself - # being embedded in the G-code - ok $gcode =~ /temp0:200/, 'temperature placeholder for first extruder correctly populated'; - ok $gcode =~ /temp1:205/, 'temperature placeholder for second extruder correctly populated'; - ok $gcode =~ /temp2:200/, 'temperature placeholder for unused extruder populated with first value'; + ok $gcode =~ /substitution:extruder1/, 'if / else / endif - first block returned'; } } @@ -132,4 +154,57 @@ use Slic3r::Test; 'layer_num grows continously'; # i.e. no duplicates or regressions } +{ + my $config = Slic3r::Config->new; + $config->set('start_gcode', qq! +;substitution:{if infill_extruder==1}if block + {elsif infill_extruder==2}elsif block 1 + {elsif infill_extruder==3}elsif block 2 + {elsif infill_extruder==4}elsif block 3 + {else}endif block{endif} + !); + my @returned = ('', 'if block', 'elsif block 1', 'elsif block 2', 'elsif block 3', 'endif block'); + for my $i (1,2,3,4,5) { + $config->set('infill_extruder', $i); + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + my $gcode = Slic3r::Test::gcode($print); + my $found_other = 0; + for my $j (1,2,3,4,5) { + next if $i == $j; + $found_other = 1 if $gcode =~ /substitution:$returned[$j]/; + } + ok $gcode =~ /substitution:$returned[$i]/, 'if / else / endif - ' . $returned[$i] . ' returned'; + ok !$found_other, 'if / else / endif - only ' . $returned[$i] . ' returned'; + } +} + +{ + my $config = Slic3r::Config->new; + $config->set('start_gcode', + ';substitution:{if infill_extruder==1}{if perimeter_extruder==1}block11{else}block12{endif}' . + '{elsif infill_extruder==2}{if perimeter_extruder==1}block21{else}block22{endif}' . + '{else}{if perimeter_extruder==1}block31{else}block32{endif}{endif}:end'); + for my $i (1,2,3) { + $config->set('infill_extruder', $i); + for my $j (1,2) { + $config->set('perimeter_extruder', $j); + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + my $gcode = Slic3r::Test::gcode($print); + ok $gcode =~ /substitution:block$i$j:end/, "two level if / else / endif - block$i$j returned"; + } + } +} + +{ + my $config = Slic3r::Config->new; + $config->set('start_gcode', + ';substitution:{if notes=="MK2"}MK2{elsif notes=="MK3"}MK3{else}MK1{endif}:end'); + for my $printer_name ("MK2", "MK3", "MK1") { + $config->set('notes', $printer_name); + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + my $gcode = Slic3r::Test::gcode($print); + ok $gcode =~ /substitution:$printer_name:end/, "printer name $printer_name matched"; + } +} + __END__ diff --git a/xs/src/boost/nowide/cenv.hpp b/xs/src/boost/nowide/cenv.hpp index 5f6d084af..a38a24b97 100644 --- a/xs/src/boost/nowide/cenv.hpp +++ b/xs/src/boost/nowide/cenv.hpp @@ -101,18 +101,16 @@ namespace nowide { { char const *key = string; char const *key_end = string; - while(*key_end!='=' && key_end!='\0') - key_end++; - if(*key_end == '\0') + while(*key_end != '=' && *key_end != 0) + ++ key_end; + if(*key_end == 0) return -1; wshort_stackstring wkey; if(!wkey.convert(key,key_end)) return -1; - wstackstring wvalue; if(!wvalue.convert(key_end+1)) return -1; - if(SetEnvironmentVariableW(wkey.c_str(),wvalue.c_str())) return 0; return -1; diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp index c5fa27392..c21c1710b 100644 --- a/xs/src/libslic3r/GCode/WipeTower.hpp +++ b/xs/src/libslic3r/GCode/WipeTower.hpp @@ -21,8 +21,8 @@ public: xy operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; } xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; } xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; } - bool operator==(const xy &rhs) { return x == rhs.x && y == rhs.y; } - bool operator!=(const xy &rhs) { return x != rhs.x || y != rhs.y; } + bool operator==(const xy &rhs) const { return x == rhs.x && y == rhs.y; } + bool operator!=(const xy &rhs) const { return x != rhs.x || y != rhs.y; } float x; float y; }; diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index 5b05897da..c80994d0f 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -21,13 +21,17 @@ #endif #endif +#include + // Spirit v2.5 allows you to suppress automatic generation // of predefined terminals to speed up complation. With // BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are // responsible in creating instances of the terminals that // you need (e.g. see qi::uint_type uint_ below). -//#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS +#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS +#define BOOST_RESULT_OF_USE_DECLTYPE +#define BOOST_SPIRIT_USE_PHOENIX_V3 #include #include #include @@ -38,6 +42,7 @@ #include #include #include +#include #include #include @@ -89,7 +94,7 @@ void PlaceholderParser::apply_config(const DynamicPrintConfig &rhs) const ConfigDef *def = rhs.def(); for (const t_config_option_key &opt_key : rhs.keys()) { const ConfigOptionDef *opt_def = def->get(opt_key); - if (opt_def->multiline || opt_key == "post_process") + if ((opt_def->multiline && boost::ends_with(opt_key, "_gcode")) || opt_key == "post_process") continue; const ConfigOption *opt = rhs.option(opt_key); // Store a copy of the config option. @@ -115,6 +120,10 @@ void PlaceholderParser::apply_env_variables() } } +namespace spirit = boost::spirit; +namespace qi = boost::spirit::qi; +namespace px = boost::phoenix; + namespace client { template @@ -125,18 +134,32 @@ namespace client boost::iterator_range it_range; }; + template + std::ostream& operator<<(std::ostream& os, OptWithPos const& opt) + { + os << std::string(opt.it_range.begin(), opt.it_range.end()); + return os; + } + template struct expr { - expr() : type(TYPE_EMPTY) { data.s = nullptr; } - expr(bool b) : type(TYPE_BOOL) { data.b = b; } - expr(int i) : type(TYPE_INT) { data.i = i; } - expr(double d) : type(TYPE_DOUBLE) { data.d = d; } - expr(const char *s) : type(TYPE_STRING) { data.s = new std::string(s); } - expr(const std::string &s) : type(TYPE_STRING) { data.s = new std::string(s); } - expr(const expr &rhs) : type(rhs.type) { data.s = (rhs.type == TYPE_STRING) ? new std::string(*rhs.data.s) : rhs.data.s; } - expr(expr &&rhs) : type(rhs.type) { data.s = rhs.data.s; rhs.data.s = nullptr; rhs.type = TYPE_EMPTY; } - ~expr() { if (type == TYPE_STRING) delete data.s; data.s = nullptr; } + expr() : type(TYPE_EMPTY) { data.s = nullptr; } + explicit expr(bool b) : type(TYPE_BOOL) { data.b = b; } + explicit expr(bool b, const Iterator &it_begin, const Iterator &it_end) : type(TYPE_BOOL), it_range(it_begin, it_end) { data.b = b; } + explicit expr(int i) : type(TYPE_INT) { data.i = i; } + explicit expr(int i, const Iterator &it_begin, const Iterator &it_end) : type(TYPE_INT), it_range(it_begin, it_end) { data.i = i; } + explicit expr(double d) : type(TYPE_DOUBLE) { data.d = d; } + explicit expr(double d, const Iterator &it_begin, const Iterator &it_end) : type(TYPE_DOUBLE), it_range(it_begin, it_end) { data.d = d; } + explicit expr(const char *s) : type(TYPE_STRING) { data.s = new std::string(s); } + explicit expr(const std::string &s) : type(TYPE_STRING) { data.s = new std::string(s); } + explicit expr(const std::string &s, const Iterator &it_begin, const Iterator &it_end) : + type(TYPE_STRING), it_range(it_begin, it_end) { data.s = new std::string(s); } + expr(const expr &rhs) : type(rhs.type) { data.s = (rhs.type == TYPE_STRING) ? new std::string(*rhs.data.s) : rhs.data.s; } + explicit expr(expr &&rhs) : type(rhs.type) { data.s = rhs.data.s; rhs.data.s = nullptr; rhs.type = TYPE_EMPTY; } + explicit expr(expr &&rhs, const Iterator &it_begin, const Iterator &it_end) : type(rhs.type), it_range(it_begin, it_end) + { data.s = rhs.data.s; rhs.data.s = nullptr; rhs.type = TYPE_EMPTY; } + ~expr() { this->reset(); } expr &operator=(const expr &rhs) { @@ -154,14 +177,29 @@ namespace client return *this; } + void reset() + { + if (this->type == TYPE_STRING) + delete data.s; + data.s = nullptr; + this->type = TYPE_EMPTY; + } + bool& b() { return data.b; } bool b() const { return data.b; } + void set_b(bool v) { this->reset(); this->data.b = v; this->type = TYPE_BOOL; } int& i() { return data.i; } int i() const { return data.i; } + void set_i(int v) { this->reset(); this->data.i = v; this->type = TYPE_INT; } + int as_i() const { return (this->type == TYPE_INT) ? this->i() : int(this->d()); } double& d() { return data.d; } double d() const { return data.d; } + void set_d(double v) { this->reset(); this->data.d = v; this->type = TYPE_DOUBLE; } + double as_d() const { return (this->type == TYPE_DOUBLE) ? this->d() : double(this->i()); } std::string& s() { return *data.s; } const std::string& s() const { return *data.s; } + void set_s(const std::string &s) { this->reset(); this->data.s = new std::string(s); this->type = TYPE_STRING; } + void set_s(std::string &&s) { this->reset(); this->data.s = new std::string(std::move(s)); this->type = TYPE_STRING; } std::string to_string() const { @@ -190,27 +228,125 @@ namespace client TYPE_STRING, }; - int type; + Type type; // Range of input iterators covering this expression. // Used for throwing parse exceptions. boost::iterator_range it_range; - static void store_iterator_range(expr &self, Iterator &it_start, boost::iterator_range &range_end) - { - self.it_range = boost::iterator_range(it_start, range_end.end()); + expr unary_minus(Iterator &start_pos) const + { + switch (this->type) { + case TYPE_INT : + return expr(- this->i(), start_pos, this->it_range.end()); + case TYPE_DOUBLE: + return expr(- this->d(), start_pos, this->it_range.end()); + default: + this->throw_exception("Cannot apply unary minus operator."); + } + assert(false); + // Suppress compiler warnings. + return expr(); } + + expr &operator+=(const expr &rhs) + { + const char *err_msg = "Cannot multiply with non-numeric type."; + this->throw_if_not_numeric(err_msg); + rhs.throw_if_not_numeric(err_msg); + if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) { + double d = this->as_d() + rhs.as_d(); + this->data.d = d; + this->type = TYPE_DOUBLE; + } else + this->data.i += rhs.i(); + this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + return *this; + } + + expr &operator-=(const expr &rhs) + { + const char *err_msg = "Cannot multiply with non-numeric type."; + this->throw_if_not_numeric(err_msg); + rhs.throw_if_not_numeric(err_msg); + if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) { + double d = this->as_d() - rhs.as_d(); + this->data.d = d; + this->type = TYPE_DOUBLE; + } else + this->data.i -= rhs.i(); + this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + return *this; + } + + expr &operator*=(const expr &rhs) + { + const char *err_msg = "Cannot multiply with non-numeric type."; + this->throw_if_not_numeric(err_msg); + rhs.throw_if_not_numeric(err_msg); + if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) { + double d = this->as_d() * rhs.as_d(); + this->data.d = d; + this->type = TYPE_DOUBLE; + } else + this->data.i *= rhs.i(); + this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + return *this; + } + + expr &operator/=(const expr &rhs) + { + this->throw_if_not_numeric("Cannot divide a non-numeric type."); + rhs.throw_if_not_numeric("Cannot divide with a non-numeric type."); + if ((this->type == TYPE_INT) ? (rhs.i() == 0) : (rhs.d() == 0.)) + rhs.throw_exception("Division by zero"); + if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) { + double d = this->as_d() / rhs.as_d(); + this->data.d = d; + this->type = TYPE_DOUBLE; + } else + this->data.i /= rhs.i(); + this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + return *this; + } + static void to_string2(expr &self, std::string &out) { out = self.to_string(); } + static void evaluate_boolean(expr &self, bool &out) { if (self.type != TYPE_BOOL) - boost::throw_exception(boost::spirit::qi::expectation_failure( - self.it_range.begin(), self.it_range.end(), boost::spirit::info("Not a boolean expression"))); + self.throw_exception("Not a boolean expression"); out = self.b(); } + + // Is lhs==rhs? Store the result into lhs. + static void compare_op(expr &lhs, expr &rhs, char op) + { + bool value = false; + if ((lhs.type == TYPE_INT || lhs.type == TYPE_DOUBLE) && + (rhs.type == TYPE_INT || rhs.type == TYPE_DOUBLE)) { + // Both types are numeric. + value = (lhs.type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) ? + (lhs.as_d() == rhs.as_d()) : (lhs.i() == rhs.i()); + } else if (lhs.type == TYPE_BOOL && rhs.type == TYPE_BOOL) { + // Both type are bool. + value = lhs.b() == rhs.b(); + } else if (lhs.type == TYPE_STRING || rhs.type == TYPE_STRING) { + // One type is string, the other could be converted to string. + value = lhs.to_string() == rhs.to_string(); + } else { + boost::throw_exception(qi::expectation_failure( + lhs.it_range.begin(), rhs.it_range.end(), spirit::info("Cannot compare the types."))); + } + lhs.type = TYPE_BOOL; + lhs.data.b = (op == '=') ? value : !value; + } + static void equal(expr &lhs, expr &rhs) { compare_op(lhs, rhs, '='); } + static void not_equal(expr &lhs, expr &rhs) { compare_op(lhs, rhs, '!'); } + static void set_if(bool &cond, bool ¬_yet_consumed, std::string &str_in, std::string &str_out) { if (cond && not_yet_consumed) { @@ -218,8 +354,36 @@ namespace client not_yet_consumed = false; } } + + void throw_exception(const char *message) const + { + boost::throw_exception(qi::expectation_failure( + this->it_range.begin(), this->it_range.end(), spirit::info(message))); + } + + void throw_if_not_numeric(const char *message) const + { + if (this->type != TYPE_INT && this->type != TYPE_DOUBLE) + this->throw_exception(message); + } }; + template + std::ostream& operator<<(std::ostream &os, const expr &expression) + { + typedef expr Expr; + os << std::string(expression.it_range.begin(), expression.it_range.end()) << ' - '; + switch (expression.type) { + case Expr::TYPE_EMPTY: os << "empty"; break; + case Expr::TYPE_BOOL: os << "bool (" << expression.b() << ")"; break; + case Expr::TYPE_INT: os << "int (" << expression.i() << ")"; break; + case Expr::TYPE_DOUBLE: os << "double (" << expression.d() << ")"; break; + case Expr::TYPE_STRING: os << "string (" << expression.s() << ")"; break; + default: os << "unknown"; + }; + return os; + } + struct MyContext { const PlaceholderParser *pp = nullptr; const DynamicConfig *config_override = nullptr; @@ -251,26 +415,26 @@ namespace client opt = ctx->resolve_symbol(opt_key_str.substr(0, idx)); if (opt != nullptr) { if (! opt->is_vector()) - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt_key.begin(), opt_key.end(), boost::spirit::info("Trying to index a scalar variable"))); + boost::throw_exception(qi::expectation_failure( + opt_key.begin(), opt_key.end(), spirit::info("Trying to index a scalar variable"))); char *endptr = nullptr; idx = strtol(opt_key_str.c_str() + idx + 1, &endptr, 10); if (endptr == nullptr || *endptr != 0) - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt_key.begin() + idx + 1, opt_key.end(), boost::spirit::info("Invalid vector index"))); + boost::throw_exception(qi::expectation_failure( + opt_key.begin() + idx + 1, opt_key.end(), spirit::info("Invalid vector index"))); } } } if (opt == nullptr) - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt_key.begin(), opt_key.end(), boost::spirit::info("Variable does not exist"))); + boost::throw_exception(qi::expectation_failure( + opt_key.begin(), opt_key.end(), spirit::info("Variable does not exist"))); if (opt->is_scalar()) output = opt->serialize(); else { const ConfigOptionVectorBase *vec = static_cast(opt); if (vec->empty()) - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt_key.begin(), opt_key.end(), boost::spirit::info("Indexing an empty vector variable"))); + boost::throw_exception(qi::expectation_failure( + opt_key.begin(), opt_key.end(), spirit::info("Indexing an empty vector variable"))); output = vec->vserialize()[(idx >= vec->size()) ? 0 : idx]; } } @@ -291,23 +455,23 @@ namespace client opt = ctx->resolve_symbol(opt_key_str); } if (! opt->is_vector()) - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt_key.begin(), opt_key.end(), boost::spirit::info("Trying to index a scalar variable"))); + boost::throw_exception(qi::expectation_failure( + opt_key.begin(), opt_key.end(), spirit::info("Trying to index a scalar variable"))); const ConfigOptionVectorBase *vec = static_cast(opt); if (vec->empty()) - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt_key.begin(), opt_key.end(), boost::spirit::info("Indexing an empty vector variable"))); + boost::throw_exception(qi::expectation_failure( + opt_key.begin(), opt_key.end(), spirit::info("Indexing an empty vector variable"))); const ConfigOption *opt_index = ctx->resolve_symbol(std::string(opt_vector_index.begin(), opt_vector_index.end())); if (opt_index == nullptr) - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt_key.begin(), opt_key.end(), boost::spirit::info("Variable does not exist"))); + boost::throw_exception(qi::expectation_failure( + opt_key.begin(), opt_key.end(), spirit::info("Variable does not exist"))); if (opt_index->type() != coInt) - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt_key.begin(), opt_key.end(), boost::spirit::info("Indexing variable has to be integer"))); + boost::throw_exception(qi::expectation_failure( + opt_key.begin(), opt_key.end(), spirit::info("Indexing variable has to be integer"))); int idx = opt_index->getInt(); if (idx < 0) - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt_key.begin(), opt_key.end(), boost::spirit::info("Negative vector index"))); + boost::throw_exception(qi::expectation_failure( + opt_key.begin(), opt_key.end(), spirit::info("Negative vector index"))); output = vec->vserialize()[(idx >= (int)vec->size()) ? 0 : idx]; } @@ -319,8 +483,8 @@ namespace client { const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end())); if (opt == nullptr) - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt_key.begin(), opt_key.end(), boost::spirit::info("Not a variable name"))); + boost::throw_exception(qi::expectation_failure( + opt_key.begin(), opt_key.end(), spirit::info("Not a variable name"))); output.opt = opt; output.it_range = opt_key; } @@ -332,264 +496,316 @@ namespace client expr &output) { if (opt.opt->is_vector()) - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt.it_range.begin(), opt.it_range.end(), boost::spirit::info("Referencing a scalar variable in a vector context"))); + boost::throw_exception(qi::expectation_failure( + opt.it_range.begin(), opt.it_range.end(), spirit::info("Referencing a scalar variable in a vector context"))); switch (opt.opt->type()) { - case coFloat: output.d() = opt.opt->getFloat(); break; - case coInt: output.i() = opt.opt->getInt(); break; - case coString: output.s() = static_cast(opt.opt)->value; break; - case coPercent: output.d() = opt.opt->getFloat(); break; - case coPoint: output.s() = opt.opt->serialize(); break; - case coBool: output.b() = opt.opt->getBool(); break; + case coFloat: output.set_d(opt.opt->getFloat()); break; + case coInt: output.set_i(opt.opt->getInt()); break; + case coString: output.set_s(static_cast(opt.opt)->value); break; + case coPercent: output.set_d(opt.opt->getFloat()); break; + case coPoint: output.set_s(opt.opt->serialize()); break; + case coBool: output.set_b(opt.opt->getBool()); break; case coFloatOrPercent: - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt.it_range.begin(), opt.it_range.end(), boost::spirit::info("FloatOrPercent variables are not supported"))); + boost::throw_exception(qi::expectation_failure( + opt.it_range.begin(), opt.it_range.end(), spirit::info("FloatOrPercent variables are not supported"))); default: - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt.it_range.begin(), opt.it_range.end(), boost::spirit::info("Unknown scalar variable type"))); + boost::throw_exception(qi::expectation_failure( + opt.it_range.begin(), opt.it_range.end(), spirit::info("Unknown scalar variable type"))); } + output.it_range = opt.it_range; } template static void vector_variable_reference( const MyContext *ctx, OptWithPos &opt, - expr &expr_index, + int &index, Iterator it_end, expr &output) { if (opt.opt->is_scalar()) - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt.it_range.begin(), opt.it_range.end(), boost::spirit::info("Referencing a vector variable in a scalar context"))); + boost::throw_exception(qi::expectation_failure( + opt.it_range.begin(), opt.it_range.end(), spirit::info("Referencing a vector variable in a scalar context"))); const ConfigOptionVectorBase *vec = static_cast(opt.opt); if (vec->empty()) - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt.it_range.begin(), opt.it_range.end(), boost::spirit::info("Indexing an empty vector variable"))); - if (expr_index.type != expr::TYPE_INT) - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt.it_range.begin(), opt.it_range.end(), boost::spirit::info("Non-integer index is not allowed to index a vector"))); - size_t idx = (expr_index.i() < 0) ? 0 : (expr_index.i() >= int(vec->size())) ? 0 : expr_index.i(); + boost::throw_exception(qi::expectation_failure( + opt.it_range.begin(), opt.it_range.end(), spirit::info("Indexing an empty vector variable"))); + size_t idx = (index < 0) ? 0 : (index >= int(vec->size())) ? 0 : size_t(index); switch (opt.opt->type()) { - case coFloats: output.d() = static_cast(opt.opt)->values[idx]; break; - case coInts: output.i() = static_cast(opt.opt)->values[idx]; break; - case coStrings: output.s() = static_cast(opt.opt)->values[idx]; break; - case coPercents: output.d() = static_cast(opt.opt)->values[idx]; break; - case coPoints: output.s() = static_cast(opt.opt)->values[idx].dump_perl(); break; - case coBools: output.b() = static_cast(opt.opt)->values[idx] != 0; break; + case coFloats: output.set_d(static_cast(opt.opt)->values[idx]); break; + case coInts: output.set_i(static_cast(opt.opt)->values[idx]); break; + case coStrings: output.set_s(static_cast(opt.opt)->values[idx]); break; + case coPercents: output.set_d(static_cast(opt.opt)->values[idx]); break; + case coPoints: output.set_s(static_cast(opt.opt)->values[idx].dump_perl()); break; + case coBools: output.set_b(static_cast(opt.opt)->values[idx] != 0); break; default: - boost::throw_exception(boost::spirit::qi::expectation_failure( - opt.it_range.begin(), opt.it_range.end(), boost::spirit::info("Unknown vector variable type"))); + boost::throw_exception(qi::expectation_failure( + opt.it_range.begin(), opt.it_range.end(), spirit::info("Unknown vector variable type"))); } output.it_range = boost::iterator_range(opt.it_range.begin(), it_end); } + // Verify that the expression returns an integer, which may be used + // to address a vector. template - static void store_start_position( - boost::iterator_range &range, - Iterator &output) + static void evaluate_index(expr &expr_index, int &output) { - output = range.begin(); - } - - template - static void store_end_position( - boost::iterator_range &range, - Iterator &output) - { - output = range.end(); + if (expr_index.type != expr::TYPE_INT) + expr_index.throw_exception("Non-integer index is not allowed to address a vector variable."); + output = expr_index.i(); } }; + // For debugging the boost::spirit parsers. Print out the string enclosed in it_range. + template + std::ostream& operator<<(std::ostream& os, const boost::iterator_range &it_range) + { + os << std::string(it_range.begin(), it_range.end()); + return os; + } + /////////////////////////////////////////////////////////////////////////// // Our calculator grammar /////////////////////////////////////////////////////////////////////////// template - struct calculator : boost::spirit::qi::grammar + struct calculator : qi::grammar { calculator() : calculator::base_type(start) { - using boost::spirit::qi::alpha; - using boost::spirit::qi::alnum; - using boost::spirit::qi::eol; - using boost::spirit::qi::eoi; - using boost::spirit::qi::eps; - using boost::spirit::qi::omit; - using boost::spirit::qi::raw; - using boost::spirit::qi::lit; - using boost::spirit::qi::lexeme; - using boost::spirit::qi::on_error; - using boost::spirit::qi::fail; - using boost::spirit::ascii::char_; - using boost::spirit::int_; - using boost::spirit::double_; - using boost::spirit::ascii::string; - using namespace boost::spirit::qi::labels; + qi::alpha_type alpha; + qi::alnum_type alnum; + qi::eol_type eol; + qi::eoi_type eoi; + qi::eps_type eps; + qi::omit_type omit; + qi::raw_type raw; + qi::lit_type lit; + qi::lexeme_type lexeme; + qi::skip_type skip; + qi::no_skip_type no_skip; + qi::uint_type uint_; + qi::real_parser> strict_double; + spirit::ascii::char_type char_; + spirit::bool_type bool_; + spirit::int_type int_; + spirit::double_type double_; + spirit::ascii::string_type string; + spirit::repository::qi::iter_pos_type iter_pos; + using namespace qi::labels; - using boost::phoenix::construct; - using boost::phoenix::val; - using boost::phoenix::begin; - - boost::spirit::qi::_val_type _val; - boost::spirit::qi::_1_type _1; - boost::spirit::qi::_2_type _2; - boost::spirit::qi::_r1_type _a1; - boost::spirit::qi::_r1_type _r1; - boost::spirit::qi::uint_type uint_; + qi::_val_type _val; + qi::_1_type _1; + qi::_2_type _2; + qi::_a_type _a; + qi::_b_type _b; + qi::_r1_type _r1; // Starting symbol of the grammer. // The leading eps is required by the "expectation point" operator ">". // Without it, some of the errors would not trigger the error handler. - start = eps > *(text [_val+=_1] - || ((lit('{') > macro(_r1) [_val+=_1] > '}') - | (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']'))); + start = eps > text_block(_r1); start.name("start"); + text_block = *( + text [_val+=_1] + // Allow back tracking after '{' in case of a text_block embedded inside a condition. + // In that case the inner-most {else} wins and the {if}/{elsif}/{else} shall be paired. + // {elsif}/{else} without an {if} will be allowed to back track from the embedded text_block. + | lit('{') >> macro(_r1) [_val+=_1] > '}' + | lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']' + ); + text_block.name("text_block"); + // Free-form text up to a first brace, including spaces and newlines. // The free-form text will be inserted into the processed text without a modification. - text = raw[+(char_ - '[' - '{')]; + text = raw[no_skip[+(char_ - '[' - '{')]]; text.name("text"); - text_or_empty = raw[*(char_ - '[' - '{')]; - text_or_empty.name("text"); // New style of macro expansion. // The macro expansion may contain numeric or string expressions, ifs and cases. macro = string("if") > if_else_output(_r1) [_val = _1] | string("switch") > switch_output(_r1) [_val = _1] - | expression(_r1) [ boost::phoenix::bind(&expr::to_string2, _1, _val) ]; + | expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]; macro.name("macro"); // An if expression enclosed in {} (the outmost {} are already parsed by the caller). if_else_output = eps[_b=true] > - omit[bool_expr_eval(_r1)[_a=_1]] > '}' > text_or_empty[boost::phoenix::bind(&expr::set_if, _a, _b, _1, _val)] > '{' > - *("elsif" > omit[bool_expr_eval(_r1)[_a=_1]] > '}' > text_or_empty[boost::phoenix::bind(&expr::set_if, _a, _b, _1, _val)]) >> - -("else" > '}' >> text_or_empty[boost::phoenix::bind(&expr::set_if, _b, _b, _1, _val)]) > + bool_expr_eval(_r1)[_a=_1] > '}' > + text_block(_r1)[px::bind(&expr::set_if, _a, _b, _1, _val)] > '{' > + *("elsif" > bool_expr_eval(_r1)[_a=_1] > '}' > + text_block(_r1)[px::bind(&expr::set_if, _a, _b, _1, _val)] > '{') > + -("else" > lit('}') > + text_block(_r1)[px::bind(&expr::set_if, _b, _b, _1, _val)] > '{') > "endif"; + if_else_output.name("if_else_output"); // A switch expression enclosed in {} (the outmost {} are already parsed by the caller). /* switch_output = eps[_b=true] > - omit[expr(_r1)[_a=_1]] > '}' > text_or_empty[boost::phoenix::bind(&expr::set_if_equal, _a, _b, _1, _val)] > '{' > - *("elsif" > omit[bool_expr_eval(_r1)[_a=_1]] > '}' > text_or_empty[boost::phoenix::bind(&expr::set_if, _a, _b, _1, _val)]) >> - -("else" > '}' >> text_or_empty[boost::phoenix::bind(&expr::set_if, _b, _b, _1, _val)]) > + omit[expr(_r1)[_a=_1]] > '}' > text_block(_r1)[px::bind(&expr::set_if_equal, _a, _b, _1, _val)] > '{' > + *("elsif" > omit[bool_expr_eval(_r1)[_a=_1]] > '}' > text_block(_r1)[px::bind(&expr::set_if, _a, _b, _1, _val)]) >> + -("else" > '}' >> text_block(_r1)[px::bind(&expr::set_if, _b, _b, _1, _val)]) > "endif"; */ // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index]. legacy_variable_expansion = (identifier >> &lit(']')) - [ boost::phoenix::bind(&MyContext::legacy_variable_expansion, _r1, _1, _val) ] + [ px::bind(&MyContext::legacy_variable_expansion, _r1, _1, _val) ] | (identifier > lit('[') > identifier > ']') - [ boost::phoenix::bind(&MyContext::legacy_variable_expansion2, _r1, _1, _2, _val) ] + [ px::bind(&MyContext::legacy_variable_expansion2, _r1, _1, _2, _val) ] ; legacy_variable_expansion.name("legacy_variable_expansion"); identifier = -// !expr.keywords >> - raw[lexeme[(alpha | '_') >> *(alnum | '_')]] - ; + ! keywords >> + raw[lexeme[(alpha | '_') >> *(alnum | '_')]]; identifier.name("identifier"); bool_expr = - expression(_r1) // [ _val = _1 ] - >> *( (lit('=') > '=' > expression(_r1) ) //[_val += _1]) - | (lit('!') > '=' > expression(_r1) )//[_val -= _1]) - | (lit('<') > '>' > expression(_r1) )//[_val -= _1]) - ) - ; + expression(_r1) [_val = _1] + >> *( ("==" > expression(_r1) ) [px::bind(&expr::equal, _val, _1)] + | ("!=" > expression(_r1) ) [px::bind(&expr::not_equal, _val, _1)] + | ("<>" > expression(_r1) ) [px::bind(&expr::not_equal, _val, _1)] + ); bool_expr.name("bool expression"); // Evaluate a boolean expression stored as expr into a boolean value. // Throw if the bool_expr does not produce a expr of boolean type. - bool_expr_eval = bool_expr(_r1) [ boost::phoenix::bind(&expr::evaluate_boolean, _1, _val) ]; + bool_expr_eval = bool_expr(_r1) [ px::bind(&expr::evaluate_boolean, _1, _val) ]; + bool_expr_eval.name("bool_expr_eval"); expression = - term(_r1) //[_val = _1] - >> *( (lit('+') > term(_r1) ) //[_val += _1]) - | (lit('-') > term(_r1) )//[_val -= _1]) - ) - ; + term(_r1) [_val = _1] + >> *( (lit('+') > term(_r1) ) [_val += _1] + | (lit('-') > term(_r1) ) [_val -= _1] + ); expression.name("expression"); term = - factor(_r1) //[_val = _1] - >> *( (lit('*') > factor(_r1) )//[_val *= _1]) - | (lit('/') > factor(_r1) )//[_val /= _1]) - ) - ; + factor(_r1) [_val = _1] + >> *( (lit('*') > factor(_r1) ) [_val *= _1] + | (lit('/') > factor(_r1) ) [_val /= _1] + ); term.name("term"); -#define START_LIT(LIT) omit[raw[lit(LIT)][boost::phoenix::bind(&MyContext::store_start_position, _1, _a)]] - factor = - int_ //[_val = expr(_1)] - | double_ //[_val = expr(_1)] - | (START_LIT('(') > expression(_r1) > ')') // [_val = std::move(_1)] >> ')' - | (START_LIT('-') > factor(_r1) ) //[_val = -_1]) - | (START_LIT('+') > factor(_r1) ) //[_val = std::move(_1)]) - | scalar_variable_reference(_r1) - ; + struct FactorActions { + static void set_start_pos(Iterator &start_pos, expr &out) + { out.it_range = boost::iterator_range(start_pos, start_pos); } + static void int_(int &value, Iterator &end_pos, expr &out) + { out = expr(value, out.it_range.begin(), end_pos); } + static void double_(double &value, Iterator &end_pos, expr &out) + { out = expr(value, out.it_range.begin(), end_pos); } + static void bool_(bool &value, Iterator &end_pos, expr &out) + { out = expr(value, out.it_range.begin(), end_pos); } + static void string_(boost::iterator_range &it_range, expr &out) + { out = expr(std::string(it_range.begin() + 1, it_range.end() - 1), it_range.begin(), it_range.end()); } + static void expr_(expr &value, Iterator &end_pos, expr &out) + { out = expr(std::move(value), out.it_range.begin(), end_pos); } + static void minus_(expr &value, expr &out) + { out = value.unary_minus(out.it_range.begin()); } + }; + factor = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( + scalar_variable_reference(_r1) [ _val = _1 ] + | (lit('(') > expression(_r1) > ')' > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] + | (lit('-') > factor(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ] + | (lit('+') > factor(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] + | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] + | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] + | (bool_ > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] + | raw[lexeme['"' > *(char_ - char_('\\') - char_('"') | '\\' > char_) > '"']] + [ px::bind(&FactorActions::string_, _1, _val) ] + ); factor.name("factor"); -#undef START_LIT scalar_variable_reference = - (variable_reference(_r1) >>'[' > expression(_r1) > - omit[raw[lit(']')][boost::phoenix::bind(&MyContext::store_end_position, _1, _a)]]) - [ boost::phoenix::bind(&MyContext::vector_variable_reference, _r1, _1, _2, _a, _val) ] - | variable_reference(_r1) - [ boost::phoenix::bind(&MyContext::scalar_variable_reference, _r1, _1, _val) ]; + variable_reference(_r1)[_a=_1] >> + ( + '[' > expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] > ']' > + iter_pos[px::bind(&MyContext::vector_variable_reference, _r1, _a, _b, _1, _val)] + | eps[px::bind(&MyContext::scalar_variable_reference, _r1, _a, _val)] + ); scalar_variable_reference.name("scalar variable reference"); variable_reference = identifier - [ boost::phoenix::bind(&MyContext::resolve_variable, _r1, _1, _val) ]; + [ px::bind(&MyContext::resolve_variable, _r1, _1, _val) ]; variable_reference.name("variable reference"); /* - on_error(start, + qi::on_error(start, phx::ref(std::cout) << "Error! Expecting " - << boost::spirit::qi::_4 + << qi::_4 << " here: '" - << construct(boost::spirit::qi::_3, boost::spirit::qi::_2) + << px::construct(qi::_3, qi::_2) << "'\n" ); */ + + keywords.add + ("if") + //("inf") + ("else") + ("elsif") + ("endif"); + + if (0) { + debug(start); + debug(text); + debug(text_block); + debug(macro); + debug(if_else_output); + debug(switch_output); + debug(legacy_variable_expansion); + debug(identifier); + debug(bool_expr); + debug(bool_expr_eval); + debug(expression); + debug(term); + debug(factor); + debug(scalar_variable_reference); + debug(variable_reference); + } } // The start of the grammar. - boost::spirit::qi::rule start; + qi::rule start; // A free-form text. - boost::spirit::qi::rule text; - // A free-form text, possibly empty. - boost::spirit::qi::rule text_or_empty; + qi::rule text; + // A free-form text, possibly empty, possibly containing macro expansions. + qi::rule text_block; // Statements enclosed in curely braces {} - boost::spirit::qi::rule macro; + qi::rule macro; // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index]. - boost::spirit::qi::rule legacy_variable_expansion; + qi::rule legacy_variable_expansion; // Parsed identifier name. - boost::spirit::qi::rule(), boost::spirit::ascii::space_type> identifier; + qi::rule(), spirit::ascii::space_type> identifier; // Math expression consisting of +- operators over terms. - boost::spirit::qi::rule(const MyContext*), boost::spirit::ascii::space_type> expression; + qi::rule(const MyContext*), spirit::ascii::space_type> expression; // Boolean expressions over expressions. - boost::spirit::qi::rule(const MyContext*), boost::spirit::ascii::space_type> bool_expr; + qi::rule(const MyContext*), spirit::ascii::space_type> bool_expr; // Evaluate boolean expression into bool. - boost::spirit::qi::rule bool_expr_eval; + qi::rule bool_expr_eval; // Math expression consisting of */ operators over factors. - boost::spirit::qi::rule(const MyContext*), boost::spirit::ascii::space_type> term; + qi::rule(const MyContext*), spirit::ascii::space_type> term; // Number literals, functions, braced expressions, variable references, variable indexing references. - boost::spirit::qi::rule(const MyContext*), boost::spirit::qi::locals, boost::spirit::ascii::space_type> factor; + qi::rule(const MyContext*), spirit::ascii::space_type> factor; // Reference of a scalar variable, or reference to a field of a vector variable. - boost::spirit::qi::rule(const MyContext*), boost::spirit::qi::locals, boost::spirit::ascii::space_type> scalar_variable_reference; + qi::rule(const MyContext*), qi::locals, int>, spirit::ascii::space_type> scalar_variable_reference; // Rule to translate an identifier to a ConfigOption, or to fail. - boost::spirit::qi::rule(const MyContext*), boost::spirit::ascii::space_type> variable_reference; + qi::rule(const MyContext*), spirit::ascii::space_type> variable_reference; - boost::spirit::qi::rule, boost::spirit::ascii::space_type> if_else_output; - boost::spirit::qi::rule, bool, std::string>, boost::spirit::ascii::space_type> switch_output; + qi::rule, spirit::ascii::space_type> if_else_output; + qi::rule, bool, std::string>, spirit::ascii::space_type> switch_output; + + qi::symbols keywords; }; } struct printer { - typedef boost::spirit::utf8_string string; + typedef spirit::utf8_string string; void element(string const& tag, string const& value, int depth) const { @@ -602,9 +818,9 @@ struct printer } }; -void print_info(boost::spirit::info const& what) +void print_info(spirit::info const& what) { - using boost::spirit::basic_info_walker; + using spirit::basic_info_walker; printer pr; basic_info_walker walker(pr, what.tag, 0); boost::apply_visitor(walker, what.value); @@ -615,7 +831,7 @@ std::string PlaceholderParser::process(const std::string &templ, unsigned int cu typedef std::string::const_iterator iterator_type; typedef client::calculator calculator; - boost::spirit::ascii::space_type space; // Our skipper + spirit::ascii::space_type space; // Our skipper calculator calc; // Our grammar std::string::const_iterator iter = templ.begin(); @@ -628,7 +844,7 @@ std::string PlaceholderParser::process(const std::string &templ, unsigned int cu context.pp = this; context.config_override = config_override; r = phrase_parse(iter, end, calc(&context), space, result); - } catch (boost::spirit::qi::expectation_failure const& x) { + } catch (qi::expectation_failure const& x) { std::cout << "expected: "; print_info(x.what_); std::cout << "got: \"" << std::string(x.first, x.last) << '"' << std::endl; } From 2312fa845ef987a0219fe76e941a3a24e8576cbc Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 26 Nov 2017 10:54:54 +0100 Subject: [PATCH 31/83] Fixed compilation on GCC, changed to handle keywords correctly. --- xs/src/libslic3r/PlaceholderParser.cpp | 57 +++++++++++++++----------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index c80994d0f..7b5469dc3 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -28,7 +28,7 @@ // BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are // responsible in creating instances of the terminals that // you need (e.g. see qi::uint_type uint_ below). -#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS +//#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS #define BOOST_RESULT_OF_USE_DECLTYPE #define BOOST_SPIRIT_USE_PHOENIX_V3 @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -234,7 +235,7 @@ namespace client // Used for throwing parse exceptions. boost::iterator_range it_range; - expr unary_minus(Iterator &start_pos) const + expr unary_minus(const Iterator start_pos) const { switch (this->type) { case TYPE_INT : @@ -564,6 +565,13 @@ namespace client return os; } + // Disable parsing int numbers (without decimals) and Inf/NaN symbols by the double parser. + struct strict_real_policies_without_nan_inf : public qi::strict_real_policies + { + template static bool parse_nan(It&, It const&, Attr&) { return false; } + template static bool parse_inf(It&, It const&, Attr&) { return false; } + }; + /////////////////////////////////////////////////////////////////////////// // Our calculator grammar /////////////////////////////////////////////////////////////////////////// @@ -572,6 +580,7 @@ namespace client { calculator() : calculator::base_type(start) { + using namespace qi::labels; qi::alpha_type alpha; qi::alnum_type alnum; qi::eol_type eol; @@ -584,14 +593,14 @@ namespace client qi::skip_type skip; qi::no_skip_type no_skip; qi::uint_type uint_; - qi::real_parser> strict_double; + qi::real_parser strict_double; spirit::ascii::char_type char_; spirit::bool_type bool_; spirit::int_type int_; spirit::double_type double_; spirit::ascii::string_type string; spirit::repository::qi::iter_pos_type iter_pos; - using namespace qi::labels; + auto kw = spirit::repository::qi::distinct(qi::copy(alnum | '_')); qi::_val_type _val; qi::_1_type _1; @@ -611,8 +620,8 @@ namespace client // Allow back tracking after '{' in case of a text_block embedded inside a condition. // In that case the inner-most {else} wins and the {if}/{elsif}/{else} shall be paired. // {elsif}/{else} without an {if} will be allowed to back track from the embedded text_block. - | lit('{') >> macro(_r1) [_val+=_1] > '}' - | lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']' + | (lit('{') >> macro(_r1) [_val+=_1] > '}') + | (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']') ); text_block.name("text_block"); @@ -624,8 +633,8 @@ namespace client // New style of macro expansion. // The macro expansion may contain numeric or string expressions, ifs and cases. macro = - string("if") > if_else_output(_r1) [_val = _1] - | string("switch") > switch_output(_r1) [_val = _1] + (kw["if"] > if_else_output(_r1) [_val = _1]) + | (kw["switch"] > switch_output(_r1) [_val = _1]) | expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]; macro.name("macro"); @@ -634,11 +643,11 @@ namespace client eps[_b=true] > bool_expr_eval(_r1)[_a=_1] > '}' > text_block(_r1)[px::bind(&expr::set_if, _a, _b, _1, _val)] > '{' > - *("elsif" > bool_expr_eval(_r1)[_a=_1] > '}' > + *(kw["elsif"] > bool_expr_eval(_r1)[_a=_1] > '}' > text_block(_r1)[px::bind(&expr::set_if, _a, _b, _1, _val)] > '{') > - -("else" > lit('}') > + -(kw["else"] > lit('}') > text_block(_r1)[px::bind(&expr::set_if, _b, _b, _1, _val)] > '{') > - "endif"; + kw["endif"]; if_else_output.name("if_else_output"); // A switch expression enclosed in {} (the outmost {} are already parsed by the caller). /* @@ -660,7 +669,7 @@ namespace client legacy_variable_expansion.name("legacy_variable_expansion"); identifier = - ! keywords >> + ! kw[keywords] >> raw[lexeme[(alpha | '_') >> *(alnum | '_')]]; identifier.name("identifier"); @@ -709,22 +718,22 @@ namespace client }; factor = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( scalar_variable_reference(_r1) [ _val = _1 ] - | (lit('(') > expression(_r1) > ')' > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] - | (lit('-') > factor(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ] - | (lit('+') > factor(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] - | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] - | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] - | (bool_ > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] - | raw[lexeme['"' > *(char_ - char_('\\') - char_('"') | '\\' > char_) > '"']] - [ px::bind(&FactorActions::string_, _1, _val) ] + | (lit('(') > expression(_r1) > ')' > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] + | (lit('-') > factor(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ] + | (lit('+') > factor(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] + | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] + | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] + | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] + | raw[lexeme['"' > *(char_ - char_('\\') - char_('"') | ('\\' > char_)) > '"']] + [ px::bind(&FactorActions::string_, _1, _val) ] ); factor.name("factor"); scalar_variable_reference = variable_reference(_r1)[_a=_1] >> ( - '[' > expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] > ']' > - iter_pos[px::bind(&MyContext::vector_variable_reference, _r1, _a, _b, _1, _val)] + ('[' > expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] > ']' > + iter_pos[px::bind(&MyContext::vector_variable_reference, _r1, _a, _b, _1, _val)]) | eps[px::bind(&MyContext::scalar_variable_reference, _r1, _a, _val)] ); scalar_variable_reference.name("scalar variable reference"); @@ -748,7 +757,9 @@ namespace client //("inf") ("else") ("elsif") - ("endif"); + ("endif") + ("false") + ("true"); if (0) { debug(start); From 5c3ba79c6f9ded60930670d2b03d42d3840c731e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 26 Nov 2017 11:16:28 +0100 Subject: [PATCH 32/83] PlaceholderParser - added an unary not operator. --- xs/src/libslic3r/PlaceholderParser.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index 7b5469dc3..39d775e89 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -250,6 +250,19 @@ namespace client return expr(); } + expr unary_not(const Iterator start_pos) const + { + switch (this->type) { + case TYPE_BOOL : + return expr(! this->b(), start_pos, this->it_range.end()); + default: + this->throw_exception("Cannot apply a not operator."); + } + assert(false); + // Suppress compiler warnings. + return expr(); + } + expr &operator+=(const expr &rhs) { const char *err_msg = "Cannot multiply with non-numeric type."; @@ -715,12 +728,15 @@ namespace client { out = expr(std::move(value), out.it_range.begin(), end_pos); } static void minus_(expr &value, expr &out) { out = value.unary_minus(out.it_range.begin()); } + static void not_(expr &value, expr &out) + { out = value.unary_not(out.it_range.begin()); } }; factor = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( scalar_variable_reference(_r1) [ _val = _1 ] | (lit('(') > expression(_r1) > ')' > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] | (lit('-') > factor(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ] | (lit('+') > factor(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] + | ((kw["not"] | '!') > factor(_r1) > iter_pos) [ px::bind(&FactorActions::not_, _1, _val) ] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] @@ -753,12 +769,15 @@ namespace client */ keywords.add + ("and") ("if") //("inf") ("else") ("elsif") ("endif") ("false") + ("not") + ("or") ("true"); if (0) { From 571d654e67896d10ea760ca62c092ae239bc3262 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 26 Nov 2017 11:52:44 +0100 Subject: [PATCH 33/83] Placeholder parser - added a comment with a reference to a C grammar. --- xs/src/libslic3r/PlaceholderParser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index 39d775e89..ace63a4b5 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -588,6 +588,7 @@ namespace client /////////////////////////////////////////////////////////////////////////// // Our calculator grammar /////////////////////////////////////////////////////////////////////////// + // Inspired by the C grammar https://www.lysator.liu.se/c/ANSI-C-grammar-y.html template struct calculator : qi::grammar { From b54a15faa2304f9d4326379e3a13a368d3464418 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 26 Nov 2017 20:43:31 +0100 Subject: [PATCH 34/83] Fix of the new PlaceholderParser: Maintain whitespaces and new lines. --- t/custom_gcode.t | 5 ++++- xs/src/libslic3r/PlaceholderParser.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/t/custom_gcode.t b/t/custom_gcode.t index 62bd7993c..2cb9a44b8 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 39; +use Test::More tests => 40; use strict; use warnings; @@ -55,6 +55,9 @@ use Slic3r::Test; is $parser->process('{temperature[foo]}'), $config->temperature->[0], "array reference"; + is $parser->process("test [ temperature_ [foo] ] \n hu"), + "test " . $config->temperature->[0] . " \n hu", + "whitespaces and newlines are maintained"; } { diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index ace63a4b5..655913d90 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -641,7 +641,7 @@ namespace client // Free-form text up to a first brace, including spaces and newlines. // The free-form text will be inserted into the processed text without a modification. - text = raw[no_skip[+(char_ - '[' - '{')]]; + text = no_skip[raw[+(char_ - '[' - '{')]]; text.name("text"); // New style of macro expansion. From bb2b180ecc9d3e3dad063c87e9054ee0829ca9a8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 26 Nov 2017 21:23:18 +0100 Subject: [PATCH 35/83] Fixed G-code export of custom G-code sections to not add a newline if the custom G-code already ends with a newline. --- xs/src/libslic3r/GCode.cpp | 5 ++++- xs/src/libslic3r/PlaceholderParser.cpp | 5 ----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 12444d1a5..3c406389e 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -264,11 +264,14 @@ inline void write(FILE *file, const std::string &what) fwrite(what.data(), 1, what.size(), file); } +// Write a string into a file. Add a newline, if the string does not end with a newline already. +// Used to export a custom G-code section processed by the PlaceholderParser. inline void writeln(FILE *file, const std::string &what) { if (! what.empty()) { write(file, what); - fprintf(file, "\n"); + if (what.back() != '\n') + fprintf(file, "\n"); } } diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index 655913d90..fbe8572d5 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -597,16 +597,11 @@ namespace client using namespace qi::labels; qi::alpha_type alpha; qi::alnum_type alnum; - qi::eol_type eol; - qi::eoi_type eoi; qi::eps_type eps; - qi::omit_type omit; qi::raw_type raw; qi::lit_type lit; qi::lexeme_type lexeme; - qi::skip_type skip; qi::no_skip_type no_skip; - qi::uint_type uint_; qi::real_parser strict_double; spirit::ascii::char_type char_; spirit::bool_type bool_; From 3c0cd3cbc8bbd1a38582fe37803228f2413de118 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 28 Nov 2017 10:08:01 +0100 Subject: [PATCH 36/83] Improve error handling of loading Slic3r profiles. --- lib/Slic3r/GUI.pm | 10 ++++++++-- xs/src/slic3r/GUI/Preset.cpp | 18 +++++++++++------- xs/src/slic3r/GUI/PresetBundle.cpp | 29 ++++++++++++++++++++++------- xs/xsp/GUI_Preset.xsp | 2 +- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index baba3baa7..c65793d4b 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -93,9 +93,15 @@ sub OnInit { $self->{preset_bundle}->set_default_suppressed($self->{app_config}->get('no_defaults') ? 1 : 0); eval { $self->{preset_bundle}->load_presets(Slic3r::data_dir); - $self->{preset_bundle}->load_selections($self->{app_config}); - $run_wizard = 1 if $self->{preset_bundle}->has_defauls_only; }; + if ($@) { + warn $@ . "\n"; + show_error(undef, $@); + } + eval { + $self->{preset_bundle}->load_selections($self->{app_config}); + }; + $run_wizard = 1 if $self->{preset_bundle}->has_defauls_only; # application frame Wx::Image::AddHandler(Wx::PNGHandler->new); diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 5a9ceb04b..214c111a4 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -119,10 +119,10 @@ DynamicPrintConfig& Preset::load(const std::vector &keys) try { this->config.load_from_ini(this->file); Preset::normalize(this->config); - } catch (const std::ifstream::failure&) { - throw std::runtime_error(std::string("The selected preset does not exist anymore: ") + this->file); - } catch (const std::runtime_error&) { - throw std::runtime_error(std::string("Failed loading the preset file: ") + this->file); + } catch (const std::ifstream::failure &err) { + throw std::runtime_error(std::string("The selected preset cannot be loaded: ") + this->file + "\n\tReason: " + err.what()); + } catch (const std::runtime_error &err) { + throw std::runtime_error(std::string("Failed loading the preset file: ") + this->file + "\n\tReason: " + err.what()); } } this->loaded = true; @@ -250,6 +250,7 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri m_dir_path = dir.string(); m_presets.erase(m_presets.begin()+1, m_presets.end()); t_config_option_keys keys = this->default_preset().config.keys(); + std::string errors_cummulative; for (auto &dir_entry : boost::filesystem::directory_iterator(dir)) if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) { std::string name = dir_entry.path().filename().string(); @@ -260,12 +261,15 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri preset.file = dir_entry.path().string(); preset.load(keys); m_presets.emplace_back(preset); - } catch (const boost::filesystem::filesystem_error &err) { - - } + } catch (const std::runtime_error &err) { + errors_cummulative += err.what(); + errors_cummulative += "\n"; + } } std::sort(m_presets.begin() + 1, m_presets.end()); this->select_preset(first_visible_idx()); + if (! errors_cummulative.empty()) + throw std::runtime_error(errors_cummulative); } // Load a preset from an already parsed config file, insert it into the sorted sequence of presets diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 00e35683a..67a7c15cc 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -78,10 +78,25 @@ void PresetBundle::setup_directories() void PresetBundle::load_presets(const std::string &dir_path) { - this->prints .load_presets(dir_path, "print"); - this->filaments.load_presets(dir_path, "filament"); - this->printers .load_presets(dir_path, "printer"); + std::string errors_cummulative; + try { + this->prints.load_presets(dir_path, "print"); + } catch (const std::runtime_error &err) { + errors_cummulative += err.what(); + } + try { + this->filaments.load_presets(dir_path, "filament"); + } catch (const std::runtime_error &err) { + errors_cummulative += err.what(); + } + try { + this->printers.load_presets(dir_path, "printer"); + } catch (const std::runtime_error &err) { + errors_cummulative += err.what(); + } this->update_multi_material_filament_presets(); + if (! errors_cummulative.empty()) + throw std::runtime_error(errors_cummulative); } static inline std::string remove_ini_suffix(const std::string &name) @@ -233,10 +248,10 @@ void PresetBundle::load_config_file(const std::string &path) try { boost::nowide::ifstream ifs(path); boost::property_tree::read_ini(ifs, tree); - } catch (const std::ifstream::failure&) { - throw std::runtime_error(std::string("The config file cannot be loaded: ") + path); - } catch (const std::runtime_error&) { - throw std::runtime_error(std::string("Failed loading the preset file: ") + path); + } catch (const std::ifstream::failure &err) { + throw std::runtime_error(std::string("The config file cannot be loaded: ") + path + "\n\tReason: " + err.what()); + } catch (const std::runtime_error &err) { + throw std::runtime_error(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what()); } // 2) Continue based on the type of the configuration file. diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp index e1f8a72c4..a7fbdd07f 100644 --- a/xs/xsp/GUI_Preset.xsp +++ b/xs/xsp/GUI_Preset.xsp @@ -111,7 +111,7 @@ PresetCollection::arrayref() try { THIS->load_presets(dir_path); } catch (std::exception& e) { - croak("Loading of Slic3r presets from %s failed:\n%s\n", dir_path, e.what()); + croak("Loading of Slic3r presets from %s failed.\n\n%s\n", dir_path, e.what()); } %}; void load_config_file(const char *path) From f58b2173694443e3a99f0508d5164aa276b02fc4 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 28 Nov 2017 11:57:33 +0100 Subject: [PATCH 37/83] Implemented loading of a Slic3r Config Bundle as a Slic3r Config file. --- xs/src/slic3r/GUI/PresetBundle.cpp | 47 ++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 67a7c15cc..3855760c2 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -336,24 +336,47 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const // 1) Load the config bundle into a temp data. PresetBundle tmp_bundle; tmp_bundle.load_configbundle(path); + std::string bundle_name = std::string(" - ") + boost::filesystem::path(path).filename().string(); // 2) Extract active configs from the config bundle, copy them and activate them in this bundle. - if (tmp_bundle.prints.get_selected_preset().is_default) - this->prints.select_preset(0); - else { - std::string new_name = tmp_bundle.prints.get_selected_preset().name; - Preset *existing = this->prints.find_preset(new_name, false); - if (existing == nullptr) { - // Save under the new_name. - } else if (existing->config == tmp_bundle.prints.get_selected_preset().config) { + auto load_one = [this, &path, &bundle_name](PresetCollection &collection_dst, PresetCollection &collection_src, const std::string &preset_name_src, bool activate) -> std::string { + Preset *preset_src = collection_src.find_preset(preset_name_src, false); + Preset *preset_dst = collection_dst.find_preset(preset_name_src, false); + assert(preset_src != nullptr); + std::string preset_name_dst; + if (preset_dst != nullptr && preset_dst->is_default) { + // No need to copy a default preset, it always exists in collection_dst. + if (activate) + collection_dst.select_preset(0); + return preset_name_src; + } else if (preset_dst != nullptr && preset_src->config == preset_dst->config) { // Don't save as the config exists in the current bundle and its content is the same. - new_name.clear(); + return preset_name_src; } else { // Generate a new unique name. + preset_name_dst = preset_name_src + bundle_name; + Preset *preset_dup = nullptr; + for (size_t i = 1; (preset_dup = collection_dst.find_preset(preset_name_dst, false)) != nullptr; ++ i) { + if (preset_src->config == preset_dup->config) + // The preset has been already copied into collection_dst. + return preset_name_dst; + // Try to generate another name. + char buf[64]; + sprintf(buf, " (%d)", i); + preset_name_dst = preset_name_src + buf + bundle_name; + } } - if (! new_name.empty()) - this->prints.load_preset(path, new_name, std::move(tmp_bundle.prints.get_selected_preset().config)); - } + assert(! preset_name_dst.empty()); + // Save preset_src->config into collection_dst under preset_name_dst. + collection_dst.load_preset(path, preset_name_dst, std::move(preset_src->config), activate).is_external = true; + return preset_name_dst; + }; + load_one(this->prints, tmp_bundle.prints, tmp_bundle.prints .get_selected_preset().name, true); + load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filaments.get_selected_preset().name, true); + load_one(this->printers, tmp_bundle.printers, tmp_bundle.printers .get_selected_preset().name, true); + this->update_multi_material_filament_presets(); + for (size_t i = 1; i < std::min(tmp_bundle.filament_presets.size(), this->filament_presets.size()); ++ i) + this->filament_presets[i] = load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filament_presets[i], false); } // Load a config bundle file, into presets and store the loaded presets into separate files From 672194b475e2ec6d25ed2cc3e6164144a9f7d84e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 28 Nov 2017 15:19:57 +0100 Subject: [PATCH 38/83] Ported the between_objects_gcode custom G-code blocks, thanks @lordofhyphens, https://github.com/alexrj/Slic3r/pull/3275 Improved handling of custom G-code blocks: Slic3r will try to extract the target extruder and bed temperatures from the custom G-code blocks. --- lib/Slic3r/GUI/Tab.pm | 9 ++ t/custom_gcode.t | 11 ++- xs/src/libslic3r/GCode.cpp | 121 ++++++++++++++++++++----- xs/src/libslic3r/GCode.hpp | 3 +- xs/src/libslic3r/Print.cpp | 148 +++++++++++++++---------------- xs/src/libslic3r/PrintConfig.cpp | 9 ++ xs/src/libslic3r/PrintConfig.hpp | 2 + xs/src/slic3r/GUI/Preset.cpp | 4 +- 8 files changed, 207 insertions(+), 100 deletions(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index d479eb911..907be7872 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -1467,6 +1467,15 @@ sub build { $option->height(150); $optgroup->append_single_option_line($option); } + { + my $optgroup = $page->new_optgroup('Between objects G-code (for sequential printing)', + label_width => 0, + ); + my $option = $optgroup->get_option('between_objects_gcode'); + $option->full_width(1); + $option->height(150); + $optgroup->append_single_option_line($option); + } } { diff --git a/t/custom_gcode.t b/t/custom_gcode.t index 2cb9a44b8..91c7e7618 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 40; +use Test::More tests => 41; use strict; use warnings; @@ -210,4 +210,13 @@ use Slic3r::Test; } } +{ + my $config = Slic3r::Config::new_from_defaults; + $config->set('complete_objects', 1); + $config->set('between_objects_gcode', '_MY_CUSTOM_GCODE_'); + my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 3); + my $gcode = Slic3r::Test::gcode($print); + is scalar(() = $gcode =~ /^_MY_CUSTOM_GCODE_/gm), 2, 'between_objects_gcode is applied correctly'; +} + __END__ diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 3c406389e..441d254f6 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -538,29 +538,23 @@ bool GCode::_do_export(Print &print, FILE *file) // Disable fan. if (! print.config.cooling.get_at(initial_extruder_id) || print.config.disable_fan_first_layers.get_at(initial_extruder_id)) write(file, m_writer.set_fan(0, true)); - - // Set bed temperature if the start G-code does not contain any bed temp control G-codes. - { - // Always call m_writer.set_bed_temperature() so it will set the internal "current" state of the bed temp as if - // the custom start G-code emited these. - //FIXME Should one parse the custom G-code to initialize the "current" bed temp state at m_writer? - std::string gcode = m_writer.set_bed_temperature(print.config.first_layer_bed_temperature.get_at(initial_extruder_id), true); - if (boost::ifind_first(print.config.start_gcode.value, std::string("M140")).empty() && - boost::ifind_first(print.config.start_gcode.value, std::string("M190")).empty()) - write(file, gcode); - } - // Set extruder(s) temperature before and after start G-code. - this->_print_first_layer_extruder_temperatures(file, print, initial_extruder_id, false); // Let the start-up script prime the 1st printing tool. m_placeholder_parser.set("initial_tool", initial_extruder_id); m_placeholder_parser.set("initial_extruder", initial_extruder_id); m_placeholder_parser.set("current_extruder", initial_extruder_id); - writeln(file, m_placeholder_parser.process(print.config.start_gcode.value, initial_extruder_id)); + std::string start_gcode = m_placeholder_parser.process(print.config.start_gcode.value, initial_extruder_id); + + // Set bed temperature if the start G-code does not contain any bed temp control G-codes. + this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true); + // Set extruder(s) temperature before and after start G-code. + this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false); + // Write the custom start G-code + writeln(file, start_gcode); // Process filament-specific gcode in extruder order. for (const std::string &start_gcode : print.config.start_filament_gcode.values) writeln(file, m_placeholder_parser.process(start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front()))); - this->_print_first_layer_extruder_temperatures(file, print, initial_extruder_id, true); + this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true); // Set other general things. write(file, this->preamble()); @@ -650,9 +644,11 @@ bool GCode::_do_export(Print &print, FILE *file) // Ff we are printing the bottom layer of an object, and we have already finished // another one, set first layer temperatures. This happens before the Z move // is triggered, so machine has more time to reach such temperatures. - write(file, m_writer.set_bed_temperature(print.config.first_layer_bed_temperature.get_at(initial_extruder_id))); - // Set first layer extruder. - this->_print_first_layer_extruder_temperatures(file, print, initial_extruder_id, false); + std::string between_objects_gcode = m_placeholder_parser.process(print.config.between_objects_gcode.value, initial_extruder_id); + // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. + this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false); + this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false); + writeln(file, between_objects_gcode); } // Reset the cooling buffer internal state (the current position, feed rate, accelerations). m_cooling_buffer->reset(); @@ -774,15 +770,96 @@ bool GCode::_do_export(Print &print, FILE *file) return true; } +// Parse the custom G-code, try to find mcode_set_temp_dont_wait and mcode_set_temp_and_wait inside the custom G-code. +// Returns true if one of the temp commands are found, and try to parse the target temperature value into temp_out. +static bool custom_gcode_sets_temperature(const std::string &gcode, const int mcode_set_temp_dont_wait, const int mcode_set_temp_and_wait, int &temp_out) +{ + temp_out = -1; + if (gcode.empty()) + return false; + + const char *ptr = gcode.data(); + bool temp_set_by_gcode = false; + while (*ptr != 0) { + // Skip whitespaces. + for (; *ptr == ' ' || *ptr == '\t'; ++ ptr); + if (*ptr == 'M') { + // Line starts with 'M'. It is a machine command. + ++ ptr; + // Parse the M code value. + char *endptr = nullptr; + int mcode = int(strtol(ptr, &endptr, 10)); + if (endptr != nullptr && endptr != ptr && (mcode == mcode_set_temp_dont_wait || mcode == mcode_set_temp_and_wait)) { + // M104/M109 or M140/M190 found. + ptr = endptr; + // Let the caller know that the custom G-code sets the temperature. + temp_set_by_gcode = true; + // Now try to parse the temperature value. + // While not at the end of the line: + while (strchr(";\r\n\0", *ptr) == nullptr) { + // Skip whitespaces. + for (; *ptr == ' ' || *ptr == '\t'; ++ ptr); + if (*ptr == 'S') { + // Skip whitespaces. + for (++ ptr; *ptr == ' ' || *ptr == '\t'; ++ ptr); + // Parse an int. + endptr = nullptr; + long temp_parsed = strtol(ptr, &endptr, 10); + if (endptr > ptr) { + ptr = endptr; + temp_out = temp_parsed; + } + } else { + // Skip this word. + for (; strchr(" \t;\r\n\0", *ptr) == nullptr; ++ ptr); + } + } + } + } + // Skip the rest of the line. + for (; *ptr != 0 && *ptr != '\r' && *ptr != '\n'; ++ ptr); + // Skip the end of line indicators. + for (; *ptr == '\r' || *ptr == '\n'; ++ ptr); + } + return temp_set_by_gcode; +} + +// Write 1st layer bed temperatures into the G-code. +// Only do that if the start G-code does not already contain any M-code controlling an extruder temperature. +// M140 - Set Extruder Temperature +// M190 - Set Extruder Temperature and Wait +void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) +{ + // Initial bed temperature based on the first extruder. + int temp = print.config.first_layer_bed_temperature.get_at(first_printing_extruder_id); + // Is the bed temperature set by the provided custom G-code? + int temp_by_gcode = -1; + bool temp_set_by_gcode = custom_gcode_sets_temperature(gcode, 140, 190, temp_by_gcode); + if (temp_by_gcode >= 0 && temp_by_gcode < 1000) + temp = temp_by_gcode; + // Always call m_writer.set_bed_temperature() so it will set the internal "current" state of the bed temp as if + // the custom start G-code emited these. + std::string set_temp_gcode = m_writer.set_bed_temperature(temp, wait); + if (! temp_by_gcode) + write(file, set_temp_gcode); +} + // Write 1st layer extruder temperatures into the G-code. // Only do that if the start G-code does not already contain any M-code controlling an extruder temperature. -// FIXME this does not work correctly for multi-extruder, single heater configuration as it emits multiple preheat commands for the same heater. // M104 - Set Extruder Temperature // M109 - Set Extruder Temperature and Wait -void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, unsigned int first_printing_extruder_id, bool wait) +void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { - if (boost::ifind_first(print.config.start_gcode.value, std::string("M104")).empty() && - boost::ifind_first(print.config.start_gcode.value, std::string("M109")).empty()) { + // Is the bed temperature set by the provided custom G-code? + int temp_by_gcode = -1; + if (custom_gcode_sets_temperature(gcode, 104, 109, temp_by_gcode)) { + // Set the extruder temperature at m_writer, but throw away the generated G-code as it will be written with the custom G-code. + int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id); + if (temp_by_gcode >= 0 && temp_by_gcode < 1000) + temp = temp_by_gcode; + m_writer.set_temperature(temp_by_gcode, wait, first_printing_extruder_id); + } else { + // Custom G-code does not set the extruder temperature. Do it now. if (print.config.single_extruder_multi_material.value) { // Set temperature of the first printing extruder only. int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id); diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index e68cf49c7..aa90ac968 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -268,7 +268,8 @@ protected: std::pair m_last_obj_copy; std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1); - void _print_first_layer_extruder_temperatures(FILE *file, Print &print, unsigned int first_printing_extruder_id, bool wait); + void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); + void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); // this flag triggers first layer speeds bool on_first_layer() const { return m_layer != nullptr && m_layer->id() == 0; } diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 4ad08192a..78ebf9294 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -81,80 +81,80 @@ bool Print::invalidate_state_by_config_options(const std::vector steps_ignore; - if (steps_ignore.empty()) { - steps_ignore.insert("avoid_crossing_perimeters"); - steps_ignore.insert("bed_shape"); - steps_ignore.insert("bed_temperature"); - steps_ignore.insert("before_layer_gcode"); - steps_ignore.insert("bridge_acceleration"); - steps_ignore.insert("bridge_fan_speed"); - steps_ignore.insert("cooling"); - steps_ignore.insert("default_acceleration"); - steps_ignore.insert("deretract_speed"); - steps_ignore.insert("disable_fan_first_layers"); - steps_ignore.insert("duplicate_distance"); - steps_ignore.insert("end_gcode"); - steps_ignore.insert("end_filament_gcode"); - steps_ignore.insert("extrusion_axis"); - steps_ignore.insert("extruder_clearance_height"); - steps_ignore.insert("extruder_clearance_radius"); - steps_ignore.insert("extruder_colour"); - steps_ignore.insert("extruder_offset"); - steps_ignore.insert("extrusion_multiplier"); - steps_ignore.insert("fan_always_on"); - steps_ignore.insert("fan_below_layer_time"); - steps_ignore.insert("filament_colour"); - steps_ignore.insert("filament_diameter"); - steps_ignore.insert("filament_density"); - steps_ignore.insert("filament_notes"); - steps_ignore.insert("filament_cost"); - steps_ignore.insert("filament_max_volumetric_speed"); - steps_ignore.insert("first_layer_acceleration"); - steps_ignore.insert("first_layer_bed_temperature"); - steps_ignore.insert("first_layer_speed"); - steps_ignore.insert("gcode_comments"); - steps_ignore.insert("gcode_flavor"); - steps_ignore.insert("infill_acceleration"); - steps_ignore.insert("infill_first"); - steps_ignore.insert("layer_gcode"); - steps_ignore.insert("min_fan_speed"); - steps_ignore.insert("max_fan_speed"); - steps_ignore.insert("min_print_speed"); - steps_ignore.insert("max_print_speed"); - steps_ignore.insert("max_volumetric_speed"); - steps_ignore.insert("max_volumetric_extrusion_rate_slope_positive"); - steps_ignore.insert("max_volumetric_extrusion_rate_slope_negative"); - steps_ignore.insert("notes"); - steps_ignore.insert("only_retract_when_crossing_perimeters"); - steps_ignore.insert("output_filename_format"); - steps_ignore.insert("perimeter_acceleration"); - steps_ignore.insert("post_process"); - steps_ignore.insert("printer_notes"); - steps_ignore.insert("retract_before_travel"); - steps_ignore.insert("retract_before_wipe"); - steps_ignore.insert("retract_layer_change"); - steps_ignore.insert("retract_length"); - steps_ignore.insert("retract_length_toolchange"); - steps_ignore.insert("retract_lift"); - steps_ignore.insert("retract_lift_above"); - steps_ignore.insert("retract_lift_below"); - steps_ignore.insert("retract_restart_extra"); - steps_ignore.insert("retract_restart_extra_toolchange"); - steps_ignore.insert("retract_speed"); - steps_ignore.insert("slowdown_below_layer_time"); - steps_ignore.insert("standby_temperature_delta"); - steps_ignore.insert("start_gcode"); - steps_ignore.insert("start_filament_gcode"); - steps_ignore.insert("toolchange_gcode"); - steps_ignore.insert("threads"); - steps_ignore.insert("travel_speed"); - steps_ignore.insert("use_firmware_retraction"); - steps_ignore.insert("use_relative_e_distances"); - steps_ignore.insert("use_volumetric_e"); - steps_ignore.insert("variable_layer_height"); - steps_ignore.insert("wipe"); - } + static std::unordered_set steps_ignore = { + "avoid_crossing_perimeters", + "bed_shape", + "bed_temperature", + "before_layer_gcode", + "between_objects_gcode", + "bridge_acceleration", + "bridge_fan_speed", + "cooling", + "default_acceleration", + "deretract_speed", + "disable_fan_first_layers", + "duplicate_distance", + "end_gcode", + "end_filament_gcode", + "extrusion_axis", + "extruder_clearance_height", + "extruder_clearance_radius", + "extruder_colour", + "extruder_offset", + "extrusion_multiplier", + "fan_always_on", + "fan_below_layer_time", + "filament_colour", + "filament_diameter", + "filament_density", + "filament_notes", + "filament_cost", + "filament_max_volumetric_speed", + "first_layer_acceleration", + "first_layer_bed_temperature", + "first_layer_speed", + "gcode_comments", + "gcode_flavor", + "infill_acceleration", + "infill_first", + "layer_gcode", + "min_fan_speed", + "max_fan_speed", + "min_print_speed", + "max_print_speed", + "max_volumetric_speed", + "max_volumetric_extrusion_rate_slope_positive", + "max_volumetric_extrusion_rate_slope_negative", + "notes", + "only_retract_when_crossing_perimeters", + "output_filename_format", + "perimeter_acceleration", + "post_process", + "printer_notes", + "retract_before_travel", + "retract_before_wipe", + "retract_layer_change", + "retract_length", + "retract_length_toolchange", + "retract_lift", + "retract_lift_above", + "retract_lift_below", + "retract_restart_extra", + "retract_restart_extra_toolchange", + "retract_speed", + "slowdown_below_layer_time", + "standby_temperature_delta", + "start_gcode", + "start_filament_gcode", + "toolchange_gcode", + "threads", + "travel_speed", + "use_firmware_retraction", + "use_relative_e_distances", + "use_volumetric_e", + "variable_layer_height", + "wipe" + }; std::vector steps; std::vector osteps; diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 54d51c237..217f9bdef 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -48,6 +48,15 @@ PrintConfigDef::PrintConfigDef() def->height = 50; def->default_value = new ConfigOptionString(""); + def = this->add("between_objects_gcode", coString); + def->label = "Between objects G-code"; + def->tooltip = "This code is inserted between objects when using sequential printing. By default extruder and bed temperature are reset using non-wait command; however if M104, M109, M140 or M190 are detected in this custom code, Slic3r will not add temperature commands. Note that you can use placeholder variables for all Slic3r settings, so you can put a \"M109 S[first_layer_temperature]\" command wherever you want."; + def->cli = "between-objects-gcode=s"; + def->multiline = true; + def->full_width = true; + def->height = 120; + def->default_value = new ConfigOptionString(""); + def = this->add("bottom_solid_layers", coInt); def->label = "Bottom"; def->category = "Layers and Perimeters"; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 20f787b1e..ab58aa356 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -452,6 +452,7 @@ class GCodeConfig : public StaticPrintConfig STATIC_PRINT_CONFIG_CACHE(GCodeConfig) public: ConfigOptionString before_layer_gcode; + ConfigOptionString between_objects_gcode; ConfigOptionFloats deretract_speed; ConfigOptionString end_gcode; ConfigOptionStrings end_filament_gcode; @@ -500,6 +501,7 @@ protected: void initialize(StaticCacheBase &cache, const char *base_ptr) { OPT_PTR(before_layer_gcode); + OPT_PTR(between_objects_gcode); OPT_PTR(deretract_speed); OPT_PTR(end_gcode); OPT_PTR(end_filament_gcode); diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 214c111a4..e4b0448cf 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -204,8 +204,8 @@ const std::vector& Preset::printer_options() s_opts = { "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", "octoprint_host", "octoprint_apikey", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", - "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", - "printer_notes" + "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", + "between_objects_gcode", "printer_notes" }; s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end()); } From 9ca63f16bc23207c06ce51e2486fc9ce5036114f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 28 Nov 2017 15:30:05 +0100 Subject: [PATCH 39/83] New PlaceholderParser variable for sequential prints: current_object_idx - zero based index of the object printed. Implements feature request https://github.com/prusa3d/Slic3r/issues/578 --- xs/src/libslic3r/GCode.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 441d254f6..ff144610e 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -543,6 +543,8 @@ bool GCode::_do_export(Print &print, FILE *file) m_placeholder_parser.set("initial_tool", initial_extruder_id); m_placeholder_parser.set("initial_extruder", initial_extruder_id); m_placeholder_parser.set("current_extruder", initial_extruder_id); + // Useful for sequential prints. + m_placeholder_parser.set("current_object_idx", 0); std::string start_gcode = m_placeholder_parser.process(print.config.start_gcode.value, initial_extruder_id); // Set bed temperature if the start G-code does not contain any bed temp control G-codes. @@ -644,6 +646,7 @@ bool GCode::_do_export(Print &print, FILE *file) // Ff we are printing the bottom layer of an object, and we have already finished // another one, set first layer temperatures. This happens before the Z move // is triggered, so machine has more time to reach such temperatures. + m_placeholder_parser.set("current_object_idx", int(finished_objects)); std::string between_objects_gcode = m_placeholder_parser.process(print.config.between_objects_gcode.value, initial_extruder_id); // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false); From 0ddbfccb08220a3dad4dccc61a81f64561236ee2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 28 Nov 2017 19:56:32 +0100 Subject: [PATCH 40/83] Added some test cases for the conditional G-code math calculator. https://github.com/prusa3d/Slic3r/issues/438 --- t/custom_gcode.t | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/t/custom_gcode.t b/t/custom_gcode.t index 91c7e7618..f19971853 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 41; +use Test::More tests => 48; use strict; use warnings; @@ -49,6 +49,7 @@ use Slic3r::Test; my $parser = Slic3r::GCode::PlaceholderParser->new; $parser->apply_config(my $config = Slic3r::Config::new_from_defaults); $parser->set('foo' => 0); + $parser->set('bar' => 2); is $parser->process('[temperature_[foo]]'), $config->temperature->[0], "nested config options (legacy syntax)"; @@ -58,6 +59,13 @@ use Slic3r::Test; is $parser->process("test [ temperature_ [foo] ] \n hu"), "test " . $config->temperature->[0] . " \n hu", "whitespaces and newlines are maintained"; + is $parser->process('{2*3}'), '6', 'math: 2*3'; + is $parser->process('{2*3/6}'), '1', 'math: 2*3/6'; + is $parser->process('{2*3/12}'), '0', 'math: 2*3/12'; + is $parser->process('{2.*3/12}'), '0.5', 'math: 2.*3/12'; + is $parser->process('{2*(3-12)}'), '-18', 'math: 2*(3-12)'; + is $parser->process('{2*foo*(3-12)}'), '0', 'math: 2*foo*(3-12)'; + is $parser->process('{2*bar*(3-12)}'), '-36', 'math: 2*bar*(3-12)'; } { From 08e81f2765777f184a220dcf69f0b9f3fe4eeef3 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 29 Nov 2017 10:52:54 +0100 Subject: [PATCH 41/83] Bumped up the build version. --- xs/src/libslic3r/libslic3r.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index e70113710..1c89e56f2 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -14,7 +14,7 @@ #include #define SLIC3R_FORK_NAME "Slic3r Prusa Edition" -#define SLIC3R_VERSION "1.33.8.devel" +#define SLIC3R_VERSION "1.38.1" #define SLIC3R_BUILD "UNKNOWN" typedef long coord_t; From 6729dc1c6d69340937ede6b09980ddefb81474a3 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 29 Nov 2017 16:35:48 +0100 Subject: [PATCH 42/83] Fixed a failing PlaceholderParser test case. --- t/custom_gcode.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/custom_gcode.t b/t/custom_gcode.t index f19971853..9f66a47df 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -62,7 +62,7 @@ use Slic3r::Test; is $parser->process('{2*3}'), '6', 'math: 2*3'; is $parser->process('{2*3/6}'), '1', 'math: 2*3/6'; is $parser->process('{2*3/12}'), '0', 'math: 2*3/12'; - is $parser->process('{2.*3/12}'), '0.5', 'math: 2.*3/12'; + ok abs($parser->process('{2.*3/12}') - 0.5) < 1e-7, 'math: 2.*3/12'; is $parser->process('{2*(3-12)}'), '-18', 'math: 2*(3-12)'; is $parser->process('{2*foo*(3-12)}'), '0', 'math: 2*foo*(3-12)'; is $parser->process('{2*bar*(3-12)}'), '-36', 'math: 2*bar*(3-12)'; From da8ffd477d04a34c3ab2e4ce6a851cc61e173b29 Mon Sep 17 00:00:00 2001 From: Eyal Date: Wed, 29 Nov 2017 17:57:21 +0200 Subject: [PATCH 43/83] Missing prerequisites in Build.PL (#593) I was unable to compile without ExtUtils::XSpp and ExtUtils::Typemaps . My platform is a fresh install of OctoPi on Raspberry Pi. I'm not sure what the `0` in those lines does. --- Build.PL | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Build.PL b/Build.PL index 09bd5b95c..31ec9cf82 100644 --- a/Build.PL +++ b/Build.PL @@ -10,6 +10,8 @@ my %prereqs = qw( Devel::CheckLib 0 ExtUtils::MakeMaker 6.80 ExtUtils::ParseXS 3.22 + ExtUtils::XSpp 0 + ExtUtils::Typemaps 0 File::Basename 0 File::Spec 0 Getopt::Long 0 From ae118519abdf6dfbcf3eab34fe5b3e2efcbec600 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 29 Nov 2017 19:27:26 +0100 Subject: [PATCH 44/83] Fixed a 32bit build bug in the new PlaceholderParser macro processor. --- t/custom_gcode.t | 3 +- xs/src/libslic3r/PlaceholderParser.cpp | 68 ++++++++++++++++---------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/t/custom_gcode.t b/t/custom_gcode.t index 9f66a47df..1ccf738aa 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 48; +use Test::More tests => 49; use strict; use warnings; @@ -66,6 +66,7 @@ use Slic3r::Test; is $parser->process('{2*(3-12)}'), '-18', 'math: 2*(3-12)'; is $parser->process('{2*foo*(3-12)}'), '0', 'math: 2*foo*(3-12)'; is $parser->process('{2*bar*(3-12)}'), '-36', 'math: 2*bar*(3-12)'; + ok abs($parser->process('{2.5*bar*(3-12)}') - -45) < 1e-7, 'math: 2.5*bar*(3-12)'; } { diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index fbe8572d5..07f07c161 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -145,7 +145,7 @@ namespace client template struct expr { - expr() : type(TYPE_EMPTY) { data.s = nullptr; } + expr() { this->reset(); } explicit expr(bool b) : type(TYPE_BOOL) { data.b = b; } explicit expr(bool b, const Iterator &it_begin, const Iterator &it_end) : type(TYPE_BOOL), it_range(it_begin, it_end) { data.b = b; } explicit expr(int i) : type(TYPE_INT) { data.i = i; } @@ -156,33 +156,39 @@ namespace client explicit expr(const std::string &s) : type(TYPE_STRING) { data.s = new std::string(s); } explicit expr(const std::string &s, const Iterator &it_begin, const Iterator &it_end) : type(TYPE_STRING), it_range(it_begin, it_end) { data.s = new std::string(s); } - expr(const expr &rhs) : type(rhs.type) { data.s = (rhs.type == TYPE_STRING) ? new std::string(*rhs.data.s) : rhs.data.s; } - explicit expr(expr &&rhs) : type(rhs.type) { data.s = rhs.data.s; rhs.data.s = nullptr; rhs.type = TYPE_EMPTY; } + expr(const expr &rhs) : type(rhs.type), it_range(rhs.it_range) + { if (rhs.type == TYPE_STRING) data.s = new std::string(*rhs.data.s); else data.set(rhs.data); } + explicit expr(expr &&rhs) : type(rhs.type), it_range(rhs.it_range) + { data.set(rhs.data); rhs.type = TYPE_EMPTY; } explicit expr(expr &&rhs, const Iterator &it_begin, const Iterator &it_end) : type(rhs.type), it_range(it_begin, it_end) - { data.s = rhs.data.s; rhs.data.s = nullptr; rhs.type = TYPE_EMPTY; } + { data.set(rhs.data); rhs.type = TYPE_EMPTY; } ~expr() { this->reset(); } expr &operator=(const expr &rhs) { - type = rhs.type; - data.s = (type == TYPE_STRING) ? new std::string(*rhs.data.s) : rhs.data.s; + this->type = rhs.type; + this->it_range = rhs.it_range; + if (rhs.type == TYPE_STRING) + this->data.s = new std::string(*rhs.data.s); + else + this->data.set(rhs.data); return *this; } expr &operator=(expr &&rhs) { - type = rhs.type; - data.s = rhs.data.s; - rhs.data.s = nullptr; - rhs.type = TYPE_EMPTY; - return *this; + type = rhs.type; + this->it_range = rhs.it_range; + data.set(rhs.data); + rhs.type = TYPE_EMPTY; + return *this; } void reset() { if (this->type == TYPE_STRING) delete data.s; - data.s = nullptr; + memset(data.raw, 0, sizeof(data.raw)); this->type = TYPE_EMPTY; } @@ -210,15 +216,23 @@ namespace client case TYPE_INT: out = boost::to_string(data.i); break; case TYPE_DOUBLE: out = boost::to_string(data.d); break; case TYPE_STRING: out = *data.s; break; + default: break; } return out; } - union { + union Data { + // Raw image of the other data members. + // The C++ compiler will consider a possible aliasing of char* with any other union member, + // therefore copying the raw data is safe. + char raw[8]; bool b; int i; double d; std::string *s; + + // Copy the largest member variable through char*, which will alias with all other union members by default. + void set(const Data &rhs) { memcpy(this->raw, rhs.raw, sizeof(rhs.raw)); } } data; enum Type { @@ -386,7 +400,7 @@ namespace client std::ostream& operator<<(std::ostream &os, const expr &expression) { typedef expr Expr; - os << std::string(expression.it_range.begin(), expression.it_range.end()) << ' - '; + os << std::string(expression.it_range.begin(), expression.it_range.end()) << " - "; switch (expression.type) { case Expr::TYPE_EMPTY: os << "empty"; break; case Expr::TYPE_BOOL: os << "bool (" << expression.b() << ")"; break; @@ -588,7 +602,7 @@ namespace client /////////////////////////////////////////////////////////////////////////// // Our calculator grammar /////////////////////////////////////////////////////////////////////////// - // Inspired by the C grammar https://www.lysator.liu.se/c/ANSI-C-grammar-y.html + // Inspired by the C grammar rules https://www.lysator.liu.se/c/ANSI-C-grammar-y.html template struct calculator : qi::grammar { @@ -644,7 +658,7 @@ namespace client macro = (kw["if"] > if_else_output(_r1) [_val = _1]) | (kw["switch"] > switch_output(_r1) [_val = _1]) - | expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]; + | additive_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]; macro.name("macro"); // An if expression enclosed in {} (the outmost {} are already parsed by the caller). @@ -683,10 +697,10 @@ namespace client identifier.name("identifier"); bool_expr = - expression(_r1) [_val = _1] - >> *( ("==" > expression(_r1) ) [px::bind(&expr::equal, _val, _1)] - | ("!=" > expression(_r1) ) [px::bind(&expr::not_equal, _val, _1)] - | ("<>" > expression(_r1) ) [px::bind(&expr::not_equal, _val, _1)] + additive_expression(_r1) [_val = _1] + >> *( ("==" > additive_expression(_r1) ) [px::bind(&expr::equal, _val, _1)] + | ("!=" > additive_expression(_r1) ) [px::bind(&expr::not_equal, _val, _1)] + | ("<>" > additive_expression(_r1) ) [px::bind(&expr::not_equal, _val, _1)] ); bool_expr.name("bool expression"); @@ -695,12 +709,12 @@ namespace client bool_expr_eval = bool_expr(_r1) [ px::bind(&expr::evaluate_boolean, _1, _val) ]; bool_expr_eval.name("bool_expr_eval"); - expression = + additive_expression = term(_r1) [_val = _1] >> *( (lit('+') > term(_r1) ) [_val += _1] | (lit('-') > term(_r1) ) [_val -= _1] ); - expression.name("expression"); + additive_expression.name("additive_expression"); term = factor(_r1) [_val = _1] @@ -729,14 +743,14 @@ namespace client }; factor = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( scalar_variable_reference(_r1) [ _val = _1 ] - | (lit('(') > expression(_r1) > ')' > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] + | (lit('(') > additive_expression(_r1) > ')' > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] | (lit('-') > factor(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ] | (lit('+') > factor(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] | ((kw["not"] | '!') > factor(_r1) > iter_pos) [ px::bind(&FactorActions::not_, _1, _val) ] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] - | raw[lexeme['"' > *(char_ - char_('\\') - char_('"') | ('\\' > char_)) > '"']] + | raw[lexeme['"' > *((char_ - char_('\\') - char_('"')) | ('\\' > char_)) > '"']] [ px::bind(&FactorActions::string_, _1, _val) ] ); factor.name("factor"); @@ -744,7 +758,7 @@ namespace client scalar_variable_reference = variable_reference(_r1)[_a=_1] >> ( - ('[' > expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] > ']' > + ('[' > additive_expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] > ']' > iter_pos[px::bind(&MyContext::vector_variable_reference, _r1, _a, _b, _1, _val)]) | eps[px::bind(&MyContext::scalar_variable_reference, _r1, _a, _val)] ); @@ -787,7 +801,7 @@ namespace client debug(identifier); debug(bool_expr); debug(bool_expr_eval); - debug(expression); + debug(additive_expression); debug(term); debug(factor); debug(scalar_variable_reference); @@ -808,7 +822,7 @@ namespace client // Parsed identifier name. qi::rule(), spirit::ascii::space_type> identifier; // Math expression consisting of +- operators over terms. - qi::rule(const MyContext*), spirit::ascii::space_type> expression; + qi::rule(const MyContext*), spirit::ascii::space_type> additive_expression; // Boolean expressions over expressions. qi::rule(const MyContext*), spirit::ascii::space_type> bool_expr; // Evaluate boolean expression into bool. From f754cb422beb95c9d66f6e9ed5cd04bfc6a6676a Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 29 Nov 2017 19:34:24 +0100 Subject: [PATCH 45/83] Bumped up the version number. --- xs/src/libslic3r/libslic3r.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 1c89e56f2..644190d6a 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -14,7 +14,7 @@ #include #define SLIC3R_FORK_NAME "Slic3r Prusa Edition" -#define SLIC3R_VERSION "1.38.1" +#define SLIC3R_VERSION "1.38.2" #define SLIC3R_BUILD "UNKNOWN" typedef long coord_t; From 2f54bf5bca2e151bb2d7c08268c8ceb4de688466 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 29 Nov 2017 20:38:19 +0100 Subject: [PATCH 46/83] Fixed a random crash in the PlaceholderParser due to deallocating an undefined pointer. --- xs/src/libslic3r/PlaceholderParser.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index 07f07c161..060252387 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -145,7 +145,7 @@ namespace client template struct expr { - expr() { this->reset(); } + expr() : type(TYPE_EMPTY) {} explicit expr(bool b) : type(TYPE_BOOL) { data.b = b; } explicit expr(bool b, const Iterator &it_begin, const Iterator &it_end) : type(TYPE_BOOL), it_range(it_begin, it_end) { data.b = b; } explicit expr(int i) : type(TYPE_INT) { data.i = i; } @@ -188,7 +188,6 @@ namespace client { if (this->type == TYPE_STRING) delete data.s; - memset(data.raw, 0, sizeof(data.raw)); this->type = TYPE_EMPTY; } From 6aff27f3abf6ea895cc458845a31bc436ffa38a8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Nov 2017 10:22:39 +0100 Subject: [PATCH 47/83] Moved initialization of Slic3r XS datadir variable to GUI.pm Solves https://github.com/prusa3d/Slic3r/issues/594 --- lib/Slic3r/GUI.pm | 8 ++++++++ slic3r.pl | 7 +------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index c65793d4b..2dc560cad 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -52,6 +52,8 @@ use constant FILE_WILDCARDS => { }; use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf prusa)}; +# Datadir provided on the command line. +our $datadir; # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. our $no_plater; our @cb; @@ -71,6 +73,12 @@ sub OnInit { $self->SetAppName('Slic3r'); $self->SetAppDisplayName('Slic3r Prusa Edition'); Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION; + + # Set the Slic3r data directory at the Slic3r XS module. + # Unix: ~/.Slic3r + # Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r" + # Mac: "~/Library/Application Support/Slic3r" + Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir); $self->{notifier} = Slic3r::GUI::Notifier->new; $self->{app_config} = Slic3r::GUI::AppConfig->new; diff --git a/slic3r.pl b/slic3r.pl index 87c8fe40c..e49457c20 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -100,17 +100,12 @@ if ($opt{save}) { my $config = Slic3r::Config::new_from_defaults; $config->apply($cli_config); -# locate or create data directory -# Unix: ~/.Slic3r -# Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r" -# Mac: "~/Library/Application Support/Slic3r" -Slic3r::set_data_dir($opt{datadir} || Wx::StandardPaths::Get->GetUserDataDir); - # launch GUI my $gui; if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") { { no warnings 'once'; + $Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // ''); $Slic3r::GUI::no_controller = $opt{no_controller}; $Slic3r::GUI::no_plater = $opt{no_plater}; $Slic3r::GUI::autosave = $opt{autosave}; From 830da1f8e490abd2b7ecf312d85759b8f2f5f8b6 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Nov 2017 13:43:02 +0100 Subject: [PATCH 48/83] Fixed a regression bug of handling the obsolete config parameters, causing crashes. --- xs/src/libslic3r/Config.cpp | 2 +- xs/src/libslic3r/PrintConfig.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index d7671c82f..728ed608b 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -243,7 +243,7 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con for (const auto &opt : def->options) { for (const t_config_option_key &opt_key2 : opt.second.aliases) { if (opt_key2 == opt_key) { - opt_key = opt_key2; + opt_key = opt.first; optdef = &opt.second; break; } diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 217f9bdef..ed54d9b6c 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -245,6 +245,7 @@ PrintConfigDef::PrintConfigDef() def->enum_labels.push_back("Hilbert Curve"); def->enum_labels.push_back("Archimedean Chords"); def->enum_labels.push_back("Octagram Spiral"); + // solid_fill_pattern is an obsolete equivalent to external_fill_pattern. def->aliases.push_back("solid_fill_pattern"); def->default_value = new ConfigOptionEnum(ipRectilinear); From 8807d288d7af21727dd371d5bcf6d9e68f02d229 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Nov 2017 15:51:51 +0100 Subject: [PATCH 49/83] Fixed a regression issue when starting Slic3r with non-existent datadir. --- xs/src/slic3r/GUI/PresetBundle.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 3855760c2..29afc07cd 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -64,12 +64,11 @@ PresetBundle::~PresetBundle() void PresetBundle::setup_directories() { - boost::filesystem::path dir = boost::filesystem::canonical(Slic3r::data_dir()); - if (! boost::filesystem::is_directory(dir)) - throw std::runtime_error(std::string("datadir does not exist: ") + Slic3r::data_dir()); - std::initializer_list names = { "print", "filament", "printer" }; - for (const char *name : names) { - boost::filesystem::path subdir = (dir / name).make_preferred(); + boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); + std::initializer_list paths = { data_dir, data_dir / "print", data_dir / "filament", data_dir / "printer" }; + for (const boost::filesystem::path &path : paths) { + boost::filesystem::path subdir = path; + subdir.make_preferred(); if (! boost::filesystem::is_directory(subdir) && ! boost::filesystem::create_directory(subdir)) throw std::runtime_error(std::string("Slic3r was unable to create its data directory at ") + subdir.string()); From 3996535e5dea9d223fe5ad9e628bb3b841aeff63 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Nov 2017 16:01:47 +0100 Subject: [PATCH 50/83] Changed handling of filament_gcode_start and filament_gcode_end custom G-codes in case of single extruder multiple material setup: At the start of the print, the filament_gcode_start is executed for the active extruder only, and the filament_gcode_start / filament_gcode_end are then executed at each tool change. When the Prusa MM wipe tower is active, the tool changes are handled a bit differently: M900 K0 is emited before the wipe tower extrusions start, and the filament_gcode_start code is executed after the wipe tower extrusions are done. This rule effectively disables the linear advance over the wipe tower. Implements https://github.com/prusa3d/Slic3r/issues/568 --- xs/src/libslic3r/GCode.cpp | 90 ++++++++++++++++++++++++++++++-------- xs/src/libslic3r/GCode.hpp | 1 + 2 files changed, 73 insertions(+), 18 deletions(-) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index ff144610e..3bdc8d321 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -30,6 +30,13 @@ #include namespace Slic3r { + +// Only add a newline in case the current G-code does not end with a newline. +static inline void check_add_eol(std::string &gcode) +{ + if (! gcode.empty() && gcode.back() != '\n') + gcode += '\n'; +} // Plan a travel move while minimizing the number of perimeter crossings. // point is in unscaled coordinates, in the coordinate system of the current active object @@ -157,6 +164,8 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T { std::string gcode; + // Disable linear advance for the wipe tower operations. + gcode += "M900 K0\n"; // Move over the wipe tower. // Retract for a tool change, using the toolchange retract value and setting the priming extra length. gcode += gcodegen.retract(true); @@ -171,8 +180,17 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T // Inform the G-code writer about the changes done behind its back. gcode += tcr.gcode; // Let the m_writer know the current extruder_id, but ignore the generated G-code. - if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)) + if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)) { gcodegen.writer().toolchange(new_extruder_id); + // Append the filament start G-code. + const std::string &start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(new_extruder_id); + if (! start_filament_gcode.empty()) { + // Process the start_filament_gcode for the active filament only. + gcodegen.placeholder_parser().set("current_extruder", new_extruder_id); + gcode += gcodegen.placeholder_parser().process(start_filament_gcode, new_extruder_id); + check_add_eol(gcode); + } + } // A phony move to the end position at the wipe tower. gcodegen.writer().travel_to_xy(Pointf(tcr.end_pos.x, tcr.end_pos.y)); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos)); @@ -199,15 +217,25 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen) std::string gcode; if (&m_priming != nullptr && ! m_priming.extrusions.empty()) { + // Disable linear advance for the wipe tower operations. + gcode += "M900 K0\n"; // Let the tool change be executed by the wipe tower class. // Inform the G-code writer about the changes done behind its back. gcode += m_priming.gcode; // Let the m_writer know the current extruder_id, but ignore the generated G-code. - gcodegen.writer().toolchange(m_priming.extrusions.back().tool); + unsigned int current_extruder_id = m_priming.extrusions.back().tool; + gcodegen.writer().toolchange(current_extruder_id); + gcodegen.placeholder_parser().set("current_extruder", current_extruder_id); // A phony move to the end position at the wipe tower. gcodegen.writer().travel_to_xy(Pointf(m_priming.end_pos.x, m_priming.end_pos.y)); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos)); - + // Append the filament start G-code, so the linear advance value will be restored. + const std::string &start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(current_extruder_id); + if (! start_filament_gcode.empty()) { + // Process the start_filament_gcode for the active filament only to restore the linear advance value. + gcode += gcodegen.placeholder_parser().process(start_filament_gcode, current_extruder_id); + check_add_eol(gcode); + } // Prepare a future wipe. gcodegen.m_wipe.path.points.clear(); // Start the wipe at the current position. @@ -554,8 +582,10 @@ bool GCode::_do_export(Print &print, FILE *file) // Write the custom start G-code writeln(file, start_gcode); // Process filament-specific gcode in extruder order. - for (const std::string &start_gcode : print.config.start_filament_gcode.values) - writeln(file, m_placeholder_parser.process(start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front()))); + if (! print.config.single_extruder_multi_material) { + for (const std::string &start_gcode : print.config.start_filament_gcode.values) + writeln(file, m_placeholder_parser.process(start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front()))); + } this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true); // Set other general things. @@ -727,8 +757,13 @@ bool GCode::_do_export(Print &print, FILE *file) write(file, this->retract()); write(file, m_writer.set_fan(false)); // Process filament-specific gcode in extruder order. - for (const std::string &end_gcode : print.config.end_filament_gcode.values) - writeln(file, m_placeholder_parser.process(end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front()))); + if (print.config.single_extruder_multi_material) { + // Process the end_filament_gcode for the active filament only. + writeln(file, m_placeholder_parser.process(print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id())); + } else { + for (const std::string &end_gcode : print.config.end_filament_gcode.values) + writeln(file, m_placeholder_parser.process(end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front()))); + } writeln(file, m_placeholder_parser.process(print.config.end_gcode, m_writer.extruder()->id())); write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100% write(file, m_writer.postamble()); @@ -2162,13 +2197,14 @@ GCode::retract(bool toolchange) std::string GCode::set_extruder(unsigned int extruder_id) { - m_placeholder_parser.set("current_extruder", extruder_id); if (!m_writer.need_toolchange(extruder_id)) return ""; // if we are running a single-extruder setup, just set the extruder and return nothing - if (!m_writer.multiple_extruders) + if (!m_writer.multiple_extruders) { + m_placeholder_parser.set("current_extruder", extruder_id); return m_writer.toolchange(extruder_id); + } // prepend retraction on the current extruder std::string gcode = this->retract(true); @@ -2176,23 +2212,41 @@ std::string GCode::set_extruder(unsigned int extruder_id) // Always reset the extrusion path, even if the tool change retract is set to zero. m_wipe.reset_path(); - // append custom toolchange G-code - if (m_writer.extruder() != nullptr && !m_config.toolchange_gcode.value.empty()) { + if (m_writer.extruder() != nullptr) { + // Process the custom end_filament_gcode in case of single_extruder_multi_material. + unsigned int old_extruder_id = m_writer.extruder()->id(); + const std::string &end_filament_gcode = m_config.end_filament_gcode.get_at(old_extruder_id); + if (m_config.single_extruder_multi_material && ! end_filament_gcode.empty()) { + gcode += m_placeholder_parser.process(end_filament_gcode, old_extruder_id); + check_add_eol(gcode); + } + } + + m_placeholder_parser.set("current_extruder", extruder_id); + + if (m_writer.extruder() != nullptr && ! m_config.toolchange_gcode.value.empty()) { + // Process the custom toolchange_gcode. DynamicConfig config; config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id())); config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); - gcode += m_placeholder_parser.process( - m_config.toolchange_gcode.value, extruder_id, &config) - + '\n'; + gcode += m_placeholder_parser.process(m_config.toolchange_gcode.value, extruder_id, &config); + check_add_eol(gcode); } - // if ooze prevention is enabled, park current extruder in the nearest - // standby point and set it to the standby temperature + // If ooze prevention is enabled, park current extruder in the nearest + // standby point and set it to the standby temperature. if (m_ooze_prevention.enable && m_writer.extruder() != nullptr) gcode += m_ooze_prevention.pre_toolchange(*this); - // append the toolchange command + // Append the toolchange command. gcode += m_writer.toolchange(extruder_id); - // set the new extruder to the operating temperature + // Append the filament start G-code for single_extruder_multi_material. + const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id); + if (m_config.single_extruder_multi_material && ! start_filament_gcode.empty()) { + // Process the start_filament_gcode for the active filament only. + gcode += m_placeholder_parser.process(start_filament_gcode, extruder_id); + check_add_eol(gcode); + } + // Set the new extruder to the operating temperature. if (m_ooze_prevention.enable) gcode += m_ooze_prevention.post_toolchange(*this); diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index aa90ac968..7c6af25b6 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -143,6 +143,7 @@ public: const FullPrintConfig &config() const { return m_config; } const Layer* layer() const { return m_layer; } GCodeWriter& writer() { return m_writer; } + PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } bool enable_cooling_markers() const { return m_enable_cooling_markers; } // For Perl bindings, to be used exclusively by unit tests. From bff7065360bfa99fc9033db5a1c21b5186ecd17c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Nov 2017 16:24:48 +0100 Subject: [PATCH 51/83] Fixed a bug in the support generator: There was half extrusion width gap created between the support and the support sheath. Now the support sheath will overlap with the support base by 10% of the extrusion width by default. --- xs/src/libslic3r/SupportMaterial.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 7a05cf998..c17468e70 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -2569,7 +2569,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // TODO: use brim ordering algorithm to_infill_polygons = to_polygons(to_infill); // TODO: use offset2_ex() - to_infill = offset_ex(to_infill, float(- flow.scaled_spacing())); + to_infill = offset_ex(to_infill, float(- 0.4 * flow.scaled_spacing())); extrusion_entities_append_paths( support_layer.support_fills.entities, to_polylines(STDMOVE(to_infill_polygons)), @@ -2776,7 +2776,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // TODO: use brim ordering algorithm Polygons to_infill_polygons = to_polygons(to_infill); // TODO: use offset2_ex() - to_infill = offset_ex(to_infill, - float(flow.scaled_spacing())); + to_infill = offset_ex(to_infill, - 0.4 * float(flow.scaled_spacing())); extrusion_entities_append_paths( base_layer.extrusions, to_polylines(STDMOVE(to_infill_polygons)), From a617e02ae667e49215ec577a7b0bfb9ccc2e0b3f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Nov 2017 17:45:03 +0100 Subject: [PATCH 52/83] New hot key for auto arrange: 'a'. --- lib/Slic3r/GUI/Plater.pm | 1 + lib/Slic3r/GUI/Plater/3D.pm | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index a6915bb7c..35e40b83c 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -99,6 +99,7 @@ sub new { $self->{canvas3D}->set_on_select_object($on_select_object); $self->{canvas3D}->set_on_double_click($on_double_click); $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); }); + $self->{canvas3D}->set_on_arrange(sub { $self->arrange }); $self->{canvas3D}->set_on_rotate_object_left(sub { $self->rotate(-45, Z, 'relative') }); $self->{canvas3D}->set_on_rotate_object_right(sub { $self->rotate( 45, Z, 'relative') }); $self->{canvas3D}->set_on_scale_object_uniformly(sub { $self->changescale(undef) }); diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 503a3d159..1c123e741 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -9,7 +9,7 @@ use Wx::Event qw(EVT_KEY_DOWN EVT_CHAR); use base qw(Slic3r::GUI::3DScene Class::Accessor); __PACKAGE__->mk_accessors(qw( - on_rotate_object_left on_rotate_object_right on_scale_object_uniformly + on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly on_remove_object on_increase_objects on_decrease_objects)); sub new { @@ -88,7 +88,9 @@ sub new { $event->Skip; } else { my $key = $event->GetKeyCode; - if ($key == ord('l')) { + if ($key == ord('a')) { + $self->on_arrange->() if $self->on_arrange; + } elsif ($key == ord('l')) { $self->on_rotate_object_left->() if $self->on_rotate_object_left; } elsif ($key == ord('r')) { $self->on_rotate_object_right->() if $self->on_rotate_object_right; @@ -122,6 +124,11 @@ sub set_on_right_click { $self->on_right_click($cb); } +sub set_on_arrange { + my ($self, $cb) = @_; + $self->on_arrange($cb); +} + sub set_on_rotate_object_left { my ($self, $cb) = @_; $self->on_rotate_object_left($cb); From cecaf6eabcba735837d9c631bbb79cf9b4aed6b4 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Nov 2017 17:55:39 +0100 Subject: [PATCH 53/83] Slight optimization of the filament_start_gcode insertion: It is not needed between the purging towers and the wipe tower brim. --- xs/src/libslic3r/GCode.cpp | 7 ------- xs/src/slic3r/GUI/PresetBundle.cpp | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 3bdc8d321..4e5f8b8fa 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -229,13 +229,6 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen) // A phony move to the end position at the wipe tower. gcodegen.writer().travel_to_xy(Pointf(m_priming.end_pos.x, m_priming.end_pos.y)); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos)); - // Append the filament start G-code, so the linear advance value will be restored. - const std::string &start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(current_extruder_id); - if (! start_filament_gcode.empty()) { - // Process the start_filament_gcode for the active filament only to restore the linear advance value. - gcode += gcodegen.placeholder_parser().process(start_filament_gcode, current_extruder_id); - check_add_eol(gcode); - } // Prepare a future wipe. gcodegen.m_wipe.path.points.clear(); // Start the wipe at the current position. diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 29afc07cd..b216cf70a 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -556,9 +556,9 @@ static inline int hex_digit_to_int(const char c) static inline bool parse_color(const std::string &scolor, unsigned char *rgb_out) { rgb_out[0] = rgb_out[1] = rgb_out[2] = 0; - const char *c = scolor.data() + 1; if (scolor.size() != 7 || scolor.front() != '#') return false; + const char *c = scolor.data() + 1; for (size_t i = 0; i < 3; ++ i) { int digit1 = hex_digit_to_int(*c ++); int digit2 = hex_digit_to_int(*c ++); From d161d4f78cd1f3ebcc4de1878574094225447fe6 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Nov 2017 18:26:15 +0100 Subject: [PATCH 54/83] There is a hack applied to add accelerator keys to the menu without being registered. Unfortunately this hack works on wxWidgets on Windows. On OSX or Linux, a warning is emited and no accelerator key is shown on the menu. This commit just removes the warnings, it does not add the menu accelerators. https://github.com/prusa3d/Slic3r/issues/539 --- lib/Slic3r/GUI/MainFrame.pm | 15 ++++++++------- lib/Slic3r/GUI/Plater.pm | 7 ++++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 09a64ca4b..2f93899a4 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -274,13 +274,14 @@ sub _init_menubar { # \xA0 is a non-breaing space. It is entered here to spoil the automatic accelerators, # as the simple numeric accelerators spoil all numeric data entry. # The camera control accelerators are captured by 3DScene Perl module instead. - $self->_append_menu_item($self->{viewMenu}, "Iso\t\xA00" , 'Iso View' , sub { $self->select_view('iso' ); }); - $self->_append_menu_item($self->{viewMenu}, "Top\t\xA01" , 'Top View' , sub { $self->select_view('top' ); }); - $self->_append_menu_item($self->{viewMenu}, "Bottom\t\xA02" , 'Bottom View' , sub { $self->select_view('bottom' ); }); - $self->_append_menu_item($self->{viewMenu}, "Front\t\xA03" , 'Front View' , sub { $self->select_view('front' ); }); - $self->_append_menu_item($self->{viewMenu}, "Rear\t\xA04" , 'Rear View' , sub { $self->select_view('rear' ); }); - $self->_append_menu_item($self->{viewMenu}, "Left\t\xA05" , 'Left View' , sub { $self->select_view('left' ); }); - $self->_append_menu_item($self->{viewMenu}, "Right\t\xA06" , 'Right View' , sub { $self->select_view('right' ); }); + my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] }; + $self->_append_menu_item($self->{viewMenu}, $accel->('Iso', '0'), 'Iso View' , sub { $self->select_view('iso' ); }); + $self->_append_menu_item($self->{viewMenu}, $accel->('Top', '1'), 'Top View' , sub { $self->select_view('top' ); }); + $self->_append_menu_item($self->{viewMenu}, $accel->('Bottom', '2'), 'Bottom View' , sub { $self->select_view('bottom' ); }); + $self->_append_menu_item($self->{viewMenu}, $accel->('Front', '3'), 'Front View' , sub { $self->select_view('front' ); }); + $self->_append_menu_item($self->{viewMenu}, $accel->('Rear', '4'), 'Rear View' , sub { $self->select_view('rear' ); }); + $self->_append_menu_item($self->{viewMenu}, $accel->('Left', '5'), 'Left View' , sub { $self->select_view('left' ); }); + $self->_append_menu_item($self->{viewMenu}, $accel->('Right', '6'), 'Right View' , sub { $self->select_view('right' ); }); } # Help menu diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 35e40b83c..5ae6ae352 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1918,13 +1918,14 @@ sub object_menu { my $frame = $self->GetFrame; my $menu = Wx::Menu->new; - $frame->_append_menu_item($menu, "Delete\t\xA0Del", 'Remove the selected object', sub { + my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] }; + $frame->_append_menu_item($menu, $accel->('Delete', 'Del'), 'Remove the selected object', sub { $self->remove; }, undef, 'brick_delete.png'); - $frame->_append_menu_item($menu, "Increase copies\t\xA0+", 'Place one more copy of the selected object', sub { + $frame->_append_menu_item($menu, $accel->('Increase copies', '+'), 'Place one more copy of the selected object', sub { $self->increase; }, undef, 'add.png'); - $frame->_append_menu_item($menu, "Decrease copies\t\xA0-", 'Remove one copy of the selected object', sub { + $frame->_append_menu_item($menu, $accel->('Decrease copies', '-'), 'Remove one copy of the selected object', sub { $self->decrease; }, undef, 'delete.png'); $frame->_append_menu_item($menu, "Set number of copies…", 'Change the number of copies of the selected object', sub { From 488feb23352b07c27b07468408f1318dd745810f Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 2 Sep 2017 13:36:20 -0500 Subject: [PATCH 55/83] Added --no-gui flag to force CLI usage (allows for CLI usage with AppImage build). Forced --gui flag in AppImage build. --- README.md | 2 ++ slic3r.pl | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4d9f90b01..40c22b98a 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,8 @@ The author of the Silk icon set is Mark James. --gui Forces the GUI launch instead of command line slicing (if you supply a model file, it will be loaded into the plater) --no-plater Disable the plater tab + --no-gui Forces the command line slicing instead of gui. + This takes precedence over --gui if both are present. --autosave Automatically export current configuration to the specified file Output options: diff --git a/slic3r.pl b/slic3r.pl index e49457c20..3a13c8230 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -31,6 +31,7 @@ my %cli_options = (); 'debug' => \$Slic3r::debug, 'gui' => \$opt{gui}, + 'no-gui' => \$opt{no_gui}, 'o|output=s' => \$opt{output}, 'save=s' => \$opt{save}, @@ -102,7 +103,7 @@ $config->apply($cli_config); # launch GUI my $gui; -if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") { +if ((!@ARGV || $opt{gui}) && !(!@ARGV || $opt{no_gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") { { no warnings 'once'; $Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // ''); @@ -267,6 +268,8 @@ Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ... --gui Forces the GUI launch instead of command line slicing (if you supply a model file, it will be loaded into the plater) --no-plater Disable the plater tab + --no-gui Forces the command line slicing instead of gui. + This takes precedence over --gui if both are present. --autosave Automatically export current configuration to the specified file Output options: From 91e1dc639d0fb4e58bf94f9953d20bbddbc0be91 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Nov 2017 19:00:26 +0100 Subject: [PATCH 56/83] Fix of the preceding cherry pick. --- slic3r.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slic3r.pl b/slic3r.pl index 3a13c8230..afddc77d3 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -103,7 +103,7 @@ $config->apply($cli_config); # launch GUI my $gui; -if ((!@ARGV || $opt{gui}) && !(!@ARGV || $opt{no_gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") { +if ((!@ARGV || $opt{gui}) && !$opt{no_gui} && !$opt{save} && eval "require Slic3r::GUI; 1") { { no warnings 'once'; $Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // ''); From 752d72f58d91abadf061be00fde9ba19be345919 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Nov 2017 19:04:07 +0100 Subject: [PATCH 57/83] Increased fill rate of the support 1st layer from 50% to 70%. --- xs/src/libslic3r/SupportMaterial.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index c17468e70..07943fb15 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -2602,7 +2602,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Base flange. filler->angle = raft_angle_1st_layer; filler->spacing = m_first_layer_flow.spacing(); - density = 0.5f; + // 70% of density on the 1st layer. + density = 0.7f; } else if (support_layer_id >= m_slicing_params.base_raft_layers) { filler->angle = raft_angle_interface; // We don't use $base_flow->spacing because we need a constant spacing From a0268a190651615ff85229e2e6115702921666ba Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Nov 2017 20:13:05 +0100 Subject: [PATCH 58/83] Some other accelerator keys were not displayed on Linux and OSX correctly. Suppress them on these systems. --- lib/Slic3r/GUI/Plater.pm | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 5ae6ae352..aeefbe6a3 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1495,6 +1495,7 @@ sub reload_from_disk { return if !defined $obj_idx; my $model_object = $self->{model}->objects->[$obj_idx]; + #FIXME convert to local file encoding return if !$model_object->input_file || !-e $model_object->input_file; @@ -1505,12 +1506,14 @@ sub reload_from_disk { my $o = $self->{model}->objects->[$new_obj_idx]; $o->clear_instances; $o->add_instance($_) for @{$model_object->instances}; + #$o->invalidate_bounding_box; if ($o->volumes_count == $model_object->volumes_count) { for my $i (0..($o->volumes_count-1)) { $o->get_volume($i)->config->apply($model_object->get_volume($i)->config); } } + #FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid, } $self->remove($obj_idx); @@ -1932,10 +1935,10 @@ sub object_menu { $self->set_number_of_copies; }, undef, 'textfield.png'); $menu->AppendSeparator(); - $frame->_append_menu_item($menu, "Rotate 45° clockwise\t\xA0l", 'Rotate the selected object by 45° clockwise', sub { + $frame->_append_menu_item($menu, $accel->('Rotate 45° clockwise', 'l'), 'Rotate the selected object by 45° clockwise', sub { $self->rotate(-45, Z, 'relative'); }, undef, 'arrow_rotate_clockwise.png'); - $frame->_append_menu_item($menu, "Rotate 45° counter-clockwise\t\xA0r", 'Rotate the selected object by 45° counter-clockwise', sub { + $frame->_append_menu_item($menu, $accel->('Rotate 45° counter-clockwise', 'r'), 'Rotate the selected object by 45° counter-clockwise', sub { $self->rotate(+45, Z, 'relative'); }, undef, 'arrow_rotate_anticlockwise.png'); @@ -1968,7 +1971,7 @@ sub object_menu { my $scaleMenu = Wx::Menu->new; my $scaleMenuItem = $menu->AppendSubMenu($scaleMenu, "Scale", 'Scale the selected object along a single axis'); $frame->_set_menu_item_icon($scaleMenuItem, 'arrow_out.png'); - $frame->_append_menu_item($scaleMenu, "Uniformly…\t\xA0s", 'Scale the selected object along the XYZ axes', sub { + $frame->_append_menu_item($scaleMenu, $accel->('Uniformly…', 's'), 'Scale the selected object along the XYZ axes', sub { $self->changescale(undef); }); $frame->_append_menu_item($scaleMenu, "Along X axis…", 'Scale the selected object along the X axis', sub { From 354408c7e62f022d86a87fbc13301d4f6d488298 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Nov 2017 20:25:59 +0100 Subject: [PATCH 59/83] Load the wxWidgets PNG handler only once. --- lib/Slic3r/GUI.pm | 2 +- xs/src/slic3r/GUI/PresetBundle.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 2dc560cad..a8799c458 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -112,7 +112,7 @@ sub OnInit { $run_wizard = 1 if $self->{preset_bundle}->has_defauls_only; # application frame - Wx::Image::AddHandler(Wx::PNGHandler->new); + Wx::Image::FindHandlerType(wxBITMAP_TYPE_PNG) || Wx::Image::AddHandler(Wx::PNGHandler->new); $self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new( # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. no_controller => $self->{app_config}->get('no_controller'), diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index b216cf70a..143e34e7f 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -34,7 +34,8 @@ PresetBundle::PresetBundle() : m_bitmapCompatible(new wxBitmap), m_bitmapIncompatible(new wxBitmap) { - ::wxInitAllImageHandlers(); + if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) + wxImage::AddHandler(new wxPNGHandler); // Create the ID config keys, as they are not part of the Static print config classes. this->prints.preset(0).config.opt_string("print_settings_id", true); From ca0626b168a15381ab499fe7d8153934a7889078 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 1 Dec 2017 18:55:57 +0100 Subject: [PATCH 60/83] Fixed regression bugs regarding print validation, fixed crashes when loading a config.ini with "compatible_printers" disabled export of "compatible_printers" into gcode and config.ini Enabled compatibility of printing multiple objects with support / no support with a wipe tower. --- xs/src/libslic3r/GCode.cpp | 3 ++- xs/src/libslic3r/Slicing.hpp | 2 +- xs/src/slic3r/GUI/PresetBundle.cpp | 27 ++++++++++++++++++++++++--- xs/src/slic3r/GUI/PresetBundle.hpp | 2 +- xs/xsp/Print.xsp | 7 ++++--- 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 4e5f8b8fa..a19fd8b53 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -794,7 +794,8 @@ bool GCode::_do_export(Print &print, FILE *file) for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++ i) { StaticPrintConfig *cfg = configs[i]; for (const std::string &key : cfg->keys()) - fprintf(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str()); + if (key != "compatible_printers") + fprintf(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str()); } } diff --git a/xs/src/libslic3r/Slicing.hpp b/xs/src/libslic3r/Slicing.hpp index 1534e19f5..b4a074bb5 100644 --- a/xs/src/libslic3r/Slicing.hpp +++ b/xs/src/libslic3r/Slicing.hpp @@ -103,7 +103,7 @@ inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters sp1.layer_height == sp2.layer_height && sp1.min_layer_height == sp2.min_layer_height && sp1.max_layer_height == sp2.max_layer_height && - sp1.max_suport_layer_height == sp2.max_suport_layer_height && +// sp1.max_suport_layer_height == sp2.max_suport_layer_height && sp1.first_print_layer_height == sp2.first_print_layer_height && sp1.first_object_layer_height == sp2.first_object_layer_height && sp1.first_object_layer_bridging == sp2.first_object_layer_bridging && diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 143e34e7f..1f872ffd6 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -1,4 +1,4 @@ -//#undef NDEBUGc +//#undef NDEBUG #include #include "PresetBundle.hpp" @@ -216,6 +216,8 @@ DynamicPrintConfig PresetBundle::full_config() const } } } + + out.erase("compatible_printers"); static const char *keys[] = { "perimeter", "infill", "solid_infill", "support_material", "support_material_interface" }; for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++ i) { @@ -278,8 +280,19 @@ void PresetBundle::load_config_file(const std::string &path) } // Load a config file from a boost property_tree. This is a private method called from load_config_file. -void PresetBundle::load_config_file_config(const std::string &path, const DynamicPrintConfig &config) +void PresetBundle::load_config_file_config(const std::string &path, DynamicPrintConfig &&config) { + // The "compatible_printers" field should not have been exported into a config.ini or a G-code anyway, + // but some of the alpha versions of Slic3r did. + { + ConfigOption *opt_compatible = config.optptr("compatible_printers"); + if (opt_compatible != nullptr) { + assert(opt_compatible->type() == coStrings); + if (opt_compatible->type() == coStrings) + static_cast(opt_compatible)->values.clear(); + } + } + // 1) Create a name from the file name. // Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles. std::string name = boost::filesystem::path(path).filename().string(); @@ -310,7 +323,7 @@ void PresetBundle::load_config_file_config(const std::string &path, const Dynami if (other_opt->is_scalar()) { for (size_t i = 0; i < configs.size(); ++ i) configs[i].option(key, false)->set(other_opt); - } else { + } else if (key != "compatible_printers") { for (size_t i = 0; i < configs.size(); ++ i) static_cast(configs[i].option(key, false))->set_at(other_opt, 0, i); } @@ -368,6 +381,14 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const } assert(! preset_name_dst.empty()); // Save preset_src->config into collection_dst under preset_name_dst. + // The "compatible_printers" field should not have been exported into a config.ini or a G-code anyway, + // but some of the alpha versions of Slic3r did. + ConfigOption *opt_compatible = preset_src->config.optptr("compatible_printers"); + if (opt_compatible != nullptr) { + assert(opt_compatible->type() == coStrings); + if (opt_compatible->type() == coStrings) + static_cast(opt_compatible)->values.clear(); + } collection_dst.load_preset(path, preset_name_dst, std::move(preset_src->config), activate).is_external = true; return preset_name_dst; }; diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/xs/src/slic3r/GUI/PresetBundle.hpp index 571dea0be..451ec69c1 100644 --- a/xs/src/slic3r/GUI/PresetBundle.hpp +++ b/xs/src/slic3r/GUI/PresetBundle.hpp @@ -78,7 +78,7 @@ public: void update_compatible_with_printer(bool select_other_if_incompatible); private: - void load_config_file_config(const std::string &path, const DynamicPrintConfig &config); + void load_config_file_config(const std::string &path, DynamicPrintConfig &&config); void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree); bool load_compatible_bitmaps(); diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 852f0dde0..bdf7b8991 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -215,10 +215,11 @@ _constant() bool has_infinite_skirt(); bool has_skirt(); std::vector extruders() const; - void validate() %code%{ + int validate() %code%{ std::string err = THIS->validate(); - if (! err.empty()) - throw std::invalid_argument(err.c_str()); + if (! err.empty()) + croak("Configuration is not valid: %s\n", err.c_str()); + RETVAL = 1; %}; Clone bounding_box(); Clone total_bounding_box(); From 73a539780a61621c1f2c3469d2ef74c2a00c62d8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 1 Dec 2017 18:56:32 +0100 Subject: [PATCH 61/83] Bumped up a version number. --- xs/src/libslic3r/libslic3r.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 644190d6a..9d2451869 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -14,7 +14,7 @@ #include #define SLIC3R_FORK_NAME "Slic3r Prusa Edition" -#define SLIC3R_VERSION "1.38.2" +#define SLIC3R_VERSION "1.38.3" #define SLIC3R_BUILD "UNKNOWN" typedef long coord_t; From 16bd3fc62457221f84f211fe90df31382a601f48 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 3 Dec 2017 09:43:00 +0100 Subject: [PATCH 62/83] Fixed an extruder ordering bug on the 1st layer wipe tower. This is a regression after introducing the extruder priming areas at the edge of the print bed. --- xs/src/libslic3r/GCode/ToolOrdering.hpp | 4 ++-- xs/src/libslic3r/GCode/WipeTower.hpp | 2 +- xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 2 +- xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 2 +- xs/src/libslic3r/Print.cpp | 12 ++++++++---- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/xs/src/libslic3r/GCode/ToolOrdering.hpp index fe2c394c5..abf1aa6b2 100644 --- a/xs/src/libslic3r/GCode/ToolOrdering.hpp +++ b/xs/src/libslic3r/GCode/ToolOrdering.hpp @@ -53,14 +53,14 @@ public: void clear() { m_layer_tools.clear(); } - // Get the first extruder printing the layer_tools, returns -1 if there is no layer printed. + // Get the first extruder printing, including the extruder priming areas, returns -1 if there is no layer printed. unsigned int first_extruder() const { return m_first_printing_extruder; } // Get the first extruder printing the layer_tools, returns -1 if there is no layer printed. unsigned int last_extruder() const { return m_last_printing_extruder; } // For a multi-material print, the printing extruders are ordered in the order they shall be primed. - std::vector all_extruders() const { return m_all_printing_extruders; } + const std::vector& all_extruders() const { return m_all_printing_extruders; } // Find LayerTools with the closest print_z. LayerTools& tools_for_layer(coordf_t print_z); diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp index c21c1710b..2c92e16e6 100644 --- a/xs/src/libslic3r/GCode/WipeTower.hpp +++ b/xs/src/libslic3r/GCode/WipeTower.hpp @@ -109,7 +109,7 @@ public: // print_z of the first layer. float first_layer_height, // Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object. - std::vector tools, + const std::vector &tools, // If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower. // If false, the last priming are will be large enough to wipe the last extruder sufficiently. bool last_wipe_inside_wipe_tower, diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index 951d71075..b81a8b04b 100644 --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -368,7 +368,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( // print_z of the first layer. float first_layer_height, // Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object. - std::vector tools, + const std::vector &tools, // If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower. // If false, the last priming are will be large enough to wipe the last extruder sufficiently. bool last_wipe_inside_wipe_tower, diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp index 15a96d367..b8c7ab31f 100644 --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp @@ -112,7 +112,7 @@ public: // print_z of the first layer. float first_layer_height, // Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object. - std::vector tools, + const std::vector &tools, // If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower. // If false, the last priming are will be large enough to wipe the last extruder sufficiently. bool last_wipe_inside_wipe_tower, diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 78ebf9294..c25e5de7d 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -974,7 +974,6 @@ void Print::_make_wipe_tower() // Let the ToolOrdering class know there will be initial priming extrusions at the start of the print. m_tool_ordering = ToolOrdering(*this, (unsigned int)-1, true); - unsigned int initial_extruder_id = m_tool_ordering.first_extruder(); if (! m_tool_ordering.has_wipe_tower()) // Don't generate any wipe tower. return; @@ -983,7 +982,7 @@ void Print::_make_wipe_tower() WipeTowerPrusaMM wipe_tower( float(this->config.wipe_tower_x.value), float(this->config.wipe_tower_y.value), float(this->config.wipe_tower_width.value), float(this->config.wipe_tower_per_color_wipe.value), - initial_extruder_id); + m_tool_ordering.first_extruder()); //wipe_tower.set_retract(); //wipe_tower.set_zhop(); @@ -1006,7 +1005,8 @@ void Print::_make_wipe_tower() // Generate the wipe tower layers. m_wipe_tower_tool_changes.reserve(m_tool_ordering.layer_tools().size()); - unsigned int current_extruder_id = initial_extruder_id; + // Set current_extruder_id to the last extruder primed. + unsigned int current_extruder_id = m_tool_ordering.all_extruders().back(); for (const ToolOrdering::LayerTools &layer_tools : m_tool_ordering.layer_tools()) { if (! layer_tools.has_wipe_tower) // This is a support only layer, or the wipe tower does not reach to this height. @@ -1021,7 +1021,11 @@ void Print::_make_wipe_tower() last_layer); std::vector tool_changes; for (unsigned int extruder_id : layer_tools.extruders) - if ((first_layer && extruder_id == initial_extruder_id) || extruder_id != current_extruder_id) { + // Call the wipe_tower.tool_change() at the first layer for the initial extruder + // to extrude the wipe tower brim, + if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || + // or when an extruder shall be switched. + extruder_id != current_extruder_id) { tool_changes.emplace_back(wipe_tower.tool_change(extruder_id, extruder_id == layer_tools.extruders.back(), WipeTower::PURPOSE_EXTRUDE)); current_extruder_id = extruder_id; } From 8af329e660904653f210e959d73ae4269b5a459c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 4 Dec 2017 10:48:40 +0100 Subject: [PATCH 63/83] Added Perl to C++ interfaces for creating the preset editor pages from C++ and to add debugging menus from C++. These lightweigth interfaces should help new team members to hack the UI without a Perl knowledge. --- lib/Slic3r/GUI.pm | 1 + lib/Slic3r/GUI/MainFrame.pm | 8 ++++++ xs/src/slic3r/GUI/GUI.cpp | 49 +++++++++++++++++++++++++++++++++++++ xs/src/slic3r/GUI/GUI.hpp | 15 ++++++++++++ xs/xsp/GUI.xsp | 15 ++++++++++++ 5 files changed, 88 insertions(+) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index a8799c458..78e82a6ef 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -79,6 +79,7 @@ sub OnInit { # Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r" # Mac: "~/Library/Application Support/Slic3r" Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir); + Slic3r::GUI::set_wxapp($self); $self->{notifier} = Slic3r::GUI::Notifier->new; $self->{app_config} = Slic3r::GUI::AppConfig->new; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 2f93899a4..370bac2ed 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -22,6 +22,7 @@ sub new { my ($class, %params) = @_; my $self = $class->SUPER::new(undef, -1, $Slic3r::FORK_NAME . ' - ' . $Slic3r::VERSION, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE); + Slic3r::GUI::set_main_frame($self); if ($^O eq 'MSWin32') { # Load the icon either from the exe, or from the ico file. my $iconfile = Slic3r::decode_path($FindBin::Bin) . '\slic3r.exe'; @@ -92,6 +93,8 @@ sub _init_tabpanel { my ($self) = @_; $self->{tabpanel} = my $panel = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL); + Slic3r::GUI::set_tab_panel($panel); + EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{tabpanel}, sub { my $panel = $self->{tabpanel}->GetCurrentPage; $panel->OnActivate if $panel->can('OnActivate'); @@ -145,6 +148,9 @@ sub _init_tabpanel { $tab->load_current_preset; $panel->AddPage($tab, $tab->title); } + +#TODO this is an example of a Slic3r XS interface call to add a new preset editor page to the main view. +# Slic3r::GUI::create_preset_tab("print"); if ($self->{plater}) { $self->{plater}->on_select_preset(sub { @@ -330,6 +336,8 @@ sub _init_menubar { $menubar->Append($windowMenu, "&Window"); $menubar->Append($self->{viewMenu}, "&View") if $self->{viewMenu}; $menubar->Append($helpMenu, "&Help"); + # Add an optional debug menu. In production code, the add_debug_menu() call should do nothing. + Slic3r::GUI::add_debug_menu($menubar); $self->SetMenuBar($menubar); } } diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 63cc7749d..8db0508f1 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -13,6 +13,14 @@ #pragma comment(lib, "user32.lib") #endif +#include +#include +#include +#include +#include +#include +#include + namespace Slic3r { namespace GUI { #if __APPLE__ @@ -134,4 +142,45 @@ void break_to_debugger() #endif /* _WIN32 */ } +// Passing the wxWidgets GUI classes instantiated by the Perl part to C++. +wxApp *g_wxApp = nullptr; +wxFrame *g_wxMainFrame = nullptr; +wxNotebook *g_wxTabPanel = nullptr; + +void set_wxapp(wxApp *app) +{ + g_wxApp = app; +} + +void set_main_frame(wxFrame *main_frame) +{ + g_wxMainFrame = main_frame; +} + +void set_tab_panel(wxNotebook *tab_panel) +{ + g_wxTabPanel = tab_panel; +} + +void add_debug_menu(wxMenuBar *menu) +{ +#if 0 + auto debug_menu = new wxMenu(); + debug_menu->Append(wxWindow::NewControlId(1), "Some debug"); + menu->Append(debug_menu, _T("&Debug")); +#endif +} + +void create_preset_tab(const char *name) +{ + auto *panel = new wxPanel(g_wxTabPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL); + // Vertical sizer to hold the choice menu and the rest of the page. + auto *sizer = new wxBoxSizer(wxVERTICAL); + sizer->SetSizeHints(panel); + panel->SetSizer(sizer); + auto *button = new wxButton(panel, wxID_ANY, "Hello World", wxDefaultPosition, wxDefaultSize, 0); + sizer->Add(button, 0, 0, 0); + g_wxTabPanel->AddPage(panel, name); +} + } } diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 955a1cd8d..3634e0bc8 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -4,6 +4,11 @@ #include #include +class wxApp; +class wxFrame; +class wxMenuBar; +class wxNotebook; + namespace Slic3r { namespace GUI { void disable_screensaver(); @@ -12,6 +17,16 @@ std::vector scan_serial_ports(); bool debugged(); void break_to_debugger(); +// Passing the wxWidgets GUI classes instantiated by the Perl part to C++. +void set_wxapp(wxApp *app); +void set_main_frame(wxFrame *main_frame); +void set_tab_panel(wxNotebook *tab_panel); + +void add_debug_menu(wxMenuBar *menu); +// Create a new preset tab (print, filament or printer), +// add it at the end of the tab panel. +void create_preset_tab(const char *name); + } } #endif diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index ce3c178a1..d6b55dbf1 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -22,3 +22,18 @@ bool debugged() void break_to_debugger() %code{% Slic3r::GUI::break_to_debugger(); %}; + +void set_wxapp(SV *ui) + %code%{ Slic3r::GUI::set_wxapp((wxApp*)wxPli_sv_2_object(aTHX_ ui, "Wx::App")); %}; + +void set_main_frame(SV *ui) + %code%{ Slic3r::GUI::set_main_frame((wxFrame*)wxPli_sv_2_object(aTHX_ ui, "Wx::Frame")); %}; + +void set_tab_panel(SV *ui) + %code%{ Slic3r::GUI::set_tab_panel((wxNotebook*)wxPli_sv_2_object(aTHX_ ui, "Wx::Notebook")); %}; + +void add_debug_menu(SV *ui) + %code%{ Slic3r::GUI::add_debug_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar")); %}; + +void create_preset_tab(const char *name) + %code%{ Slic3r::GUI::create_preset_tab(name); %}; From 2b0b8e6e688fc60c79edb3f1282b7e06b60dfc17 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 4 Dec 2017 11:57:54 +0100 Subject: [PATCH 64/83] Removed the hard-coded priming line when both single_extruder_multi_material and wipe_tower are enabled, and the print prints with a single extruder only. Newly the same situation will be handled through a conditional G-code in the following format: {if not has_wipe_tower} ; Do the priming {endif} --- xs/src/libslic3r/GCode.cpp | 75 ++++++++++++-------------- xs/src/libslic3r/GCode.hpp | 1 - xs/src/libslic3r/PlaceholderParser.hpp | 1 + 3 files changed, 36 insertions(+), 41 deletions(-) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index a19fd8b53..bc3a86993 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -241,19 +241,6 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen) return gcode; } -std::string WipeTowerIntegration::prime_single_color_print(const Print & /* print */, unsigned int initial_tool, GCode & /* gcodegen */) -{ - std::string gcode = "\ -G1 Z0.250 F7200.000\n\ -G1 X50.0 E80.0 F1000.0\n\ -G1 X160.0 E20.0 F1000.0\n\ -G1 Z0.200 F7200.000\n\ -G1 X220.0 E13 F1000.0\n\ -G1 X240.0 E0 F1000.0\n\ -G1 E-4 F1000.0\n"; - return gcode; -} - std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer) { std::string gcode; @@ -530,6 +517,7 @@ bool GCode::_do_export(Print &print, FILE *file) unsigned int initial_extruder_id = (unsigned int)-1; unsigned int final_extruder_id = (unsigned int)-1; size_t initial_print_object_id = 0; + bool has_wipe_tower = false; if (print.config.complete_objects.value) { // Find the 1st printing object, find its tool ordering and the initial extruder ID. for (; initial_print_object_id < print.objects.size(); ++initial_print_object_id) { @@ -544,6 +532,7 @@ bool GCode::_do_export(Print &print, FILE *file) ToolOrdering(print, initial_extruder_id) : print.m_tool_ordering; initial_extruder_id = tool_ordering.first_extruder(); + has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower(); } if (initial_extruder_id == (unsigned int)-1) { // Nothing to print! @@ -566,6 +555,8 @@ bool GCode::_do_export(Print &print, FILE *file) m_placeholder_parser.set("current_extruder", initial_extruder_id); // Useful for sequential prints. m_placeholder_parser.set("current_object_idx", 0); + // For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided. + m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); std::string start_gcode = m_placeholder_parser.process(print.config.start_gcode.value, initial_extruder_id); // Set bed temperature if the start G-code does not contain any bed temp control G-codes. @@ -575,7 +566,14 @@ bool GCode::_do_export(Print &print, FILE *file) // Write the custom start G-code writeln(file, start_gcode); // Process filament-specific gcode in extruder order. - if (! print.config.single_extruder_multi_material) { + if (print.config.single_extruder_multi_material) { + if (has_wipe_tower) { + // Wipe tower will control the extruder switching, it will call the start_filament_gcode. + } else { + // Only initialize the initial extruder. + writeln(file, m_placeholder_parser.process(print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id)); + } + } else { for (const std::string &start_gcode : print.config.start_filament_gcode.values) writeln(file, m_placeholder_parser.process(start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front()))); } @@ -705,32 +703,29 @@ bool GCode::_do_export(Print &print, FILE *file) // All extrusion moves with the same top layer height are extruded uninterrupted. std::vector>> layers_to_print = collect_layers_to_print(print); // Prusa Multi-Material wipe tower. - if (print.has_wipe_tower() && ! layers_to_print.empty()) { - if (tool_ordering.has_wipe_tower()) { - m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get())); - write(file, m_wipe_tower->prime(*this)); - // Verify, whether the print overaps the priming extrusions. - BoundingBoxf bbox_print(get_print_extrusions_extents(print)); - coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON; - for (const PrintObject *print_object : print.objects) - bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz)); - bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz)); - BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print)); - bbox_prime.offset(0.5f); - // Beep for 500ms, tone 800Hz. Yet better, play some Morse. - write(file, this->retract()); - fprintf(file, "M300 S800 P500\n"); - if (bbox_prime.overlap(bbox_print)) { - // Wait for the user to remove the priming extrusions, otherwise they would - // get covered by the print. - fprintf(file, "M1 Remove priming towers and click button.\n"); - } else { - // Just wait for a bit to let the user check, that the priming succeeded. - //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix. - fprintf(file, "M1 S10\n"); - } - } else - write(file, WipeTowerIntegration::prime_single_color_print(print, initial_extruder_id, *this)); + if (has_wipe_tower && ! layers_to_print.empty()) { + m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get())); + write(file, m_wipe_tower->prime(*this)); + // Verify, whether the print overaps the priming extrusions. + BoundingBoxf bbox_print(get_print_extrusions_extents(print)); + coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON; + for (const PrintObject *print_object : print.objects) + bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz)); + bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz)); + BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print)); + bbox_prime.offset(0.5f); + // Beep for 500ms, tone 800Hz. Yet better, play some Morse. + write(file, this->retract()); + fprintf(file, "M300 S800 P500\n"); + if (bbox_prime.overlap(bbox_print)) { + // Wait for the user to remove the priming extrusions, otherwise they would + // get covered by the print. + fprintf(file, "M1 Remove priming towers and click button.\n"); + } else { + // Just wait for a bit to let the user check, that the priming succeeded. + //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix. + fprintf(file, "M1 S10\n"); + } } // Extrude the layers. for (auto &layer : layers_to_print) { diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 7c6af25b6..72c39fe38 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -90,7 +90,6 @@ public: m_brim_done(false) {} std::string prime(GCode &gcodegen); - static std::string prime_single_color_print(const Print & /* print */, unsigned int initial_tool, GCode & /* gcodegen */); void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; } std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer); std::string finalize(GCode &gcodegen); diff --git a/xs/src/libslic3r/PlaceholderParser.hpp b/xs/src/libslic3r/PlaceholderParser.hpp index 9ec2eedea..25264d461 100644 --- a/xs/src/libslic3r/PlaceholderParser.hpp +++ b/xs/src/libslic3r/PlaceholderParser.hpp @@ -22,6 +22,7 @@ public: void set(const std::string &key, const std::string &value) { this->set(key, new ConfigOptionString(value)); } void set(const std::string &key, int value) { this->set(key, new ConfigOptionInt(value)); } void set(const std::string &key, unsigned int value) { this->set(key, int(value)); } + void set(const std::string &key, bool value) { this->set(key, new ConfigOptionBool(value)); } void set(const std::string &key, double value) { this->set(key, new ConfigOptionFloat(value)); } void set(const std::string &key, const std::vector &values) { this->set(key, new ConfigOptionStrings(values)); } void set(const std::string &key, ConfigOption *opt) { m_config.set_key_value(key, opt); } From fb1bebd982c6a92ccb5217f2d0530d0bcf42ce76 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 4 Dec 2017 17:42:35 +0100 Subject: [PATCH 65/83] PlaceholderParser: simplistic extension to parse UTF8 characters in the G-code and string constants. Solves https://github.com/prusa3d/Slic3r/issues/600 --- xs/src/libslic3r/PlaceholderParser.cpp | 40 ++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index 060252387..6c90a6eb6 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -22,6 +22,8 @@ #endif #include +// Unicode iterator to iterate over utf8. +#include // Spirit v2.5 allows you to suppress automatic generation // of predefined terminals to speed up complation. With @@ -598,6 +600,39 @@ namespace client template static bool parse_inf(It&, It const&, Attr&) { return false; } }; + struct unicode_char_parser : qi::primitive_parser + { + // Define the attribute type exposed by this parser component + template + struct attribute + { + typedef wchar_t type; + }; + + // This function is called during the actual parsing process + template + bool parse(Iterator& first, Iterator const& last, Context& context, Skipper const& skipper, Attribute& attr) const + { + skip_over(first, last, skipper); + if (first == last) return false; + + boost::u8_to_u32_iterator f(first); + boost::u8_to_u32_iterator l(last); + if (f == l) return false; + + attr = *f++; + first = f.base(); + return true; + } + + // This function is called during error handling to create a human readable string for the error context. + template + spirit::info what(Context&) const + { + return spirit::info("unicode_char"); + } + }; + /////////////////////////////////////////////////////////////////////////// // Our calculator grammar /////////////////////////////////////////////////////////////////////////// @@ -617,6 +652,7 @@ namespace client qi::no_skip_type no_skip; qi::real_parser strict_double; spirit::ascii::char_type char_; + unicode_char_parser utf8char; spirit::bool_type bool_; spirit::int_type int_; spirit::double_type double_; @@ -649,7 +685,7 @@ namespace client // Free-form text up to a first brace, including spaces and newlines. // The free-form text will be inserted into the processed text without a modification. - text = no_skip[raw[+(char_ - '[' - '{')]]; + text = no_skip[raw[+(utf8char - char_('[') - char_('{'))]]; text.name("text"); // New style of macro expansion. @@ -749,7 +785,7 @@ namespace client | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] - | raw[lexeme['"' > *((char_ - char_('\\') - char_('"')) | ('\\' > char_)) > '"']] + | raw[lexeme['"' > *((utf8char - char_('\\') - char_('"')) | ('\\' > char_)) > '"']] [ px::bind(&FactorActions::string_, _1, _val) ] ); factor.name("factor"); From 1244fd09eba10f301278778dc9d10e0b752b8c76 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 4 Dec 2017 18:22:42 +0100 Subject: [PATCH 66/83] More efficient utf8 parser for the PlaceholderParser. --- xs/src/libslic3r/PlaceholderParser.cpp | 63 +++++++++++++++++++------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index 6c90a6eb6..b09e06133 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -600,37 +600,66 @@ namespace client template static bool parse_inf(It&, It const&, Attr&) { return false; } }; - struct unicode_char_parser : qi::primitive_parser + // This parser is to be used inside a raw[] directive to accept a single valid UTF-8 character. + // If an invalid UTF-8 sequence is encountered, a qi::expectation_failure is thrown. + struct utf8_char_skipper_parser : qi::primitive_parser { // Define the attribute type exposed by this parser component template struct attribute { - typedef wchar_t type; - }; + typedef wchar_t type; + }; // This function is called during the actual parsing process template bool parse(Iterator& first, Iterator const& last, Context& context, Skipper const& skipper, Attribute& attr) const { - skip_over(first, last, skipper); - if (first == last) return false; - - boost::u8_to_u32_iterator f(first); - boost::u8_to_u32_iterator l(last); - if (f == l) return false; - - attr = *f++; - first = f.base(); - return true; - } + // The skipper shall always be empty, any white space will be accepted. + // skip_over(first, last, skipper); + if (first == last) + return false; + // Iterator over the UTF-8 sequence. + auto it = first; + // Read the first byte of the UTF-8 sequence. + unsigned char c = static_cast(*it ++); + // UTF-8 sequence must not start with a continuation character: + if ((c & 0xC0) == 0x80) + goto err; + // Skip high surrogate first if there is one. + // If the most significant bit with a zero in it is in position + // 8-N then there are N bytes in this UTF-8 sequence: + unsigned int cnt = 0; + { + unsigned char mask = 0x80u; + unsigned int result = 0; + while (c & mask) { + ++ result; + mask >>= 1; + } + cnt = (result == 0) ? 1 : ((result > 4) ? 4 : result); + } + // Since we haven't read in a value, we need to validate the code points: + for (-- cnt; cnt > 0; -- cnt) { + if (it == last) + goto err; + c = static_cast(*it ++); + // We must have a continuation byte: + if (cnt > 1 && (c & 0xC0) != 0x80) + goto err; + } + first = it; + return true; + err: + boost::throw_exception(qi::expectation_failure(first, last, spirit::info("Invalid utf8 sequence"))); + } // This function is called during error handling to create a human readable string for the error context. template - spirit::info what(Context&) const + spirit::info what(Context&) const { return spirit::info("unicode_char"); - } + } }; /////////////////////////////////////////////////////////////////////////// @@ -652,7 +681,7 @@ namespace client qi::no_skip_type no_skip; qi::real_parser strict_double; spirit::ascii::char_type char_; - unicode_char_parser utf8char; + utf8_char_skipper_parser utf8char; spirit::bool_type bool_; spirit::int_type int_; spirit::double_type double_; From 8746f84fa2b8585270d36b4070693a188e722e33 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 5 Dec 2017 15:54:24 +0100 Subject: [PATCH 67/83] Improved error reporting of the PlaceholderParser. The PlaceholderParser is currently used by the GCode.cpp and by Printer.cpp to generate a new name for the exported G-code or SVG file. The PlaceholderParser::process() will throw a runtime_error with a comprehensive error message. The G-code export will include these error messages into the G-code text with !!!!!! separators, and the GUI will inform the user, that the G-code export failed. --- lib/Slic3r/GUI/MainFrame.pm | 1 + lib/Slic3r/GUI/Plater.pm | 9 +- lib/Slic3r/Print.pm | 10 +- slic3r.pl | 2 + xs/src/libslic3r/GCode.cpp | 78 ++++++---- xs/src/libslic3r/GCode.hpp | 10 +- xs/src/libslic3r/PlaceholderParser.cpp | 188 +++++++++++-------------- xs/src/libslic3r/Print.cpp | 6 +- xs/xsp/GCode.xsp | 9 +- xs/xsp/PlaceholderParser.xsp | 8 +- xs/xsp/Print.xsp | 10 +- 11 files changed, 190 insertions(+), 141 deletions(-) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 370bac2ed..5ae6ac08f 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -422,6 +422,7 @@ sub quick_slice { if ($params{reslice}) { $output_file = $qs_last_output_file if defined $qs_last_output_file; } elsif ($params{save_as}) { + # The following line may die if the output_filename_format template substitution fails. $output_file = $sprint->output_filepath; $output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/ if $params{export_svg}; my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:', diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index aeefbe6a3..0cb433309 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1291,9 +1291,11 @@ sub export_gcode { # select output file if ($output_file) { - $self->{export_gcode_output_file} = $self->{print}->output_filepath($output_file); + $self->{export_gcode_output_file} = eval { $self->{print}->output_filepath($output_file) }; + Slic3r::GUI::catch_error($self) and return; } else { - my $default_output_file = $self->{print}->output_filepath($main::opt{output} // ''); + my $default_output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') }; + Slic3r::GUI::catch_error($self) and return; my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', wxTheApp->{app_config}->get_last_output_dir(dirname($default_output_file)), basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); @@ -1544,7 +1546,8 @@ sub export_amf { sub _get_export_file { my ($self, $format) = @_; my $suffix = $format eq 'STL' ? '.stl' : '.amf.xml'; - my $output_file = $self->{print}->output_filepath($main::opt{output} // ''); + my $output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') }; + Slic3r::GUI::catch_error($self) and return undef; $output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/; my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file), basename($output_file), &Slic3r::GUI::MODEL_WILDCARD, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 1613b7e45..12ad2f12f 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -65,6 +65,9 @@ sub process { } # G-code export process, running at a background thread. +# The export_gcode may die for various reasons (fails to process output_filename_format, +# write error into the G-code, cannot execute post-processing scripts). +# It is up to the caller to show an error message. sub export_gcode { my $self = shift; my %params = @_; @@ -73,11 +76,12 @@ sub export_gcode { $self->process; # output everything to a G-code file + # The following call may die if the output_filename_format template substitution fails. my $output_file = $self->output_filepath($params{output_file} // ''); $self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : "")); - die "G-code export to " . $output_file . " failed\n" - if ! Slic3r::GCode->new->do_export($self, $output_file); + # The following line may die for multiple reasons. + Slic3r::GCode->new->do_export($self, $output_file); # run post-processing scripts if (@{$self->config->post_process}) { @@ -99,6 +103,7 @@ sub export_gcode { } # Export SVG slices for the offline SLA printing. +# The export_svg is expected to be executed inside an eval block. sub export_svg { my $self = shift; my %params = @_; @@ -107,6 +112,7 @@ sub export_svg { my $fh = $params{output_fh}; if (!$fh) { + # The following line may die if the output_filename_format template substitution fails. my $output_file = $self->output_filepath($params{output_file}); $output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/; Slic3r::open(\$fh, ">", $output_file) or die "Failed to open $output_file for writing\n"; diff --git a/slic3r.pl b/slic3r.pl index afddc77d3..c2bee2219 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -219,6 +219,8 @@ if (@ARGV) { # slicing from command line $sprint->export_svg; } else { my $t0 = [gettimeofday]; + # The following call may die if the output_filename_format template substitution fails, + # if the file cannot be written into, or if the post processing scripts cannot be executed. $sprint->export_gcode; # output some statistics diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index bc3a86993..b5f186630 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -187,7 +187,7 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T if (! start_filament_gcode.empty()) { // Process the start_filament_gcode for the active filament only. gcodegen.placeholder_parser().set("current_extruder", new_extruder_id); - gcode += gcodegen.placeholder_parser().process(start_filament_gcode, new_extruder_id); + gcode += gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id); check_add_eol(gcode); } } @@ -362,7 +362,7 @@ std::vector>> GCode::collec return layers_to_print; } -bool GCode::do_export(Print *print, const char *path) +void GCode::do_export(Print *print, const char *path) { // Remove the old g-code if it exists. boost::nowide::remove(path); @@ -372,23 +372,34 @@ bool GCode::do_export(Print *print, const char *path) FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb"); if (file == nullptr) - return false; + throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); - bool result = this->_do_export(*print, file); + this->m_placeholder_parser_failed_templates.clear(); + this->_do_export(*print, file); fclose(file); - - if (result && boost::nowide::rename(path_tmp.c_str(), path) != 0) { - boost::nowide::cerr << "Failed to remove the output G-code file from " << path_tmp << " to " << path - << ". Is " << path_tmp << " locked?" << std::endl; - result = false; - } - - if (! result) + if (ferror(file)) { boost::nowide::remove(path_tmp.c_str()); - return result; + throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); + } + if (! this->m_placeholder_parser_failed_templates.empty()) { + // G-code export proceeded, but some of the PlaceholderParser substitutions failed. + std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n"; + for (const std::string &name : this->m_placeholder_parser_failed_templates) + msg += std::string("\t") + name + "\n"; + msg += "\nPlease inspect the file "; + msg += path_tmp + " for error messages enclosed between\n"; + msg += " !!!!! Failed to process the custom G-code template ...\n"; + msg += "and\n"; + msg += " !!!!! End of an error report for the custom G-code template ...\n"; + throw std::runtime_error(msg); + } + if (boost::nowide::rename(path_tmp.c_str(), path) != 0) + throw std::runtime_error( + std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + + "Is " + path_tmp + " locked?" + '\n'); } -bool GCode::_do_export(Print &print, FILE *file) +void GCode::_do_export(Print &print, FILE *file) { // How many times will be change_layer() called? // change_layer() in turn increments the progress bar status. @@ -557,7 +568,7 @@ bool GCode::_do_export(Print &print, FILE *file) m_placeholder_parser.set("current_object_idx", 0); // For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided. m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); - std::string start_gcode = m_placeholder_parser.process(print.config.start_gcode.value, initial_extruder_id); + std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config.start_gcode.value, initial_extruder_id); // Set bed temperature if the start G-code does not contain any bed temp control G-codes. this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true); @@ -571,11 +582,11 @@ bool GCode::_do_export(Print &print, FILE *file) // Wipe tower will control the extruder switching, it will call the start_filament_gcode. } else { // Only initialize the initial extruder. - writeln(file, m_placeholder_parser.process(print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id)); + writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id)); } } else { for (const std::string &start_gcode : print.config.start_filament_gcode.values) - writeln(file, m_placeholder_parser.process(start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front()))); + writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front()))); } this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true); @@ -668,7 +679,7 @@ bool GCode::_do_export(Print &print, FILE *file) // another one, set first layer temperatures. This happens before the Z move // is triggered, so machine has more time to reach such temperatures. m_placeholder_parser.set("current_object_idx", int(finished_objects)); - std::string between_objects_gcode = m_placeholder_parser.process(print.config.between_objects_gcode.value, initial_extruder_id); + std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config.between_objects_gcode.value, initial_extruder_id); // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false); this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false); @@ -747,12 +758,12 @@ bool GCode::_do_export(Print &print, FILE *file) // Process filament-specific gcode in extruder order. if (print.config.single_extruder_multi_material) { // Process the end_filament_gcode for the active filament only. - writeln(file, m_placeholder_parser.process(print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id())); + writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id())); } else { for (const std::string &end_gcode : print.config.end_filament_gcode.values) - writeln(file, m_placeholder_parser.process(end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front()))); + writeln(file, this->placeholder_parser_process("end_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front()))); } - writeln(file, m_placeholder_parser.process(print.config.end_gcode, m_writer.extruder()->id())); + writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id())); write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100% write(file, m_writer.postamble()); @@ -793,8 +804,21 @@ bool GCode::_do_export(Print &print, FILE *file) fprintf(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str()); } } +} - return true; +std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) +{ + try { + return m_placeholder_parser.process(templ, current_extruder_id, config_override); + } catch (std::runtime_error &err) { + // Collect the names of failed template substitutions for error reporting. + this->m_placeholder_parser_failed_templates.insert(name); + // Insert the macro error message into the G-code. + return + std::string("!!!!! Failed to process the custom G-code template ") + name + "\n" + + err.what() + + "!!!!! End of an error report for the custom G-code template " + name + "\n"; + } } // Parse the custom G-code, try to find mcode_set_temp_dont_wait and mcode_set_temp_and_wait inside the custom G-code. @@ -997,7 +1021,7 @@ void GCode::process_layer( DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1)); config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); - gcode += m_placeholder_parser.process( + gcode += this->placeholder_parser_process("before_layer_gcode", print.config.before_layer_gcode.value, m_writer.extruder()->id(), &config) + "\n"; } @@ -1007,7 +1031,7 @@ void GCode::process_layer( DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); - gcode += m_placeholder_parser.process( + gcode += this->placeholder_parser_process("layer_gcode", print.config.layer_gcode.value, m_writer.extruder()->id(), &config) + "\n"; } @@ -2206,7 +2230,7 @@ std::string GCode::set_extruder(unsigned int extruder_id) unsigned int old_extruder_id = m_writer.extruder()->id(); const std::string &end_filament_gcode = m_config.end_filament_gcode.get_at(old_extruder_id); if (m_config.single_extruder_multi_material && ! end_filament_gcode.empty()) { - gcode += m_placeholder_parser.process(end_filament_gcode, old_extruder_id); + gcode += placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id); check_add_eol(gcode); } } @@ -2218,7 +2242,7 @@ std::string GCode::set_extruder(unsigned int extruder_id) DynamicConfig config; config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id())); config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); - gcode += m_placeholder_parser.process(m_config.toolchange_gcode.value, extruder_id, &config); + gcode += placeholder_parser_process("toolchange_gcode", m_config.toolchange_gcode.value, extruder_id, &config); check_add_eol(gcode); } @@ -2232,7 +2256,7 @@ std::string GCode::set_extruder(unsigned int extruder_id) const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id); if (m_config.single_extruder_multi_material && ! start_filament_gcode.empty()) { // Process the start_filament_gcode for the active filament only. - gcode += m_placeholder_parser.process(start_filament_gcode, extruder_id); + gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id); check_add_eol(gcode); } // Set the new extruder to the operating temperature. diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 72c39fe38..2fd3b39d3 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -130,7 +130,8 @@ public: {} ~GCode() {} - bool do_export(Print *print, const char *path); + // throws std::runtime_exception + void do_export(Print *print, const char *path); // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests. const Pointf& origin() const { return m_origin; } @@ -143,6 +144,9 @@ public: const Layer* layer() const { return m_layer; } GCodeWriter& writer() { return m_writer; } PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } + // Process a template through the placeholder parser, collect error messages to be reported + // inside the generated string and after the G-code export finishes. + std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr); bool enable_cooling_markers() const { return m_enable_cooling_markers; } // For Perl bindings, to be used exclusively by unit tests. @@ -151,7 +155,7 @@ public: void apply_print_config(const PrintConfig &print_config); protected: - bool _do_export(Print &print, FILE *file); + void _do_export(Print &print, FILE *file); // Object and support extrusions of the same PrintObject at the same print_z. struct LayerToPrint @@ -223,6 +227,8 @@ protected: FullPrintConfig m_config; GCodeWriter m_writer; PlaceholderParser m_placeholder_parser; + // Collection of templates, on which the placeholder substitution failed. + std::set m_placeholder_parser_failed_templates; OozePrevention m_ooze_prevention; Wipe m_wipe; AvoidCrossingPerimeters m_avoid_crossing_perimeters; diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index b09e06133..f9f72a9e9 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -368,7 +368,7 @@ namespace client value = lhs.to_string() == rhs.to_string(); } else { boost::throw_exception(qi::expectation_failure( - lhs.it_range.begin(), rhs.it_range.end(), spirit::info("Cannot compare the types."))); + lhs.it_range.begin(), rhs.it_range.end(), spirit::info("*Cannot compare the types."))); } lhs.type = TYPE_BOOL; lhs.data.b = (op == '=') ? value : !value; @@ -387,7 +387,7 @@ namespace client void throw_exception(const char *message) const { boost::throw_exception(qi::expectation_failure( - this->it_range.begin(), this->it_range.end(), spirit::info(message))); + this->it_range.begin(), this->it_range.end(), spirit::info(std::string("*") + message))); } void throw_if_not_numeric(const char *message) const @@ -417,6 +417,7 @@ namespace client const PlaceholderParser *pp = nullptr; const DynamicConfig *config_override = nullptr; const size_t current_extruder_id = 0; + std::string error_message; const ConfigOption* resolve_symbol(const std::string &opt_key) const { @@ -444,26 +445,22 @@ namespace client opt = ctx->resolve_symbol(opt_key_str.substr(0, idx)); if (opt != nullptr) { if (! opt->is_vector()) - boost::throw_exception(qi::expectation_failure( - opt_key.begin(), opt_key.end(), spirit::info("Trying to index a scalar variable"))); + ctx->throw_exception("Trying to index a scalar variable", opt_key); char *endptr = nullptr; idx = strtol(opt_key_str.c_str() + idx + 1, &endptr, 10); if (endptr == nullptr || *endptr != 0) - boost::throw_exception(qi::expectation_failure( - opt_key.begin() + idx + 1, opt_key.end(), spirit::info("Invalid vector index"))); + ctx->throw_exception("Invalid vector index", boost::iterator_range(opt_key.begin() + idx + 1, opt_key.end())); } } } if (opt == nullptr) - boost::throw_exception(qi::expectation_failure( - opt_key.begin(), opt_key.end(), spirit::info("Variable does not exist"))); + ctx->throw_exception("Variable does not exist", boost::iterator_range(opt_key.begin(), opt_key.end())); if (opt->is_scalar()) output = opt->serialize(); else { const ConfigOptionVectorBase *vec = static_cast(opt); if (vec->empty()) - boost::throw_exception(qi::expectation_failure( - opt_key.begin(), opt_key.end(), spirit::info("Indexing an empty vector variable"))); + ctx->throw_exception("Indexing an empty vector variable", opt_key); output = vec->vserialize()[(idx >= vec->size()) ? 0 : idx]; } } @@ -484,23 +481,18 @@ namespace client opt = ctx->resolve_symbol(opt_key_str); } if (! opt->is_vector()) - boost::throw_exception(qi::expectation_failure( - opt_key.begin(), opt_key.end(), spirit::info("Trying to index a scalar variable"))); + ctx->throw_exception("Trying to index a scalar variable", opt_key); const ConfigOptionVectorBase *vec = static_cast(opt); if (vec->empty()) - boost::throw_exception(qi::expectation_failure( - opt_key.begin(), opt_key.end(), spirit::info("Indexing an empty vector variable"))); + ctx->throw_exception("Indexing an empty vector variable", boost::iterator_range(opt_key.begin(), opt_key.end())); const ConfigOption *opt_index = ctx->resolve_symbol(std::string(opt_vector_index.begin(), opt_vector_index.end())); if (opt_index == nullptr) - boost::throw_exception(qi::expectation_failure( - opt_key.begin(), opt_key.end(), spirit::info("Variable does not exist"))); + ctx->throw_exception("Variable does not exist", opt_key); if (opt_index->type() != coInt) - boost::throw_exception(qi::expectation_failure( - opt_key.begin(), opt_key.end(), spirit::info("Indexing variable has to be integer"))); + ctx->throw_exception("Indexing variable has to be integer", opt_key); int idx = opt_index->getInt(); if (idx < 0) - boost::throw_exception(qi::expectation_failure( - opt_key.begin(), opt_key.end(), spirit::info("Negative vector index"))); + ctx->throw_exception("Negative vector index", opt_key); output = vec->vserialize()[(idx >= (int)vec->size()) ? 0 : idx]; } @@ -512,8 +504,7 @@ namespace client { const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end())); if (opt == nullptr) - boost::throw_exception(qi::expectation_failure( - opt_key.begin(), opt_key.end(), spirit::info("Not a variable name"))); + ctx->throw_exception("Not a variable name", opt_key); output.opt = opt; output.it_range = opt_key; } @@ -525,8 +516,7 @@ namespace client expr &output) { if (opt.opt->is_vector()) - boost::throw_exception(qi::expectation_failure( - opt.it_range.begin(), opt.it_range.end(), spirit::info("Referencing a scalar variable in a vector context"))); + ctx->throw_exception("Referencing a scalar variable in a vector context", opt.it_range); switch (opt.opt->type()) { case coFloat: output.set_d(opt.opt->getFloat()); break; case coInt: output.set_i(opt.opt->getInt()); break; @@ -535,11 +525,9 @@ namespace client case coPoint: output.set_s(opt.opt->serialize()); break; case coBool: output.set_b(opt.opt->getBool()); break; case coFloatOrPercent: - boost::throw_exception(qi::expectation_failure( - opt.it_range.begin(), opt.it_range.end(), spirit::info("FloatOrPercent variables are not supported"))); + ctx->throw_exception("FloatOrPercent variables are not supported", opt.it_range); default: - boost::throw_exception(qi::expectation_failure( - opt.it_range.begin(), opt.it_range.end(), spirit::info("Unknown scalar variable type"))); + ctx->throw_exception("Unknown scalar variable type", opt.it_range); } output.it_range = opt.it_range; } @@ -553,12 +541,10 @@ namespace client expr &output) { if (opt.opt->is_scalar()) - boost::throw_exception(qi::expectation_failure( - opt.it_range.begin(), opt.it_range.end(), spirit::info("Referencing a vector variable in a scalar context"))); + ctx->throw_exception("Referencing a vector variable in a scalar context", opt.it_range); const ConfigOptionVectorBase *vec = static_cast(opt.opt); if (vec->empty()) - boost::throw_exception(qi::expectation_failure( - opt.it_range.begin(), opt.it_range.end(), spirit::info("Indexing an empty vector variable"))); + ctx->throw_exception("Indexing an empty vector variable", opt.it_range); size_t idx = (index < 0) ? 0 : (index >= int(vec->size())) ? 0 : size_t(index); switch (opt.opt->type()) { case coFloats: output.set_d(static_cast(opt.opt)->values[idx]); break; @@ -568,8 +554,7 @@ namespace client case coPoints: output.set_s(static_cast(opt.opt)->values[idx].dump_perl()); break; case coBools: output.set_b(static_cast(opt.opt)->values[idx] != 0); break; default: - boost::throw_exception(qi::expectation_failure( - opt.it_range.begin(), opt.it_range.end(), spirit::info("Unknown vector variable type"))); + ctx->throw_exception("Unknown vector variable type", opt.it_range); } output.it_range = boost::iterator_range(opt.it_range.begin(), it_end); } @@ -579,10 +564,54 @@ namespace client template static void evaluate_index(expr &expr_index, int &output) { - if (expr_index.type != expr::TYPE_INT) + if (expr_index.type != expr::TYPE_INT) expr_index.throw_exception("Non-integer index is not allowed to address a vector variable."); output = expr_index.i(); } + + template + static void throw_exception(const std::string &msg, const boost::iterator_range &it_range) + { + // An asterix is added to the start of the string to differentiate the boost::spirit::info::tag content + // between the grammer terminal / non-terminal symbol name and a free-form error message. + boost::throw_exception(qi::expectation_failure(it_range.begin(), it_range.end(), spirit::info(std::string("*") + msg))); + } + + template + static void process_error_message(const MyContext *context, const boost::spirit::info &info, const Iterator &it_begin, const Iterator &it_end) + { + struct expectation_printer + { + expectation_printer(std::string &msg) : result(msg) {} + std::string &result; + void element(std::string const& tag, std::string const& value, int depth) + { + // Indent to depth. + for (int i = 0; i < depth * 4; ++ i) + result += ' '; + if (tag.empty() || tag.front() != '*') { + if (depth == 0) + this->result += "Expecting "; + this->result += "tag: "; + this->result += tag; + } else + this->result += tag.substr(1); + if (! value.empty()) { + this->result += ", value: "; + this->result += value; + } + this->result += '\n'; + } + }; + + std::string &msg = const_cast(context)->error_message; + msg += "Error! "; + expectation_printer ep(msg); + spirit::basic_info_walker walker(ep, info.tag, 0); + boost::apply_visitor(walker, info.value); + msg += " got: \""; + msg += std::string(it_begin, it_end) + "\"\n"; + } }; // For debugging the boost::spirit parsers. Print out the string enclosed in it_range. @@ -651,7 +680,8 @@ namespace client first = it; return true; err: - boost::throw_exception(qi::expectation_failure(first, last, spirit::info("Invalid utf8 sequence"))); + MyContext::throw_exception("Invalid utf8 sequence", boost::iterator_range(first, last)); + return false; } // This function is called during error handling to create a human readable string for the error context. @@ -692,6 +722,8 @@ namespace client qi::_val_type _val; qi::_1_type _1; qi::_2_type _2; + qi::_3_type _3; + qi::_4_type _4; qi::_a_type _a; qi::_b_type _b; qi::_r1_type _r1; @@ -701,6 +733,7 @@ namespace client // Without it, some of the errors would not trigger the error handler. start = eps > text_block(_r1); start.name("start"); + qi::on_error(start, px::bind(&MyContext::process_error_message, _r1, _4, _3, _2)); text_block = *( text [_val+=_1] @@ -831,16 +864,6 @@ namespace client variable_reference = identifier [ px::bind(&MyContext::resolve_variable, _r1, _1, _val) ]; variable_reference.name("variable reference"); -/* - qi::on_error(start, - phx::ref(std::cout) - << "Error! Expecting " - << qi::_4 - << " here: '" - << px::construct(qi::_3, qi::_2) - << "'\n" - ); -*/ keywords.add ("and") @@ -907,69 +930,30 @@ namespace client }; } -struct printer -{ - typedef spirit::utf8_string string; - - void element(string const& tag, string const& value, int depth) const - { - for (int i = 0; i < (depth*4); ++i) // indent to depth - std::cout << ' '; - std::cout << "tag: " << tag; - if (value != "") - std::cout << ", value: " << value; - std::cout << std::endl; - } -}; - -void print_info(spirit::info const& what) -{ - using spirit::basic_info_walker; - printer pr; - basic_info_walker walker(pr, what.tag, 0); - boost::apply_visitor(walker, what.value); -} - std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) const { typedef std::string::const_iterator iterator_type; typedef client::calculator calculator; - spirit::ascii::space_type space; // Our skipper - calculator calc; // Our grammar - + // Our whitespace skipper. + spirit::ascii::space_type space; + // Our grammar. + calculator calc; + // Iterators over the source template. std::string::const_iterator iter = templ.begin(); - std::string::const_iterator end = templ.end(); - //std::string result; - std::string result; - bool r = false; - try { - client::MyContext context; - context.pp = this; - context.config_override = config_override; - r = phrase_parse(iter, end, calc(&context), space, result); - } catch (qi::expectation_failure const& x) { - std::cout << "expected: "; print_info(x.what_); - std::cout << "got: \"" << std::string(x.first, x.last) << '"' << std::endl; + std::string::const_iterator end = templ.end(); + // Accumulator for the processed template. + std::string output; + client::MyContext context; + context.pp = this; + context.config_override = config_override; + bool res = phrase_parse(iter, end, calc(&context), space, output); + if (! context.error_message.empty()) { + if (context.error_message.back() != '\n' && context.error_message.back() != '\r') + context.error_message += '\n'; + throw std::runtime_error(context.error_message); } - - if (r && iter == end) - { -// std::cout << "-------------------------\n"; -// std::cout << "Parsing succeeded\n"; -// std::cout << "result = " << result << std::endl; -// std::cout << "-------------------------\n"; - } - else - { - std::string rest(iter, end); - std::cout << "-------------------------\n"; - std::cout << "Parsing failed\n"; - std::cout << "stopped at: \" " << rest << "\"\n"; - std::cout << "source: \n" << templ; - std::cout << "-------------------------\n"; - } - return result; + return output; } } diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index c25e5de7d..190fbfe6e 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -1075,7 +1075,11 @@ void Print::_make_wipe_tower() std::string Print::output_filename() { this->placeholder_parser.update_timestamp(); - return this->placeholder_parser.process(this->config.output_filename_format.value, 0); + try { + return this->placeholder_parser.process(this->config.output_filename_format.value, 0); + } catch (std::runtime_error &err) { + throw std::runtime_error(std::string("Failed processing of the output_filename_format template.\n") + err.what()); + } } std::string Print::output_filepath(const std::string &path) diff --git a/xs/xsp/GCode.xsp b/xs/xsp/GCode.xsp index 92341394f..9e6df85dc 100644 --- a/xs/xsp/GCode.xsp +++ b/xs/xsp/GCode.xsp @@ -17,7 +17,14 @@ %name{Slic3r::GCode} class GCode { GCode(); ~GCode(); - std::string do_export(Print *print, const char *path); + void do_export(Print *print, const char *path) + %code%{ + try { + THIS->do_export(print, path); + } catch (std::exception& e) { + croak(e.what()); + } + %}; Ref origin() %code{% RETVAL = &(THIS->origin()); %}; diff --git a/xs/xsp/PlaceholderParser.xsp b/xs/xsp/PlaceholderParser.xsp index 08630f9d5..ab06450f9 100644 --- a/xs/xsp/PlaceholderParser.xsp +++ b/xs/xsp/PlaceholderParser.xsp @@ -14,5 +14,11 @@ %code%{ THIS->apply_config(*config); %}; void set(std::string key, int value); std::string process(std::string str) const - %code%{ RETVAL = THIS->process(str, 0); %}; + %code%{ + try { + RETVAL = THIS->process(str, 0); + } catch (std::exception& e) { + croak(e.what()); + } + %}; }; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index bdf7b8991..1148fd4aa 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -206,8 +206,14 @@ _constant() double max_allowed_layer_height() const; bool has_support_material() const; void auto_assign_extruders(ModelObject* model_object); - std::string output_filename(); - std::string output_filepath(std::string path = ""); + std::string output_filepath(std::string path = "") + %code%{ + try { + RETVAL = THIS->output_filepath(path); + } catch (std::exception& e) { + croak(e.what()); + } + %}; void add_model_object(ModelObject* model_object, int idx = -1); bool apply_config(DynamicPrintConfig* config) From c34ec9b7d3ea32a92cc58d61fccb1e446687f6f2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 5 Dec 2017 17:38:29 +0100 Subject: [PATCH 68/83] PlaceholderParser: Improved error reporting https://github.com/prusa3d/Slic3r/issues/600 Fixed '+' operator for strings. --- xs/src/libslic3r/GCode.cpp | 6 +- xs/src/libslic3r/PlaceholderParser.cpp | 96 +++++++++++++++----------- 2 files changed, 57 insertions(+), 45 deletions(-) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index b5f186630..131693b4e 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -814,10 +814,10 @@ std::string GCode::placeholder_parser_process(const std::string &name, const std // Collect the names of failed template substitutions for error reporting. this->m_placeholder_parser_failed_templates.insert(name); // Insert the macro error message into the G-code. - return - std::string("!!!!! Failed to process the custom G-code template ") + name + "\n" + + return + std::string("\n!!!!! Failed to process the custom G-code template ") + name + "\n" + err.what() + - "!!!!! End of an error report for the custom G-code template " + name + "\n"; + "!!!!! End of an error report for the custom G-code template " + name + "\n\n"; } } diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index f9f72a9e9..ed75508ac 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -280,22 +280,31 @@ namespace client expr &operator+=(const expr &rhs) { - const char *err_msg = "Cannot multiply with non-numeric type."; - this->throw_if_not_numeric(err_msg); - rhs.throw_if_not_numeric(err_msg); - if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) { - double d = this->as_d() + rhs.as_d(); - this->data.d = d; - this->type = TYPE_DOUBLE; - } else - this->data.i += rhs.i(); + if (this->type == TYPE_STRING) { + // Convert the right hand side to string and append. + *this->data.s += rhs.to_string(); + } else if (rhs.type == TYPE_STRING) { + // Conver the left hand side to string, append rhs. + this->data.s = new std::string(this->to_string() + rhs.s()); + this->type = TYPE_STRING; + } else { + const char *err_msg = "Cannot add non-numeric types."; + this->throw_if_not_numeric(err_msg); + rhs.throw_if_not_numeric(err_msg); + if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) { + double d = this->as_d() + rhs.as_d(); + this->data.d = d; + this->type = TYPE_DOUBLE; + } else + this->data.i += rhs.i(); + } this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); return *this; } expr &operator-=(const expr &rhs) { - const char *err_msg = "Cannot multiply with non-numeric type."; + const char *err_msg = "Cannot subtract non-numeric types."; this->throw_if_not_numeric(err_msg); rhs.throw_if_not_numeric(err_msg); if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) { @@ -578,39 +587,42 @@ namespace client } template - static void process_error_message(const MyContext *context, const boost::spirit::info &info, const Iterator &it_begin, const Iterator &it_end) + static void process_error_message(const MyContext *context, const boost::spirit::info &info, const Iterator &it_begin, const Iterator &it_end, const Iterator &it_error) { - struct expectation_printer - { - expectation_printer(std::string &msg) : result(msg) {} - std::string &result; - void element(std::string const& tag, std::string const& value, int depth) - { - // Indent to depth. - for (int i = 0; i < depth * 4; ++ i) - result += ' '; - if (tag.empty() || tag.front() != '*') { - if (depth == 0) - this->result += "Expecting "; - this->result += "tag: "; - this->result += tag; - } else - this->result += tag.substr(1); - if (! value.empty()) { - this->result += ", value: "; - this->result += value; - } - this->result += '\n'; - } - }; - std::string &msg = const_cast(context)->error_message; - msg += "Error! "; - expectation_printer ep(msg); - spirit::basic_info_walker walker(ep, info.tag, 0); - boost::apply_visitor(walker, info.value); - msg += " got: \""; - msg += std::string(it_begin, it_end) + "\"\n"; + std::string first(it_begin, it_error); + std::string last(it_error, it_end); + auto first_pos = first.rfind('\n'); + auto last_pos = last.find('\n'); + int line_nr = 1; + if (first_pos == std::string::npos) + first_pos = 0; + else { + // Calculate the current line number. + for (size_t i = 0; i <= first_pos; ++ i) + if (first[i] == '\n') + ++ line_nr; + ++ first_pos; + } + auto error_line = std::string(first, first_pos) + std::string(last, 0, last_pos); + // Position of the it_error from the start of its line. + auto error_pos = (it_error - it_begin) - first_pos; + msg += "Parsing error at line " + std::to_string(line_nr); + if (! info.tag.empty() && info.tag.front() == '*') { + // The gat contains an explanatory string. + msg += ": "; + msg += info.tag.substr(1); + } else { + // A generic error report based on the nonterminal or terminal symbol name. + msg += ". Expecting tag "; + msg += info.tag; + } + msg += '\n'; + msg += error_line; + msg += '\n'; + for (size_t i = 0; i < error_pos; ++ i) + msg += ' '; + msg += "^\n"; } }; @@ -733,7 +745,7 @@ namespace client // Without it, some of the errors would not trigger the error handler. start = eps > text_block(_r1); start.name("start"); - qi::on_error(start, px::bind(&MyContext::process_error_message, _r1, _4, _3, _2)); + qi::on_error(start, px::bind(&MyContext::process_error_message, _r1, _4, _1, _2, _3)); text_block = *( text [_val+=_1] From 0a2be9d7bf22faba489a1a66c4a3ebbcdca87cba Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 5 Dec 2017 17:52:12 +0100 Subject: [PATCH 69/83] Fixed compilation on unices. --- xs/src/libslic3r/PlaceholderParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index ed75508ac..8f2f6d6e7 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -664,13 +664,13 @@ namespace client auto it = first; // Read the first byte of the UTF-8 sequence. unsigned char c = static_cast(*it ++); + unsigned int cnt = 0; // UTF-8 sequence must not start with a continuation character: if ((c & 0xC0) == 0x80) goto err; // Skip high surrogate first if there is one. // If the most significant bit with a zero in it is in position // 8-N then there are N bytes in this UTF-8 sequence: - unsigned int cnt = 0; { unsigned char mask = 0x80u; unsigned int result = 0; From 7892dfd53c083f7b5c26dad04dca0f84ab29b2a7 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 5 Dec 2017 18:40:46 +0100 Subject: [PATCH 70/83] Fixed a regression bug in G-code export, where ferror was called on released FILE structure. --- xs/src/libslic3r/GCode.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 131693b4e..528d7132b 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -376,11 +376,13 @@ void GCode::do_export(Print *print, const char *path) this->m_placeholder_parser_failed_templates.clear(); this->_do_export(*print, file); - fclose(file); + fflush(file); if (ferror(file)) { + fclose(file); boost::nowide::remove(path_tmp.c_str()); throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); } + fclose(file); if (! this->m_placeholder_parser_failed_templates.empty()) { // G-code export proceeded, but some of the PlaceholderParser substitutions failed. std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n"; From b0f84c5cb2368eea3683be9ea876a07b8a3bfdc5 Mon Sep 17 00:00:00 2001 From: qtux Date: Tue, 5 Dec 2017 19:05:49 +0100 Subject: [PATCH 71/83] Add used filament length to the "Sliced Info" box (#585) --- lib/Slic3r/GUI/Plater.pm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 0cb433309..f1db05dc4 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -430,6 +430,7 @@ sub new { $grid_sizer->AddGrowableCol(3, 1); $print_info_sizer->Add($grid_sizer, 0, wxEXPAND); my @info = ( + fil_m => "Used Filament (m)", fil_mm3 => "Used Filament (mm^3)", fil_g => "Used Filament (g)", cost => "Cost", @@ -1426,6 +1427,7 @@ sub on_export_completed { $self->{"print_info_cost"}->SetLabel(sprintf("%.2f" , $self->{print}->total_cost)); $self->{"print_info_fil_g"}->SetLabel(sprintf("%.2f" , $self->{print}->total_weight)); $self->{"print_info_fil_mm3"}->SetLabel(sprintf("%.2f" , $self->{print}->total_extruded_volume)); + $self->{"print_info_fil_m"}->SetLabel(sprintf("%.2f" , $self->{print}->total_used_filament / 1000)); $self->{"print_info_box_show"}->(1); # this updates buttons status From 2eeca93a971b07248caed9730dc65fb51bb7d76c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 5 Dec 2017 20:06:19 +0100 Subject: [PATCH 72/83] Feature Request: Add to Plater: Ctrl+O implements https://github.com/prusa3d/Slic3r/issues/379 thanks @alexrj --- lib/Slic3r/GUI/MainFrame.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 5ae6ac08f..498ebafe5 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -171,6 +171,9 @@ sub _init_menubar { # File menu my $fileMenu = Wx::Menu->new; { + wxTheApp->append_menu_item($fileMenu, "Open STL/OBJ/AMF…\tCtrl+O", 'Open a model', sub { + $self->{plater}->add if $self->{plater}; + }, undef, undef); #'brick_add.png'); $self->_append_menu_item($fileMenu, "&Load Config…\tCtrl+L", 'Load exported configuration file', sub { $self->load_config_file; }, undef, 'plugin_add.png'); From 75dcdb84b1996a2ae507643042df504e1cd5d056 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 6 Dec 2017 16:47:53 +0100 Subject: [PATCH 73/83] Fix of a crash due to the way how the presets are sorted and searched for in the PresetCollection: The 1st preset is always the "-- default --" even if there are some presets starting with an ASCII character lower than '-'. https://github.com/prusa3d/Slic3r/issues/603 --- xs/src/slic3r/GUI/Preset.cpp | 19 +++++++++---------- xs/src/slic3r/GUI/Preset.hpp | 12 ++++++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index e4b0448cf..4b21b18a0 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -283,10 +283,11 @@ Preset& PresetCollection::load_preset(const std::string &path, const std::string Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select) { - Preset key(m_type, name); - auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key); - if (it == m_presets.end() || it->name != name) + auto it = this->find_preset_internal(name); + if (it == m_presets.end() || it->name != name) { + // The preset was not found. Create a new preset. it = m_presets.emplace(it, Preset(m_type, name, false)); + } Preset &preset = *it; preset.file = path; preset.config = std::move(config); @@ -301,9 +302,8 @@ void PresetCollection::save_current_preset(const std::string &new_name) { // 1) Find the preset with a new_name or create a new one, // initialize it with the edited config. - Preset key(m_type, new_name, false); - auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key); - if (it != m_presets.end() && it->name == key.name) { + auto it = this->find_preset_internal(new_name); + if (it != m_presets.end() && it->name == new_name) { // Preset with the same name found. Preset &preset = *it; if (preset.is_default) @@ -356,7 +356,7 @@ bool PresetCollection::load_bitmap_default(const std::string &file_name) Preset* PresetCollection::find_preset(const std::string &name, bool first_visible_if_not_found) { Preset key(m_type, name, false); - auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key); + auto it = this->find_preset_internal(name); // Ensure that a temporary copy is returned if the preset found is currently selected. return (it != m_presets.end() && it->name == key.name) ? &this->preset(it - m_presets.begin()) : first_visible_if_not_found ? &this->first_visible() : nullptr; @@ -509,10 +509,9 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b { std::string name = Preset::remove_suffix_modified(name_w_suffix); // 1) Try to find the preset by its name. - Preset key(m_type, name, false); - auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key); + auto it = this->find_preset_internal(name); size_t idx = 0; - if (it != m_presets.end() && it->name == key.name) + if (it != m_presets.end() && it->name == name) // Preset found by its name. idx = it - m_presets.begin(); else { diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp index f44fd1590..74ae7411c 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -212,6 +212,18 @@ private: PresetCollection(const PresetCollection &other); PresetCollection& operator=(const PresetCollection &other); + // Find a preset in the sorted list of presets. + // The "-- default -- " preset is always the first, so it needs + // to be handled differently. + std::deque::iterator find_preset_internal(const std::string &name) + { + Preset key(m_type, name); + auto it = std::lower_bound(m_presets.begin() + 1, m_presets.end(), key); + return (it == m_presets.end() && m_presets.front().name == name) ? m_presets.begin() : it; + } + std::deque::const_iterator find_preset_internal(const std::string &name) const + { return const_cast(this)->find_preset_internal(name); } + // Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER. Preset::Type m_type; // List of presets, starting with the "- default -" preset. From f0e154d54c44583d89d64645b216786af973582f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sat, 9 Dec 2017 15:49:43 +0100 Subject: [PATCH 74/83] Bumped up the build version. --- xs/src/libslic3r/libslic3r.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 9d2451869..f71311e7f 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -14,7 +14,7 @@ #include #define SLIC3R_FORK_NAME "Slic3r Prusa Edition" -#define SLIC3R_VERSION "1.38.3" +#define SLIC3R_VERSION "1.38.4" #define SLIC3R_BUILD "UNKNOWN" typedef long coord_t; From 8509e4b5f5ca87b5408b878d524051f7324c6908 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sat, 9 Dec 2017 16:39:49 +0100 Subject: [PATCH 75/83] Fixes regression error introduced in Slic3r 1.38.2: Slic3r 1.38.3 gcodes not building up bed temperature https://github.com/prusa3d/Slic3r/issues/614 --- xs/src/libslic3r/GCode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 528d7132b..b297ddab0 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -888,12 +888,12 @@ void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const s // Is the bed temperature set by the provided custom G-code? int temp_by_gcode = -1; bool temp_set_by_gcode = custom_gcode_sets_temperature(gcode, 140, 190, temp_by_gcode); - if (temp_by_gcode >= 0 && temp_by_gcode < 1000) + if (temp_set_by_gcode && temp_by_gcode >= 0 && temp_by_gcode < 1000) temp = temp_by_gcode; // Always call m_writer.set_bed_temperature() so it will set the internal "current" state of the bed temp as if // the custom start G-code emited these. std::string set_temp_gcode = m_writer.set_bed_temperature(temp, wait); - if (! temp_by_gcode) + if (! temp_set_by_gcode) write(file, set_temp_gcode); } From 9a80ff57b26b41bf1e4ad275ccd8416cd0d567b0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sat, 9 Dec 2017 18:48:21 +0100 Subject: [PATCH 76/83] Improved robustness of handling preset files stored into a wrong location. Fixes https://github.com/prusa3d/Slic3r/issues/616 --- xs/src/slic3r/GUI/Preset.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 4b21b18a0..8ba3029ba 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -79,7 +79,8 @@ void Preset::set_num_extruders(DynamicPrintConfig &config, unsigned int num_extr auto *opt = config.option(key, false); assert(opt != nullptr); assert(opt->is_vector()); - static_cast(opt)->resize(num_extruders, defaults.option(key)); + if (opt != nullptr && opt->is_vector()) + static_cast(opt)->resize(num_extruders, defaults.option(key)); } } From 657f2734f19da1d6483b9d70b5d8102e727bc34b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 10 Dec 2017 13:19:44 +0100 Subject: [PATCH 77/83] Extended the Config Wizard to offer a selection of config bundles bundled with Slic3r installation, and install it into user's Slic3r profile. These bundled config bundles will be contained in the Slic3r source tree under Slic3r/resources/profiles. Breaking change! The Slic3r user directory has been renamed to Slic3rPE for the Prusa Edition. Also it is likely, that the Slic3rPE directory will be reorganized before the final 1.38 release to reserve space for temporary profiles downloaded from the Internet. --- lib/Slic3r.pm | 1 + lib/Slic3r/GUI.pm | 2 +- lib/Slic3r/GUI/ConfigWizard.pm | 81 +++++++++++++++++++++++++--------- lib/Slic3r/GUI/MainFrame.pm | 42 ++++++++++++++---- xs/src/libslic3r/Utils.hpp | 5 +++ xs/src/libslic3r/utils.cpp | 12 +++++ xs/xsp/XS.xsp | 12 +++++ 7 files changed, 125 insertions(+), 30 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 53b2e3663..45eedc48d 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -42,6 +42,7 @@ use FindBin; # Let the XS module know where the GUI resources reside. set_var_dir(decode_path($FindBin::Bin) . "/var"); +set_resources_dir(decode_path($FindBin::Bin) . (($^O eq 'darwin') ? '/Resources' : '/resources')); use Moo 1.003001; diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 78e82a6ef..7157f7e92 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -70,7 +70,7 @@ our $grey = Wx::Colour->new(200,200,200); sub OnInit { my ($self) = @_; - $self->SetAppName('Slic3r'); + $self->SetAppName('Slic3rPE'); $self->SetAppDisplayName('Slic3r Prusa Edition'); Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION; diff --git a/lib/Slic3r/GUI/ConfigWizard.pm b/lib/Slic3r/GUI/ConfigWizard.pm index e540f9ea7..c18741396 100644 --- a/lib/Slic3r/GUI/ConfigWizard.pm +++ b/lib/Slic3r/GUI/ConfigWizard.pm @@ -14,14 +14,14 @@ our $wizard = 'Wizard'; $wizard = 'Assistant' if &Wx::wxMAC || &Wx::wxGTK; sub new { - my $class = shift; - my ($parent) = @_; + my ($class, $parent, $presets) = @_; my $self = $class->SUPER::new($parent, -1, "Configuration $wizard"); # initialize an empty repository $self->{config} = Slic3r::Config->new; - $self->add_page(Slic3r::GUI::ConfigWizard::Page::Welcome->new($self)); + my $welcome_page = Slic3r::GUI::ConfigWizard::Page::Welcome->new($self); + $self->add_page($welcome_page); $self->add_page(Slic3r::GUI::ConfigWizard::Page::Firmware->new($self)); $self->add_page(Slic3r::GUI::ConfigWizard::Page::Bed->new($self)); $self->add_page(Slic3r::GUI::ConfigWizard::Page::Nozzle->new($self)); @@ -32,12 +32,13 @@ sub new { $_->build_index for @{$self->{pages}}; + $welcome_page->set_selection_presets([@{$presets}, 'Other']); + return $self; } sub add_page { - my $self = shift; - my ($page) = @_; + my ($self, $page) = @_; my $n = push @{$self->{pages}}, $page; # add first page to the page area sizer @@ -48,13 +49,13 @@ sub add_page { } sub run { - my $self = shift; - + my ($self) = @_; + my $result = undef; if (Wx::Wizard::RunWizard($self, $self->{pages}[0])) { - - # it would be cleaner to have these defined inside each page class, - # in some event getting called before leaving the page - { + my $preset_name = $self->{pages}[0]->{preset_name}; + if ($preset_name eq 'Other') { + # it would be cleaner to have these defined inside each page class, + # in some event getting called before leaving the page # set first_layer_height + layer_height based on nozzle_diameter my $nozzle = $self->{config}->nozzle_diameter; $self->{config}->set('first_layer_height', $nozzle->[0]); @@ -66,14 +67,13 @@ sub run { # set first_layer_bed_temperature to temperature + 5 $self->{config}->set('first_layer_bed_temperature', [ ($self->{config}->bed_temperature->[0] > 0) ? ($self->{config}->bed_temperature->[0] + 5) : 0 ]); + $result = $self->{config}; + } else { + $result = $preset_name; } - - $self->Destroy; - return $self->{config}; - } else { - $self->Destroy; - return undef; } + $self->Destroy; + return $result; } package Slic3r::GUI::ConfigWizard::Index; @@ -127,6 +127,8 @@ sub repaint { $dc->SetTextForeground(Wx::Colour->new(128, 128, 128)) if $i > $self->{own_index}; $dc->DrawLabel($_, $bullet, Wx::Rect->new(0, $i * ($label_h + $gap), $label_w, $label_h)); + # Only show the first bullet if this is the only wizard page to be displayed. + last if $i == 0 && $self->{just_welcome}; $i++; } @@ -263,19 +265,58 @@ sub config { package Slic3r::GUI::ConfigWizard::Page::Welcome; use base 'Slic3r::GUI::ConfigWizard::Page'; +use Wx qw(:misc :sizer wxID_FORWARD); +use Wx::Event qw(EVT_ACTIVATE EVT_CHOICE); sub new { my $class = shift; my ($parent) = @_; my $self = $class->SUPER::new($parent, "Welcome to the Slic3r Configuration $wizard", 'Welcome'); + $self->{full_wizard_workflow} = 1; - $self->append_text('Hello, welcome to Slic3r! This '.lc($wizard).' helps you with the initial configuration; just a few settings and you will be ready to print.'); - $self->append_text('To import an existing configuration instead, cancel this '.lc($wizard).' and use the Open Config menu item found in the File menu.'); - $self->append_text('To continue, click Next.'); + $self->append_text('Hello, welcome to Slic3r Prusa Edition! This '.lc($wizard).' helps you with the initial configuration; just a few settings and you will be ready to print.'); + $self->append_text('Please select your printer vendor and printer type. If your printer is not listed, you may try your luck and select a similar one. If you select "Other", this ' . lc($wizard) . ' will let you set the basic 3D printer parameters.'); + # To import an existing configuration instead, cancel this '.lc($wizard).' and use the Open Config menu item found in the File menu.'); + $self->append_text('If you received a configuration file or a config bundle from your 3D printer vendor, cancel this '.lc($wizard).' and use the "File->Load Config" or "File->Load Config Bundle" menu.'); + + $self->{choice} = my $choice = Wx::Choice->new($self, -1, wxDefaultPosition, wxDefaultSize, []); + $self->{vsizer}->Add($choice, 0, wxEXPAND | wxTOP | wxBOTTOM, 10); + + EVT_CHOICE($parent, $choice, sub { + my $sel = $self->{choice}->GetStringSelection; + $self->{preset_name} = $sel; + $self->set_full_wizard_workflow(($sel eq 'Other') || ($sel eq '')); + }); + + EVT_ACTIVATE($parent, sub { + $self->set_full_wizard_workflow($self->{preset_name} eq 'Other'); + }); return $self; } +sub set_full_wizard_workflow { + my ($self, $full_workflow) = @_; + $self->{full_wizard_workflow} = $full_workflow; + $self->{index}->{just_welcome} = !$full_workflow; + $self->{index}->Refresh; + my $next_button = $self->GetParent->FindWindow(wxID_FORWARD); + $next_button->SetLabel($full_workflow ? "&Next >" : "&Finish"); +} + +# Set the preset names, select the first item. +sub set_selection_presets { + my ($self, $names) = @_; + $self->{choice}->Append($names); + $self->{choice}->SetSelection(0); + $self->{preset_name} = $names->[0]; +} + +sub GetNext { + my $self = shift; + return $self->{full_wizard_workflow} ? $self->{next_page} : undef; +} + package Slic3r::GUI::ConfigWizard::Page::Firmware; use base 'Slic3r::GUI::ConfigWizard::Page'; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 498ebafe5..a6baef8f9 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -617,16 +617,40 @@ sub config_wizard { my ($self) = @_; # Exit wizard if there are unsaved changes and the user cancels the action. return unless $self->check_unsaved_changes; - if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) { - for my $tab (values %{$self->{options_tabs}}) { - # Select the first visible preset, force. - $tab->select_preset(undef, 1); + # Enumerate the profiles bundled with the Slic3r installation under resources/profiles. + my $directory = Slic3r::resources_dir() . "/profiles"; + my @profiles = (); + if (opendir(DIR, Slic3r::encode_path($directory))) { + while (my $file = readdir(DIR)) { + if ($file =~ /\.ini$/) { + $file =~ s/\.ini$//; + push @profiles, Slic3r::decode_path($file); + } } - # Load the config over the previously selected defaults. - $self->load_config($config); - for my $tab (values %{$self->{options_tabs}}) { - # Save the settings under a new name, select the name. - $tab->save_preset('My Settings'); + closedir(DIR); + } + # Open the wizard. + if (my $config = Slic3r::GUI::ConfigWizard->new($self, \@profiles)->run) { + if (ref($config)) { + # Wizard returned a config. Add the config to each of the preset types. + for my $tab (values %{$self->{options_tabs}}) { + # Select the first visible preset, force. + $tab->select_preset(undef, 1); + } + # Load the config over the previously selected defaults. + $self->load_config($config); + for my $tab (values %{$self->{options_tabs}}) { + # Save the settings under a new name, select the name. + $tab->save_preset('My Settings'); + } + } else { + # Wizard returned a name of a preset bundle bundled with the installation. Unpack it. + eval { wxTheApp->{preset_bundle}->load_configbundle($directory . '/' . $config . '.ini'); }; + Slic3r::GUI::catch_error($self) and return; + # Load the currently selected preset into the GUI, update the preset selection box. + foreach my $tab (values %{$self->{options_tabs}}) { + $tab->load_current_preset; + } } } } diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 308a2a118..d988de860 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -15,6 +15,11 @@ const std::string& var_dir(); // Return a full resource path for a file_name. std::string var(const std::string &file_name); +// Set a path with various static definition data (for example the initial config bundles). +void set_resources_dir(const std::string &path); +// Return a full path to the resources directory. +const std::string& resources_dir(); + // Set a path with preset files. void set_data_dir(const std::string &path); // Return a full path to the GUI resource files. diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index f4c03ef50..53efa466b 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -89,6 +89,18 @@ std::string var(const std::string &file_name) return file.string(); } +static std::string g_resources_dir; + +void set_resources_dir(const std::string &dir) +{ + g_resources_dir = dir; +} + +const std::string& resources_dir() +{ + return g_resources_dir; +} + static std::string g_data_dir; void set_data_dir(const std::string &dir) diff --git a/xs/xsp/XS.xsp b/xs/xsp/XS.xsp index 2be42d383..d59f826b7 100644 --- a/xs/xsp/XS.xsp +++ b/xs/xsp/XS.xsp @@ -60,6 +60,18 @@ var_dir() RETVAL = const_cast(Slic3r::var_dir().c_str()); OUTPUT: RETVAL +void +set_resources_dir(dir) + char *dir; + CODE: + Slic3r::set_resources_dir(dir); + +char* +resources_dir() + CODE: + RETVAL = const_cast(Slic3r::resources_dir().c_str()); + OUTPUT: RETVAL + std::string var(file_name) const char *file_name; From 98fdb46da9741a4624a427b00ef355b7ee25abc6 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 10 Dec 2017 13:21:06 +0100 Subject: [PATCH 78/83] Added a first config bundle for the Prusa MK2 & MK3 machines. --- ...l Prusa i3 MK3, MK2S, MK2 and MK2S-MMU.ini | 3629 +++++++++++++++++ 1 file changed, 3629 insertions(+) create mode 100644 resources/profiles/Original Prusa i3 MK3, MK2S, MK2 and MK2S-MMU.ini diff --git a/resources/profiles/Original Prusa i3 MK3, MK2S, MK2 and MK2S-MMU.ini b/resources/profiles/Original Prusa i3 MK3, MK2S, MK2 and MK2S-MMU.ini new file mode 100644 index 000000000..ae21f862d --- /dev/null +++ b/resources/profiles/Original Prusa i3 MK3, MK2S, MK2 and MK2S-MMU.ini @@ -0,0 +1,3629 @@ +# generated by Slic3r Prusa Edition 1.38.4 on 2017-12-10 at 09:05:01 + +[print:0.05mm DETAIL] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 10 +bridge_acceleration = 300 +bridge_angle = 0 +bridge_flow_ratio = 0.7 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2";"Original Prusa i3 MK2 MM Single Mode" +complete_objects = 0 +default_acceleration = 500 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 20 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 25% +fill_pattern = cubic +first_layer_acceleration = 500 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 20 +gcode_comments = 0 +infill_acceleration = 800 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.5 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 30 +interface_shells = 0 +layer_height = 0.05 +max_print_speed = 80 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 300 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 30 +perimeters = 3 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 15 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 30 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 1 +support_material_extrusion_width = 0.3 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 1 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 1.5 +support_material_speed = 30 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.45 +top_solid_infill_speed = 20 +top_solid_layers = 15 +travel_speed = 180 +wipe_tower = 0 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.05mm DETAIL 0.25 nozzle] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 10 +bridge_acceleration = 300 +bridge_angle = 0 +bridge_flow_ratio = 0.7 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2 0.25 nozzle";"Original Prusa i3 MK2 MM Single Mode 0.25 nozzle" +complete_objects = 0 +default_acceleration = 500 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0 +external_perimeter_speed = 20 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.28 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 500 +first_layer_extrusion_width = 0.3 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 20 +gcode_comments = 0 +infill_acceleration = 800 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 20 +interface_shells = 0 +layer_height = 0.05 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 300 +perimeter_extruder = 1 +perimeter_extrusion_width = 0 +perimeter_speed = 20 +perimeters = 4 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 10 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0 +solid_infill_speed = 20 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 1 +support_material_extrusion_width = 0.18 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 1 +support_material_interface_layers = 0 +support_material_interface_spacing = 0.15 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 1 +support_material_speed = 20 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 150% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0 +top_solid_infill_speed = 20 +top_solid_layers = 15 +travel_speed = 200 +wipe_tower = 0 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.05mm DETAIL MK3] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 10 +bridge_acceleration = 300 +bridge_angle = 0 +bridge_flow_ratio = 0.7 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK3" +complete_objects = 0 +default_acceleration = 500 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 20 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 25% +fill_pattern = cubic +first_layer_acceleration = 500 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 20 +gcode_comments = 0 +infill_acceleration = 800 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.5 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 30 +interface_shells = 0 +layer_height = 0.05 +max_print_speed = 80 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 300 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 30 +perimeters = 3 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 15 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 30 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 1 +support_material_extrusion_width = 0.3 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 1 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 1.5 +support_material_speed = 30 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.45 +top_solid_infill_speed = 20 +top_solid_layers = 15 +travel_speed = 180 +wipe_tower = 0 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.10mm DETAIL] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 7 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.7 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2";"Original Prusa i3 MK2 MM Single Mode" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 40 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.45 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 60 +interface_shells = 0 +layer_height = 0.1 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 50 +perimeters = 3 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 50 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 1 +support_material_extrusion_width = 0.35 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 1 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.45 +top_solid_infill_speed = 40 +top_solid_layers = 9 +travel_speed = 120 +wipe_tower = 0 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.10mm DETAIL 0.25 nozzle] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 7 +bridge_acceleration = 600 +bridge_angle = 0 +bridge_flow_ratio = 0.7 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2 0.25 nozzle";"Original Prusa i3 MK2 MM Single Mode 0.25 nozzle" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.25 +external_perimeter_speed = 20 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.25 +fill_angle = 45 +fill_density = 15% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.25 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 1600 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.25 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 40 +interface_shells = 0 +layer_height = 0.1 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 600 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.25 +perimeter_speed = 25 +perimeters = 4 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 10 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.25 +solid_infill_speed = 40 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 1 +support_material_extrusion_width = 0.18 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 1 +support_material_interface_layers = 0 +support_material_interface_spacing = 0.15 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 1 +support_material_speed = 50 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 150% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.25 +top_solid_infill_speed = 30 +top_solid_layers = 9 +travel_speed = 120 +wipe_tower = 0 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.10mm DETAIL MK3] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 4 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.8 +bridge_speed = 30 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK3" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 40 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 3500 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.45 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 35% +infill_speed = 200 +interface_shells = 0 +layer_height = 0.1 +max_print_speed = 250 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 60 +perimeters = 2 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 200 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_extrusion_width = 0.35 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 0 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.4 +top_solid_infill_speed = 50 +top_solid_layers = 5 +travel_speed = 250 +wipe_tower = 0 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.15mm 100mms Linear Advance] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 4 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.95 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2";"Original Prusa i3 MK2 MM Single Mode";"Original Prusa i3 MK2 MultiMaterial" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 50 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.45 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 100 +interface_shells = 0 +layer_height = 0.15 +max_print_speed = 150 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 60 +perimeters = 2 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 30 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 100 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_extrusion_width = 0.35 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 0 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 60 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.4 +top_solid_infill_speed = 70 +top_solid_layers = 5 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.15mm OPTIMAL] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 5 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.8 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2";"Original Prusa i3 MK2 MM Single Mode";"Original Prusa i3 MK2 MultiMaterial" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 40 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.45 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 60 +interface_shells = 0 +layer_height = 0.15 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 50 +perimeters = 3 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 50 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 1 +support_material_extrusion_width = 0.35 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 1 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.45 +top_solid_infill_speed = 40 +top_solid_layers = 7 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.15mm OPTIMAL 0.25 nozzle] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 5 +bridge_acceleration = 600 +bridge_angle = 0 +bridge_flow_ratio = 0.7 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2 0.25 nozzle";"Original Prusa i3 MK2 MM Single Mode 0.25 nozzle";"Original Prusa i3 MK2 MultiMaterial 0.25 nozzle" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.25 +external_perimeter_speed = 20 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.25 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.25 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 1600 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.25 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 40 +interface_shells = 0 +layer_height = 0.15 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 600 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.25 +perimeter_speed = 25 +perimeters = 2 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 10 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.25 +solid_infill_speed = 40 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 1 +support_material_extrusion_width = 0.2 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 1 +support_material_interface_layers = 0 +support_material_interface_spacing = 0.15 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 1 +support_material_speed = 50 +support_material_synchronize_layers = 0 +support_material_threshold = 35 +support_material_with_sheath = 0 +support_material_xy_spacing = 150% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.25 +top_solid_infill_speed = 30 +top_solid_layers = 7 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.15mm OPTIMAL 0.6 nozzle] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 5 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.8 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2 0.6 nozzle";"Original Prusa i3 MK2 MM Single Mode 0.6 nozzle";"Original Prusa i3 MK2 MultiMaterial 0.6 nozzle" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.61 +external_perimeter_speed = 40 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.67 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.65 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.75 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 60 +interface_shells = 0 +layer_height = 0.15 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.65 +perimeter_speed = 50 +perimeters = 3 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.65 +solid_infill_speed = 50 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_extrusion_width = 0.35 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 1 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.6 +top_solid_infill_speed = 40 +top_solid_layers = 7 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.15mm OPTIMAL MK3] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 4 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.8 +bridge_speed = 30 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK3" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 40 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 3500 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.45 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 35% +infill_speed = 200 +interface_shells = 0 +layer_height = 0.15 +max_print_speed = 250 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 60 +perimeters = 2 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 200 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_extrusion_width = 0.35 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 0 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.4 +top_solid_infill_speed = 50 +top_solid_layers = 5 +travel_speed = 250 +wipe_tower = 1 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.15mm OPTIMAL SOLUBLE FULL] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 5 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.8 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2 MultiMaterial" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 25 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.45 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 60 +interface_shells = 0 +layer_height = 0.15 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = Set your solluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 1 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 40 +perimeters = 3 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 0 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 40 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 1 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0 +support_material_enforce_layers = 0 +support_material_extruder = 4 +support_material_extrusion_width = 0.45 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 4 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.1 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 1 +support_material_threshold = 80 +support_material_with_sheath = 1 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.45 +top_solid_infill_speed = 30 +top_solid_layers = 7 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 20 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.15mm OPTIMAL SOLUBLE INTERFACE] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 5 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.8 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2 MultiMaterial" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 25 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.45 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 60 +interface_shells = 0 +layer_height = 0.15 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = Set your solluble extruder in Multiple Extruders > Support material/raft interface extruder +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 1 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 40 +perimeters = 3 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 0 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 40 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 1 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_extrusion_width = 0.45 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 4 +support_material_interface_layers = 3 +support_material_interface_spacing = 0.1 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 1 +support_material_threshold = 80 +support_material_with_sheath = 0 +support_material_xy_spacing = 120% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.45 +top_solid_infill_speed = 30 +top_solid_layers = 7 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 20 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.20mm 100mms Linear Advance] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 4 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.95 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2";"Original Prusa i3 MK2 MM Single Mode";"Original Prusa i3 MK2 MultiMaterial" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 50 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.45 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 100 +interface_shells = 0 +layer_height = 0.2 +max_print_speed = 150 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 60 +perimeters = 2 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 30 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 100 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_extrusion_width = 0.35 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 0 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 60 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.4 +top_solid_infill_speed = 70 +top_solid_layers = 5 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.20mm NORMAL] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 4 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.95 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2";"Original Prusa i3 MK2 MM Single Mode";"Original Prusa i3 MK2 MultiMaterial" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 40 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.45 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 60 +interface_shells = 0 +layer_height = 0.2 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 50 +perimeters = 2 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 50 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_extrusion_width = 0.35 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 0 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.4 +top_solid_infill_speed = 40 +top_solid_layers = 5 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.20mm NORMAL 0.6 nozzle] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 4 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.8 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2 0.6 nozzle";"Original Prusa i3 MK2 MM Single Mode 0.6 nozzle";"Original Prusa i3 MK2 MultiMaterial 0.6 nozzle" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.61 +external_perimeter_speed = 40 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.67 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.65 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.75 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 60 +interface_shells = 0 +layer_height = 0.2 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.65 +perimeter_speed = 50 +perimeters = 3 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.65 +solid_infill_speed = 50 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_extrusion_width = 0.35 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 1 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.6 +top_solid_infill_speed = 40 +top_solid_layers = 5 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.20mm NORMAL MK3] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 4 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.8 +bridge_speed = 30 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK3" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 40 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 3500 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.45 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 35% +infill_speed = 200 +interface_shells = 0 +layer_height = 0.2 +max_print_speed = 250 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 60 +perimeters = 2 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 200 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_extrusion_width = 0.35 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 0 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.4 +top_solid_infill_speed = 50 +top_solid_layers = 5 +travel_speed = 250 +wipe_tower = 1 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.20mm NORMAL SOLUBLE FULL] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 4 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.95 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2 MultiMaterial" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 30 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.45 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 60 +interface_shells = 0 +layer_height = 0.2 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = Set your solluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 1 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 40 +perimeters = 2 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 0 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 40 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 1 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0 +support_material_enforce_layers = 0 +support_material_extruder = 4 +support_material_extrusion_width = 0.45 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 4 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.1 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 1 +support_material_threshold = 80 +support_material_with_sheath = 1 +support_material_xy_spacing = 120% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.4 +top_solid_infill_speed = 30 +top_solid_layers = 5 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 20 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.20mm NORMAL SOLUBLE INTERFACE] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 4 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.95 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2 MultiMaterial" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.45 +external_perimeter_speed = 30 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.45 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 60 +interface_shells = 0 +layer_height = 0.2 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = Set your solluble extruder in Multiple Extruders > Support material/raft interface extruder +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 1 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 40 +perimeters = 2 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 0 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.45 +solid_infill_speed = 40 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 1 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_extrusion_width = 0.45 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 4 +support_material_interface_layers = 3 +support_material_interface_spacing = 0.1 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 1 +support_material_threshold = 80 +support_material_with_sheath = 0 +support_material_xy_spacing = 120% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.4 +top_solid_infill_speed = 30 +top_solid_layers = 5 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 20 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.35mm FAST] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 3 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.95 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2";"Original Prusa i3 MK2 MM Single Mode";"Original Prusa i3 MK2 MultiMaterial" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.6 +external_perimeter_speed = 40 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.7 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 60 +interface_shells = 0 +layer_height = 0.35 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.43 +perimeter_speed = 50 +perimeters = 2 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.7 +solid_infill_speed = 60 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 1 +support_material_extrusion_width = 0.35 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 1 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.43 +top_solid_infill_speed = 50 +top_solid_layers = 4 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.35mm FAST 0.6 nozzle] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 7 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.8 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2 0.6 nozzle";"Original Prusa i3 MK2 MM Single Mode 0.6 nozzle";"Original Prusa i3 MK2 MultiMaterial 0.6 nozzle" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.61 +external_perimeter_speed = 40 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.67 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.65 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.75 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 60 +interface_shells = 0 +layer_height = 0.35 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.65 +perimeter_speed = 50 +perimeters = 3 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.65 +solid_infill_speed = 60 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_extrusion_width = 0.35 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 1 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.6 +top_solid_infill_speed = 50 +top_solid_layers = 9 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.35mm FAST MK3] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 4 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.8 +bridge_speed = 30 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK3" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.6 +external_perimeter_speed = 40 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.45 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.42 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 3500 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.7 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 35% +infill_speed = 200 +interface_shells = 0 +layer_height = 0.35 +max_print_speed = 250 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 0 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.45 +perimeter_speed = 60 +perimeters = 2 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 1 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.7 +solid_infill_speed = 200 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_extrusion_width = 0.35 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 0 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 0 +support_material_threshold = 45 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.45 +top_solid_infill_speed = 50 +top_solid_layers = 4 +travel_speed = 250 +wipe_tower = 1 +wipe_tower_per_color_wipe = 15 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.35mm FAST sol full 0.6 nozzle] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 3 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.8 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.6 +external_perimeter_speed = 30 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.67 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.65 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.75 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 60 +interface_shells = 0 +layer_height = 0.35 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = Set your solluble extruder in Multiple Extruders > Support material/raft interface extruder +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 1 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.65 +perimeter_speed = 40 +perimeters = 2 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 0 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.65 +solid_infill_speed = 60 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 1 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0 +support_material_enforce_layers = 0 +support_material_extruder = 4 +support_material_extrusion_width = 0.55 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 4 +support_material_interface_layers = 3 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 1 +support_material_threshold = 80 +support_material_with_sheath = 0 +support_material_xy_spacing = 120% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.57 +top_solid_infill_speed = 50 +top_solid_layers = 4 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 20 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:0.35mm FAST sol int 0.6 nozzle] +avoid_crossing_perimeters = 0 +bottom_solid_layers = 3 +bridge_acceleration = 1000 +bridge_angle = 0 +bridge_flow_ratio = 0.8 +bridge_speed = 20 +brim_width = 0 +clip_multipart_objects = 1 +compatible_printers = "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle" +complete_objects = 0 +default_acceleration = 1000 +dont_support_bridges = 1 +elefant_foot_compensation = 0 +ensure_vertical_shell_thickness = 1 +external_fill_pattern = rectilinear +external_perimeter_extrusion_width = 0.6 +external_perimeter_speed = 30 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 20 +extruder_clearance_radius = 20 +extrusion_width = 0.67 +fill_angle = 45 +fill_density = 20% +fill_pattern = cubic +first_layer_acceleration = 1000 +first_layer_extrusion_width = 0.65 +first_layer_height = 0.2 +first_layer_speed = 30 +gap_fill_speed = 40 +gcode_comments = 0 +infill_acceleration = 2000 +infill_every_layers = 1 +infill_extruder = 1 +infill_extrusion_width = 0.75 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 60 +interface_shells = 0 +layer_height = 0.35 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = Set your solluble extruder in Multiple Extruders > Support material/raft interface extruder +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = [input_filename_base].gcode +overhangs = 1 +perimeter_acceleration = 800 +perimeter_extruder = 1 +perimeter_extrusion_width = 0.65 +perimeter_speed = 40 +perimeters = 2 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = nearest +skirt_distance = 2 +skirt_height = 3 +skirts = 0 +small_perimeter_speed = 20 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_extrusion_width = 0.65 +solid_infill_speed = 60 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 1 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_extrusion_width = 0.55 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 4 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 50 +support_material_synchronize_layers = 1 +support_material_threshold = 80 +support_material_with_sheath = 0 +support_material_xy_spacing = 150% +thin_walls = 0 +threads = 4 +top_infill_extrusion_width = 0.57 +top_solid_infill_speed = 50 +top_solid_layers = 4 +travel_speed = 120 +wipe_tower = 1 +wipe_tower_per_color_wipe = 20 +wipe_tower_width = 60 +wipe_tower_x = 180 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[filament:ColorFabb Brass Bronze 1.75mm] +bed_temperature = 60 +bridge_fan_speed = 100 +compatible_printers = "Original Prusa i3 MK2";"Original Prusa i3 MK2 0.6 nozzle";"Original Prusa i3 MK2 MM Single Mode";"Original Prusa i3 MK2 MM Single Mode 0.6 nozzle";"Original Prusa i3 MK2 MultiMaterial";"Original Prusa i3 MK2 MultiMaterial 0.6 nozzle";"Original Prusa i3 MK3" +cooling = 1 +disable_fan_first_layers = 1 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1.3 +fan_always_on = 1 +fan_below_layer_time = 100 +filament_colour = #804040 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 10 +filament_notes = "" +filament_settings_id = +filament_soluble = 0 +filament_type = PLA +first_layer_bed_temperature = 60 +first_layer_temperature = 210 +max_fan_speed = 100 +min_fan_speed = 85 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}10{endif}; Filament gcode" +temperature = 210 + +[filament:ColorFabb HT 1.75mm] +bed_temperature = 105 +bridge_fan_speed = 30 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 0 +fan_below_layer_time = 10 +filament_colour = #FF8000 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 10 +filament_notes = "" +filament_settings_id = +filament_soluble = 0 +filament_type = PLA +first_layer_bed_temperature = 105 +first_layer_temperature = 270 +max_fan_speed = 20 +min_fan_speed = 10 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}45{endif}; Filament gcode" +temperature = 270 + +[filament:ColorFabb PLA-PHA] +bed_temperature = 60 +bridge_fan_speed = 100 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 1 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 1 +fan_below_layer_time = 100 +filament_colour = #FF3232 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 15 +filament_notes = "List of materials tested with standart PLA print settings for MK2:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladeč PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH" +filament_settings_id = +filament_soluble = 0 +filament_type = PLA +first_layer_bed_temperature = 60 +first_layer_temperature = 215 +max_fan_speed = 100 +min_fan_speed = 85 +min_print_speed = 15 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}30{endif}; Filament gcode" +temperature = 210 + +[filament:ColorFabb Woodfil 1.75mm] +bed_temperature = 60 +bridge_fan_speed = 100 +compatible_printers = "Original Prusa i3 MK2";"Original Prusa i3 MK2 0.6 nozzle";"Original Prusa i3 MK2 MM Single Mode";"Original Prusa i3 MK2 MM Single Mode 0.6 nozzle";"Original Prusa i3 MK2 MultiMaterial";"Original Prusa i3 MK2 MultiMaterial 0.6 nozzle";"Original Prusa i3 MK3" +cooling = 1 +disable_fan_first_layers = 1 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1.2 +fan_always_on = 1 +fan_below_layer_time = 100 +filament_colour = #804040 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 10 +filament_notes = "" +filament_settings_id = +filament_soluble = 0 +filament_type = PLA +first_layer_bed_temperature = 60 +first_layer_temperature = 200 +max_fan_speed = 100 +min_fan_speed = 85 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}10{endif}; Filament gcode" +temperature = 200 + +[filament:ColorFabb XT 1.75mm] +bed_temperature = 65 +bridge_fan_speed = 50 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 1 +fan_below_layer_time = 20 +filament_colour = #FF8000 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 10 +filament_notes = "" +filament_settings_id = +filament_soluble = 0 +filament_type = PLA +first_layer_bed_temperature = 90 +first_layer_temperature = 240 +max_fan_speed = 50 +min_fan_speed = 30 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}45{endif}; Filament gcode" +temperature = 240 + +[filament:ColorFabb XT-CF20 1.75mm] +bed_temperature = 90 +bridge_fan_speed = 50 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1.2 +fan_always_on = 1 +fan_below_layer_time = 20 +filament_colour = #804040 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 1 +filament_notes = "" +filament_settings_id = +filament_soluble = 0 +filament_type = PET +first_layer_bed_temperature = 90 +first_layer_temperature = 260 +max_fan_speed = 50 +min_fan_speed = 30 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}30{endif}; Filament gcode" +temperature = 260 + +[filament:ColorFabb nGen 1.75mm] +bed_temperature = 85 +bridge_fan_speed = 40 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 0 +fan_below_layer_time = 10 +filament_colour = #FF8000 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 10 +filament_notes = "" +filament_settings_id = +filament_soluble = 0 +filament_type = NGEN +first_layer_bed_temperature = 85 +first_layer_temperature = 240 +max_fan_speed = 35 +min_fan_speed = 20 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}45{endif}; Filament gcode" +temperature = 240 + +[filament:ColorFabb nGen flex] +bed_temperature = 85 +bridge_fan_speed = 40 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 0 +fan_below_layer_time = 10 +filament_colour = #FF8000 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 5 +filament_notes = "" +filament_settings_id = +filament_soluble = 0 +filament_type = FLEX +first_layer_bed_temperature = 85 +first_layer_temperature = 260 +max_fan_speed = 35 +min_fan_speed = 20 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}10{endif}; Filament gcode" +temperature = 260 + +[filament:E3D Edge] +bed_temperature = 90 +bridge_fan_speed = 50 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 1 +fan_below_layer_time = 20 +filament_colour = #FF8000 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 10 +filament_notes = "List of manufacturers tested with standart PET print settings for MK2:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladeč PETG" +filament_settings_id = +filament_soluble = 0 +filament_type = PET +first_layer_bed_temperature = 85 +first_layer_temperature = 230 +max_fan_speed = 50 +min_fan_speed = 30 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}45{endif}; Filament gcode" +temperature = 240 + +[filament:E3D PC-ABS 1.75mm] +bed_temperature = 100 +bridge_fan_speed = 30 +compatible_printers = +cooling = 0 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 0 +fan_below_layer_time = 20 +filament_colour = #3A80CA +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 13 +filament_notes = "" +filament_settings_id = +filament_soluble = 0 +filament_type = PLA +first_layer_bed_temperature = 100 +first_layer_temperature = 270 +max_fan_speed = 30 +min_fan_speed = 10 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}30{endif}; Filament gcode" +temperature = 270 + +[filament:Fillamentum ABS 1.75mm] +bed_temperature = 100 +bridge_fan_speed = 30 +compatible_printers = +cooling = 0 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 0 +fan_below_layer_time = 20 +filament_colour = #3A80CA +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 13 +filament_notes = "" +filament_settings_id = +filament_soluble = 0 +filament_type = ABS +first_layer_bed_temperature = 100 +first_layer_temperature = 240 +max_fan_speed = 30 +min_fan_speed = 10 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}30{endif}; Filament gcode" +temperature = 240 + +[filament:Fillamentum ASA 1.75mm] +bed_temperature = 100 +bridge_fan_speed = 30 +compatible_printers = +cooling = 0 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 1 +fan_below_layer_time = 20 +filament_colour = #3A80CA +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 13 +filament_notes = "" +filament_settings_id = +filament_soluble = 0 +filament_type = PLA +first_layer_bed_temperature = 100 +first_layer_temperature = 265 +max_fan_speed = 30 +min_fan_speed = 10 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}30{endif}; Filament gcode" +temperature = 265 + +[filament:Fillamentum CPE HG100 HM100] +bed_temperature = 90 +bridge_fan_speed = 50 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 1 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 1 +fan_below_layer_time = 20 +filament_colour = #FF8000 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 10 +filament_notes = "CPE HG100 , CPE HM100" +filament_settings_id = +filament_soluble = 0 +filament_type = PET +first_layer_bed_temperature = 90 +first_layer_temperature = 260 +max_fan_speed = 80 +min_fan_speed = 80 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}45{endif}; Filament gcode" +temperature = 260 + +[filament:Fillamentum Timberfil] +bed_temperature = 60 +bridge_fan_speed = 100 +compatible_printers = "Original Prusa i3 MK2";"Original Prusa i3 MK2 0.6 nozzle";"Original Prusa i3 MK2 MM Single Mode";"Original Prusa i3 MK2 MM Single Mode 0.6 nozzle";"Original Prusa i3 MK2 MultiMaterial";"Original Prusa i3 MK2 MultiMaterial 0.6 nozzle";"Original Prusa i3 MK3" +cooling = 1 +disable_fan_first_layers = 1 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1.2 +fan_always_on = 1 +fan_below_layer_time = 100 +filament_colour = #804040 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 10 +filament_notes = "" +filament_settings_id = +filament_soluble = 0 +filament_type = PLA +first_layer_bed_temperature = 60 +first_layer_temperature = 190 +max_fan_speed = 100 +min_fan_speed = 85 +min_print_speed = 15 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}10{endif}; Filament gcode" +temperature = 190 + +[filament:Generic ABS 1.75mm] +bed_temperature = 100 +bridge_fan_speed = 30 +compatible_printers = +cooling = 0 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 0 +fan_below_layer_time = 20 +filament_colour = #3A80CA +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 13 +filament_notes = "List of materials tested with standart ABS print settings for MK2:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladeč ABS" +filament_settings_id = +filament_soluble = 0 +filament_type = ABS +first_layer_bed_temperature = 100 +first_layer_temperature = 255 +max_fan_speed = 30 +min_fan_speed = 10 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}30{endif}; Filament gcode" +temperature = 255 + +[filament:Generic PET 1.75mm] +bed_temperature = 90 +bridge_fan_speed = 50 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 1 +fan_below_layer_time = 20 +filament_colour = #FF8000 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 10 +filament_notes = "List of manufacturers tested with standart PET print settings for MK2:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladeč PETG" +filament_settings_id = +filament_soluble = 0 +filament_type = PET +first_layer_bed_temperature = 85 +first_layer_temperature = 230 +max_fan_speed = 50 +min_fan_speed = 30 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}45{endif}; Filament gcode" +temperature = 240 + +[filament:Generic PLA 1.75mm] +bed_temperature = 60 +bridge_fan_speed = 100 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 1 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 1 +fan_below_layer_time = 100 +filament_colour = #FF3232 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 15 +filament_notes = "List of materials tested with standart PLA print settings for MK2:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladeč PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH" +filament_settings_id = +filament_soluble = 0 +filament_type = PLA +first_layer_bed_temperature = 60 +first_layer_temperature = 215 +max_fan_speed = 100 +min_fan_speed = 85 +min_print_speed = 15 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}30{endif}; Filament gcode" +temperature = 210 + +[filament:Polymaker PC-Max] +bed_temperature = 120 +bridge_fan_speed = 30 +compatible_printers = +cooling = 0 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 0 +fan_below_layer_time = 20 +filament_colour = #3A80CA +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 13 +filament_notes = "List of materials tested with standart ABS print settings for MK2:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladeč ABS" +filament_settings_id = +filament_soluble = 0 +filament_type = ABS +first_layer_bed_temperature = 120 +first_layer_temperature = 270 +max_fan_speed = 30 +min_fan_speed = 10 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}30{endif}; Filament gcode" +temperature = 270 + +[filament:Primavalue PVA] +bed_temperature = 60 +bridge_fan_speed = 100 +compatible_printers = +cooling = 0 +disable_fan_first_layers = 1 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 0 +fan_below_layer_time = 100 +filament_colour = #FFFFD7 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 10 +filament_notes = "List of materials tested with standart PVA print settings for MK2:\n\nPrimaSelect PVA+\nICE FILAMENTS PVA 'NAUGHTY NATURAL'\nVerbatim BVOH" +filament_settings_id = +filament_soluble = 1 +filament_type = PVA +first_layer_bed_temperature = 60 +first_layer_temperature = 195 +max_fan_speed = 100 +min_fan_speed = 85 +min_print_speed = 15 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}10{endif}; Filament gcode" +temperature = 195 + +[filament:Prusa ABS 1.75mm] +bed_temperature = 100 +bridge_fan_speed = 30 +compatible_printers = +cooling = 0 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 0 +fan_below_layer_time = 20 +filament_colour = #3A80CA +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 13 +filament_notes = "List of materials tested with standart ABS print settings for MK2:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladeč ABS" +filament_settings_id = +filament_soluble = 0 +filament_type = ABS +first_layer_bed_temperature = 100 +first_layer_temperature = 255 +max_fan_speed = 30 +min_fan_speed = 10 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}30{endif}; Filament gcode" +temperature = 255 + +[filament:Prusa HIPS 1.75mm] +bed_temperature = 100 +bridge_fan_speed = 50 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 0.9 +fan_always_on = 1 +fan_below_layer_time = 10 +filament_colour = #FFFFD7 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 13 +filament_notes = "" +filament_settings_id = +filament_soluble = 1 +filament_type = HIPS +first_layer_bed_temperature = 100 +first_layer_temperature = 220 +max_fan_speed = 20 +min_fan_speed = 20 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}10{endif}; Filament gcode" +temperature = 220 + +[filament:Prusa PET 1.75mm] +bed_temperature = 90 +bridge_fan_speed = 50 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 1 +fan_below_layer_time = 20 +filament_colour = #FF8000 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 10 +filament_notes = "List of manufacturers tested with standart PET print settings for MK2:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladeč PETG" +filament_settings_id = +filament_soluble = 0 +filament_type = PET +first_layer_bed_temperature = 85 +first_layer_temperature = 230 +max_fan_speed = 50 +min_fan_speed = 30 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}45{endif}; Filament gcode" +temperature = 240 + +[filament:Prusa PLA 1.75mm] +bed_temperature = 60 +bridge_fan_speed = 100 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 1 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 1 +fan_below_layer_time = 100 +filament_colour = #FF3232 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 15 +filament_notes = "List of materials tested with standart PLA print settings for MK2:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladeč PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH" +filament_settings_id = +filament_soluble = 0 +filament_type = PLA +first_layer_bed_temperature = 60 +first_layer_temperature = 215 +max_fan_speed = 100 +min_fan_speed = 85 +min_print_speed = 15 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}30{endif}; Filament gcode" +temperature = 210 + +[filament:SemiFlex or Flexfill 98A] +bed_temperature = 50 +bridge_fan_speed = 100 +compatible_printers = "Original Prusa i3 MK2";"Original Prusa i3 MK2 0.6 nozzle";"Original Prusa i3 MK2 MM Single Mode";"Original Prusa i3 MK2 MM Single Mode 0.6 nozzle";"Original Prusa i3 MK3" +cooling = 0 +disable_fan_first_layers = 1 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1.2 +fan_always_on = 0 +fan_below_layer_time = 100 +filament_colour = #00CA0A +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 2.5 +filament_notes = "List of materials tested with FLEX print settings & FLEX material settings for MK2:\n\nFillamentum Flex 98A\nFillamentum Flex 92A\nPlasty Mladeč PP\nPlasty Mladeč TPE32 \nPlasty Mladeč TPE88" +filament_settings_id = +filament_soluble = 0 +filament_type = FLEX +first_layer_bed_temperature = 50 +first_layer_temperature = 220 +max_fan_speed = 90 +min_fan_speed = 70 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}10{endif}; Filament gcode" +temperature = 230 + +[filament:Taulman Bridge 1.75mm] +bed_temperature = 50 +bridge_fan_speed = 40 +compatible_printers = +cooling = 0 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 0 +fan_below_layer_time = 20 +filament_colour = #DEE0E6 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 10 +filament_notes = "" +filament_settings_id = +filament_soluble = 0 +filament_type = PLA +first_layer_bed_temperature = 90 +first_layer_temperature = 240 +max_fan_speed = 5 +min_fan_speed = 0 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}10{endif}; Filament gcode" +temperature = 250 + +[filament:Taulman T-Glase 1.75mm] +bed_temperature = 90 +bridge_fan_speed = 40 +compatible_printers = +cooling = 0 +disable_fan_first_layers = 3 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 0 +fan_below_layer_time = 20 +filament_colour = #FF8000 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 10 +filament_notes = "" +filament_settings_id = +filament_soluble = 0 +filament_type = PET +first_layer_bed_temperature = 90 +first_layer_temperature = 240 +max_fan_speed = 5 +min_fan_speed = 0 +min_print_speed = 5 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}30{endif}; Filament gcode" +temperature = 240 + +[filament:Verbatim BVOH] +bed_temperature = 60 +bridge_fan_speed = 100 +compatible_printers = +cooling = 0 +disable_fan_first_layers = 1 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 0 +fan_below_layer_time = 100 +filament_colour = #FFFFD7 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 10 +filament_notes = "List of materials tested with standart PLA print settings for MK2:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladeč PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH" +filament_settings_id = +filament_soluble = 1 +filament_type = PLA +first_layer_bed_temperature = 60 +first_layer_temperature = 215 +max_fan_speed = 100 +min_fan_speed = 85 +min_print_speed = 15 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}10{endif}; Filament gcode" +temperature = 210 + +[filament:Verbatim PP] +bed_temperature = 100 +bridge_fan_speed = 100 +compatible_printers = +cooling = 1 +disable_fan_first_layers = 2 +end_filament_gcode = "; Filament-specific end gcode" +extrusion_multiplier = 1 +fan_always_on = 1 +fan_below_layer_time = 100 +filament_colour = #DEE0E6 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_max_volumetric_speed = 5 +filament_notes = "List of materials tested with standart PLA print settings for MK2:\n\nEsun PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladeč PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nEUMAKERS PLA" +filament_settings_id = +filament_soluble = 0 +filament_type = PLA +first_layer_bed_temperature = 100 +first_layer_temperature = 220 +max_fan_speed = 100 +min_fan_speed = 100 +min_print_speed = 15 +slowdown_below_layer_time = 10 +start_filament_gcode = "M900 K{if printer_preset==\"Original Prusa i3 MK2 MultiMaterial\"}200{elsif printer_preset==\"Original Prusa i3 MK2 MM Single Mode\"}200{else}10{endif}; Filament gcode" +temperature = 220 + +[printer:Original Prusa i3 MK2] +bed_shape = 0x0,250x0,250x210,0x210 +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n\n +between_objects_gcode = +deretract_speed = 0 +end_gcode = G4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors +extruder_colour = #FFFF00 +extruder_offset = 0x0 +gcode_flavor = reprap +layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] +max_layer_height = 0.25 +min_layer_height = 0.07 +nozzle_diameter = 0.4 +octoprint_apikey = +octoprint_host = +printer_notes = +printer_settings_id = +retract_before_travel = 1 +retract_before_wipe = 0% +retract_layer_change = 1 +retract_length = 0.8 +retract_length_toolchange = 3 +retract_lift = 0.5 +retract_lift_above = 1 +retract_lift_below = 199 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 35 +serial_port = +serial_speed = 250000 +single_extruder_multi_material = 0 +start_gcode = M115 U3.1.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +toolchange_gcode = +use_firmware_retraction = 0 +use_relative_e_distances = 1 +use_volumetric_e = 0 +variable_layer_height = 1 +wipe = 1 +z_offset = 0 + +[printer:Original Prusa i3 MK2 0.25 nozzle] +bed_shape = 0x0,250x0,250x210,0x210 +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n\n +between_objects_gcode = +deretract_speed = 0 +end_gcode = G4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors +extruder_colour = #FFFF00 +extruder_offset = 0x0 +gcode_flavor = reprap +layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] +max_layer_height = 0.1 +min_layer_height = 0.05 +nozzle_diameter = 0.25 +octoprint_apikey = +octoprint_host = +printer_notes = +printer_settings_id = +retract_before_travel = 1 +retract_before_wipe = 0% +retract_layer_change = 1 +retract_length = 1 +retract_length_toolchange = 3 +retract_lift = 0.5 +retract_lift_above = 1 +retract_lift_below = 199 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 50 +serial_port = +serial_speed = 250000 +single_extruder_multi_material = 0 +start_gcode = M115 U3.1.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +toolchange_gcode = +use_firmware_retraction = 0 +use_relative_e_distances = 1 +use_volumetric_e = 0 +variable_layer_height = 0 +wipe = 1 +z_offset = 0 + +[printer:Original Prusa i3 MK2 0.6 nozzle] +bed_shape = 0x0,250x0,250x210,0x210 +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n\n +between_objects_gcode = +deretract_speed = 0 +end_gcode = G4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors +extruder_colour = #FFFF00 +extruder_offset = 0x0 +gcode_flavor = reprap +layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] +max_layer_height = 0.35 +min_layer_height = 0.1 +nozzle_diameter = 0.6 +octoprint_apikey = +octoprint_host = +printer_notes = +printer_settings_id = +retract_before_travel = 1 +retract_before_wipe = 0% +retract_layer_change = 1 +retract_length = 0.8 +retract_length_toolchange = 3 +retract_lift = 0.5 +retract_lift_above = 1 +retract_lift_below = 199 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 35 +serial_port = +serial_speed = 250000 +single_extruder_multi_material = 0 +start_gcode = M115 U3.1.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +toolchange_gcode = +use_firmware_retraction = 0 +use_relative_e_distances = 1 +use_volumetric_e = 0 +variable_layer_height = 1 +wipe = 1 +z_offset = 0 + +[printer:Original Prusa i3 MK2 MM Single Mode] +bed_shape = 0x0,250x0,250x210,0x210 +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n\n +between_objects_gcode = +deretract_speed = 50 +end_gcode = G1 E-4 F2100.00000\nG91\nG1 Z1 F7200.000\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7 \nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3 \nG1 E-15.0000 F5000\nG1 E-50.0000 F5400\nG1 E-15.0000 F3000\nG1 E-12.0000 F2000\nG1 F1600\nG1 X0 Y1 E3.0000\nG1 X50 Y1 E-5.0000\nG1 F2000\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-3.0000\nG4 S0\nM107 ; fan off\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM84 ; disable motors\n\n +extruder_colour = #FFAA55 +extruder_offset = 0x0 +gcode_flavor = reprap +layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] +max_layer_height = 0.25 +min_layer_height = 0.07 +nozzle_diameter = 0.4 +octoprint_apikey = +octoprint_host = +printer_notes = +printer_settings_id = +retract_before_travel = 3 +retract_before_wipe = 0% +retract_layer_change = 0 +retract_length = 4 +retract_length_toolchange = 6 +retract_lift = 0.5 +retract_lift_above = 0 +retract_lift_below = 199 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 80 +serial_port = +serial_speed = 250000 +single_extruder_multi_material = 1 +start_gcode = M115 U3.1.0 ; tell printer latest fw version\n; Start G-Code sequence START\nT?\nM104 S[first_layer_temperature]\nM140 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nM190 S[first_layer_bed_temperature]\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100\nM92 E140\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG1 E-4 F1000.0\nG92 E0.0 +toolchange_gcode = +use_firmware_retraction = 0 +use_relative_e_distances = 1 +use_volumetric_e = 0 +variable_layer_height = 1 +wipe = 1 +z_offset = 0 + +[printer:Original Prusa i3 MK2 MM Single Mode 0.6 nozzle] +bed_shape = 0x0,250x0,250x210,0x210 +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n\n +between_objects_gcode = +deretract_speed = 50 +end_gcode = G1 E-4 F2100.00000\nG91\nG1 Z1 F7200.000\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7 \nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3 \nG1 E-15.0000 F5000\nG1 E-50.0000 F5400\nG1 E-15.0000 F3000\nG1 E-12.0000 F2000\nG1 F1600\nG1 X0 Y1 E3.0000\nG1 X50 Y1 E-5.0000\nG1 F2000\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-3.0000\nG4 S0\nM107 ; fan off\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM84 ; disable motors\n\n +extruder_colour = #FFAA55 +extruder_offset = 0x0 +gcode_flavor = reprap +layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] +max_layer_height = 0.25 +min_layer_height = 0.07 +nozzle_diameter = 0.6 +octoprint_apikey = +octoprint_host = +printer_notes = +printer_settings_id = +retract_before_travel = 3 +retract_before_wipe = 0% +retract_layer_change = 0 +retract_length = 4 +retract_length_toolchange = 6 +retract_lift = 0.5 +retract_lift_above = 0 +retract_lift_below = 199 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 80 +serial_port = +serial_speed = 250000 +single_extruder_multi_material = 1 +start_gcode = M115 U3.1.0 ; tell printer latest fw version\n; Start G-Code sequence START\nT?\nM104 S[first_layer_temperature]\nM140 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nM190 S[first_layer_bed_temperature]\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100\nM92 E140\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG1 E-4 F1000.0\nG92 E0.0 +toolchange_gcode = +use_firmware_retraction = 0 +use_relative_e_distances = 1 +use_volumetric_e = 0 +variable_layer_height = 1 +wipe = 1 +z_offset = 0 + +[printer:Original Prusa i3 MK2 MultiMaterial] +bed_shape = 0x0,250x0,250x210,0x210 +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n\n +between_objects_gcode = +deretract_speed = 50,50,50,50 +end_gcode = {if not has_wipe_tower}\n; Pull the filament into the cooling tubes.\nG1 E-4 F2100.00000\nG91\nG1 Z1 F7200.000\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7 \nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3 \nG1 E-15.0000 F5000\nG1 E-50.0000 F5400\nG1 E-15.0000 F3000\nG1 E-12.0000 F2000\nG1 F1600\nG1 X0 Y1 E3.0000\nG1 X50 Y1 E-5.0000\nG1 F2000\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-3.0000\nG4 S0\n{endif}\nM107 ; fan off\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM84 ; disable motors +extruder_colour = #FFAA55;#5182DB;#4ECDD3;#FB7259 +extruder_offset = 0x0,0x0,0x0,0x0 +gcode_flavor = reprap +layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] +max_layer_height = 0.25,0.25,0.25,0.25 +min_layer_height = 0.07,0.07,0.07,0.07 +nozzle_diameter = 0.4,0.4,0.4,0.4 +octoprint_apikey = +octoprint_host = +printer_notes = +printer_settings_id = +retract_before_travel = 3,3,3,3 +retract_before_wipe = 60%,60%,60%,60% +retract_layer_change = 0,0,0,0 +retract_length = 4,4,4,4 +retract_length_toolchange = 4,4,4,4 +retract_lift = 0.5,0.5,0.5,0.5 +retract_lift_above = 0,0,0,0 +retract_lift_below = 199,199,199,199 +retract_restart_extra = 0,0,0,0 +retract_restart_extra_toolchange = 0,0,0,0 +retract_speed = 80,80,80,80 +serial_port = +serial_speed = 250000 +single_extruder_multi_material = 1 +start_gcode = M115 U3.1.0 ; tell printer latest fw version\n; Start G-Code sequence START\nT[initial_tool]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100 ; set max feedrate\nM92 E140 ; E-steps per filament milimeter\n{if not has_wipe_tower}\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG1 E-4 F1000.0\n{endif}\nG92 E0.0 +toolchange_gcode = +use_firmware_retraction = 0 +use_relative_e_distances = 1 +use_volumetric_e = 0 +variable_layer_height = 0 +wipe = 1,1,1,1 +z_offset = 0 + +[printer:Original Prusa i3 MK2 MultiMaterial 0.6 nozzle] +bed_shape = 0x0,250x0,250x210,0x210 +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n\n +between_objects_gcode = +deretract_speed = 50,50,50,50 +end_gcode = {if not has_wipe_tower}\nG1 E-4 F2100.00000\nG91\nG1 Z1 F7200.000\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7 \nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3 \nG1 E-15.0000 F5000\nG1 E-50.0000 F5400\nG1 E-15.0000 F3000\nG1 E-12.0000 F2000\nG1 F1600\nG1 X0 Y1 E3.0000\nG1 X50 Y1 E-5.0000\nG1 F2000\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-3.0000\nG4 S0\n{endif}\nM107 ; fan off\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM84 ; disable motors\n +extruder_colour = #FFAA55;#5182DB;#4ECDD3;#FB7259 +extruder_offset = 0x0,0x0,0x0,0x0 +gcode_flavor = reprap +layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] +max_layer_height = 0.25,0.25,0.25,0.25 +min_layer_height = 0.07,0.07,0.07,0.07 +nozzle_diameter = 0.6,0.6,0.6,0.6 +octoprint_apikey = +octoprint_host = +printer_notes = +printer_settings_id = +retract_before_travel = 3,3,3,3 +retract_before_wipe = 60%,60%,60%,60% +retract_layer_change = 0,0,0,0 +retract_length = 4,4,4,4 +retract_length_toolchange = 4,4,4,4 +retract_lift = 0.5,0.5,0.5,0.5 +retract_lift_above = 0,0,0,0 +retract_lift_below = 199,199,199,199 +retract_restart_extra = 0,0,0,0 +retract_restart_extra_toolchange = 0,0,0,0 +retract_speed = 80,80,80,80 +serial_port = +serial_speed = 250000 +single_extruder_multi_material = 1 +start_gcode = M115 U3.1.0 ; tell printer latest fw version\n; Start G-Code sequence START\nT[initial_tool]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100\nM92 E140\n{if not has_wipe_tower}\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG1 E-4 F1000.0\n{endif}\nG92 E0.0 +toolchange_gcode = +use_firmware_retraction = 0 +use_relative_e_distances = 1 +use_volumetric_e = 0 +variable_layer_height = 0 +wipe = 1,1,1,1 +z_offset = 0 + +[printer:Original Prusa i3 MK3] +bed_shape = 0x0,250x0,250x210,0x210 +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n\n +between_objects_gcode = +deretract_speed = 0 +end_gcode = G4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors +extruder_colour = #FFFF00 +extruder_offset = 0x0 +gcode_flavor = reprap +layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] +max_layer_height = 0.25 +min_layer_height = 0.07 +nozzle_diameter = 0.4 +octoprint_apikey = +octoprint_host = +printer_notes = +retract_before_travel = 1 +retract_before_wipe = 0% +retract_layer_change = 1 +retract_length = 0.8 +retract_length_toolchange = 3 +retract_lift = 0.5 +retract_lift_above = 1 +retract_lift_below = 199 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 35 +serial_port = +serial_speed = 250000 +single_extruder_multi_material = 0 +start_gcode = M115 U3.1.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside pritn area\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line +toolchange_gcode = +use_firmware_retraction = 0 +use_relative_e_distances = 1 +use_volumetric_e = 0 +variable_layer_height = 1 +wipe = 1 +z_offset = 0 + +[presets] +print = 0.15mm 100mms Linear Advance +printer = Original Prusa i3 MK2 +filament = Prusa PLA 1.75mm From 455f9befbcb190da3ad412d8c0eb412ed70e44e0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 10 Dec 2017 20:51:45 +0100 Subject: [PATCH 79/83] Moved the icons and images from Slic3r/var to Slic3r/resources/icons to simplify installation of application static data (icons, images and printer profiles) onto a target system. --- {var => resources/icons}/Slic3r-console.ico | Bin {var => resources/icons}/Slic3r.icns | Bin {var => resources/icons}/Slic3r.ico | Bin {var => resources/icons}/Slic3r.png | Bin {var => resources/icons}/Slic3r_128px.png | Bin {var => resources/icons}/Slic3r_192px.png | Bin .../icons}/Slic3r_192px_transparent.png | Bin {var => resources/icons}/add.png | Bin {var => resources/icons}/application_view_tile.png | Bin {var => resources/icons}/arrow_down.png | Bin {var => resources/icons}/arrow_left.png | Bin {var => resources/icons}/arrow_out.png | Bin {var => resources/icons}/arrow_refresh.png | Bin {var => resources/icons}/arrow_right.png | Bin .../icons}/arrow_rotate_anticlockwise.png | Bin {var => resources/icons}/arrow_rotate_clockwise.png | Bin {var => resources/icons}/arrow_up.png | Bin {var => resources/icons}/box.png | Bin {var => resources/icons}/brick.png | Bin {var => resources/icons}/brick_add.png | Bin {var => resources/icons}/brick_delete.png | Bin {var => resources/icons}/brick_go.png | Bin {var => resources/icons}/bricks.png | Bin {var => resources/icons}/building.png | Bin {var => resources/icons}/bullet_arrow_down.png | Bin {var => resources/icons}/bullet_arrow_up.png | Bin {var => resources/icons}/bullet_black.png | Bin {var => resources/icons}/bullet_blue.png | Bin {var => resources/icons}/bullet_green.png | Bin {var => resources/icons}/bullet_red.png | Bin {var => resources/icons}/bullet_white.png | Bin {var => resources/icons}/cog.png | Bin {var => resources/icons}/cog_go.png | Bin {var => resources/icons}/control_pause.png | Bin {var => resources/icons}/control_pause_blue.png | Bin {var => resources/icons}/control_play.png | Bin {var => resources/icons}/control_play_blue.png | Bin {var => resources/icons}/control_stop.png | Bin {var => resources/icons}/control_stop_blue.png | Bin {var => resources/icons}/cross.png | Bin {var => resources/icons}/delete.png | Bin {var => resources/icons}/disk.png | Bin {var => resources/icons}/error.png | Bin {var => resources/icons}/flag-green-icon.png | Bin {var => resources/icons}/flag-red-icon.png | Bin {var => resources/icons}/funnel.png | Bin {var => resources/icons}/hourglass.png | Bin {var => resources/icons}/house.png | Bin {var => resources/icons}/infill.png | Bin {var => resources/icons}/joystick.png | Bin {var => resources/icons}/layers.png | Bin {var => resources/icons}/lorry_add.png | Bin {var => resources/icons}/lorry_go.png | Bin {var => resources/icons}/note.png | Bin {var => resources/icons}/package.png | Bin {var => resources/icons}/package_green.png | Bin {var => resources/icons}/page_white_go.png | Bin {var => resources/icons}/plugin.png | Bin {var => resources/icons}/plugin_add.png | Bin {var => resources/icons}/plugin_go.png | Bin {var => resources/icons}/printer_empty.png | Bin {var => resources/icons}/reslice.png | Bin {var => resources/icons}/shape_flip_horizontal.png | Bin {var => resources/icons}/shape_handles.png | Bin {var => resources/icons}/shape_ungroup.png | Bin {var => resources/icons}/spool.png | Bin {var => resources/icons}/tag_blue.png | Bin {var => resources/icons}/textfield.png | Bin {var => resources/icons}/time.png | Bin {var => resources/icons}/variable_layer_height.png | Bin .../icons}/variable_layer_height_reset.png | Bin .../icons}/variable_layer_height_tooltip.png | Bin {var => resources/icons}/wand.png | Bin {var => resources/icons}/wrench.png | Bin {var => resources/icons}/zoom.png | Bin 75 files changed, 0 insertions(+), 0 deletions(-) rename {var => resources/icons}/Slic3r-console.ico (100%) rename {var => resources/icons}/Slic3r.icns (100%) rename {var => resources/icons}/Slic3r.ico (100%) rename {var => resources/icons}/Slic3r.png (100%) rename {var => resources/icons}/Slic3r_128px.png (100%) rename {var => resources/icons}/Slic3r_192px.png (100%) rename {var => resources/icons}/Slic3r_192px_transparent.png (100%) rename {var => resources/icons}/add.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/application_view_tile.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/arrow_down.png (100%) rename {var => resources/icons}/arrow_left.png (100%) rename {var => resources/icons}/arrow_out.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/arrow_refresh.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/arrow_right.png (100%) rename {var => resources/icons}/arrow_rotate_anticlockwise.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/arrow_rotate_clockwise.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/arrow_up.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/box.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/brick.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/brick_add.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/brick_delete.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/brick_go.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/bricks.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/building.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/bullet_arrow_down.png (100%) rename {var => resources/icons}/bullet_arrow_up.png (100%) rename {var => resources/icons}/bullet_black.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/bullet_blue.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/bullet_green.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/bullet_red.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/bullet_white.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/cog.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/cog_go.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/control_pause.png (100%) rename {var => resources/icons}/control_pause_blue.png (100%) rename {var => resources/icons}/control_play.png (100%) rename {var => resources/icons}/control_play_blue.png (100%) rename {var => resources/icons}/control_stop.png (100%) rename {var => resources/icons}/control_stop_blue.png (100%) rename {var => resources/icons}/cross.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/delete.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/disk.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/error.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/flag-green-icon.png (100%) rename {var => resources/icons}/flag-red-icon.png (100%) rename {var => resources/icons}/funnel.png (100%) rename {var => resources/icons}/hourglass.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/house.png (100%) rename {var => resources/icons}/infill.png (100%) rename {var => resources/icons}/joystick.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/layers.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/lorry_add.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/lorry_go.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/note.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/package.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/package_green.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/page_white_go.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/plugin.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/plugin_add.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/plugin_go.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/printer_empty.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/reslice.png (100%) rename {var => resources/icons}/shape_flip_horizontal.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/shape_handles.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/shape_ungroup.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/spool.png (100%) rename {var => resources/icons}/tag_blue.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/textfield.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/time.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/variable_layer_height.png (100%) rename {var => resources/icons}/variable_layer_height_reset.png (100%) rename {var => resources/icons}/variable_layer_height_tooltip.png (100%) rename {var => resources/icons}/wand.png (100%) rename {var => resources/icons}/wrench.png (100%) mode change 100755 => 100644 rename {var => resources/icons}/zoom.png (100%) mode change 100755 => 100644 diff --git a/var/Slic3r-console.ico b/resources/icons/Slic3r-console.ico similarity index 100% rename from var/Slic3r-console.ico rename to resources/icons/Slic3r-console.ico diff --git a/var/Slic3r.icns b/resources/icons/Slic3r.icns similarity index 100% rename from var/Slic3r.icns rename to resources/icons/Slic3r.icns diff --git a/var/Slic3r.ico b/resources/icons/Slic3r.ico similarity index 100% rename from var/Slic3r.ico rename to resources/icons/Slic3r.ico diff --git a/var/Slic3r.png b/resources/icons/Slic3r.png similarity index 100% rename from var/Slic3r.png rename to resources/icons/Slic3r.png diff --git a/var/Slic3r_128px.png b/resources/icons/Slic3r_128px.png similarity index 100% rename from var/Slic3r_128px.png rename to resources/icons/Slic3r_128px.png diff --git a/var/Slic3r_192px.png b/resources/icons/Slic3r_192px.png similarity index 100% rename from var/Slic3r_192px.png rename to resources/icons/Slic3r_192px.png diff --git a/var/Slic3r_192px_transparent.png b/resources/icons/Slic3r_192px_transparent.png similarity index 100% rename from var/Slic3r_192px_transparent.png rename to resources/icons/Slic3r_192px_transparent.png diff --git a/var/add.png b/resources/icons/add.png old mode 100755 new mode 100644 similarity index 100% rename from var/add.png rename to resources/icons/add.png diff --git a/var/application_view_tile.png b/resources/icons/application_view_tile.png old mode 100755 new mode 100644 similarity index 100% rename from var/application_view_tile.png rename to resources/icons/application_view_tile.png diff --git a/var/arrow_down.png b/resources/icons/arrow_down.png similarity index 100% rename from var/arrow_down.png rename to resources/icons/arrow_down.png diff --git a/var/arrow_left.png b/resources/icons/arrow_left.png similarity index 100% rename from var/arrow_left.png rename to resources/icons/arrow_left.png diff --git a/var/arrow_out.png b/resources/icons/arrow_out.png old mode 100755 new mode 100644 similarity index 100% rename from var/arrow_out.png rename to resources/icons/arrow_out.png diff --git a/var/arrow_refresh.png b/resources/icons/arrow_refresh.png old mode 100755 new mode 100644 similarity index 100% rename from var/arrow_refresh.png rename to resources/icons/arrow_refresh.png diff --git a/var/arrow_right.png b/resources/icons/arrow_right.png similarity index 100% rename from var/arrow_right.png rename to resources/icons/arrow_right.png diff --git a/var/arrow_rotate_anticlockwise.png b/resources/icons/arrow_rotate_anticlockwise.png old mode 100755 new mode 100644 similarity index 100% rename from var/arrow_rotate_anticlockwise.png rename to resources/icons/arrow_rotate_anticlockwise.png diff --git a/var/arrow_rotate_clockwise.png b/resources/icons/arrow_rotate_clockwise.png old mode 100755 new mode 100644 similarity index 100% rename from var/arrow_rotate_clockwise.png rename to resources/icons/arrow_rotate_clockwise.png diff --git a/var/arrow_up.png b/resources/icons/arrow_up.png old mode 100755 new mode 100644 similarity index 100% rename from var/arrow_up.png rename to resources/icons/arrow_up.png diff --git a/var/box.png b/resources/icons/box.png old mode 100755 new mode 100644 similarity index 100% rename from var/box.png rename to resources/icons/box.png diff --git a/var/brick.png b/resources/icons/brick.png old mode 100755 new mode 100644 similarity index 100% rename from var/brick.png rename to resources/icons/brick.png diff --git a/var/brick_add.png b/resources/icons/brick_add.png old mode 100755 new mode 100644 similarity index 100% rename from var/brick_add.png rename to resources/icons/brick_add.png diff --git a/var/brick_delete.png b/resources/icons/brick_delete.png old mode 100755 new mode 100644 similarity index 100% rename from var/brick_delete.png rename to resources/icons/brick_delete.png diff --git a/var/brick_go.png b/resources/icons/brick_go.png old mode 100755 new mode 100644 similarity index 100% rename from var/brick_go.png rename to resources/icons/brick_go.png diff --git a/var/bricks.png b/resources/icons/bricks.png old mode 100755 new mode 100644 similarity index 100% rename from var/bricks.png rename to resources/icons/bricks.png diff --git a/var/building.png b/resources/icons/building.png old mode 100755 new mode 100644 similarity index 100% rename from var/building.png rename to resources/icons/building.png diff --git a/var/bullet_arrow_down.png b/resources/icons/bullet_arrow_down.png similarity index 100% rename from var/bullet_arrow_down.png rename to resources/icons/bullet_arrow_down.png diff --git a/var/bullet_arrow_up.png b/resources/icons/bullet_arrow_up.png similarity index 100% rename from var/bullet_arrow_up.png rename to resources/icons/bullet_arrow_up.png diff --git a/var/bullet_black.png b/resources/icons/bullet_black.png old mode 100755 new mode 100644 similarity index 100% rename from var/bullet_black.png rename to resources/icons/bullet_black.png diff --git a/var/bullet_blue.png b/resources/icons/bullet_blue.png old mode 100755 new mode 100644 similarity index 100% rename from var/bullet_blue.png rename to resources/icons/bullet_blue.png diff --git a/var/bullet_green.png b/resources/icons/bullet_green.png old mode 100755 new mode 100644 similarity index 100% rename from var/bullet_green.png rename to resources/icons/bullet_green.png diff --git a/var/bullet_red.png b/resources/icons/bullet_red.png old mode 100755 new mode 100644 similarity index 100% rename from var/bullet_red.png rename to resources/icons/bullet_red.png diff --git a/var/bullet_white.png b/resources/icons/bullet_white.png old mode 100755 new mode 100644 similarity index 100% rename from var/bullet_white.png rename to resources/icons/bullet_white.png diff --git a/var/cog.png b/resources/icons/cog.png old mode 100755 new mode 100644 similarity index 100% rename from var/cog.png rename to resources/icons/cog.png diff --git a/var/cog_go.png b/resources/icons/cog_go.png old mode 100755 new mode 100644 similarity index 100% rename from var/cog_go.png rename to resources/icons/cog_go.png diff --git a/var/control_pause.png b/resources/icons/control_pause.png similarity index 100% rename from var/control_pause.png rename to resources/icons/control_pause.png diff --git a/var/control_pause_blue.png b/resources/icons/control_pause_blue.png similarity index 100% rename from var/control_pause_blue.png rename to resources/icons/control_pause_blue.png diff --git a/var/control_play.png b/resources/icons/control_play.png similarity index 100% rename from var/control_play.png rename to resources/icons/control_play.png diff --git a/var/control_play_blue.png b/resources/icons/control_play_blue.png similarity index 100% rename from var/control_play_blue.png rename to resources/icons/control_play_blue.png diff --git a/var/control_stop.png b/resources/icons/control_stop.png similarity index 100% rename from var/control_stop.png rename to resources/icons/control_stop.png diff --git a/var/control_stop_blue.png b/resources/icons/control_stop_blue.png similarity index 100% rename from var/control_stop_blue.png rename to resources/icons/control_stop_blue.png diff --git a/var/cross.png b/resources/icons/cross.png old mode 100755 new mode 100644 similarity index 100% rename from var/cross.png rename to resources/icons/cross.png diff --git a/var/delete.png b/resources/icons/delete.png old mode 100755 new mode 100644 similarity index 100% rename from var/delete.png rename to resources/icons/delete.png diff --git a/var/disk.png b/resources/icons/disk.png old mode 100755 new mode 100644 similarity index 100% rename from var/disk.png rename to resources/icons/disk.png diff --git a/var/error.png b/resources/icons/error.png old mode 100755 new mode 100644 similarity index 100% rename from var/error.png rename to resources/icons/error.png diff --git a/var/flag-green-icon.png b/resources/icons/flag-green-icon.png similarity index 100% rename from var/flag-green-icon.png rename to resources/icons/flag-green-icon.png diff --git a/var/flag-red-icon.png b/resources/icons/flag-red-icon.png similarity index 100% rename from var/flag-red-icon.png rename to resources/icons/flag-red-icon.png diff --git a/var/funnel.png b/resources/icons/funnel.png similarity index 100% rename from var/funnel.png rename to resources/icons/funnel.png diff --git a/var/hourglass.png b/resources/icons/hourglass.png old mode 100755 new mode 100644 similarity index 100% rename from var/hourglass.png rename to resources/icons/hourglass.png diff --git a/var/house.png b/resources/icons/house.png similarity index 100% rename from var/house.png rename to resources/icons/house.png diff --git a/var/infill.png b/resources/icons/infill.png similarity index 100% rename from var/infill.png rename to resources/icons/infill.png diff --git a/var/joystick.png b/resources/icons/joystick.png old mode 100755 new mode 100644 similarity index 100% rename from var/joystick.png rename to resources/icons/joystick.png diff --git a/var/layers.png b/resources/icons/layers.png old mode 100755 new mode 100644 similarity index 100% rename from var/layers.png rename to resources/icons/layers.png diff --git a/var/lorry_add.png b/resources/icons/lorry_add.png old mode 100755 new mode 100644 similarity index 100% rename from var/lorry_add.png rename to resources/icons/lorry_add.png diff --git a/var/lorry_go.png b/resources/icons/lorry_go.png old mode 100755 new mode 100644 similarity index 100% rename from var/lorry_go.png rename to resources/icons/lorry_go.png diff --git a/var/note.png b/resources/icons/note.png old mode 100755 new mode 100644 similarity index 100% rename from var/note.png rename to resources/icons/note.png diff --git a/var/package.png b/resources/icons/package.png old mode 100755 new mode 100644 similarity index 100% rename from var/package.png rename to resources/icons/package.png diff --git a/var/package_green.png b/resources/icons/package_green.png old mode 100755 new mode 100644 similarity index 100% rename from var/package_green.png rename to resources/icons/package_green.png diff --git a/var/page_white_go.png b/resources/icons/page_white_go.png old mode 100755 new mode 100644 similarity index 100% rename from var/page_white_go.png rename to resources/icons/page_white_go.png diff --git a/var/plugin.png b/resources/icons/plugin.png old mode 100755 new mode 100644 similarity index 100% rename from var/plugin.png rename to resources/icons/plugin.png diff --git a/var/plugin_add.png b/resources/icons/plugin_add.png old mode 100755 new mode 100644 similarity index 100% rename from var/plugin_add.png rename to resources/icons/plugin_add.png diff --git a/var/plugin_go.png b/resources/icons/plugin_go.png old mode 100755 new mode 100644 similarity index 100% rename from var/plugin_go.png rename to resources/icons/plugin_go.png diff --git a/var/printer_empty.png b/resources/icons/printer_empty.png old mode 100755 new mode 100644 similarity index 100% rename from var/printer_empty.png rename to resources/icons/printer_empty.png diff --git a/var/reslice.png b/resources/icons/reslice.png similarity index 100% rename from var/reslice.png rename to resources/icons/reslice.png diff --git a/var/shape_flip_horizontal.png b/resources/icons/shape_flip_horizontal.png old mode 100755 new mode 100644 similarity index 100% rename from var/shape_flip_horizontal.png rename to resources/icons/shape_flip_horizontal.png diff --git a/var/shape_handles.png b/resources/icons/shape_handles.png old mode 100755 new mode 100644 similarity index 100% rename from var/shape_handles.png rename to resources/icons/shape_handles.png diff --git a/var/shape_ungroup.png b/resources/icons/shape_ungroup.png old mode 100755 new mode 100644 similarity index 100% rename from var/shape_ungroup.png rename to resources/icons/shape_ungroup.png diff --git a/var/spool.png b/resources/icons/spool.png similarity index 100% rename from var/spool.png rename to resources/icons/spool.png diff --git a/var/tag_blue.png b/resources/icons/tag_blue.png old mode 100755 new mode 100644 similarity index 100% rename from var/tag_blue.png rename to resources/icons/tag_blue.png diff --git a/var/textfield.png b/resources/icons/textfield.png old mode 100755 new mode 100644 similarity index 100% rename from var/textfield.png rename to resources/icons/textfield.png diff --git a/var/time.png b/resources/icons/time.png old mode 100755 new mode 100644 similarity index 100% rename from var/time.png rename to resources/icons/time.png diff --git a/var/variable_layer_height.png b/resources/icons/variable_layer_height.png similarity index 100% rename from var/variable_layer_height.png rename to resources/icons/variable_layer_height.png diff --git a/var/variable_layer_height_reset.png b/resources/icons/variable_layer_height_reset.png similarity index 100% rename from var/variable_layer_height_reset.png rename to resources/icons/variable_layer_height_reset.png diff --git a/var/variable_layer_height_tooltip.png b/resources/icons/variable_layer_height_tooltip.png similarity index 100% rename from var/variable_layer_height_tooltip.png rename to resources/icons/variable_layer_height_tooltip.png diff --git a/var/wand.png b/resources/icons/wand.png similarity index 100% rename from var/wand.png rename to resources/icons/wand.png diff --git a/var/wrench.png b/resources/icons/wrench.png old mode 100755 new mode 100644 similarity index 100% rename from var/wrench.png rename to resources/icons/wrench.png diff --git a/var/zoom.png b/resources/icons/zoom.png old mode 100755 new mode 100644 similarity index 100% rename from var/zoom.png rename to resources/icons/zoom.png From ca4bd96d5d31c5515fd14b144c3b281876861c11 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 10 Dec 2017 21:14:03 +0100 Subject: [PATCH 80/83] Modification of Slic3r to search the icons in resources/icons after they have been moved from var --- lib/Slic3r.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 45eedc48d..68579af81 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -41,8 +41,8 @@ warn "Running Slic3r under Perl 5.16 is neither supported nor recommended\n" use FindBin; # Let the XS module know where the GUI resources reside. -set_var_dir(decode_path($FindBin::Bin) . "/var"); -set_resources_dir(decode_path($FindBin::Bin) . (($^O eq 'darwin') ? '/Resources' : '/resources')); +set_resources_dir(decode_path($FindBin::Bin) . (($^O eq 'darwin') ? '../Resources' : '/resources')); +set_var_dir(resources_dir() . "/icons"); use Moo 1.003001; From 679aa2822c71e77624e68e83305fe8638b062a39 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 10 Dec 2017 22:11:00 +0100 Subject: [PATCH 81/83] Moved the Slic3rPE/print,filament,printer folders to Slic3rPE/presets/print,filament,printer to separate the presets from further data stored into the Slic3rPE directory. --- lib/Slic3r/GUI.pm | 8 ++------ lib/Slic3r/GUI/Tab.pm | 2 +- xs/src/libslic3r/Utils.hpp | 5 ----- xs/src/libslic3r/utils.cpp | 13 ------------- xs/src/slic3r/GUI/PresetBundle.cpp | 15 ++++++++++++--- xs/src/slic3r/GUI/PresetBundle.hpp | 4 ++-- xs/xsp/GUI_Preset.xsp | 7 ++++--- xs/xsp/XS.xsp | 8 -------- 8 files changed, 21 insertions(+), 41 deletions(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 7157f7e92..5176c9c91 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -100,16 +100,12 @@ sub OnInit { # Suppress the '- default -' presets. $self->{preset_bundle}->set_default_suppressed($self->{app_config}->get('no_defaults') ? 1 : 0); - eval { - $self->{preset_bundle}->load_presets(Slic3r::data_dir); - }; + eval { $self->{preset_bundle}->load_presets }; if ($@) { warn $@ . "\n"; show_error(undef, $@); } - eval { - $self->{preset_bundle}->load_selections($self->{app_config}); - }; + eval { $self->{preset_bundle}->load_selections($self->{app_config}) }; $run_wizard = 1 if $self->{preset_bundle}->has_defauls_only; # application frame diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 907be7872..30429e88b 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -147,7 +147,7 @@ sub save_preset { return unless $dlg->ShowModal == wxID_OK; $name = $dlg->get_name; } - # Save the preset into Slic3r::data_dir/section_name/preset_name.ini + # Save the preset into Slic3r::data_dir/presets/section_name/preset_name.ini eval { $self->{presets}->save_current_preset($name); }; Slic3r::GUI::catch_error($self) and return; # Add the new item into the UI component, remove dirty flags and activate the saved item. diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index d988de860..3afbc912f 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -24,11 +24,6 @@ const std::string& resources_dir(); void set_data_dir(const std::string &path); // Return a full path to the GUI resource files. const std::string& data_dir(); -// Return a full path to a configuration file given its file name.. -std::string config_path(const std::string &file_name); -// Return a full path to a configuration file given the section and name. -// The suffix ".ini" will be added if it is missing in the name. -std::string config_path(const std::string §ion, const std::string &name); extern std::string encode_path(const char *src); extern std::string decode_path(const char *src); diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 53efa466b..ee579161f 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -113,19 +113,6 @@ const std::string& data_dir() return g_data_dir; } -std::string config_path(const std::string &file_name) -{ - auto file = (boost::filesystem::path(g_data_dir) / file_name).make_preferred(); - return file.string(); -} - -std::string config_path(const std::string §ion, const std::string &name) -{ - auto file_name = boost::algorithm::iends_with(name, ".ini") ? name : name + ".ini"; - auto file = (boost::filesystem::path(g_data_dir) / section / file_name).make_preferred(); - return file.string(); -} - } // namespace Slic3r #ifdef SLIC3R_HAS_BROKEN_CROAK diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 1f872ffd6..76fe3ae95 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -66,7 +66,12 @@ PresetBundle::~PresetBundle() void PresetBundle::setup_directories() { boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); - std::initializer_list paths = { data_dir, data_dir / "print", data_dir / "filament", data_dir / "printer" }; + std::initializer_list paths = { + data_dir, + data_dir / "presets", + data_dir / "presets" / "print", + data_dir / "presets" / "filament", + data_dir / "presets" / "printer" }; for (const boost::filesystem::path &path : paths) { boost::filesystem::path subdir = path; subdir.make_preferred(); @@ -76,9 +81,10 @@ void PresetBundle::setup_directories() } } -void PresetBundle::load_presets(const std::string &dir_path) +void PresetBundle::load_presets() { std::string errors_cummulative; + const std::string dir_path = data_dir() + "/presets"; try { this->prints.load_presets(dir_path, "print"); } catch (const std::runtime_error &err) { @@ -465,8 +471,11 @@ size_t PresetBundle::load_configbundle(const std::string &path) for (auto &kvp : section.second) config.set_deserialize(kvp.first, kvp.second.data()); Preset::normalize(config); + // Decide a full path to this .ini file. + auto file_name = boost::algorithm::iends_with(preset_name, ".ini") ? preset_name : preset_name + ".ini"; + auto file_path = (boost::filesystem::path(data_dir()) / "presets" / presets->name() / file_name).make_preferred(); // Load the preset into the list of presets, save it to disk. - presets->load_preset(Slic3r::config_path(presets->name(), preset_name), preset_name, std::move(config), false).save(); + presets->load_preset(file_path.string(), preset_name, std::move(config), false).save(); ++ presets_loaded; } } diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/xs/src/slic3r/GUI/PresetBundle.hpp index 451ec69c1..238e7c802 100644 --- a/xs/src/slic3r/GUI/PresetBundle.hpp +++ b/xs/src/slic3r/GUI/PresetBundle.hpp @@ -17,8 +17,8 @@ public: void setup_directories(); - // Load ini files of all types (print, filament, printer) from the provided directory path. - void load_presets(const std::string &dir_path); + // Load ini files of all types (print, filament, printer) from Slic3r::data_dir() / presets. + void load_presets(); // Load selections (current print, current filaments, current printer) from config.ini // This is done just once on application start up. diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp index a7fbdd07f..d6af38d15 100644 --- a/xs/xsp/GUI_Preset.xsp +++ b/xs/xsp/GUI_Preset.xsp @@ -106,12 +106,13 @@ PresetCollection::arrayref() croak("Cannot create configuration directories:\n%s\n", e.what()); } %}; - void load_presets(const char *dir_path) + void load_presets() %code%{ try { - THIS->load_presets(dir_path); + THIS->load_presets(); } catch (std::exception& e) { - croak("Loading of Slic3r presets from %s failed.\n\n%s\n", dir_path, e.what()); + croak("Loading of Slic3r presets from %s failed.\n\n%s\n", + Slic3r::data_dir().c_str(), e.what()); } %}; void load_config_file(const char *path) diff --git a/xs/xsp/XS.xsp b/xs/xsp/XS.xsp index d59f826b7..b2f772834 100644 --- a/xs/xsp/XS.xsp +++ b/xs/xsp/XS.xsp @@ -91,14 +91,6 @@ data_dir() RETVAL = const_cast(Slic3r::data_dir().c_str()); OUTPUT: RETVAL -std::string -config_path(section, name) - const char *section; - const char *name; - CODE: - RETVAL = Slic3r::config_path(section, name); - OUTPUT: RETVAL - std::string encode_path(src) const char *src; From ae5863f5e043a92188377c4673b4efd87ca09976 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 10 Dec 2017 23:27:22 +0100 Subject: [PATCH 82/83] Fixed a typo in a path to Resources on OSX. --- lib/Slic3r.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 68579af81..22a6ee389 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -41,7 +41,7 @@ warn "Running Slic3r under Perl 5.16 is neither supported nor recommended\n" use FindBin; # Let the XS module know where the GUI resources reside. -set_resources_dir(decode_path($FindBin::Bin) . (($^O eq 'darwin') ? '../Resources' : '/resources')); +set_resources_dir(decode_path($FindBin::Bin) . (($^O eq 'darwin') ? '/../Resources' : '/resources')); set_var_dir(resources_dir() . "/icons"); use Moo 1.003001; From 19388285205ff46379ce0f9b0291aff1badd6568 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 11 Dec 2017 09:31:29 +0100 Subject: [PATCH 83/83] Slic3r version was not set by the placeholder parser. https://github.com/prusa3d/Slic3r/issues/615 --- xs/src/libslic3r/PlaceholderParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index 8f2f6d6e7..77ef64e8d 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -56,7 +56,7 @@ namespace Slic3r { PlaceholderParser::PlaceholderParser() { - this->set("version", SLIC3R_VERSION); + this->set("version", std::string(SLIC3R_VERSION)); this->apply_env_variables(); this->update_timestamp(); }