Merge branch 'master' into wipe_tower_improvements
|
@ -116,6 +116,8 @@ The author of the Silk icon set is Mark James.
|
|||
--gui Forces the GUI launch instead of command line slicing (if you
|
||||
supply a model file, it will be loaded into the plater)
|
||||
--no-plater Disable the plater tab
|
||||
--no-gui Forces the command line slicing instead of gui.
|
||||
This takes precedence over --gui if both are present.
|
||||
--autosave <file> Automatically export current configuration to the specified file
|
||||
|
||||
Output options:
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
brew install dylibbundler
|
||||
|
||||
Install cmake
|
||||
brew install cmake
|
||||
|
||||
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/
|
||||
|
||||
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:
|
||||
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.
|
||||
Available perl versions could be listed by calling
|
||||
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
|
||||
execute cpan command, from the cpan prompt, run
|
||||
install App::cpanminus
|
||||
install ExtUtils::CppGuess
|
||||
install ExtUtils::Typemaps
|
||||
install ExtUtils::Typemaps::Basic
|
||||
install PAR
|
||||
install PAR::Packer
|
||||
install Module::Build
|
||||
install Module::Pluggable
|
||||
install Module::Runtime
|
||||
install Moo
|
||||
install Test::Pod
|
||||
install Test::Pod::Coverage
|
||||
quit
|
||||
|
||||
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
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
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:
|
||||
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.
|
||||
|
||||
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.
|
||||
|
|
|
@ -41,7 +41,8 @@ warn "Running Slic3r under Perl 5.16 is neither supported nor recommended\n"
|
|||
use FindBin;
|
||||
|
||||
# Let the XS module know where the GUI resources reside.
|
||||
set_var_dir(decode_path($FindBin::Bin) . "/var");
|
||||
set_resources_dir(decode_path($FindBin::Bin) . (($^O eq 'darwin') ? '/../Resources' : '/resources'));
|
||||
set_var_dir(resources_dir() . "/icons");
|
||||
|
||||
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;
|
||||
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 {
|
||||
my ($cb) = @_;
|
||||
@_ = ();
|
||||
|
|
|
@ -70,7 +70,7 @@ our $grey = Wx::Colour->new(200,200,200);
|
|||
sub OnInit {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->SetAppName('Slic3r');
|
||||
$self->SetAppName('Slic3rPE');
|
||||
$self->SetAppDisplayName('Slic3r Prusa Edition');
|
||||
Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION;
|
||||
|
||||
|
@ -79,6 +79,7 @@ sub OnInit {
|
|||
# Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r"
|
||||
# Mac: "~/Library/Application Support/Slic3r"
|
||||
Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir);
|
||||
Slic3r::GUI::set_wxapp($self);
|
||||
|
||||
$self->{notifier} = Slic3r::GUI::Notifier->new;
|
||||
$self->{app_config} = Slic3r::GUI::AppConfig->new;
|
||||
|
@ -99,29 +100,22 @@ sub OnInit {
|
|||
|
||||
# Suppress the '- default -' presets.
|
||||
$self->{preset_bundle}->set_default_suppressed($self->{app_config}->get('no_defaults') ? 1 : 0);
|
||||
eval {
|
||||
$self->{preset_bundle}->load_presets(Slic3r::data_dir);
|
||||
};
|
||||
eval { $self->{preset_bundle}->load_presets };
|
||||
if ($@) {
|
||||
warn $@ . "\n";
|
||||
show_error(undef, $@);
|
||||
}
|
||||
eval {
|
||||
$self->{preset_bundle}->load_selections($self->{app_config});
|
||||
};
|
||||
eval { $self->{preset_bundle}->load_selections($self->{app_config}) };
|
||||
$run_wizard = 1 if $self->{preset_bundle}->has_defauls_only;
|
||||
|
||||
# application frame
|
||||
Wx::Image::AddHandler(Wx::PNGHandler->new);
|
||||
Wx::Image::FindHandlerType(wxBITMAP_TYPE_PNG) || Wx::Image::AddHandler(Wx::PNGHandler->new);
|
||||
$self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new(
|
||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||
no_controller => $self->{app_config}->get('no_controller'),
|
||||
no_plater => $no_plater,
|
||||
);
|
||||
$self->SetTopWindow($frame);
|
||||
if ($run_wizard) {
|
||||
$self->{mainframe}->config_wizard;
|
||||
}
|
||||
|
||||
EVT_IDLE($frame, sub {
|
||||
while (my $cb = shift @cb) {
|
||||
|
@ -129,6 +123,15 @@ sub OnInit {
|
|||
}
|
||||
$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;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ package Slic3r::GUI::3DScene::Base;
|
|||
use strict;
|
||||
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);
|
||||
# must load OpenGL *before* Wx::GLCanvas
|
||||
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,
|
||||
# which exports some new WX_GL_XXX constants, namely WX_GL_SAMPLE_BUFFERS and WX_GL_SAMPLES.
|
||||
my $can_multisample =
|
||||
! wxTheApp->{app_config}->get('use_legacy_opengl') &&
|
||||
Wx::wxVERSION >= 3.000003 &&
|
||||
defined Wx::GLCanvas->can('WX_GL_SAMPLE_BUFFERS') &&
|
||||
defined Wx::GLCanvas->can('WX_GL_SAMPLES');
|
||||
|
@ -956,6 +957,16 @@ sub UseVBOs {
|
|||
my ($self) = @_;
|
||||
|
||||
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
|
||||
# first when an OpenGL widget is shown for the first time. How ugly.
|
||||
return 0 if (! $self->init && $^O eq 'linux');
|
||||
|
|
|
@ -14,14 +14,14 @@ our $wizard = 'Wizard';
|
|||
$wizard = 'Assistant' if &Wx::wxMAC || &Wx::wxGTK;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent) = @_;
|
||||
my ($class, $parent, $presets, $fresh_start) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, "Configuration $wizard");
|
||||
|
||||
# initialize an empty repository
|
||||
$self->{config} = Slic3r::Config->new;
|
||||
|
||||
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Welcome->new($self));
|
||||
my $welcome_page = Slic3r::GUI::ConfigWizard::Page::Welcome->new($self, $fresh_start);
|
||||
$self->add_page($welcome_page);
|
||||
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Firmware->new($self));
|
||||
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Bed->new($self));
|
||||
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Nozzle->new($self));
|
||||
|
@ -32,12 +32,13 @@ sub new {
|
|||
|
||||
$_->build_index for @{$self->{pages}};
|
||||
|
||||
$welcome_page->set_selection_presets([@{$presets}, 'Other']);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub add_page {
|
||||
my $self = shift;
|
||||
my ($page) = @_;
|
||||
my ($self, $page) = @_;
|
||||
|
||||
my $n = push @{$self->{pages}}, $page;
|
||||
# add first page to the page area sizer
|
||||
|
@ -48,13 +49,17 @@ sub add_page {
|
|||
}
|
||||
|
||||
sub run {
|
||||
my $self = shift;
|
||||
|
||||
my ($self) = @_;
|
||||
my $result;
|
||||
if (Wx::Wizard::RunWizard($self, $self->{pages}[0])) {
|
||||
|
||||
# it would be cleaner to have these defined inside each page class,
|
||||
# in some event getting called before leaving the page
|
||||
{
|
||||
my $preset_name = $self->{pages}[0]->{preset_name};
|
||||
$result = {
|
||||
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
|
||||
my $nozzle = $self->{config}->nozzle_diameter;
|
||||
$self->{config}->set('first_layer_height', $nozzle->[0]);
|
||||
|
@ -66,14 +71,11 @@ sub run {
|
|||
# set first_layer_bed_temperature to temperature + 5
|
||||
$self->{config}->set('first_layer_bed_temperature',
|
||||
[ ($self->{config}->bed_temperature->[0] > 0) ? ($self->{config}->bed_temperature->[0] + 5) : 0 ]);
|
||||
$result->{config} = $self->{config};
|
||||
}
|
||||
|
||||
$self->Destroy;
|
||||
return $self->{config};
|
||||
} else {
|
||||
$self->Destroy;
|
||||
return undef;
|
||||
}
|
||||
$self->Destroy;
|
||||
return $result;
|
||||
}
|
||||
|
||||
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->DrawLabel($_, $bullet, Wx::Rect->new(0, $i * ($label_h + $gap), $label_w, $label_h));
|
||||
# Only show the first bullet if this is the only wizard page to be displayed.
|
||||
last if $i == 0 && $self->{just_welcome};
|
||||
$i++;
|
||||
}
|
||||
|
||||
|
@ -263,19 +267,87 @@ sub config {
|
|||
|
||||
package Slic3r::GUI::ConfigWizard::Page::Welcome;
|
||||
use base 'Slic3r::GUI::ConfigWizard::Page';
|
||||
use Wx qw(:misc :sizer wxID_FORWARD);
|
||||
use Wx::Event qw(EVT_ACTIVATE EVT_CHOICE EVT_CHECKBOX);
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent) = @_;
|
||||
my ($class, $parent, $fresh_start) = @_;
|
||||
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.');
|
||||
$self->append_text('To import an existing configuration instead, cancel this '.lc($wizard).' and use the Open Config menu item found in the File menu.');
|
||||
$self->append_text('To continue, click Next.');
|
||||
# Test for the existence of the old config path.
|
||||
my $message_has_legacy;
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
sub set_full_wizard_workflow {
|
||||
my ($self, $full_workflow) = @_;
|
||||
$self->{full_wizard_workflow} = $full_workflow;
|
||||
$self->{index}->{just_welcome} = !$full_workflow;
|
||||
$self->{index}->Refresh;
|
||||
my $next_button = $self->GetParent->FindWindow(wxID_FORWARD);
|
||||
$next_button->SetLabel($full_workflow ? "&Next >" : "&Finish");
|
||||
}
|
||||
|
||||
# Set the preset names, select the first item.
|
||||
sub set_selection_presets {
|
||||
my ($self, $names) = @_;
|
||||
$self->{choice}->Append($names);
|
||||
$self->{choice}->SetSelection(0);
|
||||
$self->{preset_name} = $names->[0];
|
||||
}
|
||||
|
||||
sub GetNext {
|
||||
my $self = shift;
|
||||
return $self->{full_wizard_workflow} ? $self->{next_page} : undef;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::ConfigWizard::Page::Firmware;
|
||||
use base 'Slic3r::GUI::ConfigWizard::Page';
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use utf8;
|
|||
|
||||
use File::Basename qw(basename dirname);
|
||||
use FindBin;
|
||||
use List::Util qw(min);
|
||||
use List::Util qw(min first);
|
||||
use Slic3r::Geometry qw(X Y);
|
||||
use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog
|
||||
:font :icon wxTheApp);
|
||||
|
@ -22,6 +22,7 @@ sub new {
|
|||
my ($class, %params) = @_;
|
||||
|
||||
my $self = $class->SUPER::new(undef, -1, $Slic3r::FORK_NAME . ' - ' . $Slic3r::VERSION, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE);
|
||||
Slic3r::GUI::set_main_frame($self);
|
||||
if ($^O eq 'MSWin32') {
|
||||
# Load the icon either from the exe, or from the ico file.
|
||||
my $iconfile = Slic3r::decode_path($FindBin::Bin) . '\slic3r.exe';
|
||||
|
@ -92,6 +93,8 @@ sub _init_tabpanel {
|
|||
my ($self) = @_;
|
||||
|
||||
$self->{tabpanel} = my $panel = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL);
|
||||
Slic3r::GUI::set_tab_panel($panel);
|
||||
|
||||
EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{tabpanel}, sub {
|
||||
my $panel = $self->{tabpanel}->GetCurrentPage;
|
||||
$panel->OnActivate if $panel->can('OnActivate');
|
||||
|
@ -129,12 +132,14 @@ sub _init_tabpanel {
|
|||
$self->{plater}->update_presets($tab_name, @_);
|
||||
if ($tab_name eq 'printer') {
|
||||
# Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors.
|
||||
wxTheApp->{preset_bundle}->print->update_tab_ui(
|
||||
$self->{options_tabs}{'print'}->{presets_choice},
|
||||
$self->{options_tabs}{'print'}->{show_incompatible_presets});
|
||||
wxTheApp->{preset_bundle}->filament->update_tab_ui(
|
||||
$self->{options_tabs}{'filament'}->{presets_choice},
|
||||
$self->{options_tabs}{'filament'}->{show_incompatible_presets});
|
||||
my ($presets, $reload_dependent_tabs) = @_;
|
||||
for my $tab_name_other (qw(print filament)) {
|
||||
# If the printer tells us that the print or filament preset has been switched or invalidated,
|
||||
# refresh the print or filament tab page. Otherwise just refresh the combo box.
|
||||
my $update_action = ($reload_dependent_tabs && (first { $_ eq $tab_name_other } (@{$reload_dependent_tabs})))
|
||||
? 'load_current_preset' : 'update_tab_ui';
|
||||
$self->{options_tabs}{$tab_name_other}->$update_action;
|
||||
}
|
||||
# Update the controller printers.
|
||||
$self->{controller}->update_presets(@_) if $self->{controller};
|
||||
}
|
||||
|
@ -145,6 +150,9 @@ sub _init_tabpanel {
|
|||
$tab->load_current_preset;
|
||||
$panel->AddPage($tab, $tab->title);
|
||||
}
|
||||
|
||||
#TODO this is an example of a Slic3r XS interface call to add a new preset editor page to the main view.
|
||||
# Slic3r::GUI::create_preset_tab("print");
|
||||
|
||||
if ($self->{plater}) {
|
||||
$self->{plater}->on_select_preset(sub {
|
||||
|
@ -165,6 +173,9 @@ sub _init_menubar {
|
|||
# File menu
|
||||
my $fileMenu = Wx::Menu->new;
|
||||
{
|
||||
wxTheApp->append_menu_item($fileMenu, "Open STL/OBJ/AMF…\tCtrl+O", 'Open a model', sub {
|
||||
$self->{plater}->add if $self->{plater};
|
||||
}, undef, undef); #'brick_add.png');
|
||||
$self->_append_menu_item($fileMenu, "&Load Config…\tCtrl+L", 'Load exported configuration file', sub {
|
||||
$self->load_config_file;
|
||||
}, undef, 'plugin_add.png');
|
||||
|
@ -274,20 +285,22 @@ sub _init_menubar {
|
|||
# \xA0 is a non-breaing space. It is entered here to spoil the automatic accelerators,
|
||||
# as the simple numeric accelerators spoil all numeric data entry.
|
||||
# The camera control accelerators are captured by 3DScene Perl module instead.
|
||||
$self->_append_menu_item($self->{viewMenu}, "Iso\t\xA00" , 'Iso View' , sub { $self->select_view('iso' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, "Top\t\xA01" , 'Top View' , sub { $self->select_view('top' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, "Bottom\t\xA02" , 'Bottom View' , sub { $self->select_view('bottom' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, "Front\t\xA03" , 'Front View' , sub { $self->select_view('front' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, "Rear\t\xA04" , 'Rear View' , sub { $self->select_view('rear' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, "Left\t\xA05" , 'Left View' , sub { $self->select_view('left' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, "Right\t\xA06" , 'Right View' , sub { $self->select_view('right' ); });
|
||||
my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] };
|
||||
$self->_append_menu_item($self->{viewMenu}, $accel->('Iso', '0'), 'Iso View' , sub { $self->select_view('iso' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, $accel->('Top', '1'), 'Top View' , sub { $self->select_view('top' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, $accel->('Bottom', '2'), 'Bottom View' , sub { $self->select_view('bottom' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, $accel->('Front', '3'), 'Front View' , sub { $self->select_view('front' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, $accel->('Rear', '4'), 'Rear View' , sub { $self->select_view('rear' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, $accel->('Left', '5'), 'Left View' , sub { $self->select_view('left' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, $accel->('Right', '6'), 'Right View' , sub { $self->select_view('right' ); });
|
||||
}
|
||||
|
||||
# Help menu
|
||||
my $helpMenu = Wx::Menu->new;
|
||||
{
|
||||
$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();
|
||||
$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($self->{viewMenu}, "&View") if $self->{viewMenu};
|
||||
$menubar->Append($helpMenu, "&Help");
|
||||
# Add an optional debug menu. In production code, the add_debug_menu() call should do nothing.
|
||||
Slic3r::GUI::add_debug_menu($menubar);
|
||||
$self->SetMenuBar($menubar);
|
||||
}
|
||||
}
|
||||
|
@ -413,6 +428,7 @@ sub quick_slice {
|
|||
if ($params{reslice}) {
|
||||
$output_file = $qs_last_output_file if defined $qs_last_output_file;
|
||||
} elsif ($params{save_as}) {
|
||||
# The following line may die if the output_filename_format template substitution fails.
|
||||
$output_file = $sprint->output_filepath;
|
||||
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/ if $params{export_svg};
|
||||
my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:',
|
||||
|
@ -565,7 +581,7 @@ sub export_configbundle {
|
|||
# to auto-install a config bundle on a fresh user account,
|
||||
# but that behavior was not documented and likely buggy.
|
||||
sub load_configbundle {
|
||||
my ($self, $file) = @_;
|
||||
my ($self, $file, $reset_user_profile) = @_;
|
||||
return unless $self->check_unsaved_changes;
|
||||
if (!$file) {
|
||||
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));
|
||||
|
||||
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;
|
||||
|
||||
# Load the currently selected preset into the GUI, update the preset selection box.
|
||||
|
@ -601,19 +617,39 @@ sub load_config {
|
|||
}
|
||||
|
||||
sub config_wizard {
|
||||
my ($self) = @_;
|
||||
my ($self, $fresh_start) = @_;
|
||||
# Exit wizard if there are unsaved changes and the user cancels the action.
|
||||
return unless $self->check_unsaved_changes;
|
||||
if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) {
|
||||
for my $tab (values %{$self->{options_tabs}}) {
|
||||
# Select the first visible preset, force.
|
||||
$tab->select_preset(undef, 1);
|
||||
# Enumerate the profiles bundled with the Slic3r installation under resources/profiles.
|
||||
my $directory = Slic3r::resources_dir() . "/profiles";
|
||||
my @profiles = ();
|
||||
if (opendir(DIR, Slic3r::encode_path($directory))) {
|
||||
while (my $file = readdir(DIR)) {
|
||||
if ($file =~ /\.ini$/) {
|
||||
$file =~ s/\.ini$//;
|
||||
push @profiles, Slic3r::decode_path($file);
|
||||
}
|
||||
}
|
||||
# Load the config over the previously selected defaults.
|
||||
$self->load_config($config);
|
||||
for my $tab (values %{$self->{options_tabs}}) {
|
||||
# Save the settings under a new name, select the name.
|
||||
$tab->save_preset('My Settings');
|
||||
closedir(DIR);
|
||||
}
|
||||
# Open the wizard.
|
||||
if (my $result = Slic3r::GUI::ConfigWizard->new($self, \@profiles, $fresh_start)->run) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ sub new {
|
|||
$self->{canvas3D}->set_on_select_object($on_select_object);
|
||||
$self->{canvas3D}->set_on_double_click($on_double_click);
|
||||
$self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); });
|
||||
$self->{canvas3D}->set_on_arrange(sub { $self->arrange });
|
||||
$self->{canvas3D}->set_on_rotate_object_left(sub { $self->rotate(-45, Z, 'relative') });
|
||||
$self->{canvas3D}->set_on_rotate_object_right(sub { $self->rotate( 45, Z, 'relative') });
|
||||
$self->{canvas3D}->set_on_scale_object_uniformly(sub { $self->changescale(undef) });
|
||||
|
@ -429,6 +430,7 @@ sub new {
|
|||
$grid_sizer->AddGrowableCol(3, 1);
|
||||
$print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
|
||||
my @info = (
|
||||
fil_m => "Used Filament (m)",
|
||||
fil_mm3 => "Used Filament (mm^3)",
|
||||
fil_g => "Used Filament (g)",
|
||||
cost => "Cost",
|
||||
|
@ -504,6 +506,7 @@ sub _on_select_preset {
|
|||
wxTheApp->{preset_bundle}->set_filament_preset($idx, $choice->GetStringSelection);
|
||||
}
|
||||
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);
|
||||
} else {
|
||||
# call GetSelection() in scalar context as it's context-aware
|
||||
|
@ -1290,9 +1293,11 @@ sub export_gcode {
|
|||
|
||||
# select output file
|
||||
if ($output_file) {
|
||||
$self->{export_gcode_output_file} = $self->{print}->output_filepath($output_file);
|
||||
$self->{export_gcode_output_file} = eval { $self->{print}->output_filepath($output_file) };
|
||||
Slic3r::GUI::catch_error($self) and return;
|
||||
} else {
|
||||
my $default_output_file = $self->{print}->output_filepath($main::opt{output} // '');
|
||||
my $default_output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') };
|
||||
Slic3r::GUI::catch_error($self) and return;
|
||||
my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:',
|
||||
wxTheApp->{app_config}->get_last_output_dir(dirname($default_output_file)),
|
||||
basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
|
@ -1423,6 +1428,7 @@ sub on_export_completed {
|
|||
$self->{"print_info_cost"}->SetLabel(sprintf("%.2f" , $self->{print}->total_cost));
|
||||
$self->{"print_info_fil_g"}->SetLabel(sprintf("%.2f" , $self->{print}->total_weight));
|
||||
$self->{"print_info_fil_mm3"}->SetLabel(sprintf("%.2f" , $self->{print}->total_extruded_volume));
|
||||
$self->{"print_info_fil_m"}->SetLabel(sprintf("%.2f" , $self->{print}->total_used_filament / 1000));
|
||||
$self->{"print_info_box_show"}->(1);
|
||||
|
||||
# this updates buttons status
|
||||
|
@ -1437,8 +1443,8 @@ sub do_print {
|
|||
my $printer_panel = $controller->add_printer($printer_preset->name, $printer_preset->config);
|
||||
|
||||
my $filament_stats = $self->{print}->filament_stats;
|
||||
my @filament_names = wxTheApp->{preset_bundle}->filament_presets;
|
||||
$filament_stats = { map { $filament_names[$_] => $filament_stats->{$_} } keys %$filament_stats };
|
||||
my $filament_names = wxTheApp->{preset_bundle}->filament_presets;
|
||||
$filament_stats = { map { $filament_names->[$_] => $filament_stats->{$_} } keys %$filament_stats };
|
||||
$printer_panel->load_print_job($self->{print_file}, $filament_stats);
|
||||
|
||||
$self->GetFrame->select_tab(1);
|
||||
|
@ -1494,6 +1500,7 @@ sub reload_from_disk {
|
|||
return if !defined $obj_idx;
|
||||
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
#FIXME convert to local file encoding
|
||||
return if !$model_object->input_file
|
||||
|| !-e $model_object->input_file;
|
||||
|
||||
|
@ -1504,12 +1511,14 @@ sub reload_from_disk {
|
|||
my $o = $self->{model}->objects->[$new_obj_idx];
|
||||
$o->clear_instances;
|
||||
$o->add_instance($_) for @{$model_object->instances};
|
||||
#$o->invalidate_bounding_box;
|
||||
|
||||
if ($o->volumes_count == $model_object->volumes_count) {
|
||||
for my $i (0..($o->volumes_count-1)) {
|
||||
$o->get_volume($i)->config->apply($model_object->get_volume($i)->config);
|
||||
}
|
||||
}
|
||||
#FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid,
|
||||
}
|
||||
|
||||
$self->remove($obj_idx);
|
||||
|
@ -1540,7 +1549,8 @@ sub export_amf {
|
|||
sub _get_export_file {
|
||||
my ($self, $format) = @_;
|
||||
my $suffix = $format eq 'STL' ? '.stl' : '.amf.xml';
|
||||
my $output_file = $self->{print}->output_filepath($main::opt{output} // '');
|
||||
my $output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') };
|
||||
Slic3r::GUI::catch_error($self) and return undef;
|
||||
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/;
|
||||
my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file),
|
||||
basename($output_file), &Slic3r::GUI::MODEL_WILDCARD, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
|
@ -1917,23 +1927,24 @@ sub object_menu {
|
|||
|
||||
my $frame = $self->GetFrame;
|
||||
my $menu = Wx::Menu->new;
|
||||
$frame->_append_menu_item($menu, "Delete\t\xA0Del", 'Remove the selected object', sub {
|
||||
my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] };
|
||||
$frame->_append_menu_item($menu, $accel->('Delete', 'Del'), 'Remove the selected object', sub {
|
||||
$self->remove;
|
||||
}, undef, 'brick_delete.png');
|
||||
$frame->_append_menu_item($menu, "Increase copies\t\xA0+", 'Place one more copy of the selected object', sub {
|
||||
$frame->_append_menu_item($menu, $accel->('Increase copies', '+'), 'Place one more copy of the selected object', sub {
|
||||
$self->increase;
|
||||
}, undef, 'add.png');
|
||||
$frame->_append_menu_item($menu, "Decrease copies\t\xA0-", 'Remove one copy of the selected object', sub {
|
||||
$frame->_append_menu_item($menu, $accel->('Decrease copies', '-'), 'Remove one copy of the selected object', sub {
|
||||
$self->decrease;
|
||||
}, undef, 'delete.png');
|
||||
$frame->_append_menu_item($menu, "Set number of copies…", 'Change the number of copies of the selected object', sub {
|
||||
$self->set_number_of_copies;
|
||||
}, undef, 'textfield.png');
|
||||
$menu->AppendSeparator();
|
||||
$frame->_append_menu_item($menu, "Rotate 45° clockwise\t\xA0l", 'Rotate the selected object by 45° clockwise', sub {
|
||||
$frame->_append_menu_item($menu, $accel->('Rotate 45° clockwise', 'l'), 'Rotate the selected object by 45° clockwise', sub {
|
||||
$self->rotate(-45, Z, 'relative');
|
||||
}, undef, 'arrow_rotate_clockwise.png');
|
||||
$frame->_append_menu_item($menu, "Rotate 45° counter-clockwise\t\xA0r", 'Rotate the selected object by 45° counter-clockwise', sub {
|
||||
$frame->_append_menu_item($menu, $accel->('Rotate 45° counter-clockwise', 'r'), 'Rotate the selected object by 45° counter-clockwise', sub {
|
||||
$self->rotate(+45, Z, 'relative');
|
||||
}, undef, 'arrow_rotate_anticlockwise.png');
|
||||
|
||||
|
@ -1966,7 +1977,7 @@ sub object_menu {
|
|||
my $scaleMenu = Wx::Menu->new;
|
||||
my $scaleMenuItem = $menu->AppendSubMenu($scaleMenu, "Scale", 'Scale the selected object along a single axis');
|
||||
$frame->_set_menu_item_icon($scaleMenuItem, 'arrow_out.png');
|
||||
$frame->_append_menu_item($scaleMenu, "Uniformly…\t\xA0s", 'Scale the selected object along the XYZ axes', sub {
|
||||
$frame->_append_menu_item($scaleMenu, $accel->('Uniformly…', 's'), 'Scale the selected object along the XYZ axes', sub {
|
||||
$self->changescale(undef);
|
||||
});
|
||||
$frame->_append_menu_item($scaleMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
|
||||
|
|
|
@ -9,7 +9,7 @@ use Wx::Event qw(EVT_KEY_DOWN EVT_CHAR);
|
|||
use base qw(Slic3r::GUI::3DScene Class::Accessor);
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(
|
||||
on_rotate_object_left on_rotate_object_right on_scale_object_uniformly
|
||||
on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly
|
||||
on_remove_object on_increase_objects on_decrease_objects));
|
||||
|
||||
sub new {
|
||||
|
@ -88,7 +88,9 @@ sub new {
|
|||
$event->Skip;
|
||||
} else {
|
||||
my $key = $event->GetKeyCode;
|
||||
if ($key == ord('l')) {
|
||||
if ($key == ord('a')) {
|
||||
$self->on_arrange->() if $self->on_arrange;
|
||||
} elsif ($key == ord('l')) {
|
||||
$self->on_rotate_object_left->() if $self->on_rotate_object_left;
|
||||
} elsif ($key == ord('r')) {
|
||||
$self->on_rotate_object_right->() if $self->on_rotate_object_right;
|
||||
|
@ -122,6 +124,11 @@ sub set_on_right_click {
|
|||
$self->on_right_click($cb);
|
||||
}
|
||||
|
||||
sub set_on_arrange {
|
||||
my ($self, $cb) = @_;
|
||||
$self->on_arrange($cb);
|
||||
}
|
||||
|
||||
sub set_on_rotate_object_left {
|
||||
my ($self, $cb) = @_;
|
||||
$self->on_rotate_object_left($cb);
|
||||
|
|
|
@ -72,6 +72,13 @@ sub new {
|
|||
'if they are marked as incompatible with the active printer',
|
||||
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);
|
||||
$sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
|
@ -90,7 +97,8 @@ sub _accept {
|
|||
my ($self) = @_;
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -147,11 +147,13 @@ sub save_preset {
|
|||
return unless $dlg->ShowModal == wxID_OK;
|
||||
$name = $dlg->get_name;
|
||||
}
|
||||
# Save the preset into Slic3r::data_dir/section_name/preset_name.ini
|
||||
# Save the preset into Slic3r::data_dir/presets/section_name/preset_name.ini
|
||||
eval { $self->{presets}->save_current_preset($name); };
|
||||
Slic3r::GUI::catch_error($self) and return;
|
||||
# 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.
|
||||
$self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets});
|
||||
$self->update_tab_ui;
|
||||
# Update the selection boxes at the platter.
|
||||
$self->_on_presets_changed;
|
||||
}
|
||||
|
@ -177,7 +179,7 @@ sub _toggle_show_hide_incompatible {
|
|||
my ($self) = @_;
|
||||
$self->{show_incompatible_presets} = ! $self->{show_incompatible_presets};
|
||||
$self->_update_show_hide_incompatible_button;
|
||||
$self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets});
|
||||
$self->update_tab_ui;
|
||||
}
|
||||
|
||||
sub _update_show_hide_incompatible_button {
|
||||
|
@ -223,8 +225,9 @@ sub _update {}
|
|||
# to update the "dirty" flags of the selection boxes,
|
||||
# to uddate number of "filament" selection boxes when the number of extruders change.
|
||||
sub _on_presets_changed {
|
||||
my ($self) = @_;
|
||||
$self->{on_presets_changed}->($self->{presets}) if $self->{on_presets_changed};
|
||||
my ($self, $reload_dependent_tabs) = @_;
|
||||
$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.
|
||||
|
@ -237,11 +240,12 @@ sub may_discard_current_dirty_preset
|
|||
my ($self, $presets, $new_printer_name) = @_;
|
||||
$presets //= $self->{presets};
|
||||
# Display a dialog showing the dirty options in a human readable form.
|
||||
my $old_preset = $presets->get_current_preset;
|
||||
my $type_name = $presets->name;
|
||||
my $name = $old_preset->default ?
|
||||
my $old_preset = $presets->get_current_preset;
|
||||
my $type_name = $presets->name;
|
||||
my $tab = ' ';
|
||||
my $name = $old_preset->default ?
|
||||
('Default ' . $type_name . ' preset') :
|
||||
($type_name . " preset \"" . $old_preset->name . "\"");
|
||||
($type_name . " preset\n$tab" . $old_preset->name);
|
||||
# Collect descriptions of the dirty options.
|
||||
my @option_names = ();
|
||||
foreach my $opt_key (@{$presets->current_dirty_options}) {
|
||||
|
@ -251,10 +255,10 @@ sub may_discard_current_dirty_preset
|
|||
push @option_names, $name;
|
||||
}
|
||||
# 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) ?
|
||||
"$name is not compatible with printer \"$new_printer_name\"\n and it has unsaved changes:" :
|
||||
"$name has unsaved changes:";
|
||||
"$name\n\nis not compatible with printer\n$tab$new_printer_name\n\nand it has the following unsaved changes:" :
|
||||
"$name\n\nhas the following unsaved changes:";
|
||||
my $confirm = Wx::MessageDialog->new($self,
|
||||
$message . "\n$changes\n\nDiscard changes and continue anyway?",
|
||||
'Unsaved Changes', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
|
||||
|
@ -267,9 +271,13 @@ sub may_discard_current_dirty_preset
|
|||
sub select_preset {
|
||||
my ($self, $name, $force) = @_;
|
||||
$force //= 0;
|
||||
my $current_dirty = $self->{presets}->current_is_dirty;
|
||||
my $canceled = 0;
|
||||
my $printer_tab = $self->{presets}->name eq 'printer';
|
||||
my $presets = $self->{presets};
|
||||
# If no name is provided, select the "-- default --" preset.
|
||||
$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) {
|
||||
$canceled = 1;
|
||||
} elsif ($printer_tab) {
|
||||
|
@ -277,50 +285,53 @@ sub select_preset {
|
|||
# are compatible with the new printer.
|
||||
# If they are not compatible and the the current print or filament are dirty, let user decide
|
||||
# whether to discard the changes or keep the current printer selection.
|
||||
my $new_printer_name = $name // '';
|
||||
my $new_printer_preset = $self->{presets}->find_preset($new_printer_name, 1);
|
||||
# my $new_nozzle_dmrs = $new_printer_preset->config->get('nozzle_diameter');
|
||||
my $print_presets = wxTheApp->{preset_bundle}->print;
|
||||
if ($print_presets->current_is_dirty &&
|
||||
! $print_presets->get_edited_preset->is_compatible_with_printer($new_printer_name)) {
|
||||
if ($self->may_discard_current_dirty_preset($print_presets, $new_printer_name)) {
|
||||
$canceled = 1;
|
||||
} else {
|
||||
$print_presets->discard_current_changes;
|
||||
}
|
||||
my $new_printer_preset = $presets->find_preset($name, 1);
|
||||
my $print_presets = wxTheApp->{preset_bundle}->print;
|
||||
my $print_preset_dirty = $print_presets->current_is_dirty;
|
||||
my $print_preset_compatible = $print_presets->get_edited_preset->is_compatible_with_printer($new_printer_preset);
|
||||
$canceled = ! $force && $print_preset_dirty && ! $print_preset_compatible &&
|
||||
! $self->may_discard_current_dirty_preset($print_presets, $name);
|
||||
my $filament_presets = wxTheApp->{preset_bundle}->filament;
|
||||
my $filament_preset_dirty = $filament_presets->current_is_dirty;
|
||||
my $filament_preset_compatible = $filament_presets->get_edited_preset->is_compatible_with_printer($new_printer_preset);
|
||||
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 ((@$new_nozzle_dmrs <= 1) &&
|
||||
if (! $canceled && $filament_presets->current_is_dirty &&
|
||||
! $filament_presets->get_edited_preset->is_compatible_with_printer($new_printer_name)) {
|
||||
if ($self->may_discard_current_dirty_preset($filament_presets, $new_printer_name)) {
|
||||
$canceled = 1;
|
||||
} else {
|
||||
$filament_presets->discard_current_changes;
|
||||
if (! $canceled) {
|
||||
if (! $print_preset_compatible) {
|
||||
# The preset will be switched to a different, compatible preset, or the '-- default --'.
|
||||
push @reload_dependent_tabs, 'print';
|
||||
$print_presets->discard_current_changes if $print_preset_dirty;
|
||||
}
|
||||
if (! $filament_preset_compatible) {
|
||||
# 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) {
|
||||
$self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets});
|
||||
# Trigger the on_presets_changed event so that we also restore the previous value in the plater selector.
|
||||
$self->update_tab_ui;
|
||||
# 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;
|
||||
} else {
|
||||
if (defined $name) {
|
||||
$self->{presets}->select_preset_by_name($name);
|
||||
} else {
|
||||
$self->{presets}->select_preset(0);
|
||||
}
|
||||
$presets->discard_current_changes if $current_dirty;
|
||||
$presets->select_preset_by_name($name);
|
||||
# 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)
|
||||
if $current_dirty || $printer_tab;
|
||||
# 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.
|
||||
sub load_current_preset {
|
||||
my ($self) = @_;
|
||||
my ($self, $dependent_tab_names) = @_;
|
||||
my $preset = $self->{presets}->get_current_preset;
|
||||
eval {
|
||||
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
|
||||
|
@ -337,8 +348,8 @@ sub load_current_preset {
|
|||
# preset dirty again
|
||||
# (not sure this is true anymore now that update_dirty is idempotent)
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets});
|
||||
$self->_on_presets_changed;
|
||||
$self->update_tab_ui;
|
||||
$self->_on_presets_changed($dependent_tab_names);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -430,11 +441,10 @@ sub _load_key_value {
|
|||
$self->{config}->set($opt_key, $value);
|
||||
# Mark the print & filament enabled if they are compatible with the currently selected preset.
|
||||
if ($opt_key eq 'compatible_printers') {
|
||||
# $opt_key eq 'compatible_printers_condition') {
|
||||
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->_update;
|
||||
}
|
||||
|
@ -485,6 +495,7 @@ sub _compatible_printers_widget {
|
|||
$btn->$method;
|
||||
# All printers have been made compatible with this preset.
|
||||
$self->_load_key_value('compatible_printers', []) if $checkbox->GetValue;
|
||||
$self->get_field('compatible_printers_condition')->toggle($checkbox->GetValue);
|
||||
});
|
||||
|
||||
EVT_BUTTON($self, $btn, sub {
|
||||
|
@ -506,6 +517,7 @@ sub _compatible_printers_widget {
|
|||
my $value = [ @presets[$dlg->GetSelections] ];
|
||||
if (!@$value) {
|
||||
$checkbox->SetValue(1);
|
||||
$self->get_field('compatible_printers_condition')->toggle(1);
|
||||
$btn->Disable;
|
||||
}
|
||||
# All printers have been made compatible with this preset.
|
||||
|
@ -523,6 +535,7 @@ sub _reload_compatible_printers_widget {
|
|||
my $method = $has_any ? 'Enable' : 'Disable';
|
||||
$self->{compatible_printers_checkbox}->SetValue(! $has_any);
|
||||
$self->{compatible_printers_btn}->$method;
|
||||
$self->get_field('compatible_printers_condition')->toggle(! $has_any);
|
||||
}
|
||||
|
||||
sub update_ui_from_settings {
|
||||
|
@ -538,10 +551,14 @@ sub update_ui_from_settings {
|
|||
} else {
|
||||
if ($self->{show_incompatible_presets}) {
|
||||
$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;
|
||||
use base 'Slic3r::GUI::Tab';
|
||||
|
@ -825,6 +842,10 @@ sub build {
|
|||
widget => $self->_compatible_printers_widget,
|
||||
);
|
||||
$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,
|
||||
);
|
||||
$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);
|
||||
}
|
||||
|
||||
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;
|
||||
use base 'Slic3r::GUI::Tab';
|
||||
use Wx qw(wxTheApp :sizer :button :bitmap :misc :id :icon :dialog);
|
||||
|
|
|
@ -65,6 +65,9 @@ sub process {
|
|||
}
|
||||
|
||||
# G-code export process, running at a background thread.
|
||||
# The export_gcode may die for various reasons (fails to process output_filename_format,
|
||||
# write error into the G-code, cannot execute post-processing scripts).
|
||||
# It is up to the caller to show an error message.
|
||||
sub export_gcode {
|
||||
my $self = shift;
|
||||
my %params = @_;
|
||||
|
@ -73,11 +76,12 @@ sub export_gcode {
|
|||
$self->process;
|
||||
|
||||
# output everything to a G-code file
|
||||
# The following call may die if the output_filename_format template substitution fails.
|
||||
my $output_file = $self->output_filepath($params{output_file} // '');
|
||||
$self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : ""));
|
||||
|
||||
die "G-code export to " . $output_file . " failed\n"
|
||||
if ! Slic3r::GCode->new->do_export($self, $output_file);
|
||||
# The following line may die for multiple reasons.
|
||||
Slic3r::GCode->new->do_export($self, $output_file);
|
||||
|
||||
# run post-processing scripts
|
||||
if (@{$self->config->post_process}) {
|
||||
|
@ -99,6 +103,7 @@ sub export_gcode {
|
|||
}
|
||||
|
||||
# Export SVG slices for the offline SLA printing.
|
||||
# The export_svg is expected to be executed inside an eval block.
|
||||
sub export_svg {
|
||||
my $self = shift;
|
||||
my %params = @_;
|
||||
|
@ -107,6 +112,7 @@ sub export_svg {
|
|||
|
||||
my $fh = $params{output_fh};
|
||||
if (!$fh) {
|
||||
# The following line may die if the output_filename_format template substitution fails.
|
||||
my $output_file = $self->output_filepath($params{output_file});
|
||||
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/;
|
||||
Slic3r::open(\$fh, ">", $output_file) or die "Failed to open $output_file for writing\n";
|
||||
|
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
0
var/add.png → resources/icons/add.png
Executable file → Normal file
Before Width: | Height: | Size: 873 B After Width: | Height: | Size: 873 B |
0
var/application_view_tile.png → resources/icons/application_view_tile.png
Executable file → Normal file
Before Width: | Height: | Size: 465 B After Width: | Height: | Size: 465 B |
Before Width: | Height: | Size: 379 B After Width: | Height: | Size: 379 B |
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
Before Width: | Height: | Size: 715 B After Width: | Height: | Size: 715 B |
0
var/arrow_refresh.png → resources/icons/arrow_refresh.png
Executable file → Normal file
Before Width: | Height: | Size: 685 B After Width: | Height: | Size: 685 B |
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 349 B |
0
var/arrow_rotate_anticlockwise.png → resources/icons/arrow_rotate_anticlockwise.png
Executable file → Normal file
Before Width: | Height: | Size: 716 B After Width: | Height: | Size: 716 B |
0
var/arrow_rotate_clockwise.png → resources/icons/arrow_rotate_clockwise.png
Executable file → Normal 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
Before Width: | Height: | Size: 503 B After Width: | Height: | Size: 503 B |
0
var/box.png → resources/icons/box.png
Executable file → Normal 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
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
Before Width: | Height: | Size: 835 B After Width: | Height: | Size: 835 B |
0
var/brick_delete.png → resources/icons/brick_delete.png
Executable file → Normal 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
Before Width: | Height: | Size: 915 B After Width: | Height: | Size: 915 B |
0
var/bricks.png → resources/icons/bricks.png
Executable file → Normal 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
Before Width: | Height: | Size: 728 B After Width: | Height: | Size: 728 B |
Before Width: | Height: | Size: 201 B After Width: | Height: | Size: 201 B |
Before Width: | Height: | Size: 201 B After Width: | Height: | Size: 201 B |
0
var/bullet_black.png → resources/icons/bullet_black.png
Executable file → Normal file
Before Width: | Height: | Size: 287 B After Width: | Height: | Size: 287 B |
0
var/bullet_blue.png → resources/icons/bullet_blue.png
Executable file → Normal file
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 289 B |
0
var/bullet_green.png → resources/icons/bullet_green.png
Executable file → Normal 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
Before Width: | Height: | Size: 287 B After Width: | Height: | Size: 287 B |
0
var/bullet_white.png → resources/icons/bullet_white.png
Executable file → Normal 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
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
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 598 B After Width: | Height: | Size: 598 B |
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 721 B |
Before Width: | Height: | Size: 592 B After Width: | Height: | Size: 592 B |
Before Width: | Height: | Size: 717 B After Width: | Height: | Size: 717 B |
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 403 B |
Before Width: | Height: | Size: 695 B After Width: | Height: | Size: 695 B |
0
var/cross.png → resources/icons/cross.png
Executable file → Normal 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
Before Width: | Height: | Size: 844 B After Width: | Height: | Size: 844 B |
0
var/disk.png → resources/icons/disk.png
Executable file → Normal 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
Before Width: | Height: | Size: 808 B After Width: | Height: | Size: 808 B |
Before Width: | Height: | Size: 672 B After Width: | Height: | Size: 672 B |
Before Width: | Height: | Size: 665 B After Width: | Height: | Size: 665 B |
Before Width: | Height: | Size: 641 B After Width: | Height: | Size: 641 B |
0
var/hourglass.png → resources/icons/hourglass.png
Executable file → Normal file
Before Width: | Height: | Size: 907 B After Width: | Height: | Size: 907 B |
Before Width: | Height: | Size: 806 B After Width: | Height: | Size: 806 B |
Before Width: | Height: | Size: 242 B After Width: | Height: | Size: 242 B |
0
var/joystick.png → resources/icons/joystick.png
Executable file → Normal 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
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
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
Before Width: | Height: | Size: 699 B After Width: | Height: | Size: 699 B |
0
var/note.png → resources/icons/note.png
Executable file → Normal 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
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
0
var/package_green.png → resources/icons/package_green.png
Executable file → Normal file
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
0
var/page_white_go.png → resources/icons/page_white_go.png
Executable file → Normal 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
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
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
Before Width: | Height: | Size: 694 B After Width: | Height: | Size: 694 B |
0
var/printer_empty.png → resources/icons/printer_empty.png
Executable file → Normal file
Before Width: | Height: | Size: 424 B After Width: | Height: | Size: 424 B |
Before Width: | Height: | Size: 665 B After Width: | Height: | Size: 665 B |
0
var/shape_flip_horizontal.png → resources/icons/shape_flip_horizontal.png
Executable file → Normal file
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 403 B |
0
var/shape_handles.png → resources/icons/shape_handles.png
Executable file → Normal file
Before Width: | Height: | Size: 538 B After Width: | Height: | Size: 538 B |
0
var/shape_ungroup.png → resources/icons/shape_ungroup.png
Executable file → Normal file
Before Width: | Height: | Size: 803 B After Width: | Height: | Size: 803 B |
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
Before Width: | Height: | Size: 741 B After Width: | Height: | Size: 741 B |
0
var/textfield.png → resources/icons/textfield.png
Executable file → Normal 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
Before Width: | Height: | Size: 959 B After Width: | Height: | Size: 959 B |
Before Width: | Height: | Size: 209 B After Width: | Height: | Size: 209 B |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 570 B After Width: | Height: | Size: 570 B |
0
var/wrench.png → resources/icons/wrench.png
Executable file → Normal 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
Before Width: | Height: | Size: 692 B After Width: | Height: | Size: 692 B |
3681
resources/profiles/Original Prusa i3 MK3, MK2S, MK2 and MK2S-MMU.ini
Normal file
11
slic3r.pl
|
@ -31,6 +31,7 @@ my %cli_options = ();
|
|||
|
||||
'debug' => \$Slic3r::debug,
|
||||
'gui' => \$opt{gui},
|
||||
'no-gui' => \$opt{no_gui},
|
||||
'o|output=s' => \$opt{output},
|
||||
|
||||
'save=s' => \$opt{save},
|
||||
|
@ -70,10 +71,10 @@ my @external_configs = ();
|
|||
if ($opt{load}) {
|
||||
foreach my $configfile (@{$opt{load}}) {
|
||||
if (-e $configfile) {
|
||||
push @external_configs, Slic3r::Config->load($configfile);
|
||||
push @external_configs, Slic3r::Config::load($configfile);
|
||||
} elsif (-e "$FindBin::Bin/$configfile") {
|
||||
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 {
|
||||
$opt{ignore_nonexistent_config} or die "Cannot find specified configuration file ($configfile).\n";
|
||||
}
|
||||
|
@ -102,7 +103,7 @@ $config->apply($cli_config);
|
|||
|
||||
# launch GUI
|
||||
my $gui;
|
||||
if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
|
||||
if ((!@ARGV || $opt{gui}) && !$opt{no_gui} && !$opt{save} && eval "require Slic3r::GUI; 1") {
|
||||
{
|
||||
no warnings 'once';
|
||||
$Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // '');
|
||||
|
@ -218,6 +219,8 @@ if (@ARGV) { # slicing from command line
|
|||
$sprint->export_svg;
|
||||
} else {
|
||||
my $t0 = [gettimeofday];
|
||||
# The following call may die if the output_filename_format template substitution fails,
|
||||
# if the file cannot be written into, or if the post processing scripts cannot be executed.
|
||||
$sprint->export_gcode;
|
||||
|
||||
# output some statistics
|
||||
|
@ -267,6 +270,8 @@ Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ...
|
|||
--gui Forces the GUI launch instead of command line slicing (if you
|
||||
supply a model file, it will be loaded into the plater)
|
||||
--no-plater Disable the plater tab
|
||||
--no-gui Forces the command line slicing instead of gui.
|
||||
This takes precedence over --gui if both are present.
|
||||
--autosave <file> Automatically export current configuration to the specified file
|
||||
|
||||
Output options:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use Test::More tests => 49;
|
||||
use Test::More tests => 71;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
|
@ -47,9 +47,13 @@ use Slic3r::Test;
|
|||
|
||||
{
|
||||
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('bar' => 2);
|
||||
$parser->set('num_extruders' => 4);
|
||||
is $parser->process('[temperature_[foo]]'),
|
||||
$config->temperature->[0],
|
||||
"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*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)';
|
||||
|
||||
# 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';
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -35,7 +35,7 @@ my %opt = ();
|
|||
# load config
|
||||
my $config = Slic3r::Config::new_from_defaults;
|
||||
if ($opt{load}) {
|
||||
$config->apply(Slic3r::Config->load($opt{load}));
|
||||
$config->apply(Slic3r::Config::load($opt{load}));
|
||||
}
|
||||
|
||||
# init print
|
||||
|
|
|
@ -440,7 +440,7 @@ if(SLIC3R_STATIC)
|
|||
# Use boost libraries linked statically to the C++ runtime.
|
||||
# set(Boost_USE_STATIC_RUNTIME ON)
|
||||
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)
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
target_link_libraries(XS ${Boost_LIBRARIES})
|
||||
|
|
|
@ -243,7 +243,7 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con
|
|||
for (const auto &opt : def->options) {
|
||||
for (const t_config_option_key &opt_key2 : opt.second.aliases) {
|
||||
if (opt_key2 == opt_key) {
|
||||
opt_key = opt_key2;
|
||||
opt_key = opt.first;
|
||||
optdef = &opt.second;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,13 @@
|
|||
#include <assert.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Only add a newline in case the current G-code does not end with a newline.
|
||||
static inline void check_add_eol(std::string &gcode)
|
||||
{
|
||||
if (! gcode.empty() && gcode.back() != '\n')
|
||||
gcode += '\n';
|
||||
}
|
||||
|
||||
// Plan a travel move while minimizing the number of perimeter crossings.
|
||||
// point is in unscaled coordinates, in the coordinate system of the current active object
|
||||
|
@ -157,6 +164,8 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
|
|||
{
|
||||
std::string gcode;
|
||||
|
||||
// Disable linear advance for the wipe tower operations.
|
||||
gcode += "M900 K0\n";
|
||||
// Move over the wipe tower.
|
||||
// Retract for a tool change, using the toolchange retract value and setting the priming extra length.
|
||||
gcode += gcodegen.retract(true);
|
||||
|
@ -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.
|
||||
if (new_extruder_id >= 0 && gcodegen.writer().need_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.
|
||||
gcodegen.writer().travel_to_xy(Pointf(tcr.end_pos.x, tcr.end_pos.y));
|
||||
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
|
||||
|
@ -199,15 +217,18 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen)
|
|||
std::string gcode;
|
||||
|
||||
if (&m_priming != nullptr && ! m_priming.extrusions.empty()) {
|
||||
// Disable linear advance for the wipe tower operations.
|
||||
gcode += "M900 K0\n";
|
||||
// Let the tool change be executed by the wipe tower class.
|
||||
// Inform the G-code writer about the changes done behind its back.
|
||||
gcode += m_priming.gcode;
|
||||
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
|
||||
gcodegen.writer().toolchange(m_priming.extrusions.back().tool);
|
||||
unsigned int current_extruder_id = m_priming.extrusions.back().tool;
|
||||
gcodegen.writer().toolchange(current_extruder_id);
|
||||
gcodegen.placeholder_parser().set("current_extruder", current_extruder_id);
|
||||
// A phony move to the end position at the wipe tower.
|
||||
gcodegen.writer().travel_to_xy(Pointf(m_priming.end_pos.x, m_priming.end_pos.y));
|
||||
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos));
|
||||
|
||||
// Prepare a future wipe.
|
||||
gcodegen.m_wipe.path.points.clear();
|
||||
// Start the wipe at the current position.
|
||||
|
@ -220,19 +241,6 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen)
|
|||
return gcode;
|
||||
}
|
||||
|
||||
std::string WipeTowerIntegration::prime_single_color_print(const Print & /* print */, unsigned int initial_tool, GCode & /* gcodegen */)
|
||||
{
|
||||
std::string gcode = "\
|
||||
G1 Z0.250 F7200.000\n\
|
||||
G1 X50.0 E80.0 F1000.0\n\
|
||||
G1 X160.0 E20.0 F1000.0\n\
|
||||
G1 Z0.200 F7200.000\n\
|
||||
G1 X220.0 E13 F1000.0\n\
|
||||
G1 X240.0 E0 F1000.0\n\
|
||||
G1 E-4 F1000.0\n";
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer)
|
||||
{
|
||||
std::string gcode;
|
||||
|
@ -354,7 +362,7 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
|
|||
return layers_to_print;
|
||||
}
|
||||
|
||||
bool GCode::do_export(Print *print, const char *path)
|
||||
void GCode::do_export(Print *print, const char *path)
|
||||
{
|
||||
// Remove the old g-code if it exists.
|
||||
boost::nowide::remove(path);
|
||||
|
@ -364,23 +372,36 @@ bool GCode::do_export(Print *print, const char *path)
|
|||
|
||||
FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb");
|
||||
if (file == nullptr)
|
||||
return false;
|
||||
throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
|
||||
|
||||
bool result = this->_do_export(*print, file);
|
||||
fclose(file);
|
||||
|
||||
if (result && boost::nowide::rename(path_tmp.c_str(), path) != 0) {
|
||||
boost::nowide::cerr << "Failed to remove the output G-code file from " << path_tmp << " to " << path
|
||||
<< ". Is " << path_tmp << " locked?" << std::endl;
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (! result)
|
||||
this->m_placeholder_parser_failed_templates.clear();
|
||||
this->_do_export(*print, file);
|
||||
fflush(file);
|
||||
if (ferror(file)) {
|
||||
fclose(file);
|
||||
boost::nowide::remove(path_tmp.c_str());
|
||||
return result;
|
||||
throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
|
||||
}
|
||||
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?
|
||||
// change_layer() in turn increments the progress bar status.
|
||||
|
@ -480,23 +501,21 @@ bool GCode::_do_export(Print &print, FILE *file)
|
|||
fprintf(file, "\n");
|
||||
}
|
||||
// Write some terse information on the slicing parameters.
|
||||
{
|
||||
const PrintObject *first_object = print.objects.front();
|
||||
const double layer_height = first_object->config.layer_height.value;
|
||||
const double first_layer_height = first_object->config.first_layer_height.get_abs_value(layer_height);
|
||||
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
|
||||
auto region = print.regions[region_id];
|
||||
fprintf(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width);
|
||||
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, "; 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);
|
||||
if (print.has_support_material())
|
||||
fprintf(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width);
|
||||
if (print.config.first_layer_extrusion_width.value > 0)
|
||||
fprintf(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width);
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
const PrintObject *first_object = print.objects.front();
|
||||
const double layer_height = first_object->config.layer_height.value;
|
||||
const double first_layer_height = first_object->config.first_layer_height.get_abs_value(layer_height);
|
||||
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
|
||||
auto region = print.regions[region_id];
|
||||
fprintf(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width);
|
||||
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, "; 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);
|
||||
if (print.has_support_material())
|
||||
fprintf(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width);
|
||||
if (print.config.first_layer_extrusion_width.value > 0)
|
||||
fprintf(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width);
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
|
||||
// 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 final_extruder_id = (unsigned int)-1;
|
||||
size_t initial_print_object_id = 0;
|
||||
bool has_wipe_tower = false;
|
||||
if (print.config.complete_objects.value) {
|
||||
// Find the 1st printing object, find its tool ordering and the initial extruder ID.
|
||||
for (; initial_print_object_id < print.objects.size(); ++initial_print_object_id) {
|
||||
|
@ -523,6 +543,7 @@ bool GCode::_do_export(Print &print, FILE *file)
|
|||
ToolOrdering(print, initial_extruder_id) :
|
||||
print.m_tool_ordering;
|
||||
initial_extruder_id = tool_ordering.first_extruder();
|
||||
has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
|
||||
}
|
||||
if (initial_extruder_id == (unsigned int)-1) {
|
||||
// Nothing to print!
|
||||
|
@ -545,7 +566,9 @@ bool GCode::_do_export(Print &print, FILE *file)
|
|||
m_placeholder_parser.set("current_extruder", initial_extruder_id);
|
||||
// Useful for sequential prints.
|
||||
m_placeholder_parser.set("current_object_idx", 0);
|
||||
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.
|
||||
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
|
||||
writeln(file, start_gcode);
|
||||
// Process filament-specific gcode in extruder order.
|
||||
for (const std::string &start_gcode : print.config.start_filament_gcode.values)
|
||||
writeln(file, m_placeholder_parser.process(start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front())));
|
||||
if (print.config.single_extruder_multi_material) {
|
||||
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);
|
||||
|
||||
// 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
|
||||
// is triggered, so machine has more time to reach such temperatures.
|
||||
m_placeholder_parser.set("current_object_idx", int(finished_objects));
|
||||
std::string between_objects_gcode = m_placeholder_parser.process(print.config.between_objects_gcode.value, initial_extruder_id);
|
||||
std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config.between_objects_gcode.value, initial_extruder_id);
|
||||
// Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
|
||||
this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
|
||||
this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
|
||||
|
@ -682,32 +714,30 @@ bool GCode::_do_export(Print &print, FILE *file)
|
|||
// 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);
|
||||
// Prusa Multi-Material wipe tower.
|
||||
if (print.has_wipe_tower() && ! layers_to_print.empty()) {
|
||||
if (tool_ordering.has_wipe_tower()) {
|
||||
m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get()));
|
||||
write(file, m_wipe_tower->prime(*this));
|
||||
// Verify, whether the print overaps the priming extrusions.
|
||||
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
|
||||
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
|
||||
for (const PrintObject *print_object : print.objects)
|
||||
bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
|
||||
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
|
||||
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
|
||||
bbox_prime.offset(0.5f);
|
||||
// Beep for 500ms, tone 800Hz. Yet better, play some Morse.
|
||||
write(file, this->retract());
|
||||
fprintf(file, "M300 S800 P500\n");
|
||||
if (bbox_prime.overlap(bbox_print)) {
|
||||
// Wait for the user to remove the priming extrusions, otherwise they would
|
||||
// get covered by the print.
|
||||
fprintf(file, "M1 Remove priming towers and click button.\n");
|
||||
} else {
|
||||
// Just wait for a bit to let the user check, that the priming succeeded.
|
||||
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
|
||||
fprintf(file, "M1 S10\n");
|
||||
}
|
||||
} else
|
||||
write(file, WipeTowerIntegration::prime_single_color_print(print, initial_extruder_id, *this));
|
||||
if (has_wipe_tower && ! layers_to_print.empty()) {
|
||||
m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get()));
|
||||
write(file, m_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));
|
||||
// Verify, whether the print overaps the priming extrusions.
|
||||
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
|
||||
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
|
||||
for (const PrintObject *print_object : print.objects)
|
||||
bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
|
||||
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
|
||||
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
|
||||
bbox_prime.offset(0.5f);
|
||||
// Beep for 500ms, tone 800Hz. Yet better, play some Morse.
|
||||
write(file, this->retract());
|
||||
fprintf(file, "M300 S800 P500\n");
|
||||
if (bbox_prime.overlap(bbox_print)) {
|
||||
// Wait for the user to remove the priming extrusions, otherwise they would
|
||||
// get covered by the print.
|
||||
fprintf(file, "M1 Remove priming towers and click button.\n");
|
||||
} else {
|
||||
// Just wait for a bit to let the user check, that the priming succeeded.
|
||||
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
|
||||
fprintf(file, "M1 S10\n");
|
||||
}
|
||||
}
|
||||
// Extrude the layers.
|
||||
for (auto &layer : layers_to_print) {
|
||||
|
@ -727,9 +757,14 @@ bool GCode::_do_export(Print &print, FILE *file)
|
|||
write(file, this->retract());
|
||||
write(file, m_writer.set_fan(false));
|
||||
// Process filament-specific gcode in extruder order.
|
||||
for (const std::string &end_gcode : print.config.end_filament_gcode.values)
|
||||
writeln(file, m_placeholder_parser.process(end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front())));
|
||||
writeln(file, m_placeholder_parser.process(print.config.end_gcode, m_writer.extruder()->id()));
|
||||
if (print.config.single_extruder_multi_material) {
|
||||
// Process the end_filament_gcode for the active filament only.
|
||||
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.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) {
|
||||
StaticPrintConfig *cfg = configs[i];
|
||||
for (const std::string &key : cfg->keys())
|
||||
fprintf(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str());
|
||||
if (key != "compatible_printers")
|
||||
fprintf(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -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?
|
||||
int temp_by_gcode = -1;
|
||||
bool temp_set_by_gcode = custom_gcode_sets_temperature(gcode, 140, 190, temp_by_gcode);
|
||||
if (temp_by_gcode >= 0 && temp_by_gcode < 1000)
|
||||
if (temp_set_by_gcode && temp_by_gcode >= 0 && temp_by_gcode < 1000)
|
||||
temp = temp_by_gcode;
|
||||
// Always call m_writer.set_bed_temperature() so it will set the internal "current" state of the bed temp as if
|
||||
// the custom start G-code emited these.
|
||||
std::string set_temp_gcode = m_writer.set_bed_temperature(temp, wait);
|
||||
if (! temp_by_gcode)
|
||||
if (! temp_set_by_gcode)
|
||||
write(file, set_temp_gcode);
|
||||
}
|
||||
|
||||
|
@ -973,7 +1022,7 @@ void GCode::process_layer(
|
|||
DynamicConfig config;
|
||||
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
|
||||
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
|
||||
gcode += m_placeholder_parser.process(
|
||||
gcode += this->placeholder_parser_process("before_layer_gcode",
|
||||
print.config.before_layer_gcode.value, m_writer.extruder()->id(), &config)
|
||||
+ "\n";
|
||||
}
|
||||
|
@ -983,7 +1032,7 @@ void GCode::process_layer(
|
|||
DynamicConfig config;
|
||||
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
|
||||
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
|
||||
gcode += m_placeholder_parser.process(
|
||||
gcode += this->placeholder_parser_process("layer_gcode",
|
||||
print.config.layer_gcode.value, m_writer.extruder()->id(), &config)
|
||||
+ "\n";
|
||||
}
|
||||
|
@ -2162,13 +2211,14 @@ GCode::retract(bool toolchange)
|
|||
|
||||
std::string GCode::set_extruder(unsigned int extruder_id)
|
||||
{
|
||||
m_placeholder_parser.set("current_extruder", extruder_id);
|
||||
if (!m_writer.need_toolchange(extruder_id))
|
||||
return "";
|
||||
|
||||
// if we are running a single-extruder setup, just set the extruder and return nothing
|
||||
if (!m_writer.multiple_extruders)
|
||||
if (!m_writer.multiple_extruders) {
|
||||
m_placeholder_parser.set("current_extruder", extruder_id);
|
||||
return m_writer.toolchange(extruder_id);
|
||||
}
|
||||
|
||||
// prepend retraction on the current extruder
|
||||
std::string gcode = this->retract(true);
|
||||
|
@ -2176,23 +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.
|
||||
m_wipe.reset_path();
|
||||
|
||||
// append custom toolchange G-code
|
||||
if (m_writer.extruder() != nullptr && !m_config.toolchange_gcode.value.empty()) {
|
||||
if (m_writer.extruder() != nullptr) {
|
||||
// Process the custom end_filament_gcode in case of single_extruder_multi_material.
|
||||
unsigned int old_extruder_id = m_writer.extruder()->id();
|
||||
const std::string &end_filament_gcode = m_config.end_filament_gcode.get_at(old_extruder_id);
|
||||
if (m_config.single_extruder_multi_material && ! end_filament_gcode.empty()) {
|
||||
gcode += 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;
|
||||
config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id()));
|
||||
config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id));
|
||||
gcode += m_placeholder_parser.process(
|
||||
m_config.toolchange_gcode.value, extruder_id, &config)
|
||||
+ '\n';
|
||||
gcode += placeholder_parser_process("toolchange_gcode", m_config.toolchange_gcode.value, extruder_id, &config);
|
||||
check_add_eol(gcode);
|
||||
}
|
||||
|
||||
// if ooze prevention is enabled, park current extruder in the nearest
|
||||
// standby point and set it to the standby temperature
|
||||
// If ooze prevention is enabled, park current extruder in the nearest
|
||||
// standby point and set it to the standby temperature.
|
||||
if (m_ooze_prevention.enable && m_writer.extruder() != nullptr)
|
||||
gcode += m_ooze_prevention.pre_toolchange(*this);
|
||||
// append the toolchange command
|
||||
// Append the toolchange command.
|
||||
gcode += m_writer.toolchange(extruder_id);
|
||||
// set the new extruder to the operating temperature
|
||||
// Append the filament start G-code for single_extruder_multi_material.
|
||||
const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id);
|
||||
if (m_config.single_extruder_multi_material && ! start_filament_gcode.empty()) {
|
||||
// Process the start_filament_gcode for the active filament only.
|
||||
gcode += 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)
|
||||
gcode += m_ooze_prevention.post_toolchange(*this);
|
||||
|
||||
|
|
|
@ -90,7 +90,6 @@ public:
|
|||
m_brim_done(false) {}
|
||||
|
||||
std::string prime(GCode &gcodegen);
|
||||
static std::string prime_single_color_print(const Print & /* print */, unsigned int initial_tool, GCode & /* gcodegen */);
|
||||
void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; }
|
||||
std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer);
|
||||
std::string finalize(GCode &gcodegen);
|
||||
|
@ -131,7 +130,8 @@ public:
|
|||
{}
|
||||
~GCode() {}
|
||||
|
||||
bool do_export(Print *print, const char *path);
|
||||
// throws std::runtime_exception
|
||||
void do_export(Print *print, const char *path);
|
||||
|
||||
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
|
||||
const Pointf& origin() const { return m_origin; }
|
||||
|
@ -143,6 +143,10 @@ public:
|
|||
const FullPrintConfig &config() const { return m_config; }
|
||||
const Layer* layer() const { return m_layer; }
|
||||
GCodeWriter& writer() { return m_writer; }
|
||||
PlaceholderParser& placeholder_parser() { return m_placeholder_parser; }
|
||||
// Process a template through the placeholder parser, collect error messages to be reported
|
||||
// inside the generated string and after the G-code export finishes.
|
||||
std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr);
|
||||
bool enable_cooling_markers() const { return m_enable_cooling_markers; }
|
||||
|
||||
// For Perl bindings, to be used exclusively by unit tests.
|
||||
|
@ -151,7 +155,7 @@ public:
|
|||
void apply_print_config(const PrintConfig &print_config);
|
||||
|
||||
protected:
|
||||
bool _do_export(Print &print, FILE *file);
|
||||
void _do_export(Print &print, FILE *file);
|
||||
|
||||
// Object and support extrusions of the same PrintObject at the same print_z.
|
||||
struct LayerToPrint
|
||||
|
@ -223,6 +227,8 @@ protected:
|
|||
FullPrintConfig m_config;
|
||||
GCodeWriter m_writer;
|
||||
PlaceholderParser m_placeholder_parser;
|
||||
// Collection of templates, on which the placeholder substitution failed.
|
||||
std::set<std::string> m_placeholder_parser_failed_templates;
|
||||
OozePrevention m_ooze_prevention;
|
||||
Wipe m_wipe;
|
||||
AvoidCrossingPerimeters m_avoid_crossing_perimeters;
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
#include "Print.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>
|
||||
|
||||
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.
|
||||
LayerTools lt_new(0.5f * (lt.print_z + lt_object.print_z));
|
||||
// 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);
|
||||
LayerTools <_extra = (m_layer_tools[j].print_z == lt_new.print_z) ?
|
||||
m_layer_tools[j] :
|
||||
*m_layer_tools.insert(m_layer_tools.begin() + j, lt_new);
|
||||
lt_extra.has_wipe_tower = true;
|
||||
lt_extra.wipe_tower_partitions = lt_object.wipe_tower_partitions;
|
||||
for (j = i + 1; j < m_layer_tools.size() && m_layer_tools[j].print_z < lt_new.print_z - EPSILON; ++ j);
|
||||
if (std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) {
|
||||
m_layer_tools[j].has_wipe_tower = true;
|
||||
} else {
|
||||
LayerTools <_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new);
|
||||
LayerTools <_prev = m_layer_tools[j - 1];
|
||||
LayerTools <_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;
|
||||
|
|
|
@ -53,14 +53,14 @@ public:
|
|||
|
||||
void clear() { m_layer_tools.clear(); }
|
||||
|
||||
// Get the first extruder printing the layer_tools, returns -1 if there is no layer printed.
|
||||
// Get the first extruder printing, including the extruder priming areas, returns -1 if there is no layer printed.
|
||||
unsigned int first_extruder() const { return m_first_printing_extruder; }
|
||||
|
||||
// Get the first extruder printing the layer_tools, returns -1 if there is no layer printed.
|
||||
unsigned int last_extruder() const { return m_last_printing_extruder; }
|
||||
|
||||
// For a multi-material print, the printing extruders are ordered in the order they shall be primed.
|
||||
std::vector<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.
|
||||
LayerTools& tools_for_layer(coordf_t print_z);
|
||||
|
@ -69,6 +69,8 @@ public:
|
|||
|
||||
const LayerTools& front() const { return m_layer_tools.front(); }
|
||||
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(); }
|
||||
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; }
|
||||
|
|
|
@ -122,7 +122,7 @@ public:
|
|||
// print_z of the first layer.
|
||||
float first_layer_height,
|
||||
// Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object.
|
||||
std::vector<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 false, the last priming are will be large enough to wipe the last extruder sufficiently.
|
||||
bool last_wipe_inside_wipe_tower,
|
||||
|
|
|
@ -389,7 +389,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
|||
// print_z of the first layer.
|
||||
float first_layer_height,
|
||||
// Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object.
|
||||
std::vector<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 false, the last priming are will be large enough to wipe the last extruder sufficiently.
|
||||
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.ru).extrude(box.lu);
|
||||
// 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)
|
||||
.travel(box.lu);
|
||||
} else
|
||||
|
@ -723,8 +724,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
|
|||
// Move to the front left corner.
|
||||
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.
|
||||
// Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
|
||||
writer.travel(wipeTower_box.rd)
|
||||
.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));
|
||||
}
|
||||
|
||||
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.
|
||||
// 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)
|
||||
.travel(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width / 2));
|
||||
else
|
||||
|
|
|
@ -151,7 +151,7 @@ public:
|
|||
// print_z of the first layer.
|
||||
float first_layer_height,
|
||||
// Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object.
|
||||
std::vector<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 false, the last priming are will be large enough to wipe the last extruder sufficiently.
|
||||
bool last_wipe_inside_wipe_tower,
|
||||
|
|