mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-08-09 06:45:25 -06:00
Work in progress: Good bye, Perl Threads!
This commit is contained in:
parent
86b79f89ad
commit
e931f75010
31 changed files with 833 additions and 1069 deletions
137
lib/Slic3r.pm
137
lib/Slic3r.pm
|
@ -1,5 +1,4 @@
|
|||
# This package loads all the non-GUI Slic3r perl packages.
|
||||
# In addition, it implements utility functions for file handling and threading.
|
||||
|
||||
package Slic3r;
|
||||
|
||||
|
@ -22,22 +21,11 @@ sub debugf {
|
|||
|
||||
our $loglevel = 0;
|
||||
|
||||
# load threads before Moo as required by it
|
||||
BEGIN {
|
||||
# Test, whether the perl was compiled with ithreads support and ithreads actually work.
|
||||
use Config;
|
||||
use Moo;
|
||||
my $have_threads = $Config{useithreads} && eval "use threads; use threads::shared; use Thread::Queue; 1";
|
||||
die "Slic3r Prusa Edition requires working Perl threads.\n" if ! $have_threads;
|
||||
die "threads.pm >= 1.96 is required, please update\n" if $threads::VERSION < 1.96;
|
||||
die "Perl threading is broken with this Moo version: " . $Moo::VERSION . "\n" if $Moo::VERSION == 1.003000;
|
||||
$debug = 1 if (defined($ENV{'SLIC3R_DEBUGOUT'}) && $ENV{'SLIC3R_DEBUGOUT'} == 1);
|
||||
print "Debugging output enabled\n" if $debug;
|
||||
}
|
||||
|
||||
warn "Running Slic3r under Perl 5.16 is neither supported nor recommended\n"
|
||||
if $^V == v5.16;
|
||||
|
||||
use FindBin;
|
||||
|
||||
# Let the XS module know where the GUI resources reside.
|
||||
|
@ -66,17 +54,11 @@ use Slic3r::Print::Object;
|
|||
use Slic3r::Print::Simple;
|
||||
use Slic3r::Surface;
|
||||
our $build = eval "use Slic3r::Build; 1";
|
||||
use Thread::Semaphore;
|
||||
|
||||
# Scaling between the float and integer coordinates.
|
||||
# Floats are in mm.
|
||||
use constant SCALING_FACTOR => 0.000001;
|
||||
|
||||
# Keep track of threads we created. Perl worker threads shall not create further threads.
|
||||
my @threads = ();
|
||||
my $pause_sema = Thread::Semaphore->new;
|
||||
my $paused = 0;
|
||||
|
||||
# Set the logging level at the Slic3r XS module.
|
||||
$Slic3r::loglevel = (defined($ENV{'SLIC3R_LOGLEVEL'}) && $ENV{'SLIC3R_LOGLEVEL'} =~ /^[1-9]/) ? $ENV{'SLIC3R_LOGLEVEL'} : 0;
|
||||
set_logging_level($Slic3r::loglevel);
|
||||
|
@ -85,121 +67,6 @@ set_logging_level($Slic3r::loglevel);
|
|||
# class instance in a thread safe manner.
|
||||
Slic3r::GCode::PlaceholderParser->new->evaluate_boolean_expression('1==1');
|
||||
|
||||
sub spawn_thread {
|
||||
my ($cb) = @_;
|
||||
@_ = ();
|
||||
my $thread = threads->create(sub {
|
||||
Slic3r::debugf "Starting thread %d...\n", threads->tid;
|
||||
local $SIG{'KILL'} = sub {
|
||||
Slic3r::debugf "Exiting thread %d...\n", threads->tid;
|
||||
Slic3r::thread_cleanup();
|
||||
threads->exit();
|
||||
};
|
||||
local $SIG{'STOP'} = sub {
|
||||
$pause_sema->down;
|
||||
$pause_sema->up;
|
||||
};
|
||||
$cb->();
|
||||
});
|
||||
push @threads, $thread->tid;
|
||||
return $thread;
|
||||
}
|
||||
|
||||
# call this at the very end of each thread (except the main one)
|
||||
# so that it does not try to free existing objects.
|
||||
# at that stage, existing objects are only those that we
|
||||
# inherited at the thread creation (thus shared) and those
|
||||
# that we are returning: destruction will be handled by the
|
||||
# main thread in both cases.
|
||||
# reminder: do not destroy inherited objects in other threads,
|
||||
# as the main thread will still try to destroy them when they
|
||||
# go out of scope; in other words, if you're undef()'ing an
|
||||
# object in a thread, make sure the main thread still holds a
|
||||
# reference so that it won't be destroyed in thread.
|
||||
sub thread_cleanup {
|
||||
# prevent destruction of shared objects
|
||||
no warnings 'redefine';
|
||||
*Slic3r::BridgeDetector::DESTROY = sub {};
|
||||
*Slic3r::Config::DESTROY = sub {};
|
||||
*Slic3r::Config::Full::DESTROY = sub {};
|
||||
*Slic3r::Config::GCode::DESTROY = sub {};
|
||||
*Slic3r::Config::Print::DESTROY = sub {};
|
||||
*Slic3r::Config::PrintObject::DESTROY = sub {};
|
||||
*Slic3r::Config::PrintRegion::DESTROY = sub {};
|
||||
*Slic3r::Config::Static::DESTROY = sub {};
|
||||
*Slic3r::ExPolygon::DESTROY = sub {};
|
||||
*Slic3r::ExPolygon::Collection::DESTROY = sub {};
|
||||
*Slic3r::ExtrusionLoop::DESTROY = sub {};
|
||||
*Slic3r::ExtrusionMultiPath::DESTROY = sub {};
|
||||
*Slic3r::ExtrusionPath::DESTROY = sub {};
|
||||
*Slic3r::ExtrusionPath::Collection::DESTROY = sub {};
|
||||
*Slic3r::ExtrusionSimulator::DESTROY = sub {};
|
||||
*Slic3r::Flow::DESTROY = sub {};
|
||||
*Slic3r::GCode::DESTROY = sub {};
|
||||
*Slic3r::GCode::PlaceholderParser::DESTROY = sub {};
|
||||
*Slic3r::GCode::PreviewData::DESTROY = sub {};
|
||||
*Slic3r::GCode::Sender::DESTROY = sub {};
|
||||
*Slic3r::Geometry::BoundingBox::DESTROY = sub {};
|
||||
*Slic3r::Geometry::BoundingBoxf::DESTROY = sub {};
|
||||
*Slic3r::Geometry::BoundingBoxf3::DESTROY = sub {};
|
||||
*Slic3r::Layer::PerimeterGenerator::DESTROY = sub {};
|
||||
*Slic3r::Line::DESTROY = sub {};
|
||||
*Slic3r::Linef3::DESTROY = sub {};
|
||||
*Slic3r::Model::DESTROY = sub {};
|
||||
*Slic3r::Model::Object::DESTROY = sub {};
|
||||
*Slic3r::Point::DESTROY = sub {};
|
||||
*Slic3r::Pointf::DESTROY = sub {};
|
||||
*Slic3r::Pointf3::DESTROY = sub {};
|
||||
*Slic3r::Polygon::DESTROY = sub {};
|
||||
*Slic3r::Polyline::DESTROY = sub {};
|
||||
*Slic3r::Polyline::Collection::DESTROY = sub {};
|
||||
*Slic3r::Print::DESTROY = sub {};
|
||||
*Slic3r::Print::Object::DESTROY = sub {};
|
||||
*Slic3r::Print::Region::DESTROY = sub {};
|
||||
*Slic3r::Surface::DESTROY = sub {};
|
||||
*Slic3r::Surface::Collection::DESTROY = sub {};
|
||||
*Slic3r::Print::SupportMaterial2::DESTROY = sub {};
|
||||
*Slic3r::TriangleMesh::DESTROY = sub {};
|
||||
*Slic3r::GUI::AppConfig::DESTROY = sub {};
|
||||
*Slic3r::GUI::PresetBundle::DESTROY = sub {};
|
||||
return undef; # this prevents a "Scalars leaked" warning
|
||||
}
|
||||
|
||||
sub _get_running_threads {
|
||||
return grep defined($_), map threads->object($_), @threads;
|
||||
}
|
||||
|
||||
sub kill_all_threads {
|
||||
# Send SIGKILL to all the running threads to let them die.
|
||||
foreach my $thread (_get_running_threads) {
|
||||
Slic3r::debugf "Thread %d killing %d...\n", threads->tid, $thread->tid;
|
||||
$thread->kill('KILL');
|
||||
}
|
||||
# unlock semaphore before we block on wait
|
||||
# otherwise we'd get a deadlock if threads were paused
|
||||
resume_all_threads();
|
||||
# in any thread we wait for our children
|
||||
foreach my $thread (_get_running_threads) {
|
||||
Slic3r::debugf " Thread %d waiting for %d...\n", threads->tid, $thread->tid;
|
||||
$thread->join; # block until threads are killed
|
||||
Slic3r::debugf " Thread %d finished waiting for %d...\n", threads->tid, $thread->tid;
|
||||
}
|
||||
@threads = ();
|
||||
}
|
||||
|
||||
sub pause_all_threads {
|
||||
return if $paused;
|
||||
$paused = 1;
|
||||
$pause_sema->down;
|
||||
$_->kill('STOP') for _get_running_threads;
|
||||
}
|
||||
|
||||
sub resume_all_threads {
|
||||
return unless $paused;
|
||||
$paused = 0;
|
||||
$pause_sema->up;
|
||||
}
|
||||
|
||||
# Open a file by converting $filename to local file system locales.
|
||||
sub open {
|
||||
my ($fh, $mode, $filename) = @_;
|
||||
|
@ -274,8 +141,4 @@ sub system_info
|
|||
return $out;
|
||||
}
|
||||
|
||||
# this package declaration prevents an ugly fatal warning to be emitted when
|
||||
# spawning a new thread
|
||||
package GLUquadricObjPtr;
|
||||
|
||||
1;
|
||||
|
|
|
@ -13,12 +13,6 @@ sub wkt {
|
|||
join ',', map "($_)", map { join ',', map "$_->[0] $_->[1]", @$_ } @$self;
|
||||
}
|
||||
|
||||
sub dump_perl {
|
||||
my $self = shift;
|
||||
return sprintf "[%s]",
|
||||
join ',', map "[$_]", map { join ',', map "[$_->[0],$_->[1]]", @$_ } @$self;
|
||||
}
|
||||
|
||||
sub offset {
|
||||
my $self = shift;
|
||||
return Slic3r::Geometry::Clipper::offset(\@$self, @_);
|
||||
|
|
|
@ -101,12 +101,6 @@ use constant GIMBALL_LOCK_THETA_MAX => 170;
|
|||
use constant VARIABLE_LAYER_THICKNESS_BAR_WIDTH => 70;
|
||||
use constant VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT => 22;
|
||||
|
||||
# make OpenGL::Array thread-safe
|
||||
{
|
||||
no warnings 'redefine';
|
||||
*OpenGL::Array::CLONE_SKIP = sub { 1 };
|
||||
}
|
||||
|
||||
sub new {
|
||||
my ($class, $parent) = @_;
|
||||
|
||||
|
|
|
@ -25,6 +25,10 @@ our $last_config;
|
|||
our $VALUE_CHANGE_EVENT = Wx::NewEventType;
|
||||
# 2) To inform about a preset selection change or a "modified" status change.
|
||||
our $PRESETS_CHANGED_EVENT = Wx::NewEventType;
|
||||
# 3) To update the status bar with the progress information.
|
||||
our $PROGRESS_BAR_EVENT = Wx::NewEventType;
|
||||
# 4) To display an error dialog box from a thread on the UI thread.
|
||||
our $ERROR_EVENT = Wx::NewEventType;
|
||||
|
||||
sub new {
|
||||
my ($class, %params) = @_;
|
||||
|
@ -48,6 +52,11 @@ sub new {
|
|||
$self->{lang_ch_event} = $params{lang_ch_event};
|
||||
$self->{preferences_event} = $params{preferences_event};
|
||||
|
||||
# initialize status bar
|
||||
$self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, -1);
|
||||
$self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"));
|
||||
$self->SetStatusBar($self->{statusbar});
|
||||
|
||||
# initialize tabpanel and menubar
|
||||
$self->_init_tabpanel;
|
||||
$self->_init_menubar;
|
||||
|
@ -56,12 +65,7 @@ sub new {
|
|||
# SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values
|
||||
# (SetAutoPop is not available on GTK.)
|
||||
eval { Wx::ToolTip::SetAutoPop(32767) };
|
||||
|
||||
# initialize status bar
|
||||
$self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, -1);
|
||||
$self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"));
|
||||
$self->SetStatusBar($self->{statusbar});
|
||||
|
||||
|
||||
$self->{loaded} = 1;
|
||||
|
||||
# initialize layout
|
||||
|
@ -170,6 +174,24 @@ sub _init_tabpanel {
|
|||
for my $tab_name (qw(print filament printer)) {
|
||||
$self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name");
|
||||
}
|
||||
|
||||
# Update progress bar with an event sent by the slicing core.
|
||||
EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
if (defined $self->{progress_dialog}) {
|
||||
# If a progress dialog is open, update it.
|
||||
$self->{progress_dialog}->Update($event->GetInt, $event->GetString . "…");
|
||||
} else {
|
||||
# Otherwise update the main window status bar.
|
||||
$self->{statusbar}->SetProgress($event->GetInt);
|
||||
$self->{statusbar}->SetStatusText($event->GetString . "…");
|
||||
}
|
||||
});
|
||||
|
||||
EVT_COMMAND($self, -1, $ERROR_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
Slic3r::GUI::show_error($self, $event->GetString);
|
||||
});
|
||||
|
||||
if ($self->{plater}) {
|
||||
$self->{plater}->on_select_preset(sub {
|
||||
|
@ -388,7 +410,6 @@ sub on_plater_selection_changed {
|
|||
sub quick_slice {
|
||||
my ($self, %params) = @_;
|
||||
|
||||
my $progress_dialog;
|
||||
eval {
|
||||
# validate configuration
|
||||
my $config = wxTheApp->{preset_bundle}->full_config();
|
||||
|
@ -429,13 +450,9 @@ sub quick_slice {
|
|||
$print_center = Slic3r::Pointf->new_unscale(@{$bed_shape->bounding_box->center});
|
||||
}
|
||||
|
||||
my $sprint = Slic3r::Print::Simple->new(
|
||||
print_center => $print_center,
|
||||
status_cb => sub {
|
||||
my ($percent, $message) = @_;
|
||||
$progress_dialog->Update($percent, "$message…");
|
||||
},
|
||||
);
|
||||
my $sprint = Slic3r::Print::Simple->new(print_center => $print_center);
|
||||
# The C++ slicing core will post a wxCommand message to the main window.
|
||||
Slic3r::GUI::set_print_callback_event($sprint, $PROGRESS_BAR_EVENT);
|
||||
|
||||
# keep model around
|
||||
my $model = Slic3r::Model->read_from_file($input_file);
|
||||
|
@ -468,9 +485,9 @@ sub quick_slice {
|
|||
}
|
||||
|
||||
# show processbar dialog
|
||||
$progress_dialog = Wx::ProgressDialog->new(L('Slicing…'), L("Processing ").$input_file_basename."…",
|
||||
$self->{progress_dialog} = Wx::ProgressDialog->new(L('Slicing…'), L("Processing ").$input_file_basename."…",
|
||||
100, $self, 0);
|
||||
$progress_dialog->Pulse;
|
||||
$self->{progress_dialog}->Pulse;
|
||||
|
||||
{
|
||||
my @warnings = ();
|
||||
|
@ -482,18 +499,17 @@ sub quick_slice {
|
|||
} else {
|
||||
$sprint->export_gcode;
|
||||
}
|
||||
$sprint->status_cb(undef);
|
||||
Slic3r::GUI::warning_catcher($self)->($_) for @warnings;
|
||||
}
|
||||
$progress_dialog->Destroy;
|
||||
undef $progress_dialog;
|
||||
$self->{progress_dialog}->Destroy;
|
||||
undef $self->{progress_dialog};
|
||||
|
||||
my $message = $input_file_basename.L(" was successfully sliced.");
|
||||
wxTheApp->notify($message);
|
||||
Wx::MessageDialog->new($self, $message, L('Slicing Done!'),
|
||||
wxOK | wxICON_INFORMATION)->ShowModal;
|
||||
};
|
||||
Slic3r::GUI::catch_error($self, sub { $progress_dialog->Destroy if $progress_dialog });
|
||||
Slic3r::GUI::catch_error($self, sub { $self->{progress_dialog}->Destroy if $self->{progress_dialog} });
|
||||
}
|
||||
|
||||
sub reslice_now {
|
||||
|
|
|
@ -8,7 +8,6 @@ use utf8;
|
|||
use File::Basename qw(basename dirname);
|
||||
use List::Util qw(sum first max);
|
||||
use Slic3r::Geometry qw(X Y Z scale unscale deg2rad rad2deg);
|
||||
use threads::shared qw(shared_clone);
|
||||
use Wx qw(:button :colour :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc
|
||||
:panel :sizer :toolbar :window wxTheApp :notebook :combobox wxNullBitmap);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_TOGGLEBUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
|
||||
|
@ -34,21 +33,16 @@ use constant TB_LAYER_EDITING => &Wx::NewId;
|
|||
|
||||
use Wx::Locale gettext => 'L';
|
||||
|
||||
# package variables to avoid passing lexicals to threads
|
||||
our $PROGRESS_BAR_EVENT : shared = Wx::NewEventType;
|
||||
our $ERROR_EVENT : shared = Wx::NewEventType;
|
||||
# Emitted from the worker thread when the G-code export is finished.
|
||||
our $EXPORT_COMPLETED_EVENT : shared = Wx::NewEventType;
|
||||
our $PROCESS_COMPLETED_EVENT : shared = Wx::NewEventType;
|
||||
|
||||
use constant FILAMENT_CHOOSERS_SPACING => 0;
|
||||
use constant PROCESS_DELAY => 0.5 * 1000; # milliseconds
|
||||
our $SLICING_COMPLETED_EVENT = Wx::NewEventType;
|
||||
our $PROCESS_COMPLETED_EVENT = Wx::NewEventType;
|
||||
|
||||
my $PreventListEvents = 0;
|
||||
|
||||
sub new {
|
||||
my ($class, $parent) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
Slic3r::GUI::set_plater($self);
|
||||
$self->{config} = Slic3r::Config::new_from_defaults_keys([qw(
|
||||
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
|
||||
serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile
|
||||
|
@ -63,14 +57,14 @@ sub new {
|
|||
# List of Perl objects Slic3r::GUI::Plater::Object, representing a 2D preview of the platter.
|
||||
$self->{objects} = [];
|
||||
$self->{gcode_preview_data} = Slic3r::GCode::PreviewData->new;
|
||||
|
||||
$self->{print}->set_status_cb(sub {
|
||||
my ($percent, $message) = @_;
|
||||
my $event = Wx::CommandEvent->new($PROGRESS_BAR_EVENT);
|
||||
$event->SetString($message);
|
||||
$event->SetInt($percent);
|
||||
Wx::PostEvent($self, $event);
|
||||
});
|
||||
$self->{background_slicing_process} = Slic3r::GUI::BackgroundSlicingProcess->new;
|
||||
$self->{background_slicing_process}->set_print($self->{print});
|
||||
$self->{background_slicing_process}->set_gcode_preview_data($self->{gcode_preview_data});
|
||||
$self->{background_slicing_process}->set_sliced_event($SLICING_COMPLETED_EVENT);
|
||||
$self->{background_slicing_process}->set_finished_event($PROCESS_COMPLETED_EVENT);
|
||||
|
||||
# The C++ slicing core will post a wxCommand message to the main window.
|
||||
Slic3r::GUI::set_print_callback_event($self->{print}, $Slic3r::GUI::MainFrame::PROGRESS_BAR_EVENT);
|
||||
|
||||
# Initialize preview notebook
|
||||
$self->{preview_notebook} = Wx::Notebook->new($self, -1, wxDefaultPosition, [335,335], wxNB_BOTTOM);
|
||||
|
@ -319,19 +313,9 @@ sub new {
|
|||
for grep defined($_),
|
||||
$self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D}, $self->{list};
|
||||
|
||||
EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub {
|
||||
EVT_COMMAND($self, -1, $SLICING_COMPLETED_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
$self->on_progress_event($event->GetInt, $event->GetString);
|
||||
});
|
||||
|
||||
EVT_COMMAND($self, -1, $ERROR_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
Slic3r::GUI::show_error($self, $event->GetString);
|
||||
});
|
||||
|
||||
EVT_COMMAND($self, -1, $EXPORT_COMPLETED_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
$self->on_export_completed($event->GetInt);
|
||||
$self->on_update_print_preview;
|
||||
});
|
||||
|
||||
EVT_COMMAND($self, -1, $PROCESS_COMPLETED_EVENT, sub {
|
||||
|
@ -788,8 +772,7 @@ sub bed_centerf {
|
|||
}
|
||||
|
||||
sub remove {
|
||||
my $self = shift;
|
||||
my ($obj_idx) = @_;
|
||||
my ($self, $obj_idx) = @_;
|
||||
|
||||
$self->stop_background_process;
|
||||
|
||||
|
@ -797,7 +780,7 @@ sub remove {
|
|||
$self->{toolpaths2D}->enabled(0) if $self->{toolpaths2D};
|
||||
$self->{preview3D}->enabled(0) if $self->{preview3D};
|
||||
|
||||
# if no object index is supplied, remove the selected one
|
||||
# If no object index is supplied, remove the selected one.
|
||||
if (! defined $obj_idx) {
|
||||
($obj_idx, undef) = $self->selected_object;
|
||||
return if ! defined $obj_idx;
|
||||
|
@ -811,11 +794,10 @@ sub remove {
|
|||
|
||||
$self->select_object(undef);
|
||||
$self->update;
|
||||
$self->schedule_background_process;
|
||||
}
|
||||
|
||||
sub reset {
|
||||
my $self = shift;
|
||||
my ($self) = @_;
|
||||
|
||||
$self->stop_background_process;
|
||||
|
||||
|
@ -840,6 +822,7 @@ sub increase {
|
|||
return if ! defined $obj_idx;
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
my $instance = $model_object->instances->[-1];
|
||||
$self->stop_background_process;
|
||||
for my $i (1..$copies) {
|
||||
$instance = $model_object->add_instance(
|
||||
offset => Slic3r::Pointf->new(map 10+$_, @{$instance->offset}),
|
||||
|
@ -849,15 +832,8 @@ sub increase {
|
|||
$self->{print}->objects->[$obj_idx]->add_copy($instance->offset);
|
||||
}
|
||||
$self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
|
||||
|
||||
# only autoarrange if user has autocentering enabled
|
||||
$self->stop_background_process;
|
||||
if (wxTheApp->{app_config}->get("autocenter")) {
|
||||
$self->arrange;
|
||||
} else {
|
||||
$self->update;
|
||||
}
|
||||
$self->schedule_background_process;
|
||||
# Only autoarrange if user has autocentering enabled.
|
||||
wxTheApp->{app_config}->get("autocenter") ? $self->arrange : $self->update;
|
||||
}
|
||||
|
||||
sub decrease {
|
||||
|
@ -866,10 +842,9 @@ sub decrease {
|
|||
my ($obj_idx, $object) = $self->selected_object;
|
||||
return if ! defined $obj_idx;
|
||||
|
||||
$self->stop_background_process;
|
||||
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
if ($model_object->instances_count > $copies) {
|
||||
$self->stop_background_process;
|
||||
for my $i (1..$copies) {
|
||||
$model_object->delete_last_instance;
|
||||
$self->{print}->objects->[$obj_idx]->delete_last_copy;
|
||||
|
@ -877,10 +852,10 @@ sub decrease {
|
|||
$self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
|
||||
} elsif (defined $copies_asked) {
|
||||
# The "decrease" came from the "set number of copies" dialog.
|
||||
$self->stop_background_process;
|
||||
$self->remove;
|
||||
} else {
|
||||
# The "decrease" came from the "-" button. Don't allow the object to disappear.
|
||||
$self->resume_background_process;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -889,24 +864,18 @@ sub decrease {
|
|||
$self->{list}->Select($obj_idx, 1);
|
||||
}
|
||||
$self->update;
|
||||
$self->schedule_background_process;
|
||||
}
|
||||
|
||||
sub set_number_of_copies {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->pause_background_process;
|
||||
|
||||
# get current number of copies
|
||||
my ($obj_idx, $object) = $self->selected_object;
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
# prompt user
|
||||
my $copies = Wx::GetNumberFromUser("", L("Enter the number of copies of the selected object:"), L("Copies"), $model_object->instances_count, 0, 1000, $self);
|
||||
my $diff = $copies - $model_object->instances_count;
|
||||
if ($diff == 0) {
|
||||
# no variation
|
||||
$self->resume_background_process;
|
||||
} elsif ($diff > 0) {
|
||||
$self->increase($diff);
|
||||
} elsif ($diff < 0) {
|
||||
|
@ -981,7 +950,6 @@ sub rotate {
|
|||
|
||||
$self->selection_changed; # refresh info (size etc.)
|
||||
$self->update;
|
||||
$self->schedule_background_process;
|
||||
}
|
||||
|
||||
sub mirror {
|
||||
|
@ -1011,7 +979,6 @@ sub mirror {
|
|||
|
||||
$self->selection_changed; # refresh info (size etc.)
|
||||
$self->update;
|
||||
$self->schedule_background_process;
|
||||
}
|
||||
|
||||
sub changescale {
|
||||
|
@ -1086,14 +1053,12 @@ sub changescale {
|
|||
|
||||
$self->selection_changed(1); # refresh info (size, volume etc.)
|
||||
$self->update;
|
||||
$self->schedule_background_process;
|
||||
}
|
||||
|
||||
sub arrange {
|
||||
my $self = shift;
|
||||
|
||||
$self->pause_background_process;
|
||||
my ($self) = @_;
|
||||
|
||||
$self->stop_background_process;
|
||||
my $bb = Slic3r::Geometry::BoundingBoxf->new_from_points($self->{config}->bed_shape);
|
||||
my $success = $self->{model}->arrange_objects(wxTheApp->{preset_bundle}->full_config->min_object_distance, $bb);
|
||||
# ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
|
||||
|
@ -1118,84 +1083,62 @@ sub split_object {
|
|||
return;
|
||||
}
|
||||
|
||||
$self->pause_background_process;
|
||||
$self->stop_background_process;
|
||||
|
||||
my @model_objects = @{$current_model_object->split_object};
|
||||
if (@model_objects == 1) {
|
||||
$self->resume_background_process;
|
||||
Slic3r::GUI::warning_catcher($self)->(L("The selected object couldn't be split because it contains only one part."));
|
||||
$self->resume_background_process;
|
||||
return;
|
||||
$self->schedule_background_process;
|
||||
} else {
|
||||
$_->center_around_origin for (@model_objects);
|
||||
$self->remove($obj_idx);
|
||||
$current_object = $obj_idx = undef;
|
||||
# load all model objects at once, otherwise the plate would be rearranged after each one
|
||||
# causing original positions not to be kept
|
||||
$self->load_model_objects(@model_objects);
|
||||
}
|
||||
|
||||
$_->center_around_origin for (@model_objects);
|
||||
|
||||
$self->remove($obj_idx);
|
||||
$current_object = $obj_idx = undef;
|
||||
|
||||
# load all model objects at once, otherwise the plate would be rearranged after each one
|
||||
# causing original positions not to be kept
|
||||
$self->load_model_objects(@model_objects);
|
||||
}
|
||||
|
||||
# Trigger $self->async_apply_config() after 500ms.
|
||||
# The call is delayed to avoid restarting the background processing during typing into an edit field.
|
||||
sub schedule_background_process {
|
||||
my ($self) = @_;
|
||||
|
||||
if (defined $self->{apply_config_timer}) {
|
||||
$self->{apply_config_timer}->Start(PROCESS_DELAY, 1); # 1 = one shot
|
||||
}
|
||||
$self->{apply_config_timer}->Start(0.5 * 1000, 1); # 1 = one shot, every half a second.
|
||||
}
|
||||
|
||||
# Executed asynchronously by a timer every PROCESS_DELAY (0.5 second).
|
||||
# The timer is started by schedule_background_process(),
|
||||
sub async_apply_config {
|
||||
my ($self) = @_;
|
||||
|
||||
# pause process thread before applying new config
|
||||
# since we don't want to touch data that is being used by the threads
|
||||
$self->pause_background_process;
|
||||
|
||||
# apply new config
|
||||
my $invalidated = $self->{print}->apply_config(wxTheApp->{preset_bundle}->full_config);
|
||||
|
||||
# Just redraw the 3D canvas without reloading the scene.
|
||||
# $self->{canvas3D}->Refresh if ($invalidated && $self->{canvas3D}->layer_editing_enabled);
|
||||
# Apply new config to the possibly running background task.
|
||||
my $was_running = $self->{background_slicing_process}->running;
|
||||
my $invalidated = $self->{background_slicing_process}->apply_config(wxTheApp->{preset_bundle}->full_config);
|
||||
# Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile.
|
||||
$self->{canvas3D}->Refresh if ($self->{canvas3D}->layer_editing_enabled);
|
||||
|
||||
# Hide the slicing results if the current slicing status is no more valid.
|
||||
$self->{"print_info_box_show"}->(0) if $invalidated;
|
||||
|
||||
if (wxTheApp->{app_config}->get("background_processing")) {
|
||||
if ($invalidated) {
|
||||
# kill current thread if any
|
||||
$self->stop_background_process;
|
||||
} else {
|
||||
$self->resume_background_process;
|
||||
}
|
||||
# schedule a new process thread in case it wasn't running
|
||||
$self->start_background_process;
|
||||
}
|
||||
|
||||
# Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared.
|
||||
# Otherwise they will be just refreshed.
|
||||
# If the apply_config caused the calculation to stop, or it was not running yet:
|
||||
if ($invalidated) {
|
||||
$self->{gcode_preview_data}->reset;
|
||||
$self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
|
||||
$self->{preview3D}->reload_print if $self->{preview3D};
|
||||
if ($was_running) {
|
||||
# Hide the slicing results if the current slicing status is no more valid.
|
||||
$self->{"print_info_box_show"}->(0);
|
||||
}
|
||||
if (wxTheApp->{app_config}->get("background_processing")) {
|
||||
$self->{background_slicing_process}->start;
|
||||
}
|
||||
if ($was_running) {
|
||||
# Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared.
|
||||
# Otherwise they will be just refreshed.
|
||||
$self->{gcode_preview_data}->reset;
|
||||
$self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
|
||||
$self->{preview3D}->reload_print if $self->{preview3D};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Background processing is started either by the "Slice now" button, by the "Export G-code button" or by async_apply_config().
|
||||
sub start_background_process {
|
||||
my ($self) = @_;
|
||||
|
||||
return if !@{$self->{objects}};
|
||||
return if $self->{process_thread};
|
||||
|
||||
# It looks like declaring a local $SIG{__WARN__} prevents the ugly
|
||||
# "Attempt to free unreferenced scalar" warning...
|
||||
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
|
||||
|
||||
# don't start process thread if config is not valid
|
||||
return if ! @{$self->{objects}} || $self->{background_slicing_process}->running;
|
||||
# Don't start process thread if config is not valid.
|
||||
eval {
|
||||
# this will throw errors if config is not valid
|
||||
wxTheApp->{preset_bundle}->full_config->validate;
|
||||
|
@ -1204,79 +1147,29 @@ sub start_background_process {
|
|||
if ($@) {
|
||||
$self->statusbar->SetStatusText($@);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
# Copy the names of active presets into the placeholder parser.
|
||||
wxTheApp->{preset_bundle}->export_selections_pp($self->{print}->placeholder_parser);
|
||||
|
||||
# start thread
|
||||
@_ = ();
|
||||
$self->{process_thread} = Slic3r::spawn_thread(sub {
|
||||
eval {
|
||||
$self->{print}->process;
|
||||
};
|
||||
my $event = Wx::CommandEvent->new($PROCESS_COMPLETED_EVENT);
|
||||
if ($@) {
|
||||
Slic3r::debugf "Background process error: $@\n";
|
||||
$event->SetInt(0);
|
||||
$event->SetString($@);
|
||||
} else {
|
||||
$event->SetInt(1);
|
||||
}
|
||||
Wx::PostEvent($self, $event);
|
||||
Slic3r::thread_cleanup();
|
||||
});
|
||||
Slic3r::debugf "Background processing started.\n";
|
||||
# Start the background process.
|
||||
$self->{background_slicing_process}->start;
|
||||
}
|
||||
|
||||
# Stop the background processing
|
||||
sub stop_background_process {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->{apply_config_timer}->Stop if defined $self->{apply_config_timer};
|
||||
# Don't call async_apply_config() while stopped.
|
||||
$self->{apply_config_timer}->Stop;
|
||||
$self->statusbar->SetCancelCallback(undef);
|
||||
$self->statusbar->StopBusy;
|
||||
$self->statusbar->SetStatusText("");
|
||||
# Stop the background task.
|
||||
$self->{background_slicing_process}->stop;
|
||||
# Update the UI with the slicing results.
|
||||
$self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
|
||||
$self->{preview3D}->reload_print if $self->{preview3D};
|
||||
|
||||
if ($self->{process_thread}) {
|
||||
Slic3r::debugf "Killing background process.\n";
|
||||
Slic3r::kill_all_threads();
|
||||
$self->{process_thread} = undef;
|
||||
} else {
|
||||
Slic3r::debugf "No background process running.\n";
|
||||
}
|
||||
|
||||
# if there's an export process, kill that one as well
|
||||
if ($self->{export_thread}) {
|
||||
Slic3r::debugf "Killing background export process.\n";
|
||||
Slic3r::kill_all_threads();
|
||||
$self->{export_thread} = undef;
|
||||
}
|
||||
}
|
||||
|
||||
sub pause_background_process {
|
||||
my ($self) = @_;
|
||||
|
||||
if ($self->{process_thread} || $self->{export_thread}) {
|
||||
Slic3r::pause_all_threads();
|
||||
return 1;
|
||||
} elsif (defined $self->{apply_config_timer} && $self->{apply_config_timer}->IsRunning) {
|
||||
$self->{apply_config_timer}->Stop;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub resume_background_process {
|
||||
my ($self) = @_;
|
||||
|
||||
if ($self->{process_thread} || $self->{export_thread}) {
|
||||
Slic3r::resume_all_threads();
|
||||
}
|
||||
}
|
||||
|
||||
# Called by the "Slice now" button, which is visible only if the background processing is disabled.
|
||||
sub reslice {
|
||||
# explicitly cancel a previous thread and start a new one.
|
||||
my ($self) = @_;
|
||||
|
@ -1371,79 +1264,22 @@ sub export_gcode {
|
|||
return $self->{export_gcode_output_file};
|
||||
}
|
||||
|
||||
# This gets called only if we have threads.
|
||||
sub on_process_completed {
|
||||
my ($self, $error) = @_;
|
||||
|
||||
$self->statusbar->SetCancelCallback(undef);
|
||||
$self->statusbar->StopBusy;
|
||||
$self->statusbar->SetStatusText($error // "");
|
||||
|
||||
Slic3r::debugf "Background processing completed.\n";
|
||||
$self->{process_thread}->detach if $self->{process_thread};
|
||||
$self->{process_thread} = undef;
|
||||
|
||||
# if we're supposed to perform an explicit export let's display the error in a dialog
|
||||
if ($error && $self->{export_gcode_output_file}) {
|
||||
$self->{export_gcode_output_file} = undef;
|
||||
Slic3r::GUI::show_error($self, $error);
|
||||
}
|
||||
|
||||
return if $error;
|
||||
# This message should be called by the background process synchronously.
|
||||
sub on_update_print_preview {
|
||||
my ($self) = @_;
|
||||
$self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
|
||||
$self->{preview3D}->reload_print if $self->{preview3D};
|
||||
|
||||
# if we have an export filename, start a new thread for exporting G-code
|
||||
if ($self->{export_gcode_output_file}) {
|
||||
@_ = ();
|
||||
|
||||
# workaround for "Attempt to free un referenced scalar..."
|
||||
our $_thread_self = $self;
|
||||
|
||||
$self->{export_thread} = Slic3r::spawn_thread(sub {
|
||||
eval {
|
||||
$_thread_self->{print}->export_gcode(output_file => $_thread_self->{export_gcode_output_file}, gcode_preview_data => $_thread_self->{gcode_preview_data});
|
||||
};
|
||||
my $export_completed_event = Wx::CommandEvent->new($EXPORT_COMPLETED_EVENT);
|
||||
if ($@) {
|
||||
{
|
||||
my $error_event = Wx::CommandEvent->new($ERROR_EVENT);
|
||||
$error_event->SetString($@);
|
||||
Wx::PostEvent($_thread_self, $error_event);
|
||||
}
|
||||
$export_completed_event->SetInt(0);
|
||||
$export_completed_event->SetString($@);
|
||||
} else {
|
||||
$export_completed_event->SetInt(1);
|
||||
}
|
||||
Wx::PostEvent($_thread_self, $export_completed_event);
|
||||
Slic3r::thread_cleanup();
|
||||
});
|
||||
Slic3r::debugf "Background G-code export started.\n";
|
||||
}
|
||||
}
|
||||
|
||||
# This gets called also if we have no threads.
|
||||
sub on_progress_event {
|
||||
my ($self, $percent, $message) = @_;
|
||||
|
||||
$self->statusbar->SetProgress($percent);
|
||||
$self->statusbar->SetStatusText("$message…");
|
||||
}
|
||||
|
||||
# Called when the G-code export finishes, either successfully or with an error.
|
||||
# This gets called also if we don't have threads.
|
||||
sub on_export_completed {
|
||||
sub on_process_completed {
|
||||
my ($self, $result) = @_;
|
||||
|
||||
$self->statusbar->SetCancelCallback(undef);
|
||||
$self->statusbar->StopBusy;
|
||||
$self->statusbar->SetStatusText("");
|
||||
|
||||
Slic3r::debugf "Background export process completed.\n";
|
||||
$self->{export_thread}->detach if $self->{export_thread};
|
||||
$self->{export_thread} = undef;
|
||||
|
||||
my $message;
|
||||
my $send_gcode = 0;
|
||||
my $do_print = 0;
|
||||
|
@ -1470,7 +1306,7 @@ sub on_export_completed {
|
|||
# Send $self->{send_gcode_file} to OctoPrint.
|
||||
if ($send_gcode) {
|
||||
my $op = Slic3r::OctoPrint->new($self->{config});
|
||||
$op->send_gcode($self->GetId(), $PROGRESS_BAR_EVENT, $ERROR_EVENT, $self->{send_gcode_file});
|
||||
$op->send_gcode($self->GetId(), $Slic3r::GUI::MainFrame::PROGRESS_BAR_EVENT, $Slic3r::GUI::MainFrame::ERROR_EVENT, $self->{send_gcode_file});
|
||||
}
|
||||
|
||||
$self->{print_file} = undef;
|
||||
|
@ -1636,27 +1472,15 @@ sub reset_thumbnail {
|
|||
# (i.e. when an object is added/removed/moved/rotated/scaled)
|
||||
sub update {
|
||||
my ($self, $force_autocenter) = @_;
|
||||
|
||||
if (wxTheApp->{app_config}->get("autocenter") || $force_autocenter) {
|
||||
$self->{model}->center_instances_around_point($self->bed_centerf);
|
||||
}
|
||||
|
||||
my $running = $self->pause_background_process;
|
||||
my $invalidated = $self->{print}->reload_model_instances();
|
||||
|
||||
# The mere fact that no steps were invalidated when reloading model instances
|
||||
# doesn't mean that all steps were done: for example, validation might have
|
||||
# failed upon previous instance move, so we have no running thread and no steps
|
||||
# are invalidated on this move, thus we need to schedule a new run.
|
||||
if ($invalidated || !$running) {
|
||||
$self->schedule_background_process;
|
||||
} else {
|
||||
$self->resume_background_process;
|
||||
}
|
||||
|
||||
$self->stop_background_process;
|
||||
$self->{print}->reload_model_instances();
|
||||
$self->{canvas}->reload_scene if $self->{canvas};
|
||||
$self->{canvas3D}->reload_scene if $self->{canvas3D};
|
||||
$self->{preview3D}->reload_print if $self->{preview3D};
|
||||
$self->schedule_background_process;
|
||||
}
|
||||
|
||||
# When a number of extruders changes, the UI needs to be updated to show a single filament selection combo box per extruder.
|
||||
|
@ -1679,7 +1503,7 @@ sub on_extruders_change {
|
|||
$choice->SetItemBitmap($_, $choices->[0]->GetItemBitmap($_)) for 0..$#presets;
|
||||
# insert new choice into sizer
|
||||
$self->{presets_sizer}->Insert(4 + ($#$choices-1)*2, 0, 0);
|
||||
$self->{presets_sizer}->Insert(5 + ($#$choices-1)*2, $choice, 0, wxEXPAND | wxBOTTOM, FILAMENT_CHOOSERS_SPACING);
|
||||
$self->{presets_sizer}->Insert(5 + ($#$choices-1)*2, $choice, 0, wxEXPAND | wxBOTTOM, 0);
|
||||
# setup the listener
|
||||
EVT_COMBOBOX($choice, $choice, sub {
|
||||
my ($choice) = @_;
|
||||
|
@ -1854,7 +1678,7 @@ sub object_settings_dialog {
|
|||
model_object => $model_object,
|
||||
config => wxTheApp->{preset_bundle}->full_config,
|
||||
);
|
||||
$self->pause_background_process;
|
||||
$self->stop_background_process;
|
||||
$dlg->ShowModal;
|
||||
|
||||
# update thumbnail since parts may have changed
|
||||
|
@ -1866,13 +1690,12 @@ sub object_settings_dialog {
|
|||
|
||||
# update print
|
||||
if ($dlg->PartsChanged || $dlg->PartSettingsChanged) {
|
||||
$self->stop_background_process;
|
||||
$self->{print}->reload_object($obj_idx);
|
||||
$self->schedule_background_process;
|
||||
$self->{canvas}->reload_scene if $self->{canvas};
|
||||
$self->{canvas3D}->reload_scene if $self->{canvas3D};
|
||||
} else {
|
||||
$self->resume_background_process;
|
||||
$self->schedule_background_process;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -152,12 +152,6 @@ __PACKAGE__->mk_accessors(qw(
|
|||
_simulation_mode
|
||||
));
|
||||
|
||||
# make OpenGL::Array thread-safe
|
||||
{
|
||||
no warnings 'redefine';
|
||||
*OpenGL::Array::CLONE_SKIP = sub { 1 };
|
||||
}
|
||||
|
||||
sub new {
|
||||
my ($class, $parent, $print) = @_;
|
||||
|
||||
|
|
|
@ -7,11 +7,6 @@ sub new_scale {
|
|||
return $class->new(map Slic3r::Geometry::scale($_), @_);
|
||||
}
|
||||
|
||||
sub dump_perl {
|
||||
my $self = shift;
|
||||
return sprintf "[%s,%s]", @$self;
|
||||
}
|
||||
|
||||
package Slic3r::Pointf;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
|
|
@ -10,9 +10,4 @@ sub new_scale {
|
|||
return $class->new(map [ Slic3r::Geometry::scale($_->[X]), Slic3r::Geometry::scale($_->[Y]) ], @points);
|
||||
}
|
||||
|
||||
sub dump_perl {
|
||||
my $self = shift;
|
||||
return sprintf "[%s]", join ',', map "[$_->[0],$_->[1]]", @$self;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -15,82 +15,16 @@ use Slic3r::Geometry::Clipper qw(diff_ex union_ex intersection_ex intersection o
|
|||
union JT_ROUND JT_SQUARE);
|
||||
use Slic3r::Print::State ':steps';
|
||||
|
||||
our $status_cb;
|
||||
|
||||
sub set_status_cb {
|
||||
my ($class, $cb) = @_;
|
||||
$status_cb = $cb;
|
||||
}
|
||||
|
||||
sub status_cb {
|
||||
return $status_cb // sub {};
|
||||
}
|
||||
|
||||
sub size {
|
||||
my $self = shift;
|
||||
return $self->bounding_box->size;
|
||||
}
|
||||
|
||||
# Slicing process, running at a background thread.
|
||||
sub process {
|
||||
my ($self) = @_;
|
||||
|
||||
Slic3r::trace(3, "Staring the slicing process.");
|
||||
$_->make_perimeters for @{$self->objects};
|
||||
|
||||
$self->status_cb->(70, "Infilling layers");
|
||||
$_->infill for @{$self->objects};
|
||||
|
||||
$_->generate_support_material for @{$self->objects};
|
||||
$self->make_skirt;
|
||||
$self->make_brim; # must come after make_skirt
|
||||
$self->make_wipe_tower;
|
||||
|
||||
# time to make some statistics
|
||||
if (0) {
|
||||
eval "use Devel::Size";
|
||||
print "MEMORY USAGE:\n";
|
||||
printf " meshes = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->meshes), @{$self->objects})/1024/1024;
|
||||
printf " layer slices = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->slices), map @{$_->layers}, @{$self->objects})/1024/1024;
|
||||
printf " region slices = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->slices), map @{$_->regions}, map @{$_->layers}, @{$self->objects})/1024/1024;
|
||||
printf " perimeters = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->perimeters), map @{$_->regions}, map @{$_->layers}, @{$self->objects})/1024/1024;
|
||||
printf " fills = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->fills), map @{$_->regions}, map @{$_->layers}, @{$self->objects})/1024/1024;
|
||||
printf " print object = %.1fMb\n", Devel::Size::total_size($self)/1024/1024;
|
||||
}
|
||||
if (0) {
|
||||
eval "use Slic3r::Test::SectionCut";
|
||||
Slic3r::Test::SectionCut->new(print => $self)->export_svg("section_cut.svg");
|
||||
}
|
||||
Slic3r::trace(3, "Slicing process finished.")
|
||||
}
|
||||
|
||||
# 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 = @_;
|
||||
|
||||
# prerequisites
|
||||
$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" : ""));
|
||||
|
||||
# The following line may die for multiple reasons.
|
||||
my $gcode = Slic3r::GCode->new;
|
||||
if (defined $params{gcode_preview_data}) {
|
||||
$gcode->do_export_w_preview($self, $output_file, $params{gcode_preview_data});
|
||||
} else {
|
||||
$gcode->do_export($self, $output_file);
|
||||
}
|
||||
|
||||
sub run_post_process_scripts {
|
||||
my ($self, $output_file) = @_;
|
||||
# run post-processing scripts
|
||||
if (@{$self->config->post_process}) {
|
||||
$self->status_cb->(95, "Running post-processing scripts");
|
||||
# $self->status_cb->(95, "Running post-processing scripts");
|
||||
$self->config->setenv;
|
||||
for my $script (@{$self->config->post_process}) {
|
||||
# Ignore empty post processing script lines.
|
||||
|
@ -205,67 +139,4 @@ EOF
|
|||
print "Done.\n" unless $params{quiet};
|
||||
}
|
||||
|
||||
sub make_skirt {
|
||||
my $self = shift;
|
||||
|
||||
# prerequisites
|
||||
$_->make_perimeters for @{$self->objects};
|
||||
$_->infill for @{$self->objects};
|
||||
$_->generate_support_material for @{$self->objects};
|
||||
|
||||
return if $self->step_done(STEP_SKIRT);
|
||||
|
||||
$self->set_step_started(STEP_SKIRT);
|
||||
$self->skirt->clear;
|
||||
if ($self->has_skirt) {
|
||||
$self->status_cb->(88, "Generating skirt");
|
||||
$self->_make_skirt();
|
||||
}
|
||||
$self->set_step_done(STEP_SKIRT);
|
||||
}
|
||||
|
||||
sub make_brim {
|
||||
my $self = shift;
|
||||
|
||||
# prerequisites
|
||||
$_->make_perimeters for @{$self->objects};
|
||||
$_->infill for @{$self->objects};
|
||||
$_->generate_support_material for @{$self->objects};
|
||||
$self->make_skirt;
|
||||
|
||||
return if $self->step_done(STEP_BRIM);
|
||||
|
||||
$self->set_step_started(STEP_BRIM);
|
||||
# since this method must be idempotent, we clear brim paths *before*
|
||||
# checking whether we need to generate them
|
||||
$self->brim->clear;
|
||||
if ($self->config->brim_width > 0) {
|
||||
$self->status_cb->(88, "Generating brim");
|
||||
$self->_make_brim;
|
||||
}
|
||||
|
||||
$self->set_step_done(STEP_BRIM);
|
||||
}
|
||||
|
||||
sub make_wipe_tower {
|
||||
my $self = shift;
|
||||
|
||||
# prerequisites
|
||||
$_->make_perimeters for @{$self->objects};
|
||||
$_->infill for @{$self->objects};
|
||||
$_->generate_support_material for @{$self->objects};
|
||||
$self->make_skirt;
|
||||
$self->make_brim;
|
||||
|
||||
return if $self->step_done(STEP_WIPE_TOWER);
|
||||
|
||||
$self->set_step_started(STEP_WIPE_TOWER);
|
||||
$self->_clear_wipe_tower;
|
||||
if ($self->has_wipe_tower) {
|
||||
# $self->status_cb->(95, "Generating wipe tower");
|
||||
$self->_make_wipe_tower;
|
||||
}
|
||||
$self->set_step_done(STEP_WIPE_TOWER);
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -21,95 +21,4 @@ sub support_layers {
|
|||
return [ map $self->get_support_layer($_), 0..($self->support_layer_count - 1) ];
|
||||
}
|
||||
|
||||
# 1) Decides Z positions of the layers,
|
||||
# 2) Initializes layers and their regions
|
||||
# 3) Slices the object meshes
|
||||
# 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
|
||||
# 5) Applies size compensation (offsets the slices in XY plane)
|
||||
# 6) Replaces bad slices by the slices reconstructed from the upper/lower layer
|
||||
# Resulting expolygons of layer regions are marked as Internal.
|
||||
#
|
||||
# this should be idempotent
|
||||
sub slice {
|
||||
my $self = shift;
|
||||
|
||||
return if $self->step_done(STEP_SLICE);
|
||||
$self->set_step_started(STEP_SLICE);
|
||||
$self->print->status_cb->(10, "Processing triangulated mesh");
|
||||
|
||||
$self->_slice;
|
||||
|
||||
my $warning = $self->_fix_slicing_errors;
|
||||
warn $warning if (defined($warning) && $warning ne '');
|
||||
|
||||
# simplify slices if required
|
||||
$self->_simplify_slices(scale($self->print->config->resolution))
|
||||
if ($self->print->config->resolution);
|
||||
|
||||
die "No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"
|
||||
if !@{$self->layers};
|
||||
|
||||
$self->set_step_done(STEP_SLICE);
|
||||
}
|
||||
|
||||
# 1) Merges typed region slices into stInternal type.
|
||||
# 2) Increases an "extra perimeters" counter at region slices where needed.
|
||||
# 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
|
||||
sub make_perimeters {
|
||||
my ($self) = @_;
|
||||
|
||||
# prerequisites
|
||||
$self->slice;
|
||||
|
||||
if (! $self->step_done(STEP_PERIMETERS)) {
|
||||
$self->print->status_cb->(20, "Generating perimeters");
|
||||
$self->_make_perimeters;
|
||||
}
|
||||
}
|
||||
|
||||
sub prepare_infill {
|
||||
my ($self) = @_;
|
||||
|
||||
# prerequisites
|
||||
$self->make_perimeters;
|
||||
|
||||
return if $self->step_done(STEP_PREPARE_INFILL);
|
||||
$self->set_step_started(STEP_PREPARE_INFILL);
|
||||
$self->print->status_cb->(30, "Preparing infill");
|
||||
|
||||
$self->_prepare_infill;
|
||||
|
||||
$self->set_step_done(STEP_PREPARE_INFILL);
|
||||
}
|
||||
|
||||
sub infill {
|
||||
my ($self) = @_;
|
||||
|
||||
# prerequisites
|
||||
$self->prepare_infill;
|
||||
$self->_infill;
|
||||
}
|
||||
|
||||
sub generate_support_material {
|
||||
my $self = shift;
|
||||
|
||||
# prerequisites
|
||||
$self->slice;
|
||||
|
||||
return if $self->step_done(STEP_SUPPORTMATERIAL);
|
||||
$self->set_step_started(STEP_SUPPORTMATERIAL);
|
||||
|
||||
$self->clear_support_layers;
|
||||
|
||||
if (($self->config->support_material || $self->config->raft_layers > 0) && scalar(@{$self->layers}) > 1) {
|
||||
$self->print->status_cb->(85, "Generating support material");
|
||||
# New supports, C++ implementation.
|
||||
$self->_generate_support_material;
|
||||
}
|
||||
|
||||
$self->set_step_done(STEP_SUPPORTMATERIAL);
|
||||
my $stats = sprintf "Weight: %.1fg, Cost: %.1f" , $self->print->total_weight, $self->print->total_cost;
|
||||
$self->print->status_cb->(85, $stats);
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -38,11 +38,6 @@ has 'duplicate_grid' => (
|
|||
default => sub { [1,1] },
|
||||
);
|
||||
|
||||
has 'status_cb' => (
|
||||
is => 'rw',
|
||||
default => sub { sub {} },
|
||||
);
|
||||
|
||||
has 'print_center' => (
|
||||
is => 'rw',
|
||||
default => sub { Slic3r::Pointf->new(100,100) },
|
||||
|
@ -90,35 +85,16 @@ sub set_model {
|
|||
}
|
||||
}
|
||||
|
||||
sub _before_export {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_print->set_status_cb($self->status_cb);
|
||||
$self->_print->validate;
|
||||
}
|
||||
|
||||
sub _after_export {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_print->set_status_cb(undef);
|
||||
}
|
||||
|
||||
sub export_gcode {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_before_export;
|
||||
$self->_print->export_gcode(output_file => $self->output_file);
|
||||
$self->_after_export;
|
||||
$self->_print->validate;
|
||||
$self->_print->export_gcode($self->output_file // '');
|
||||
}
|
||||
|
||||
sub export_svg {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_before_export;
|
||||
|
||||
$self->_print->validate;
|
||||
$self->_print->export_svg(output_file => $self->output_file);
|
||||
|
||||
$self->_after_export;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue