Merge branch 'master' into wipe_tower_improvements

This commit is contained in:
Lukas Matena 2017-12-21 13:47:33 +01:00
commit 4583d62edd
126 changed files with 5240 additions and 607 deletions

View File

@ -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 --gui Forces the GUI launch instead of command line slicing (if you
supply a model file, it will be loaded into the plater) supply a model file, it will be loaded into the plater)
--no-plater Disable the plater tab --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 <file> Automatically export current configuration to the specified file --autosave <file> Automatically export current configuration to the specified file
Output options: Output options:

View File

@ -1,6 +1,6 @@
How to build Slic3r on Mac OS X 10.7 Lion 64bit How to build Slic3r on Mac OS X 10.9 Maveric
--------------------------------------------- ---------------------------------------------
Vojtech Bubnik, 2016-04-26 Vojtech Bubnik, 2017-12-12
1) Install Mac OS X 10.7 Lion 64 bit with X Code 1) Install Mac OS X 10.7 Lion 64 bit with X Code
@ -55,6 +55,9 @@ brew install boost --universal
Install dylibbundler tool. The dylibbundler tool serves to collect dependent dynamic libraries and fix their linkage. Execute Install dylibbundler tool. The dylibbundler tool serves to collect dependent dynamic libraries and fix their linkage. Execute
brew install dylibbundler brew install dylibbundler
Install cmake
brew install cmake
3) Install perl 3) Install perl
--------------- ---------------
@ -62,18 +65,31 @@ We don't want to distribute perl pre-installed on the Mac OS box. First, the sys
http://perlbrew.pl/ http://perlbrew.pl/
First install perlbrew First install perlbrew
\curl -L http://install.perlbrew.pl | bash curl -L http://install.perlbrew.pl | bash
Then compile the newest perl with the rellocatable @INC path and with multithreading enabled, execute following line: Then compile the newest perl with the rellocatable @INC path and with multithreading enabled, execute following line:
perlbrew install --threads -Duserelocatableinc --switch perl-5.22.0 perlbrew install --threads -Duserelocatableinc --switch perl-5.26.1
The --switch parameter switches the active perl to the currently compiled one. The --switch parameter switches the active perl to the currently compiled one.
Available perl versions could be listed by calling Available perl versions could be listed by calling
perlbrew available perlbrew available
Switch to the newly compiled perl
perl5/perlbrew/bin/perlbrew switch perl-5.26.1
Install cpanm
perlbrew install-cpanm
Initialize CPAN, install PAR and PAR::Packer modules Initialize CPAN, install PAR and PAR::Packer modules
execute cpan command, from the cpan prompt, run execute cpan command, from the cpan prompt, run
install App::cpanminus install App::cpanminus
install ExtUtils::CppGuess
install ExtUtils::Typemaps
install ExtUtils::Typemaps::Basic
install PAR install PAR
install PAR::Packer install PAR::Packer
install Module::Build
install Module::Pluggable
install Module::Runtime
install Moo
install Test::Pod
install Test::Pod::Coverage
quit quit
4) Download and install Slic3r 4) Download and install Slic3r
@ -92,6 +108,21 @@ to have the model sliced.
5) Download and compile the GUI libraries needed to execute Slic3r in GUI mode 5) Download and compile the GUI libraries needed to execute Slic3r in GUI mode
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
Building the Perl Alien-Wx containing a wxWidgets library:
We use wxWidgets-3.0.3.
patch wxWidgets-3.0.3//src/osx/cocoa/textctrl.mm , see https://github.com/prusa3d/Slic3r/issues/600
perl -I. Build.PL --wxWidgets-extraflags="--with-osx_cocoa --with-macosx-version-min=10.9 --with-macosx-sdk=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk --with-libjpeg=builtin --with-libpng=builtin --with-regex=builtin --with-libtiff=builtin --with-zlib=builtin --with-expat=builtin --with-opengl"
perl -I. Build
perl -I. Build test
perl -I. Build
Building the Perl Wx package:
cpan install Wx
Building the Perl OpenGL package:
OpenGL needs to be patched to support MSAA, see the Windows patch.
For the current Slic3r 1.2.30 code base, set the environment variable SLIC3R_STATIC to link a static version of the boost library: For the current Slic3r 1.2.30 code base, set the environment variable SLIC3R_STATIC to link a static version of the boost library:
export SLIC3R_STATIC=1 export SLIC3R_STATIC=1
@ -271,3 +302,14 @@ Debugging the C++ code works fine using the latest Eclipse for C++ and the gdb o
It is yet a bit more complicated. The Strawberry MINGW is compiled for a different C++ exception passing model (SJLJ) than the other MINGWs, so one cannot simply combine MINGWs on Windows. For an unknown reason the nice debugger of the QT Creator hangs when debugging the C++ compiled by the Strawberry MINGW. Mabe it is because of the different exception passing models. It is yet a bit more complicated. The Strawberry MINGW is compiled for a different C++ exception passing model (SJLJ) than the other MINGWs, so one cannot simply combine MINGWs on Windows. For an unknown reason the nice debugger of the QT Creator hangs when debugging the C++ compiled by the Strawberry MINGW. Mabe it is because of the different exception passing models.
And to disable optimization of the C/C++ code, one has to manually modify Config_heavy.pl in the Perl central installation. The SLIC3R_DEBUG environment variable did not override all the -O2 and -O3 flags that the perl build adds the gcc execution line. And to disable optimization of the C/C++ code, one has to manually modify Config_heavy.pl in the Perl central installation. The SLIC3R_DEBUG environment variable did not override all the -O2 and -O3 flags that the perl build adds the gcc execution line.
----------------------------------------------------------------------
Building boost.
One may save compilation time by compiling just what Slic3r needs.
./bootstrap.sh --with-libraries=system,filesystem,thread,log,locale,regex
The -fPIC flag is required on Linux to make the static libraries rellocatable,
so they could be embedded into a shared library.
./bjam -a link=static variant=release threading=multi cxxflags=-fPIC cflags=-fPIC
To install on Linux to /usr/local/..., run the line above with the additional install keyword and with sudo.

View File

@ -41,7 +41,8 @@ warn "Running Slic3r under Perl 5.16 is neither supported nor recommended\n"
use FindBin; use FindBin;
# Let the XS module know where the GUI resources reside. # 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_var_dir(resources_dir() . "/icons");
use Moo 1.003001; use Moo 1.003001;
@ -79,6 +80,10 @@ my $paused = 0;
$Slic3r::loglevel = (defined($ENV{'SLIC3R_LOGLEVEL'}) && $ENV{'SLIC3R_LOGLEVEL'} =~ /^[1-9]/) ? $ENV{'SLIC3R_LOGLEVEL'} : 0; $Slic3r::loglevel = (defined($ENV{'SLIC3R_LOGLEVEL'}) && $ENV{'SLIC3R_LOGLEVEL'} =~ /^[1-9]/) ? $ENV{'SLIC3R_LOGLEVEL'} : 0;
set_logging_level($Slic3r::loglevel); set_logging_level($Slic3r::loglevel);
# Let the palceholder parser evaluate one expression to initialize its local static macro_processor
# class instance in a thread safe manner.
Slic3r::GCode::PlaceholderParser->new->evaluate_boolean_expression('1==1');
sub spawn_thread { sub spawn_thread {
my ($cb) = @_; my ($cb) = @_;
@_ = (); @_ = ();

View File

@ -70,7 +70,7 @@ our $grey = Wx::Colour->new(200,200,200);
sub OnInit { sub OnInit {
my ($self) = @_; my ($self) = @_;
$self->SetAppName('Slic3r'); $self->SetAppName('Slic3rPE');
$self->SetAppDisplayName('Slic3r Prusa Edition'); $self->SetAppDisplayName('Slic3r Prusa Edition');
Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION; Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION;
@ -79,6 +79,7 @@ sub OnInit {
# Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r" # Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r"
# Mac: "~/Library/Application Support/Slic3r" # Mac: "~/Library/Application Support/Slic3r"
Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir); Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir);
Slic3r::GUI::set_wxapp($self);
$self->{notifier} = Slic3r::GUI::Notifier->new; $self->{notifier} = Slic3r::GUI::Notifier->new;
$self->{app_config} = Slic3r::GUI::AppConfig->new; $self->{app_config} = Slic3r::GUI::AppConfig->new;
@ -99,29 +100,22 @@ sub OnInit {
# Suppress the '- default -' presets. # Suppress the '- default -' presets.
$self->{preset_bundle}->set_default_suppressed($self->{app_config}->get('no_defaults') ? 1 : 0); $self->{preset_bundle}->set_default_suppressed($self->{app_config}->get('no_defaults') ? 1 : 0);
eval { eval { $self->{preset_bundle}->load_presets };
$self->{preset_bundle}->load_presets(Slic3r::data_dir);
};
if ($@) { if ($@) {
warn $@ . "\n"; warn $@ . "\n";
show_error(undef, $@); show_error(undef, $@);
} }
eval { eval { $self->{preset_bundle}->load_selections($self->{app_config}) };
$self->{preset_bundle}->load_selections($self->{app_config});
};
$run_wizard = 1 if $self->{preset_bundle}->has_defauls_only; $run_wizard = 1 if $self->{preset_bundle}->has_defauls_only;
# application frame # 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( $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. # 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'), no_controller => $self->{app_config}->get('no_controller'),
no_plater => $no_plater, no_plater => $no_plater,
); );
$self->SetTopWindow($frame); $self->SetTopWindow($frame);
if ($run_wizard) {
$self->{mainframe}->config_wizard;
}
EVT_IDLE($frame, sub { EVT_IDLE($frame, sub {
while (my $cb = shift @cb) { while (my $cb = shift @cb) {
@ -129,6 +123,15 @@ sub OnInit {
} }
$self->{app_config}->save if $self->{app_config}->dirty; $self->{app_config}->save if $self->{app_config}->dirty;
}); });
if ($run_wizard) {
# On OSX the UI was not initialized correctly if the wizard was called
# before the UI was up and running.
$self->CallAfter(sub {
# Run the config wizard, don't offer the "reset user profile" checkbox.
$self->{mainframe}->config_wizard(1);
});
}
return 1; return 1;
} }

View File

@ -15,7 +15,7 @@ package Slic3r::GUI::3DScene::Base;
use strict; use strict;
use warnings; use warnings;
use Wx qw(:timer :bitmap :icon :dialog); use Wx qw(wxTheApp :timer :bitmap :icon :dialog);
use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_CHAR EVT_TIMER); use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_CHAR EVT_TIMER);
# must load OpenGL *before* Wx::GLCanvas # must load OpenGL *before* Wx::GLCanvas
use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
@ -108,6 +108,7 @@ sub new {
# We can only enable multi sample anti aliasing wih wxWidgets 3.0.3 and with a hacked Wx::GLCanvas, # We can only enable multi sample anti aliasing wih wxWidgets 3.0.3 and with a hacked Wx::GLCanvas,
# which exports some new WX_GL_XXX constants, namely WX_GL_SAMPLE_BUFFERS and WX_GL_SAMPLES. # which exports some new WX_GL_XXX constants, namely WX_GL_SAMPLE_BUFFERS and WX_GL_SAMPLES.
my $can_multisample = my $can_multisample =
! wxTheApp->{app_config}->get('use_legacy_opengl') &&
Wx::wxVERSION >= 3.000003 && Wx::wxVERSION >= 3.000003 &&
defined Wx::GLCanvas->can('WX_GL_SAMPLE_BUFFERS') && defined Wx::GLCanvas->can('WX_GL_SAMPLE_BUFFERS') &&
defined Wx::GLCanvas->can('WX_GL_SAMPLES'); defined Wx::GLCanvas->can('WX_GL_SAMPLES');
@ -956,6 +957,16 @@ sub UseVBOs {
my ($self) = @_; my ($self) = @_;
if (! defined ($self->{use_VBOs})) { if (! defined ($self->{use_VBOs})) {
my $use_legacy = wxTheApp->{app_config}->get('use_legacy_opengl');
if ($use_legacy eq '1') {
# Disable OpenGL 2.0 rendering.
$self->{use_VBOs} = 0;
# Don't enable the layer editing tool.
$self->{layer_editing_enabled} = 0;
# 2 means failed
$self->{layer_editing_initialized} = 2;
return 0;
}
# This is a special path for wxWidgets on GTK, where an OpenGL context is initialized # This is a special path for wxWidgets on GTK, where an OpenGL context is initialized
# first when an OpenGL widget is shown for the first time. How ugly. # first when an OpenGL widget is shown for the first time. How ugly.
return 0 if (! $self->init && $^O eq 'linux'); return 0 if (! $self->init && $^O eq 'linux');

View File

@ -14,14 +14,14 @@ our $wizard = 'Wizard';
$wizard = 'Assistant' if &Wx::wxMAC || &Wx::wxGTK; $wizard = 'Assistant' if &Wx::wxMAC || &Wx::wxGTK;
sub new { sub new {
my $class = shift; my ($class, $parent, $presets, $fresh_start) = @_;
my ($parent) = @_;
my $self = $class->SUPER::new($parent, -1, "Configuration $wizard"); my $self = $class->SUPER::new($parent, -1, "Configuration $wizard");
# initialize an empty repository # initialize an empty repository
$self->{config} = Slic3r::Config->new; $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, $fresh_start);
$self->add_page($welcome_page);
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Firmware->new($self)); $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::Bed->new($self));
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Nozzle->new($self)); $self->add_page(Slic3r::GUI::ConfigWizard::Page::Nozzle->new($self));
@ -32,12 +32,13 @@ sub new {
$_->build_index for @{$self->{pages}}; $_->build_index for @{$self->{pages}};
$welcome_page->set_selection_presets([@{$presets}, 'Other']);
return $self; return $self;
} }
sub add_page { sub add_page {
my $self = shift; my ($self, $page) = @_;
my ($page) = @_;
my $n = push @{$self->{pages}}, $page; my $n = push @{$self->{pages}}, $page;
# add first page to the page area sizer # add first page to the page area sizer
@ -48,13 +49,17 @@ sub add_page {
} }
sub run { sub run {
my $self = shift; my ($self) = @_;
my $result;
if (Wx::Wizard::RunWizard($self, $self->{pages}[0])) { if (Wx::Wizard::RunWizard($self, $self->{pages}[0])) {
my $preset_name = $self->{pages}[0]->{preset_name};
# it would be cleaner to have these defined inside each page class, $result = {
# in some event getting called before leaving the page preset_name => $preset_name,
{ reset_user_profile => $self->{pages}[0]->{reset_user_profile}
};
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 # set first_layer_height + layer_height based on nozzle_diameter
my $nozzle = $self->{config}->nozzle_diameter; my $nozzle = $self->{config}->nozzle_diameter;
$self->{config}->set('first_layer_height', $nozzle->[0]); $self->{config}->set('first_layer_height', $nozzle->[0]);
@ -66,14 +71,11 @@ sub run {
# set first_layer_bed_temperature to temperature + 5 # set first_layer_bed_temperature to temperature + 5
$self->{config}->set('first_layer_bed_temperature', $self->{config}->set('first_layer_bed_temperature',
[ ($self->{config}->bed_temperature->[0] > 0) ? ($self->{config}->bed_temperature->[0] + 5) : 0 ]); [ ($self->{config}->bed_temperature->[0] > 0) ? ($self->{config}->bed_temperature->[0] + 5) : 0 ]);
$result->{config} = $self->{config};
} }
$self->Destroy;
return $self->{config};
} else {
$self->Destroy;
return undef;
} }
$self->Destroy;
return $result;
} }
package Slic3r::GUI::ConfigWizard::Index; package Slic3r::GUI::ConfigWizard::Index;
@ -127,6 +129,8 @@ sub repaint {
$dc->SetTextForeground(Wx::Colour->new(128, 128, 128)) if $i > $self->{own_index}; $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)); $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++; $i++;
} }
@ -263,19 +267,87 @@ sub config {
package Slic3r::GUI::ConfigWizard::Page::Welcome; package Slic3r::GUI::ConfigWizard::Page::Welcome;
use base 'Slic3r::GUI::ConfigWizard::Page'; use base 'Slic3r::GUI::ConfigWizard::Page';
use Wx qw(:misc :sizer wxID_FORWARD);
use Wx::Event qw(EVT_ACTIVATE EVT_CHOICE EVT_CHECKBOX);
sub new { sub new {
my $class = shift; my ($class, $parent, $fresh_start) = @_;
my ($parent) = @_;
my $self = $class->SUPER::new($parent, "Welcome to the Slic3r Configuration $wizard", 'Welcome'); my $self = $class->SUPER::new($parent, "Welcome to the Slic3r Configuration $wizard", 'Welcome');
$self->{full_wizard_workflow} = 1;
$self->{reset_user_profile} = 0;
$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.'); # Test for the existence of the old config path.
$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.'); my $message_has_legacy;
$self->append_text('To continue, click Next.'); {
my $datadir = Slic3r::data_dir;
if ($datadir =~ /Slic3rPE/) {
# Check for existence of the legacy Slic3r directory.
my $datadir_legacy = substr $datadir, 0, -2;
my $dir_enc = Slic3r::encode_path($datadir_legacy);
if (-e $dir_enc && -d $dir_enc &&
-e ($dir_enc . '/print') && -d ($dir_enc . '/print') &&
-e ($dir_enc . '/filament') && -d ($dir_enc . '/filament') &&
-e ($dir_enc . '/printer') && -d ($dir_enc . '/printer') &&
-e ($dir_enc . '/slic3r.ini')) {
$message_has_legacy = "Starting with Slic3r 1.38.4, the user profile directory has been renamed to $datadir. You may consider closing Slic3r and renaming $datadir_legacy to $datadir.";
}
}
}
$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.');
$self->append_text($message_has_legacy) if defined $message_has_legacy;
# 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);
if (! $fresh_start) {
$self->{reset_checkbox} = Wx::CheckBox->new($self, -1, "Reset user profile, install from scratch");
$self->{vsizer}->Add($self->{reset_checkbox}, 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 ''));
});
if (! $fresh_start) {
EVT_CHECKBOX($self, $self->{reset_checkbox}, sub {
$self->{reset_user_profile} = $self->{reset_checkbox}->GetValue();
});
}
EVT_ACTIVATE($parent, sub {
$self->set_full_wizard_workflow($self->{preset_name} eq 'Other');
});
return $self; 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; package Slic3r::GUI::ConfigWizard::Page::Firmware;
use base 'Slic3r::GUI::ConfigWizard::Page'; use base 'Slic3r::GUI::ConfigWizard::Page';

View File

@ -7,7 +7,7 @@ use utf8;
use File::Basename qw(basename dirname); use File::Basename qw(basename dirname);
use FindBin; use FindBin;
use List::Util qw(min); use List::Util qw(min first);
use Slic3r::Geometry qw(X Y); use Slic3r::Geometry qw(X Y);
use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog
:font :icon wxTheApp); :font :icon wxTheApp);
@ -22,6 +22,7 @@ sub new {
my ($class, %params) = @_; my ($class, %params) = @_;
my $self = $class->SUPER::new(undef, -1, $Slic3r::FORK_NAME . ' - ' . $Slic3r::VERSION, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE); 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') { if ($^O eq 'MSWin32') {
# Load the icon either from the exe, or from the ico file. # Load the icon either from the exe, or from the ico file.
my $iconfile = Slic3r::decode_path($FindBin::Bin) . '\slic3r.exe'; my $iconfile = Slic3r::decode_path($FindBin::Bin) . '\slic3r.exe';
@ -92,6 +93,8 @@ sub _init_tabpanel {
my ($self) = @_; my ($self) = @_;
$self->{tabpanel} = my $panel = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL); $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 { EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{tabpanel}, sub {
my $panel = $self->{tabpanel}->GetCurrentPage; my $panel = $self->{tabpanel}->GetCurrentPage;
$panel->OnActivate if $panel->can('OnActivate'); $panel->OnActivate if $panel->can('OnActivate');
@ -129,12 +132,14 @@ sub _init_tabpanel {
$self->{plater}->update_presets($tab_name, @_); $self->{plater}->update_presets($tab_name, @_);
if ($tab_name eq 'printer') { if ($tab_name eq 'printer') {
# Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors. # Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors.
wxTheApp->{preset_bundle}->print->update_tab_ui( my ($presets, $reload_dependent_tabs) = @_;
$self->{options_tabs}{'print'}->{presets_choice}, for my $tab_name_other (qw(print filament)) {
$self->{options_tabs}{'print'}->{show_incompatible_presets}); # If the printer tells us that the print or filament preset has been switched or invalidated,
wxTheApp->{preset_bundle}->filament->update_tab_ui( # refresh the print or filament tab page. Otherwise just refresh the combo box.
$self->{options_tabs}{'filament'}->{presets_choice}, my $update_action = ($reload_dependent_tabs && (first { $_ eq $tab_name_other } (@{$reload_dependent_tabs})))
$self->{options_tabs}{'filament'}->{show_incompatible_presets}); ? 'load_current_preset' : 'update_tab_ui';
$self->{options_tabs}{$tab_name_other}->$update_action;
}
# Update the controller printers. # Update the controller printers.
$self->{controller}->update_presets(@_) if $self->{controller}; $self->{controller}->update_presets(@_) if $self->{controller};
} }
@ -145,6 +150,9 @@ sub _init_tabpanel {
$tab->load_current_preset; $tab->load_current_preset;
$panel->AddPage($tab, $tab->title); $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}) { if ($self->{plater}) {
$self->{plater}->on_select_preset(sub { $self->{plater}->on_select_preset(sub {
@ -165,6 +173,9 @@ sub _init_menubar {
# File menu # File menu
my $fileMenu = Wx::Menu->new; 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->_append_menu_item($fileMenu, "&Load Config…\tCtrl+L", 'Load exported configuration file', sub {
$self->load_config_file; $self->load_config_file;
}, undef, 'plugin_add.png'); }, undef, 'plugin_add.png');
@ -274,20 +285,22 @@ sub _init_menubar {
# \xA0 is a non-breaing space. It is entered here to spoil the automatic accelerators, # \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. # as the simple numeric accelerators spoil all numeric data entry.
# The camera control accelerators are captured by 3DScene Perl module instead. # 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' ); }); my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] };
$self->_append_menu_item($self->{viewMenu}, "Top\t\xA01" , 'Top View' , sub { $self->select_view('top' ); }); $self->_append_menu_item($self->{viewMenu}, $accel->('Iso', '0'), 'Iso View' , sub { $self->select_view('iso' ); });
$self->_append_menu_item($self->{viewMenu}, "Bottom\t\xA02" , 'Bottom View' , sub { $self->select_view('bottom' ); }); $self->_append_menu_item($self->{viewMenu}, $accel->('Top', '1'), 'Top View' , sub { $self->select_view('top' ); });
$self->_append_menu_item($self->{viewMenu}, "Front\t\xA03" , 'Front View' , sub { $self->select_view('front' ); }); $self->_append_menu_item($self->{viewMenu}, $accel->('Bottom', '2'), 'Bottom View' , sub { $self->select_view('bottom' ); });
$self->_append_menu_item($self->{viewMenu}, "Rear\t\xA04" , 'Rear View' , sub { $self->select_view('rear' ); }); $self->_append_menu_item($self->{viewMenu}, $accel->('Front', '3'), 'Front View' , sub { $self->select_view('front' ); });
$self->_append_menu_item($self->{viewMenu}, "Left\t\xA05" , 'Left View' , sub { $self->select_view('left' ); }); $self->_append_menu_item($self->{viewMenu}, $accel->('Rear', '4'), 'Rear View' , sub { $self->select_view('rear' ); });
$self->_append_menu_item($self->{viewMenu}, "Right\t\xA06" , 'Right View' , sub { $self->select_view('right' ); }); $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 # Help menu
my $helpMenu = Wx::Menu->new; my $helpMenu = Wx::Menu->new;
{ {
$self->_append_menu_item($helpMenu, "&Configuration $Slic3r::GUI::ConfigWizard::wizard…", "Run Configuration $Slic3r::GUI::ConfigWizard::wizard", sub { $self->_append_menu_item($helpMenu, "&Configuration $Slic3r::GUI::ConfigWizard::wizard…", "Run Configuration $Slic3r::GUI::ConfigWizard::wizard", sub {
$self->config_wizard; # Run the config wizard, offer the "reset user profile" checkbox.
$self->config_wizard(0);
}); });
$helpMenu->AppendSeparator(); $helpMenu->AppendSeparator();
$self->_append_menu_item($helpMenu, "Prusa 3D Drivers", 'Open the Prusa3D drivers download page in your browser', sub { $self->_append_menu_item($helpMenu, "Prusa 3D Drivers", 'Open the Prusa3D drivers download page in your browser', sub {
@ -329,6 +342,8 @@ sub _init_menubar {
$menubar->Append($windowMenu, "&Window"); $menubar->Append($windowMenu, "&Window");
$menubar->Append($self->{viewMenu}, "&View") if $self->{viewMenu}; $menubar->Append($self->{viewMenu}, "&View") if $self->{viewMenu};
$menubar->Append($helpMenu, "&Help"); $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); $self->SetMenuBar($menubar);
} }
} }
@ -413,6 +428,7 @@ sub quick_slice {
if ($params{reslice}) { if ($params{reslice}) {
$output_file = $qs_last_output_file if defined $qs_last_output_file; $output_file = $qs_last_output_file if defined $qs_last_output_file;
} elsif ($params{save_as}) { } elsif ($params{save_as}) {
# The following line may die if the output_filename_format template substitution fails.
$output_file = $sprint->output_filepath; $output_file = $sprint->output_filepath;
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/ if $params{export_svg}; $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:', my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:',
@ -565,7 +581,7 @@ sub export_configbundle {
# to auto-install a config bundle on a fresh user account, # to auto-install a config bundle on a fresh user account,
# but that behavior was not documented and likely buggy. # but that behavior was not documented and likely buggy.
sub load_configbundle { sub load_configbundle {
my ($self, $file) = @_; my ($self, $file, $reset_user_profile) = @_;
return unless $self->check_unsaved_changes; return unless $self->check_unsaved_changes;
if (!$file) { if (!$file) {
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:',
@ -580,7 +596,7 @@ sub load_configbundle {
wxTheApp->{app_config}->update_config_dir(dirname($file)); wxTheApp->{app_config}->update_config_dir(dirname($file));
my $presets_imported = 0; my $presets_imported = 0;
eval { $presets_imported = wxTheApp->{preset_bundle}->load_configbundle($file); }; eval { $presets_imported = wxTheApp->{preset_bundle}->load_configbundle($file, $reset_user_profile ? 1 : 0); };
Slic3r::GUI::catch_error($self) and return; Slic3r::GUI::catch_error($self) and return;
# Load the currently selected preset into the GUI, update the preset selection box. # Load the currently selected preset into the GUI, update the preset selection box.
@ -601,19 +617,39 @@ sub load_config {
} }
sub config_wizard { sub config_wizard {
my ($self) = @_; my ($self, $fresh_start) = @_;
# Exit wizard if there are unsaved changes and the user cancels the action. # Exit wizard if there are unsaved changes and the user cancels the action.
return unless $self->check_unsaved_changes; return unless $self->check_unsaved_changes;
if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) { # Enumerate the profiles bundled with the Slic3r installation under resources/profiles.
for my $tab (values %{$self->{options_tabs}}) { my $directory = Slic3r::resources_dir() . "/profiles";
# Select the first visible preset, force. my @profiles = ();
$tab->select_preset(undef, 1); 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. closedir(DIR);
$self->load_config($config); }
for my $tab (values %{$self->{options_tabs}}) { # Open the wizard.
# Save the settings under a new name, select the name. if (my $result = Slic3r::GUI::ConfigWizard->new($self, \@profiles, $fresh_start)->run) {
$tab->save_preset('My Settings'); eval {
if ($result->{reset_user_profile}) {
wxTheApp->{preset_bundle}->reset(1);
}
if (defined $result->{config}) {
# Load and save the settings into print, filament and printer presets.
wxTheApp->{preset_bundle}->load_config('My Settings', $result->{config});
} else {
# Wizard returned a name of a preset bundle bundled with the installation. Unpack it.
wxTheApp->{preset_bundle}->load_configbundle($directory . '/' . $result->{preset_name} . '.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;
} }
} }
} }

View File

@ -99,6 +99,7 @@ sub new {
$self->{canvas3D}->set_on_select_object($on_select_object); $self->{canvas3D}->set_on_select_object($on_select_object);
$self->{canvas3D}->set_on_double_click($on_double_click); $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_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_left(sub { $self->rotate(-45, Z, 'relative') });
$self->{canvas3D}->set_on_rotate_object_right(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) }); $self->{canvas3D}->set_on_scale_object_uniformly(sub { $self->changescale(undef) });
@ -429,6 +430,7 @@ sub new {
$grid_sizer->AddGrowableCol(3, 1); $grid_sizer->AddGrowableCol(3, 1);
$print_info_sizer->Add($grid_sizer, 0, wxEXPAND); $print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
my @info = ( my @info = (
fil_m => "Used Filament (m)",
fil_mm3 => "Used Filament (mm^3)", fil_mm3 => "Used Filament (mm^3)",
fil_g => "Used Filament (g)", fil_g => "Used Filament (g)",
cost => "Cost", cost => "Cost",
@ -504,6 +506,7 @@ sub _on_select_preset {
wxTheApp->{preset_bundle}->set_filament_preset($idx, $choice->GetStringSelection); wxTheApp->{preset_bundle}->set_filament_preset($idx, $choice->GetStringSelection);
} }
if ($group eq 'filament' && @{$self->{preset_choosers}{filament}} > 1) { if ($group eq 'filament' && @{$self->{preset_choosers}{filament}} > 1) {
# Only update the platter UI for the 2nd and other filaments.
wxTheApp->{preset_bundle}->update_platter_filament_ui($idx, $choice); wxTheApp->{preset_bundle}->update_platter_filament_ui($idx, $choice);
} else { } else {
# call GetSelection() in scalar context as it's context-aware # call GetSelection() in scalar context as it's context-aware
@ -1290,9 +1293,11 @@ sub export_gcode {
# select output file # select output file
if ($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 { } 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:', my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:',
wxTheApp->{app_config}->get_last_output_dir(dirname($default_output_file)), 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); basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
@ -1423,6 +1428,7 @@ sub on_export_completed {
$self->{"print_info_cost"}->SetLabel(sprintf("%.2f" , $self->{print}->total_cost)); $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_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_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); $self->{"print_info_box_show"}->(1);
# this updates buttons status # this updates buttons status
@ -1437,8 +1443,8 @@ sub do_print {
my $printer_panel = $controller->add_printer($printer_preset->name, $printer_preset->config); my $printer_panel = $controller->add_printer($printer_preset->name, $printer_preset->config);
my $filament_stats = $self->{print}->filament_stats; my $filament_stats = $self->{print}->filament_stats;
my @filament_names = wxTheApp->{preset_bundle}->filament_presets; my $filament_names = wxTheApp->{preset_bundle}->filament_presets;
$filament_stats = { map { $filament_names[$_] => $filament_stats->{$_} } keys %$filament_stats }; $filament_stats = { map { $filament_names->[$_] => $filament_stats->{$_} } keys %$filament_stats };
$printer_panel->load_print_job($self->{print_file}, $filament_stats); $printer_panel->load_print_job($self->{print_file}, $filament_stats);
$self->GetFrame->select_tab(1); $self->GetFrame->select_tab(1);
@ -1494,6 +1500,7 @@ sub reload_from_disk {
return if !defined $obj_idx; return if !defined $obj_idx;
my $model_object = $self->{model}->objects->[$obj_idx]; my $model_object = $self->{model}->objects->[$obj_idx];
#FIXME convert to local file encoding
return if !$model_object->input_file return if !$model_object->input_file
|| !-e $model_object->input_file; || !-e $model_object->input_file;
@ -1504,12 +1511,14 @@ sub reload_from_disk {
my $o = $self->{model}->objects->[$new_obj_idx]; my $o = $self->{model}->objects->[$new_obj_idx];
$o->clear_instances; $o->clear_instances;
$o->add_instance($_) for @{$model_object->instances}; $o->add_instance($_) for @{$model_object->instances};
#$o->invalidate_bounding_box;
if ($o->volumes_count == $model_object->volumes_count) { if ($o->volumes_count == $model_object->volumes_count) {
for my $i (0..($o->volumes_count-1)) { for my $i (0..($o->volumes_count-1)) {
$o->get_volume($i)->config->apply($model_object->get_volume($i)->config); $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); $self->remove($obj_idx);
@ -1540,7 +1549,8 @@ sub export_amf {
sub _get_export_file { sub _get_export_file {
my ($self, $format) = @_; my ($self, $format) = @_;
my $suffix = $format eq 'STL' ? '.stl' : '.amf.xml'; 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/; $output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/;
my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file), 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); basename($output_file), &Slic3r::GUI::MODEL_WILDCARD, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
@ -1917,23 +1927,24 @@ sub object_menu {
my $frame = $self->GetFrame; my $frame = $self->GetFrame;
my $menu = Wx::Menu->new; 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; $self->remove;
}, undef, 'brick_delete.png'); }, 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; $self->increase;
}, undef, 'add.png'); }, 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; $self->decrease;
}, undef, 'delete.png'); }, undef, 'delete.png');
$frame->_append_menu_item($menu, "Set number of copies…", 'Change the number of copies of the selected object', sub { $frame->_append_menu_item($menu, "Set number of copies…", 'Change the number of copies of the selected object', sub {
$self->set_number_of_copies; $self->set_number_of_copies;
}, undef, 'textfield.png'); }, undef, 'textfield.png');
$menu->AppendSeparator(); $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'); $self->rotate(-45, Z, 'relative');
}, undef, 'arrow_rotate_clockwise.png'); }, 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'); $self->rotate(+45, Z, 'relative');
}, undef, 'arrow_rotate_anticlockwise.png'); }, undef, 'arrow_rotate_anticlockwise.png');
@ -1966,7 +1977,7 @@ sub object_menu {
my $scaleMenu = Wx::Menu->new; my $scaleMenu = Wx::Menu->new;
my $scaleMenuItem = $menu->AppendSubMenu($scaleMenu, "Scale", 'Scale the selected object along a single axis'); my $scaleMenuItem = $menu->AppendSubMenu($scaleMenu, "Scale", 'Scale the selected object along a single axis');
$frame->_set_menu_item_icon($scaleMenuItem, 'arrow_out.png'); $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); $self->changescale(undef);
}); });
$frame->_append_menu_item($scaleMenu, "Along X axis…", 'Scale the selected object along the X axis', sub { $frame->_append_menu_item($scaleMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {

View File

@ -9,7 +9,7 @@ use Wx::Event qw(EVT_KEY_DOWN EVT_CHAR);
use base qw(Slic3r::GUI::3DScene Class::Accessor); use base qw(Slic3r::GUI::3DScene Class::Accessor);
__PACKAGE__->mk_accessors(qw( __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)); on_remove_object on_increase_objects on_decrease_objects));
sub new { sub new {
@ -88,7 +88,9 @@ sub new {
$event->Skip; $event->Skip;
} else { } else {
my $key = $event->GetKeyCode; 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; $self->on_rotate_object_left->() if $self->on_rotate_object_left;
} elsif ($key == ord('r')) { } elsif ($key == ord('r')) {
$self->on_rotate_object_right->() if $self->on_rotate_object_right; $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); $self->on_right_click($cb);
} }
sub set_on_arrange {
my ($self, $cb) = @_;
$self->on_arrange($cb);
}
sub set_on_rotate_object_left { sub set_on_rotate_object_left {
my ($self, $cb) = @_; my ($self, $cb) = @_;
$self->on_rotate_object_left($cb); $self->on_rotate_object_left($cb);

View File

@ -72,6 +72,13 @@ sub new {
'if they are marked as incompatible with the active printer', 'if they are marked as incompatible with the active printer',
default => $app_config->get("show_incompatible_presets"), default => $app_config->get("show_incompatible_presets"),
)); ));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'use_legacy_opengl',
type => 'bool',
label => 'Use legacy OpenGL 1.1 rendering',
tooltip => 'If you have rendering issues caused by a buggy OpenGL 2.0 driver, you may try to check this checkbox. This will disable the layer height editing and anti aliasing, so it is likely better to upgrade your graphics driver.',
default => $app_config->get("use_legacy_opengl"),
));
my $sizer = Wx::BoxSizer->new(wxVERTICAL); my $sizer = Wx::BoxSizer->new(wxVERTICAL);
$sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
@ -90,7 +97,8 @@ sub _accept {
my ($self) = @_; my ($self) = @_;
if (defined($self->{values}{no_controller}) || if (defined($self->{values}{no_controller}) ||
defined($self->{values}{no_defaults})) { defined($self->{values}{no_defaults}) ||
defined($self->{values}{use_legacy_opengl})) {
Slic3r::GUI::warning_catcher($self)->("You need to restart Slic3r to make the changes effective."); Slic3r::GUI::warning_catcher($self)->("You need to restart Slic3r to make the changes effective.");
} }

View File

@ -147,11 +147,13 @@ sub save_preset {
return unless $dlg->ShowModal == wxID_OK; return unless $dlg->ShowModal == wxID_OK;
$name = $dlg->get_name; $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); }; eval { $self->{presets}->save_current_preset($name); };
Slic3r::GUI::catch_error($self) and return; Slic3r::GUI::catch_error($self) and return;
# Mark the print & filament enabled if they are compatible with the currently selected preset.
wxTheApp->{preset_bundle}->update_compatible_with_printer(0);
# Add the new item into the UI component, remove dirty flags and activate the saved item. # 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->{show_incompatible_presets}); $self->update_tab_ui;
# Update the selection boxes at the platter. # Update the selection boxes at the platter.
$self->_on_presets_changed; $self->_on_presets_changed;
} }
@ -177,7 +179,7 @@ sub _toggle_show_hide_incompatible {
my ($self) = @_; my ($self) = @_;
$self->{show_incompatible_presets} = ! $self->{show_incompatible_presets}; $self->{show_incompatible_presets} = ! $self->{show_incompatible_presets};
$self->_update_show_hide_incompatible_button; $self->_update_show_hide_incompatible_button;
$self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets}); $self->update_tab_ui;
} }
sub _update_show_hide_incompatible_button { sub _update_show_hide_incompatible_button {
@ -223,8 +225,9 @@ sub _update {}
# to update the "dirty" flags 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. # to uddate number of "filament" selection boxes when the number of extruders change.
sub _on_presets_changed { sub _on_presets_changed {
my ($self) = @_; my ($self, $reload_dependent_tabs) = @_;
$self->{on_presets_changed}->($self->{presets}) if $self->{on_presets_changed}; $self->{on_presets_changed}->($self->{presets}, $reload_dependent_tabs)
if $self->{on_presets_changed};
} }
# For the printer profile, generate the extruder pages after a preset is loaded. # For the printer profile, generate the extruder pages after a preset is loaded.
@ -237,11 +240,12 @@ sub may_discard_current_dirty_preset
my ($self, $presets, $new_printer_name) = @_; my ($self, $presets, $new_printer_name) = @_;
$presets //= $self->{presets}; $presets //= $self->{presets};
# Display a dialog showing the dirty options in a human readable form. # Display a dialog showing the dirty options in a human readable form.
my $old_preset = $presets->get_current_preset; my $old_preset = $presets->get_current_preset;
my $type_name = $presets->name; my $type_name = $presets->name;
my $name = $old_preset->default ? my $tab = ' ';
my $name = $old_preset->default ?
('Default ' . $type_name . ' preset') : ('Default ' . $type_name . ' preset') :
($type_name . " preset \"" . $old_preset->name . "\""); ($type_name . " preset\n$tab" . $old_preset->name);
# Collect descriptions of the dirty options. # Collect descriptions of the dirty options.
my @option_names = (); my @option_names = ();
foreach my $opt_key (@{$presets->current_dirty_options}) { foreach my $opt_key (@{$presets->current_dirty_options}) {
@ -251,10 +255,10 @@ sub may_discard_current_dirty_preset
push @option_names, $name; push @option_names, $name;
} }
# Show a confirmation dialog with the list of dirty options. # Show a confirmation dialog with the list of dirty options.
my $changes = join "\n", map "- $_", @option_names; my $changes = join "\n", map "$tab$_", @option_names;
my $message = (defined $new_printer_name) ? my $message = (defined $new_printer_name) ?
"$name is not compatible with printer \"$new_printer_name\"\n and it has unsaved changes:" : "$name\n\nis not compatible with printer\n$tab$new_printer_name\n\nand it has the following unsaved changes:" :
"$name has unsaved changes:"; "$name\n\nhas the following unsaved changes:";
my $confirm = Wx::MessageDialog->new($self, my $confirm = Wx::MessageDialog->new($self,
$message . "\n$changes\n\nDiscard changes and continue anyway?", $message . "\n$changes\n\nDiscard changes and continue anyway?",
'Unsaved Changes', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); 'Unsaved Changes', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
@ -267,9 +271,13 @@ sub may_discard_current_dirty_preset
sub select_preset { sub select_preset {
my ($self, $name, $force) = @_; my ($self, $name, $force) = @_;
$force //= 0; $force //= 0;
my $current_dirty = $self->{presets}->current_is_dirty; my $presets = $self->{presets};
my $canceled = 0; # If no name is provided, select the "-- default --" preset.
my $printer_tab = $self->{presets}->name eq 'printer'; $name //= $presets->default_preset->name;
my $current_dirty = $presets->current_is_dirty;
my $canceled = 0;
my $printer_tab = $presets->name eq 'printer';
my @reload_dependent_tabs = ();
if (! $force && $current_dirty && ! $self->may_discard_current_dirty_preset) { if (! $force && $current_dirty && ! $self->may_discard_current_dirty_preset) {
$canceled = 1; $canceled = 1;
} elsif ($printer_tab) { } elsif ($printer_tab) {
@ -277,50 +285,53 @@ sub select_preset {
# are compatible with the new printer. # are compatible with the new printer.
# If they are not compatible and the the current print or filament are dirty, let user decide # 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. # whether to discard the changes or keep the current printer selection.
my $new_printer_name = $name // ''; my $new_printer_preset = $presets->find_preset($name, 1);
my $new_printer_preset = $self->{presets}->find_preset($new_printer_name, 1); my $print_presets = wxTheApp->{preset_bundle}->print;
# my $new_nozzle_dmrs = $new_printer_preset->config->get('nozzle_diameter'); my $print_preset_dirty = $print_presets->current_is_dirty;
my $print_presets = wxTheApp->{preset_bundle}->print; my $print_preset_compatible = $print_presets->get_edited_preset->is_compatible_with_printer($new_printer_preset);
if ($print_presets->current_is_dirty && $canceled = ! $force && $print_preset_dirty && ! $print_preset_compatible &&
! $print_presets->get_edited_preset->is_compatible_with_printer($new_printer_name)) { ! $self->may_discard_current_dirty_preset($print_presets, $name);
if ($self->may_discard_current_dirty_preset($print_presets, $new_printer_name)) { my $filament_presets = wxTheApp->{preset_bundle}->filament;
$canceled = 1; my $filament_preset_dirty = $filament_presets->current_is_dirty;
} else { my $filament_preset_compatible = $filament_presets->get_edited_preset->is_compatible_with_printer($new_printer_preset);
$print_presets->discard_current_changes; if (! $canceled && ! $force) {
} $canceled = $filament_preset_dirty && ! $filament_preset_compatible &&
! $self->may_discard_current_dirty_preset($filament_presets, $name);
} }
my $filament_presets = wxTheApp->{preset_bundle}->filament; if (! $canceled) {
# if ((@$new_nozzle_dmrs <= 1) && if (! $print_preset_compatible) {
if (! $canceled && $filament_presets->current_is_dirty && # The preset will be switched to a different, compatible preset, or the '-- default --'.
! $filament_presets->get_edited_preset->is_compatible_with_printer($new_printer_name)) { push @reload_dependent_tabs, 'print';
if ($self->may_discard_current_dirty_preset($filament_presets, $new_printer_name)) { $print_presets->discard_current_changes if $print_preset_dirty;
$canceled = 1; }
} else { if (! $filament_preset_compatible) {
$filament_presets->discard_current_changes; # The preset will be switched to a different, compatible preset, or the '-- default --'.
push @reload_dependent_tabs, 'filament';
$filament_presets->discard_current_changes if $filament_preset_dirty;
} }
} }
} }
if ($canceled) { if ($canceled) {
$self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets}); $self->update_tab_ui;
# Trigger the on_presets_changed event so that we also restore the previous value in the plater selector. # Trigger the on_presets_changed event so that we also restore the previous value in the plater selector,
# if this action was initiated from the platter.
$self->_on_presets_changed; $self->_on_presets_changed;
} else { } else {
if (defined $name) { $presets->discard_current_changes if $current_dirty;
$self->{presets}->select_preset_by_name($name); $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. # Mark the print & filament enabled if they are compatible with the currently selected preset.
# The following method should not discard changes of current print or filament presets on change of a printer profile,
# if they are compatible with the current printer.
wxTheApp->{preset_bundle}->update_compatible_with_printer(1) wxTheApp->{preset_bundle}->update_compatible_with_printer(1)
if $current_dirty || $printer_tab; if $current_dirty || $printer_tab;
# Initialize the UI from the current preset. # Initialize the UI from the current preset.
$self->load_current_preset; $self->load_current_preset(\@reload_dependent_tabs);
} }
} }
# Initialize the UI from the current preset. # Initialize the UI from the current preset.
sub load_current_preset { sub load_current_preset {
my ($self) = @_; my ($self, $dependent_tab_names) = @_;
my $preset = $self->{presets}->get_current_preset; my $preset = $self->{presets}->get_current_preset;
eval { eval {
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
@ -337,8 +348,8 @@ sub load_current_preset {
# preset dirty again # preset dirty again
# (not sure this is true anymore now that update_dirty is idempotent) # (not sure this is true anymore now that update_dirty is idempotent)
wxTheApp->CallAfter(sub { wxTheApp->CallAfter(sub {
$self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets}); $self->update_tab_ui;
$self->_on_presets_changed; $self->_on_presets_changed($dependent_tab_names);
}); });
} }
@ -430,11 +441,10 @@ sub _load_key_value {
$self->{config}->set($opt_key, $value); $self->{config}->set($opt_key, $value);
# Mark the print & filament enabled if they are compatible with the currently selected preset. # Mark the print & filament enabled if they are compatible with the currently selected preset.
if ($opt_key eq 'compatible_printers') { if ($opt_key eq 'compatible_printers') {
# $opt_key eq 'compatible_printers_condition') {
wxTheApp->{preset_bundle}->update_compatible_with_printer(0); 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->{presets}->update_dirty_ui($self->{presets_choice});
$self->_on_presets_changed; $self->_on_presets_changed;
$self->_update; $self->_update;
} }
@ -485,6 +495,7 @@ sub _compatible_printers_widget {
$btn->$method; $btn->$method;
# All printers have been made compatible with this preset. # All printers have been made compatible with this preset.
$self->_load_key_value('compatible_printers', []) if $checkbox->GetValue; $self->_load_key_value('compatible_printers', []) if $checkbox->GetValue;
$self->get_field('compatible_printers_condition')->toggle($checkbox->GetValue);
}); });
EVT_BUTTON($self, $btn, sub { EVT_BUTTON($self, $btn, sub {
@ -506,6 +517,7 @@ sub _compatible_printers_widget {
my $value = [ @presets[$dlg->GetSelections] ]; my $value = [ @presets[$dlg->GetSelections] ];
if (!@$value) { if (!@$value) {
$checkbox->SetValue(1); $checkbox->SetValue(1);
$self->get_field('compatible_printers_condition')->toggle(1);
$btn->Disable; $btn->Disable;
} }
# All printers have been made compatible with this preset. # All printers have been made compatible with this preset.
@ -523,6 +535,7 @@ sub _reload_compatible_printers_widget {
my $method = $has_any ? 'Enable' : 'Disable'; my $method = $has_any ? 'Enable' : 'Disable';
$self->{compatible_printers_checkbox}->SetValue(! $has_any); $self->{compatible_printers_checkbox}->SetValue(! $has_any);
$self->{compatible_printers_btn}->$method; $self->{compatible_printers_btn}->$method;
$self->get_field('compatible_printers_condition')->toggle(! $has_any);
} }
sub update_ui_from_settings { sub update_ui_from_settings {
@ -538,10 +551,14 @@ sub update_ui_from_settings {
} else { } else {
if ($self->{show_incompatible_presets}) { if ($self->{show_incompatible_presets}) {
$self->{show_incompatible_presets} = 0; $self->{show_incompatible_presets} = 0;
$self->{presets}->update_tab_ui($self->{presets_choice}, 0); $self->update_tab_ui;
} }
} }
} }
sub update_tab_ui {
my ($self) = @_;
$self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets})
}
package Slic3r::GUI::Tab::Print; package Slic3r::GUI::Tab::Print;
use base 'Slic3r::GUI::Tab'; use base 'Slic3r::GUI::Tab';
@ -825,6 +842,10 @@ sub build {
widget => $self->_compatible_printers_widget, widget => $self->_compatible_printers_widget,
); );
$optgroup->append_line($line); $optgroup->append_line($line);
my $option = $optgroup->get_option('compatible_printers_condition');
$option->full_width(1);
$optgroup->append_single_option_line($option);
} }
} }
} }
@ -1194,6 +1215,10 @@ sub build {
widget => $self->_compatible_printers_widget, widget => $self->_compatible_printers_widget,
); );
$optgroup->append_line($line); $optgroup->append_line($line);
my $option = $optgroup->get_option('compatible_printers_condition');
$option->full_width(1);
$optgroup->append_single_option_line($option);
} }
} }
} }
@ -1223,6 +1248,12 @@ sub _update {
for qw(min_fan_speed disable_fan_first_layers); for qw(min_fan_speed disable_fan_first_layers);
} }
sub OnActivate {
my ($self) = @_;
$self->{volumetric_speed_description_line}->SetText(
Slic3r::GUI::PresetHints::maximum_volumetric_flow_description(wxTheApp->{preset_bundle}));
}
package Slic3r::GUI::Tab::Printer; package Slic3r::GUI::Tab::Printer;
use base 'Slic3r::GUI::Tab'; use base 'Slic3r::GUI::Tab';
use Wx qw(wxTheApp :sizer :button :bitmap :misc :id :icon :dialog); use Wx qw(wxTheApp :sizer :button :bitmap :misc :id :icon :dialog);

View File

@ -65,6 +65,9 @@ sub process {
} }
# G-code export process, running at a background thread. # 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 { sub export_gcode {
my $self = shift; my $self = shift;
my %params = @_; my %params = @_;
@ -73,11 +76,12 @@ sub export_gcode {
$self->process; $self->process;
# output everything to a G-code file # 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} // ''); my $output_file = $self->output_filepath($params{output_file} // '');
$self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : "")); $self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : ""));
die "G-code export to " . $output_file . " failed\n" # The following line may die for multiple reasons.
if ! Slic3r::GCode->new->do_export($self, $output_file); Slic3r::GCode->new->do_export($self, $output_file);
# run post-processing scripts # run post-processing scripts
if (@{$self->config->post_process}) { if (@{$self->config->post_process}) {
@ -99,6 +103,7 @@ sub export_gcode {
} }
# Export SVG slices for the offline SLA printing. # Export SVG slices for the offline SLA printing.
# The export_svg is expected to be executed inside an eval block.
sub export_svg { sub export_svg {
my $self = shift; my $self = shift;
my %params = @_; my %params = @_;
@ -107,6 +112,7 @@ sub export_svg {
my $fh = $params{output_fh}; my $fh = $params{output_fh};
if (!$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}); my $output_file = $self->output_filepath($params{output_file});
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/; $output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/;
Slic3r::open(\$fh, ">", $output_file) or die "Failed to open $output_file for writing\n"; Slic3r::open(\$fh, ">", $output_file) or die "Failed to open $output_file for writing\n";

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

0
var/add.png → resources/icons/add.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 873 B

After

Width:  |  Height:  |  Size: 873 B

View File

Before

Width:  |  Height:  |  Size: 465 B

After

Width:  |  Height:  |  Size: 465 B

View File

Before

Width:  |  Height:  |  Size: 379 B

After

Width:  |  Height:  |  Size: 379 B

View File

Before

Width:  |  Height:  |  Size: 345 B

After

Width:  |  Height:  |  Size: 345 B

0
var/arrow_out.png → resources/icons/arrow_out.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 715 B

After

Width:  |  Height:  |  Size: 715 B

View File

Before

Width:  |  Height:  |  Size: 685 B

After

Width:  |  Height:  |  Size: 685 B

View File

Before

Width:  |  Height:  |  Size: 349 B

After

Width:  |  Height:  |  Size: 349 B

View File

Before

Width:  |  Height:  |  Size: 716 B

After

Width:  |  Height:  |  Size: 716 B

View File

Before

Width:  |  Height:  |  Size: 706 B

After

Width:  |  Height:  |  Size: 706 B

0
var/arrow_up.png → resources/icons/arrow_up.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 503 B

After

Width:  |  Height:  |  Size: 503 B

0
var/box.png → resources/icons/box.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 968 B

After

Width:  |  Height:  |  Size: 968 B

0
var/brick.png → resources/icons/brick.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 525 B

After

Width:  |  Height:  |  Size: 525 B

0
var/brick_add.png → resources/icons/brick_add.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 835 B

After

Width:  |  Height:  |  Size: 835 B

View File

Before

Width:  |  Height:  |  Size: 865 B

After

Width:  |  Height:  |  Size: 865 B

0
var/brick_go.png → resources/icons/brick_go.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 915 B

After

Width:  |  Height:  |  Size: 915 B

0
var/bricks.png → resources/icons/bricks.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 960 B

After

Width:  |  Height:  |  Size: 960 B

0
var/building.png → resources/icons/building.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 728 B

After

Width:  |  Height:  |  Size: 728 B

View File

Before

Width:  |  Height:  |  Size: 201 B

After

Width:  |  Height:  |  Size: 201 B

View File

Before

Width:  |  Height:  |  Size: 201 B

After

Width:  |  Height:  |  Size: 201 B

View File

Before

Width:  |  Height:  |  Size: 287 B

After

Width:  |  Height:  |  Size: 287 B

View File

Before

Width:  |  Height:  |  Size: 289 B

After

Width:  |  Height:  |  Size: 289 B

View File

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 295 B

0
var/bullet_red.png → resources/icons/bullet_red.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 287 B

After

Width:  |  Height:  |  Size: 287 B

View File

Before

Width:  |  Height:  |  Size: 276 B

After

Width:  |  Height:  |  Size: 276 B

0
var/cog.png → resources/icons/cog.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 591 B

After

Width:  |  Height:  |  Size: 591 B

0
var/cog_go.png → resources/icons/cog_go.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 598 B

After

Width:  |  Height:  |  Size: 598 B

View File

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 721 B

View File

Before

Width:  |  Height:  |  Size: 592 B

After

Width:  |  Height:  |  Size: 592 B

View File

Before

Width:  |  Height:  |  Size: 717 B

After

Width:  |  Height:  |  Size: 717 B

View File

Before

Width:  |  Height:  |  Size: 403 B

After

Width:  |  Height:  |  Size: 403 B

View File

Before

Width:  |  Height:  |  Size: 695 B

After

Width:  |  Height:  |  Size: 695 B

0
var/cross.png → resources/icons/cross.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 846 B

After

Width:  |  Height:  |  Size: 846 B

0
var/delete.png → resources/icons/delete.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 844 B

After

Width:  |  Height:  |  Size: 844 B

0
var/disk.png → resources/icons/disk.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 974 B

After

Width:  |  Height:  |  Size: 974 B

0
var/error.png → resources/icons/error.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 808 B

After

Width:  |  Height:  |  Size: 808 B

View File

Before

Width:  |  Height:  |  Size: 672 B

After

Width:  |  Height:  |  Size: 672 B

View File

Before

Width:  |  Height:  |  Size: 665 B

After

Width:  |  Height:  |  Size: 665 B

View File

Before

Width:  |  Height:  |  Size: 641 B

After

Width:  |  Height:  |  Size: 641 B

0
var/hourglass.png → resources/icons/hourglass.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 907 B

After

Width:  |  Height:  |  Size: 907 B

View File

Before

Width:  |  Height:  |  Size: 806 B

After

Width:  |  Height:  |  Size: 806 B

View File

Before

Width:  |  Height:  |  Size: 242 B

After

Width:  |  Height:  |  Size: 242 B

0
var/joystick.png → resources/icons/joystick.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 559 B

After

Width:  |  Height:  |  Size: 559 B

0
var/layers.png → resources/icons/layers.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

0
var/lorry_add.png → resources/icons/lorry_add.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 689 B

After

Width:  |  Height:  |  Size: 689 B

0
var/lorry_go.png → resources/icons/lorry_go.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 699 B

After

Width:  |  Height:  |  Size: 699 B

0
var/note.png → resources/icons/note.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 740 B

After

Width:  |  Height:  |  Size: 740 B

0
var/package.png → resources/icons/package.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 702 B

After

Width:  |  Height:  |  Size: 702 B

0
var/plugin.png → resources/icons/plugin.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 778 B

After

Width:  |  Height:  |  Size: 778 B

0
var/plugin_add.png → resources/icons/plugin_add.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 691 B

After

Width:  |  Height:  |  Size: 691 B

0
var/plugin_go.png → resources/icons/plugin_go.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 694 B

After

Width:  |  Height:  |  Size: 694 B

View File

Before

Width:  |  Height:  |  Size: 424 B

After

Width:  |  Height:  |  Size: 424 B

View File

Before

Width:  |  Height:  |  Size: 665 B

After

Width:  |  Height:  |  Size: 665 B

View File

Before

Width:  |  Height:  |  Size: 403 B

After

Width:  |  Height:  |  Size: 403 B

View File

Before

Width:  |  Height:  |  Size: 538 B

After

Width:  |  Height:  |  Size: 538 B

View File

Before

Width:  |  Height:  |  Size: 803 B

After

Width:  |  Height:  |  Size: 803 B

View File

Before

Width:  |  Height:  |  Size: 545 B

After

Width:  |  Height:  |  Size: 545 B

0
var/tag_blue.png → resources/icons/tag_blue.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 741 B

After

Width:  |  Height:  |  Size: 741 B

0
var/textfield.png → resources/icons/textfield.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 153 B

After

Width:  |  Height:  |  Size: 153 B

0
var/time.png → resources/icons/time.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 959 B

After

Width:  |  Height:  |  Size: 959 B

View File

Before

Width:  |  Height:  |  Size: 209 B

After

Width:  |  Height:  |  Size: 209 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Before

Width:  |  Height:  |  Size: 570 B

After

Width:  |  Height:  |  Size: 570 B

0
var/wrench.png → resources/icons/wrench.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 742 B

After

Width:  |  Height:  |  Size: 742 B

0
var/zoom.png → resources/icons/zoom.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 692 B

After

Width:  |  Height:  |  Size: 692 B

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,7 @@ my %cli_options = ();
'debug' => \$Slic3r::debug, 'debug' => \$Slic3r::debug,
'gui' => \$opt{gui}, 'gui' => \$opt{gui},
'no-gui' => \$opt{no_gui},
'o|output=s' => \$opt{output}, 'o|output=s' => \$opt{output},
'save=s' => \$opt{save}, 'save=s' => \$opt{save},
@ -70,10 +71,10 @@ my @external_configs = ();
if ($opt{load}) { if ($opt{load}) {
foreach my $configfile (@{$opt{load}}) { foreach my $configfile (@{$opt{load}}) {
if (-e $configfile) { if (-e $configfile) {
push @external_configs, Slic3r::Config->load($configfile); push @external_configs, Slic3r::Config::load($configfile);
} elsif (-e "$FindBin::Bin/$configfile") { } elsif (-e "$FindBin::Bin/$configfile") {
printf STDERR "Loading $FindBin::Bin/$configfile\n"; printf STDERR "Loading $FindBin::Bin/$configfile\n";
push @external_configs, Slic3r::Config->load("$FindBin::Bin/$configfile"); push @external_configs, Slic3r::Config::load("$FindBin::Bin/$configfile");
} else { } else {
$opt{ignore_nonexistent_config} or die "Cannot find specified configuration file ($configfile).\n"; $opt{ignore_nonexistent_config} or die "Cannot find specified configuration file ($configfile).\n";
} }
@ -102,7 +103,7 @@ $config->apply($cli_config);
# launch GUI # launch GUI
my $gui; my $gui;
if ((!@ARGV || $opt{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'; no warnings 'once';
$Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // ''); $Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // '');
@ -218,6 +219,8 @@ if (@ARGV) { # slicing from command line
$sprint->export_svg; $sprint->export_svg;
} else { } else {
my $t0 = [gettimeofday]; 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; $sprint->export_gcode;
# output some statistics # output some statistics
@ -267,6 +270,8 @@ Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ...
--gui Forces the GUI launch instead of command line slicing (if you --gui Forces the GUI launch instead of command line slicing (if you
supply a model file, it will be loaded into the plater) supply a model file, it will be loaded into the plater)
--no-plater Disable the plater tab --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 <file> Automatically export current configuration to the specified file --autosave <file> Automatically export current configuration to the specified file
Output options: Output options:

View File

@ -1,4 +1,4 @@
use Test::More tests => 49; use Test::More tests => 71;
use strict; use strict;
use warnings; use warnings;
@ -47,9 +47,13 @@ use Slic3r::Test;
{ {
my $parser = Slic3r::GCode::PlaceholderParser->new; my $parser = Slic3r::GCode::PlaceholderParser->new;
$parser->apply_config(my $config = Slic3r::Config::new_from_defaults); my $config = Slic3r::Config::new_from_defaults;
$config->set('printer_notes', ' PRINTER_VENDOR_PRUSA3D PRINTER_MODEL_MK2 ');
$config->set('nozzle_diameter', [0.6, 0.6, 0.6, 0.6]);
$parser->apply_config($config);
$parser->set('foo' => 0); $parser->set('foo' => 0);
$parser->set('bar' => 2); $parser->set('bar' => 2);
$parser->set('num_extruders' => 4);
is $parser->process('[temperature_[foo]]'), is $parser->process('[temperature_[foo]]'),
$config->temperature->[0], $config->temperature->[0],
"nested config options (legacy syntax)"; "nested config options (legacy syntax)";
@ -67,6 +71,32 @@ use Slic3r::Test;
is $parser->process('{2*foo*(3-12)}'), '0', 'math: 2*foo*(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)'; 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)'; ok abs($parser->process('{2.5*bar*(3-12)}') - -45) < 1e-7, 'math: 2.5*bar*(3-12)';
# Test the boolean expression parser.
is $parser->evaluate_boolean_expression('12 == 12'), 1, 'boolean expression parser: 12 == 12';
is $parser->evaluate_boolean_expression('12 != 12'), 0, 'boolean expression parser: 12 != 12';
is $parser->evaluate_boolean_expression('"has some PATTERN embedded" =~ /.*PATTERN.*/'), 1, 'boolean expression parser: regex matches';
is $parser->evaluate_boolean_expression('"has some PATTERN embedded" =~ /.*PTRN.*/'), 0, 'boolean expression parser: regex does not match';
is $parser->evaluate_boolean_expression('foo + 2 == bar'), 1, 'boolean expression parser: accessing variables, equal';
is $parser->evaluate_boolean_expression('foo + 3 == bar'), 0, 'boolean expression parser: accessing variables, not equal';
is $parser->evaluate_boolean_expression('(12 == 12) and (13 != 14)'), 1, 'boolean expression parser: (12 == 12) and (13 != 14)';
is $parser->evaluate_boolean_expression('(12 == 12) && (13 != 14)'), 1, 'boolean expression parser: (12 == 12) && (13 != 14)';
is $parser->evaluate_boolean_expression('(12 == 12) or (13 == 14)'), 1, 'boolean expression parser: (12 == 12) or (13 == 14)';
is $parser->evaluate_boolean_expression('(12 == 12) || (13 == 14)'), 1, 'boolean expression parser: (12 == 12) || (13 == 14)';
is $parser->evaluate_boolean_expression('(12 == 12) and not (13 == 14)'), 1, 'boolean expression parser: (12 == 12) and not (13 == 14)';
is $parser->evaluate_boolean_expression('(12 == 12) ? (1 - 1 == 0) : (2 * 2 == 3)'), 1, 'boolean expression parser: ternary true';
is $parser->evaluate_boolean_expression('(12 == 21/2) ? (1 - 1 == 0) : (2 * 2 == 3)'), 0, 'boolean expression parser: ternary false';
is $parser->evaluate_boolean_expression('(12 == 13) ? (1 - 1 == 3) : (2 * 2 == 4)"'), 1, 'boolean expression parser: ternary false';
is $parser->evaluate_boolean_expression('(12 == 2 * 6) ? (1 - 1 == 3) : (2 * 2 == 4)"'), 0, 'boolean expression parser: ternary true';
is $parser->evaluate_boolean_expression('12 < 3'), 0, 'boolean expression parser: lower than - false';
is $parser->evaluate_boolean_expression('12 < 22'), 1, 'boolean expression parser: lower than - true';
is $parser->evaluate_boolean_expression('12 > 3'), 1, 'boolean expression parser: lower than - true';
is $parser->evaluate_boolean_expression('12 > 22'), 0, 'boolean expression parser: lower than - false';
is $parser->evaluate_boolean_expression('printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 and num_extruders>1'), 1, 'complex expression';
is $parser->evaluate_boolean_expression('printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.6 and num_extruders>1)'), 1, 'complex expression2';
is $parser->evaluate_boolean_expression('printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.3 and num_extruders>1)'), 0, 'complex expression3';
} }
{ {

View File

@ -35,7 +35,7 @@ my %opt = ();
# load config # load config
my $config = Slic3r::Config::new_from_defaults; my $config = Slic3r::Config::new_from_defaults;
if ($opt{load}) { if ($opt{load}) {
$config->apply(Slic3r::Config->load($opt{load})); $config->apply(Slic3r::Config::load($opt{load}));
} }
# init print # init print

View File

@ -440,7 +440,7 @@ if(SLIC3R_STATIC)
# Use boost libraries linked statically to the C++ runtime. # Use boost libraries linked statically to the C++ runtime.
# set(Boost_USE_STATIC_RUNTIME ON) # set(Boost_USE_STATIC_RUNTIME ON)
endif() endif()
find_package(Boost REQUIRED COMPONENTS system filesystem thread log locale) find_package(Boost REQUIRED COMPONENTS system filesystem thread log locale regex)
if(Boost_FOUND) if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(XS ${Boost_LIBRARIES}) target_link_libraries(XS ${Boost_LIBRARIES})

View File

@ -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 auto &opt : def->options) {
for (const t_config_option_key &opt_key2 : opt.second.aliases) { for (const t_config_option_key &opt_key2 : opt.second.aliases) {
if (opt_key2 == opt_key) { if (opt_key2 == opt_key) {
opt_key = opt_key2; opt_key = opt.first;
optdef = &opt.second; optdef = &opt.second;
break; break;
} }

View File

@ -30,6 +30,13 @@
#include <assert.h> #include <assert.h>
namespace Slic3r { 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. // 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 // 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; std::string gcode;
// Disable linear advance for the wipe tower operations.
gcode += "M900 K0\n";
// Move over the wipe tower. // Move over the wipe tower.
// Retract for a tool change, using the toolchange retract value and setting the priming extra length. // Retract for a tool change, using the toolchange retract value and setting the priming extra length.
gcode += gcodegen.retract(true); gcode += gcodegen.retract(true);
@ -173,6 +182,15 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
// Let the m_writer know the current extruder_id, but ignore the generated G-code. // 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); gcodegen.writer().toolchange(new_extruder_id);
// Always append the filament start G-code even if the extruder did not switch,
// because the wipe tower resets the linear advance and we want it to be re-enabled.
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", start_filament_gcode, new_extruder_id);
check_add_eol(gcode);
}
// A phony move to the end position at the wipe tower. // 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.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)); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
@ -199,15 +217,18 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen)
std::string gcode; std::string gcode;
if (&m_priming != nullptr && ! m_priming.extrusions.empty()) { 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. // Let the tool change be executed by the wipe tower class.
// Inform the G-code writer about the changes done behind its back. // Inform the G-code writer about the changes done behind its back.
gcode += m_priming.gcode; gcode += m_priming.gcode;
// Let the m_writer know the current extruder_id, but ignore the generated G-code. // 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. // 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.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)); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos));
// Prepare a future wipe. // Prepare a future wipe.
gcodegen.m_wipe.path.points.clear(); gcodegen.m_wipe.path.points.clear();
// Start the wipe at the current position. // Start the wipe at the current position.
@ -220,19 +241,6 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen)
return gcode; 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 WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer)
{ {
std::string gcode; std::string gcode;
@ -354,7 +362,7 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
return layers_to_print; 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. // Remove the old g-code if it exists.
boost::nowide::remove(path); boost::nowide::remove(path);
@ -364,23 +372,36 @@ bool GCode::do_export(Print *print, const char *path)
FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb"); FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb");
if (file == nullptr) 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();
fclose(file); this->_do_export(*print, file);
fflush(file);
if (result && boost::nowide::rename(path_tmp.c_str(), path) != 0) { if (ferror(file)) {
boost::nowide::cerr << "Failed to remove the output G-code file from " << path_tmp << " to " << path fclose(file);
<< ". Is " << path_tmp << " locked?" << std::endl;
result = false;
}
if (! result)
boost::nowide::remove(path_tmp.c_str()); 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");
}
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";
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? // How many times will be change_layer() called?
// change_layer() in turn increments the progress bar status. // change_layer() in turn increments the progress bar status.
@ -480,23 +501,21 @@ bool GCode::_do_export(Print &print, FILE *file)
fprintf(file, "\n"); fprintf(file, "\n");
} }
// Write some terse information on the slicing parameters. // Write some terse information on the slicing parameters.
{ const PrintObject *first_object = print.objects.front();
const PrintObject *first_object = print.objects.front(); const double layer_height = first_object->config.layer_height.value;
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);
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) {
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) { auto region = print.regions[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);
fprintf(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width); fprintf(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width);
fprintf(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width); fprintf(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width);
fprintf(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width); fprintf(file, "; solid infill extrusion width = %.2fmm\n", region->flow(frSolidInfill, layer_height, false, false, -1., *first_object).width);
fprintf(file, "; solid infill extrusion width = %.2fmm\n", region->flow(frSolidInfill, layer_height, false, false, -1., *first_object).width); fprintf(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width);
fprintf(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width); if (print.has_support_material())
if (print.has_support_material()) fprintf(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width);
fprintf(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width); if (print.config.first_layer_extrusion_width.value > 0)
if (print.config.first_layer_extrusion_width.value > 0) fprintf(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_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");
fprintf(file, "\n");
}
} }
// Prepare the helper object for replacing placeholders in custom G-code and output filename. // Prepare the helper object for replacing placeholders in custom G-code and output filename.
@ -509,6 +528,7 @@ bool GCode::_do_export(Print &print, FILE *file)
unsigned int initial_extruder_id = (unsigned int)-1; unsigned int initial_extruder_id = (unsigned int)-1;
unsigned int final_extruder_id = (unsigned int)-1; unsigned int final_extruder_id = (unsigned int)-1;
size_t initial_print_object_id = 0; size_t initial_print_object_id = 0;
bool has_wipe_tower = false;
if (print.config.complete_objects.value) { if (print.config.complete_objects.value) {
// Find the 1st printing object, find its tool ordering and the initial extruder ID. // 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) { for (; initial_print_object_id < print.objects.size(); ++initial_print_object_id) {
@ -523,6 +543,7 @@ bool GCode::_do_export(Print &print, FILE *file)
ToolOrdering(print, initial_extruder_id) : ToolOrdering(print, initial_extruder_id) :
print.m_tool_ordering; print.m_tool_ordering;
initial_extruder_id = tool_ordering.first_extruder(); 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) { if (initial_extruder_id == (unsigned int)-1) {
// Nothing to print! // Nothing to print!
@ -545,7 +566,9 @@ bool GCode::_do_export(Print &print, FILE *file)
m_placeholder_parser.set("current_extruder", initial_extruder_id); m_placeholder_parser.set("current_extruder", initial_extruder_id);
// Useful for sequential prints. // Useful for sequential prints.
m_placeholder_parser.set("current_object_idx", 0); m_placeholder_parser.set("current_object_idx", 0);
std::string start_gcode = m_placeholder_parser.process(print.config.start_gcode.value, initial_extruder_id); // 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 = 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. // 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); this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true);
@ -554,8 +577,17 @@ bool GCode::_do_export(Print &print, FILE *file)
// Write the custom start G-code // Write the custom start G-code
writeln(file, start_gcode); writeln(file, start_gcode);
// Process filament-specific gcode in extruder order. // Process filament-specific gcode in extruder order.
for (const std::string &start_gcode : print.config.start_filament_gcode.values) if (print.config.single_extruder_multi_material) {
writeln(file, m_placeholder_parser.process(start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front()))); 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, 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, 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); this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
// Set other general things. // Set other general things.
@ -647,7 +679,7 @@ bool GCode::_do_export(Print &print, FILE *file)
// another one, set first layer temperatures. This happens before the Z move // another one, set first layer temperatures. This happens before the Z move
// is triggered, so machine has more time to reach such temperatures. // is triggered, so machine has more time to reach such temperatures.
m_placeholder_parser.set("current_object_idx", int(finished_objects)); 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. // 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_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); this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
@ -682,32 +714,30 @@ bool GCode::_do_export(Print &print, FILE *file)
// All extrusion moves with the same top layer height are extruded uninterrupted. // All extrusion moves with the same top layer height are extruded uninterrupted.
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print); std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
// Prusa Multi-Material wipe tower. // Prusa Multi-Material wipe tower.
if (print.has_wipe_tower() && ! layers_to_print.empty()) { if (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()));
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_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
write(file, m_wipe_tower->prime(*this)); write(file, m_wipe_tower->prime(*this));
// Verify, whether the print overaps the priming extrusions. // Verify, whether the print overaps the priming extrusions.
BoundingBoxf bbox_print(get_print_extrusions_extents(print)); 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; 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) for (const PrintObject *print_object : print.objects)
bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz)); bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz)); bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print)); BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
bbox_prime.offset(0.5f); bbox_prime.offset(0.5f);
// Beep for 500ms, tone 800Hz. Yet better, play some Morse. // Beep for 500ms, tone 800Hz. Yet better, play some Morse.
write(file, this->retract()); write(file, this->retract());
fprintf(file, "M300 S800 P500\n"); fprintf(file, "M300 S800 P500\n");
if (bbox_prime.overlap(bbox_print)) { if (bbox_prime.overlap(bbox_print)) {
// Wait for the user to remove the priming extrusions, otherwise they would // Wait for the user to remove the priming extrusions, otherwise they would
// get covered by the print. // get covered by the print.
fprintf(file, "M1 Remove priming towers and click button.\n"); fprintf(file, "M1 Remove priming towers and click button.\n");
} else { } else {
// Just wait for a bit to let the user check, that the priming succeeded. // 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. //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
fprintf(file, "M1 S10\n"); fprintf(file, "M1 S10\n");
} }
} else
write(file, WipeTowerIntegration::prime_single_color_print(print, initial_extruder_id, *this));
} }
// Extrude the layers. // Extrude the layers.
for (auto &layer : layers_to_print) { for (auto &layer : layers_to_print) {
@ -727,9 +757,14 @@ bool GCode::_do_export(Print &print, FILE *file)
write(file, this->retract()); write(file, this->retract());
write(file, m_writer.set_fan(false)); write(file, m_writer.set_fan(false));
// Process filament-specific gcode in extruder order. // Process filament-specific gcode in extruder order.
for (const std::string &end_gcode : print.config.end_filament_gcode.values) if (print.config.single_extruder_multi_material) {
writeln(file, m_placeholder_parser.process(end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front()))); // Process the end_filament_gcode for the active filament only.
writeln(file, m_placeholder_parser.process(print.config.end_gcode, 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, this->placeholder_parser_process("end_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front())));
}
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.update_progress(m_layer_count, m_layer_count, true)); // 100%
write(file, m_writer.postamble()); write(file, m_writer.postamble());
@ -766,11 +801,25 @@ bool GCode::_do_export(Print &print, FILE *file)
for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++ i) { for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++ i) {
StaticPrintConfig *cfg = configs[i]; StaticPrintConfig *cfg = configs[i];
for (const std::string &key : cfg->keys()) 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());
} }
} }
}
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("\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\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. // Parse the custom G-code, try to find mcode_set_temp_dont_wait and mcode_set_temp_and_wait inside the custom G-code.
@ -838,12 +887,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? // Is the bed temperature set by the provided custom G-code?
int temp_by_gcode = -1; int temp_by_gcode = -1;
bool temp_set_by_gcode = custom_gcode_sets_temperature(gcode, 140, 190, temp_by_gcode); 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; 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 // 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. // the custom start G-code emited these.
std::string set_temp_gcode = m_writer.set_bed_temperature(temp, wait); 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); write(file, set_temp_gcode);
} }
@ -973,7 +1022,7 @@ void GCode::process_layer(
DynamicConfig config; DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1)); config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); 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) print.config.before_layer_gcode.value, m_writer.extruder()->id(), &config)
+ "\n"; + "\n";
} }
@ -983,7 +1032,7 @@ void GCode::process_layer(
DynamicConfig config; DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); 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) print.config.layer_gcode.value, m_writer.extruder()->id(), &config)
+ "\n"; + "\n";
} }
@ -2162,13 +2211,14 @@ GCode::retract(bool toolchange)
std::string GCode::set_extruder(unsigned int extruder_id) std::string GCode::set_extruder(unsigned int extruder_id)
{ {
m_placeholder_parser.set("current_extruder", extruder_id);
if (!m_writer.need_toolchange(extruder_id)) if (!m_writer.need_toolchange(extruder_id))
return ""; return "";
// if we are running a single-extruder setup, just set the extruder and return nothing // 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); return m_writer.toolchange(extruder_id);
}
// prepend retraction on the current extruder // prepend retraction on the current extruder
std::string gcode = this->retract(true); std::string gcode = this->retract(true);
@ -2176,23 +2226,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. // Always reset the extrusion path, even if the tool change retract is set to zero.
m_wipe.reset_path(); m_wipe.reset_path();
// append custom toolchange G-code if (m_writer.extruder() != nullptr) {
if (m_writer.extruder() != nullptr && !m_config.toolchange_gcode.value.empty()) { // 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 += placeholder_parser_process("end_filament_gcode", 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; DynamicConfig config;
config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id())); config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id()));
config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id));
gcode += m_placeholder_parser.process( gcode += placeholder_parser_process("toolchange_gcode", m_config.toolchange_gcode.value, extruder_id, &config);
m_config.toolchange_gcode.value, extruder_id, &config) check_add_eol(gcode);
+ '\n';
} }
// if ooze prevention is enabled, park current extruder in the nearest // If ooze prevention is enabled, park current extruder in the nearest
// standby point and set it to the standby temperature // standby point and set it to the standby temperature.
if (m_ooze_prevention.enable && m_writer.extruder() != nullptr) if (m_ooze_prevention.enable && m_writer.extruder() != nullptr)
gcode += m_ooze_prevention.pre_toolchange(*this); gcode += m_ooze_prevention.pre_toolchange(*this);
// append the toolchange command // Append the toolchange command.
gcode += m_writer.toolchange(extruder_id); 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 += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id);
check_add_eol(gcode);
}
// Set the new extruder to the operating temperature.
if (m_ooze_prevention.enable) if (m_ooze_prevention.enable)
gcode += m_ooze_prevention.post_toolchange(*this); gcode += m_ooze_prevention.post_toolchange(*this);

View File

@ -90,7 +90,6 @@ public:
m_brim_done(false) {} m_brim_done(false) {}
std::string prime(GCode &gcodegen); 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; } 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 tool_change(GCode &gcodegen, int extruder_id, bool finish_layer);
std::string finalize(GCode &gcodegen); std::string finalize(GCode &gcodegen);
@ -131,7 +130,8 @@ public:
{} {}
~GCode() {} ~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. // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
const Pointf& origin() const { return m_origin; } const Pointf& origin() const { return m_origin; }
@ -143,6 +143,10 @@ public:
const FullPrintConfig &config() const { return m_config; } const FullPrintConfig &config() const { return m_config; }
const Layer* layer() const { return m_layer; } const Layer* layer() const { return m_layer; }
GCodeWriter& writer() { return m_writer; } 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; } bool enable_cooling_markers() const { return m_enable_cooling_markers; }
// For Perl bindings, to be used exclusively by unit tests. // For Perl bindings, to be used exclusively by unit tests.
@ -151,7 +155,7 @@ public:
void apply_print_config(const PrintConfig &print_config); void apply_print_config(const PrintConfig &print_config);
protected: 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. // Object and support extrusions of the same PrintObject at the same print_z.
struct LayerToPrint struct LayerToPrint
@ -223,6 +227,8 @@ protected:
FullPrintConfig m_config; FullPrintConfig m_config;
GCodeWriter m_writer; GCodeWriter m_writer;
PlaceholderParser m_placeholder_parser; PlaceholderParser m_placeholder_parser;
// Collection of templates, on which the placeholder substitution failed.
std::set<std::string> m_placeholder_parser_failed_templates;
OozePrevention m_ooze_prevention; OozePrevention m_ooze_prevention;
Wipe m_wipe; Wipe m_wipe;
AvoidCrossingPerimeters m_avoid_crossing_perimeters; AvoidCrossingPerimeters m_avoid_crossing_perimeters;

View File

@ -1,7 +1,16 @@
#include "Print.hpp" #include "Print.hpp"
#include "ToolOrdering.hpp" #include "ToolOrdering.hpp"
#include <assert.h> // #define SLIC3R_DEBUG
// Make assert active if SLIC3R_DEBUG
#ifdef SLIC3R_DEBUG
#define DEBUG
#define _DEBUG
#undef NDEBUG
#endif
#include <cassert>
#include <limits> #include <limits>
namespace Slic3r { namespace Slic3r {
@ -256,12 +265,19 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_
// Insert one additional wipe tower layer between lh.print_z and lt_object.print_z. // Insert one additional wipe tower layer between lh.print_z and lt_object.print_z.
LayerTools lt_new(0.5f * (lt.print_z + lt_object.print_z)); LayerTools lt_new(0.5f * (lt.print_z + lt_object.print_z));
// Find the 1st layer above lt_new. // Find the 1st layer above lt_new.
for (j = i + 1; j < m_layer_tools.size() && m_layer_tools[j].print_z < lt_new.print_z; ++ j); for (j = i + 1; j < m_layer_tools.size() && m_layer_tools[j].print_z < lt_new.print_z - EPSILON; ++ j);
LayerTools &lt_extra = (m_layer_tools[j].print_z == lt_new.print_z) ? if (std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) {
m_layer_tools[j] : m_layer_tools[j].has_wipe_tower = true;
*m_layer_tools.insert(m_layer_tools.begin() + j, lt_new); } else {
lt_extra.has_wipe_tower = true; LayerTools &lt_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new);
lt_extra.wipe_tower_partitions = lt_object.wipe_tower_partitions; LayerTools &lt_prev = m_layer_tools[j - 1];
LayerTools &lt_next = m_layer_tools[j + 1];
assert(! lt_prev.extruders.empty() && ! lt_next.extruders.empty());
assert(lt_prev.extruders.back() == lt_next.extruders.front());
lt_extra.has_wipe_tower = true;
lt_extra.extruders.push_back(lt_next.extruders.front());
lt_extra.wipe_tower_partitions = lt_next.wipe_tower_partitions;
}
} }
} }
break; break;

View File

@ -53,14 +53,14 @@ public:
void clear() { m_layer_tools.clear(); } 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; } 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. // 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; } 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. // For a multi-material print, the printing extruders are ordered in the order they shall be primed.
std::vector<unsigned int> all_extruders() const { return m_all_printing_extruders; } const std::vector<unsigned int>& all_extruders() const { return m_all_printing_extruders; }
// Find LayerTools with the closest print_z. // Find LayerTools with the closest print_z.
LayerTools& tools_for_layer(coordf_t print_z); LayerTools& tools_for_layer(coordf_t print_z);
@ -69,6 +69,8 @@ public:
const LayerTools& front() const { return m_layer_tools.front(); } const LayerTools& front() const { return m_layer_tools.front(); }
const LayerTools& back() const { return m_layer_tools.back(); } const LayerTools& back() const { return m_layer_tools.back(); }
std::vector<LayerTools>::const_iterator begin() const { return m_layer_tools.begin(); }
std::vector<LayerTools>::const_iterator end() const { return m_layer_tools.end(); }
bool empty() const { return m_layer_tools.empty(); } bool empty() const { return m_layer_tools.empty(); }
const std::vector<LayerTools>& layer_tools() const { return m_layer_tools; } const std::vector<LayerTools>& layer_tools() const { return m_layer_tools; }
bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().wipe_tower_partitions > 0; } bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().wipe_tower_partitions > 0; }

View File

@ -122,7 +122,7 @@ public:
// print_z of the first layer. // print_z of the first layer.
float first_layer_height, 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. // 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<unsigned int> tools, const std::vector<unsigned int> &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 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. // If false, the last priming are will be large enough to wipe the last extruder sufficiently.
bool last_wipe_inside_wipe_tower, bool last_wipe_inside_wipe_tower,

View File

@ -389,7 +389,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
// print_z of the first layer. // print_z of the first layer.
float first_layer_height, 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. // 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<unsigned int> tools, const std::vector<unsigned int> &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 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. // If false, the last priming are will be large enough to wipe the last extruder sufficiently.
bool last_wipe_inside_wipe_tower, bool last_wipe_inside_wipe_tower,
@ -615,7 +615,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
.extrude(box.ld, 3200).extrude(box.rd) .extrude(box.ld, 3200).extrude(box.rd)
.extrude(box.ru).extrude(box.lu); .extrude(box.ru).extrude(box.lu);
// Wipe the nozzle. // Wipe the nozzle.
if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) //if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
// Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
writer.travel(box.ru, 7200) writer.travel(box.ru, 7200)
.travel(box.lu); .travel(box.lu);
} else } else
@ -723,8 +724,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
// Move to the front left corner. // Move to the front left corner.
writer.travel(wipeTower_box.ld, 7000); writer.travel(wipeTower_box.ld, 7000);
if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) //if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
// Wipe along the front edge. // Wipe along the front edge.
// Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
writer.travel(wipeTower_box.rd) writer.travel(wipeTower_box.rd)
.travel(wipeTower_box.ld); .travel(wipeTower_box.ld);
@ -1083,8 +1085,10 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
.extrude(fill_box.ru + xy(-m_perimeter_width, -m_perimeter_width)); .extrude(fill_box.ru + xy(-m_perimeter_width, -m_perimeter_width));
} }
if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) // if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
if (true)
// Wipe along the front side of the current wiping box. // Wipe along the front side of the current wiping box.
// Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
writer.travel(fill_box.ld + xy( m_perimeter_width, m_perimeter_width / 2), 7200) writer.travel(fill_box.ld + xy( m_perimeter_width, m_perimeter_width / 2), 7200)
.travel(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width / 2)); .travel(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width / 2));
else else

View File

@ -151,7 +151,7 @@ public:
// print_z of the first layer. // print_z of the first layer.
float first_layer_height, 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. // 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<unsigned int> tools, const std::vector<unsigned int> &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 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. // If false, the last priming are will be large enough to wipe the last extruder sufficiently.
bool last_wipe_inside_wipe_tower, bool last_wipe_inside_wipe_tower,

Some files were not shown because too many files have changed in this diff Show More