From e931f7501001feb8e2af29489e218ecca7080f48 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 23 Mar 2018 11:41:20 +0100 Subject: [PATCH 01/36] Work in progress: Good bye, Perl Threads! --- Build.PL | 4 - lib/Slic3r.pm | 137 ----- lib/Slic3r/ExPolygon.pm | 6 - lib/Slic3r/GUI/3DScene.pm | 6 - lib/Slic3r/GUI/MainFrame.pm | 56 +- lib/Slic3r/GUI/Plater.pm | 335 +++-------- lib/Slic3r/GUI/Plater/2DToolpaths.pm | 6 - lib/Slic3r/Point.pm | 5 - lib/Slic3r/Polyline.pm | 5 - lib/Slic3r/Print.pm | 135 +---- lib/Slic3r/Print/Object.pm | 91 --- lib/Slic3r/Print/Simple.pm | 30 +- slic3r.pl | 4 - t/threads.t | 35 -- xs/CMakeLists.txt | 3 + xs/lib/Slic3r/XS.pm | 7 - xs/src/libslic3r/Print.cpp | 76 ++- xs/src/libslic3r/Print.hpp | 36 +- xs/src/libslic3r/PrintConfig.cpp | 1 + xs/src/libslic3r/PrintObject.cpp | 541 ++++++++++-------- xs/src/perlglue.cpp | 1 + .../slic3r/GUI/BackgroundSlicingProcess.cpp | 138 +++++ .../slic3r/GUI/BackgroundSlicingProcess.hpp | 82 +++ xs/src/slic3r/GUI/GUI.cpp | 20 + xs/src/slic3r/GUI/GUI.hpp | 7 + xs/t/20_print.t | 2 - xs/xsp/GUI.xsp | 6 + xs/xsp/GUI_BackgroundSlicingProcess.xsp | 23 + xs/xsp/Print.xsp | 100 +--- xs/xsp/my.map | 2 + xs/xsp/typemap.xspt | 2 + 31 files changed, 833 insertions(+), 1069 deletions(-) delete mode 100644 t/threads.t create mode 100644 xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp create mode 100644 xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp create mode 100644 xs/xsp/GUI_BackgroundSlicingProcess.xsp diff --git a/Build.PL b/Build.PL index e006d11eb0..3b10235f84 100644 --- a/Build.PL +++ b/Build.PL @@ -20,9 +20,7 @@ my %prereqs = qw( POSIX 0 Scalar::Util 0 Test::More 0 - Thread::Semaphore 0 IO::Scalar 0 - threads 1.96 Time::HiRes 0 ); my %recommends = qw( @@ -44,8 +42,6 @@ if ($gui) { ); if ($^O eq 'MSWin32') { $recommends{"Win32::TieRegistry"} = 0; - # we need an up-to-date Win32::API because older aren't thread-safe (GH #2517) - $prereqs{'Win32::API'} = 0.79; } } diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index e55e24a7ad..020edd70f8 100644 --- a/lib/Slic3r.pm +++ b/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; diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 27aa1a59b4..dfad9db344 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -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, @_); diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 75a154281f..72423b9461 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -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) = @_; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 4868ed7e24..98fdbcd3b6 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -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 { diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d3513897f3..b6450fbdd8 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -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; } } diff --git a/lib/Slic3r/GUI/Plater/2DToolpaths.pm b/lib/Slic3r/GUI/Plater/2DToolpaths.pm index 96a252a08b..f6edcbe248 100644 --- a/lib/Slic3r/GUI/Plater/2DToolpaths.pm +++ b/lib/Slic3r/GUI/Plater/2DToolpaths.pm @@ -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) = @_; diff --git a/lib/Slic3r/Point.pm b/lib/Slic3r/Point.pm index 535a97194b..1134138ea3 100644 --- a/lib/Slic3r/Point.pm +++ b/lib/Slic3r/Point.pm @@ -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; diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 9cc1424093..a42b5d1c4a 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -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; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 8300fdbede..5a0ddd38f5 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -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; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index e275fdde58..7370881ea3 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -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; diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm index 4fe3eb820b..9dbc7fefae 100644 --- a/lib/Slic3r/Print/Simple.pm +++ b/lib/Slic3r/Print/Simple.pm @@ -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; diff --git a/slic3r.pl b/slic3r.pl index d9bed0ab66..0d58647c05 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -202,10 +202,6 @@ if (@ARGV) { # slicing from command line duplicate_grid => $opt{duplicate_grid} // [1,1], print_center => $opt{print_center} // Slic3r::Pointf->new(100,100), dont_arrange => $opt{dont_arrange} // 0, - status_cb => sub { - my ($percent, $message) = @_; - printf "=> %s\n", $message; - }, output_file => $opt{output}, ); diff --git a/t/threads.t b/t/threads.t deleted file mode 100644 index 7fede33283..0000000000 --- a/t/threads.t +++ /dev/null @@ -1,35 +0,0 @@ -use Test::More tests => 2; -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use List::Util qw(first); -use Slic3r; -use Slic3r::Test; - -{ - my $print = Slic3r::Test::init_print('20mm_cube'); - { - my $thread = threads->create(sub { Slic3r::thread_cleanup(); return 1; }); - ok $thread->join, "print survives thread spawning"; - } -} - -{ - my $thread = threads->create(sub { - { - my $print = Slic3r::Test::init_print('20mm_cube'); - Slic3r::Test::gcode($print); - } - Slic3r::thread_cleanup(); - return 1; - }); - ok $thread->join, "process print in a separate thread"; -} - -__END__ diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 63b99a8351..0a76721a44 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -171,6 +171,8 @@ add_library(libslic3r STATIC add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/AppConfig.cpp ${LIBDIR}/slic3r/GUI/AppConfig.hpp + ${LIBDIR}/slic3r/GUI/BackgroundSlicingProcess.cpp + ${LIBDIR}/slic3r/GUI/BackgroundSlicingProcess.hpp ${LIBDIR}/slic3r/GUI/3DScene.cpp ${LIBDIR}/slic3r/GUI/3DScene.hpp ${LIBDIR}/slic3r/GUI/GLShader.cpp @@ -331,6 +333,7 @@ set(XS_XSP_FILES ${XSP_DIR}/Geometry.xsp ${XSP_DIR}/GUI.xsp ${XSP_DIR}/GUI_AppConfig.xsp + ${XSP_DIR}/GUI_BackgroundSlicingProcess.xsp ${XSP_DIR}/GUI_3DScene.xsp ${XSP_DIR}/GUI_Preset.xsp ${XSP_DIR}/GUI_Tab.xsp diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index 47a584343e..5105ee0add 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -239,23 +239,16 @@ sub new { ); } -package Slic3r::GUI::_3DScene::GLShader; -sub CLONE_SKIP { 1 } - package Slic3r::GUI::_3DScene::GLVolume::Collection; use overload '@{}' => sub { $_[0]->arrayref }, 'fallback' => 1; -sub CLONE_SKIP { 1 } - package Slic3r::GUI::PresetCollection; use overload '@{}' => sub { $_[0]->arrayref }, 'fallback' => 1; -sub CLONE_SKIP { 1 } - package main; for my $class (qw( Slic3r::BridgeDetector diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index a3f5e231f7..a74afdaec7 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -5,11 +5,13 @@ #include "Flow.hpp" #include "Geometry.hpp" #include "SupportMaterial.hpp" +#include "GCode.hpp" #include "GCode/WipeTowerPrusaMM.hpp" #include #include #include #include +#include namespace Slic3r { @@ -793,6 +795,71 @@ void Print::auto_assign_extruders(ModelObject* model_object) const } } +// Slicing process, running at a background thread. +void Print::process() +{ + BOOST_LOG_TRIVIAL(info) << "Staring the slicing process."; + for (PrintObject *obj : this->objects) + obj->make_perimeters(); + this->set_status(70, "Infilling layers"); + for (PrintObject *obj : this->objects) + obj->infill(); + for (PrintObject *obj : this->objects) + obj->generate_support_material(); + if (! this->state.is_done(psSkirt)) { + this->state.set_started(psSkirt); + this->skirt.clear(); + if (this->has_skirt()) { + this->set_status(88, "Generating skirt"); + this->_make_skirt(); + } + this->state.set_done(psSkirt); + } + if (! this->state.is_done(psBrim)) { + this->state.set_started(psBrim); + this->brim.clear(); + if (this->config.brim_width > 0) { + this->set_status(88, "Generating brim"); + this->_make_brim(); + } + this->state.set_done(psBrim); + } + if (! this->state.is_done(psWipeTower)) { + this->state.set_started(psWipeTower); + this->_clear_wipe_tower(); + if (this->has_wipe_tower()) { + //this->set_status(95, "Generating wipe tower"); + this->_make_wipe_tower(); + } + this->state.set_done(psWipeTower); + } + BOOST_LOG_TRIVIAL(info) << "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. +void Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data) +{ + // prerequisites + this->process(); + + // output everything to a G-code file + // The following call may die if the output_filename_format template substitution fails. + std::string path = this->output_filepath(path_template); + std::string message = "Exporting G-code"; + if (! path.empty()) { + message += " to "; + message += path; + } + this->set_status(90, message); + + // The following line may die for multiple reasons. + GCode gcode; + gcode.do_export(this, path.c_str(), preview_data); +} + void Print::_make_skirt() { // First off we need to decide how tall the skirt must be. @@ -978,10 +1045,6 @@ void Print::_clear_wipe_tower() void Print::_make_wipe_tower() { - this->_clear_wipe_tower(); - if (! this->has_wipe_tower()) - return; - // Let the ToolOrdering class know there will be initial priming extrusions at the start of the print. m_tool_ordering = ToolOrdering(*this, (unsigned int)-1, true); if (! m_tool_ordering.has_wipe_tower()) @@ -1153,9 +1216,4 @@ std::string Print::output_filepath(const std::string &path) return path; } -void Print::set_status(int percent, const std::string &message) -{ - printf("Print::status %d => %s\n", percent, message.c_str()); -} - } diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index c56e64c6c4..dfde2650ad 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "BoundingBox.hpp" #include "Flow.hpp" #include "PrintConfig.hpp" @@ -23,6 +24,7 @@ namespace Slic3r { class Print; class PrintObject; class ModelObject; +class GCodePreviewData; // Print step IDs for keeping track of the print state. enum PrintStep { @@ -190,23 +192,26 @@ public: // (layer height, first layer height, raft settings, print nozzle diameter etc). SlicingParameters slicing_parameters() const; +private: + void slice(); + void make_perimeters(); + void prepare_infill(); + void infill(); + void generate_support_material(); + void _slice(); std::string _fix_slicing_errors(); void _simplify_slices(double distance); - void _prepare_infill(); bool has_support_material() const; void detect_surfaces_type(); void process_external_surfaces(); void discover_vertical_shells(); void bridge_over_infill(); - void _make_perimeters(); - void _infill(); void clip_fill_surfaces(); void discover_horizontal_shells(); void combine_infill(); void _generate_support_material(); -private: Print* _print; ModelObject* _model_object; Points _copies; // Slic3r::Point objects in scaled G-code coordinates @@ -232,7 +237,6 @@ public: PrintObjectPtrs objects; PrintRegionPtrs regions; PlaceholderParser placeholder_parser; - // TODO: status_cb std::string estimated_print_time; double total_used_filament, total_extruded_volume, total_cost, total_weight; std::map filament_stats; @@ -283,13 +287,11 @@ public: bool has_support_material() const; void auto_assign_extruders(ModelObject* model_object) const; - void _make_skirt(); - void _make_brim(); + void process(); + void export_gcode(const std::string &path_template, GCodePreviewData *preview_data); // Wipe tower support. bool has_wipe_tower() const; - void _clear_wipe_tower(); - void _make_wipe_tower(); // Tool ordering of a non-sequential print has to be known to calculate the wipe tower. // Cache it here, so it does not need to be recalculated during the G-code generation. ToolOrdering m_tool_ordering; @@ -301,8 +303,14 @@ public: std::string output_filename(); std::string output_filepath(const std::string &path); + typedef std::function status_callback_type; + void set_status_callback(status_callback_type cb) { m_status_callback = cb; } + void reset_status_callback() { m_status_callback = nullptr; } // Calls a registered callback to update the status. - void set_status(int percent, const std::string &message); + void set_status(int percent, const std::string &message) { + if (m_status_callback) m_status_callback(percent, message); + else printf("%d => %s\n", percent, message.c_str()); + } // Cancel the running computation. Stop execution of all the background threads. void cancel() { m_canceled = true; } // Cancel the running computation. Stop execution of all the background threads. @@ -314,8 +322,14 @@ private: bool invalidate_state_by_config_options(const std::vector &opt_keys); PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume); + void _make_skirt(); + void _make_brim(); + void _clear_wipe_tower(); + void _make_wipe_tower(); + // Has the calculation been canceled? - tbb::atomic m_canceled; + tbb::atomic m_canceled; + status_callback_type m_status_callback; }; #define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index d7ee1e5914..298fe9207a 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -111,6 +111,7 @@ PrintConfigDef::PrintConfigDef() "with cooling (use a fan) before tweaking this."); def->cli = "bridge-flow-ratio=f"; def->min = 0; + def->max = 2; def->default_value = new ConfigOptionFloat(1); def = this->add("bridge_speed", coFloat); diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index c61fc102b9..dd2c4e3776 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -31,6 +31,8 @@ #include #endif +#define PARALLEL_FOR_CANCEL do { if (this->print()->canceled()) return; } while (0) + namespace Slic3r { PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) : @@ -104,6 +106,305 @@ bool PrintObject::reload_model_instances() return this->set_copies(copies); } +// 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 +void PrintObject::slice() +{ + if (this->state.is_done(posSlice)) + return; + this->state.set_started(posSlice); + this->_print->set_status(10, "Processing triangulated mesh"); + this->_slice(); + // Fix the model. + //FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend. + std::string warning = this->_fix_slicing_errors(); + if (! warning.empty()) + BOOST_LOG_TRIVIAL(info) << warning; + // Simplify slices if required. + if (this->_print->config.resolution) + this->_simplify_slices(scale_(this->_print->config.resolution)); + if (this->layers.empty()) + throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); + this->state.set_done(posSlice); +} + +// 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). +void PrintObject::make_perimeters() +{ + // prerequisites + this->slice(); + + if (this->state.is_done(posPerimeters)) + return; + + this->state.set_started(posPerimeters); + this->_print->set_status(20, "Generating perimeters"); + BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; + + // merge slices if they were split into types + if (this->typed_slices) { + FOREACH_LAYER(this, layer_it) + (*layer_it)->merge_slices(); + this->typed_slices = false; + this->state.invalidate(posPrepareInfill); + } + + // compare each layer to the one below, and mark those slices needing + // one additional inner perimeter, like the top of domed objects- + + // this algorithm makes sure that at least one perimeter is overlapping + // but we don't generate any extra perimeter if fill density is zero, as they would be floating + // inside the object - infill_only_where_needed should be the method of choice for printing + // hollow objects + FOREACH_REGION(this->_print, region_it) { + size_t region_id = region_it - this->_print->regions.begin(); + const PrintRegion ®ion = **region_it; + + + if (!region.config.extra_perimeters + || region.config.perimeters == 0 + || region.config.fill_density == 0 + || this->layer_count() < 2) + continue; + + BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start"; + tbb::parallel_for( + tbb::blocked_range(0, this->layers.size() - 1), + [this, ®ion, region_id](const tbb::blocked_range& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + PARALLEL_FOR_CANCEL; + LayerRegion &layerm = *this->layers[layer_idx]->regions[region_id]; + const LayerRegion &upper_layerm = *this->layers[layer_idx+1]->regions[region_id]; + const Polygons upper_layerm_polygons = upper_layerm.slices; + // Filter upper layer polygons in intersection_ppl by their bounding boxes? + // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; + const double total_loop_length = total_length(upper_layerm_polygons); + const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing(); + const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter); + const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width(); + const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing(); + + for (Surface &slice : layerm.slices.surfaces) { + for (;;) { + // compute the total thickness of perimeters + const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2 + + (region.config.perimeters-1 + slice.extra_perimeters) * perimeter_spacing; + // define a critical area where we don't want the upper slice to fall into + // (it should either lay over our perimeters or outside this area) + const coord_t critical_area_depth = coord_t(perimeter_spacing * 1.5); + const Polygons critical_area = diff( + offset(slice.expolygon, float(- perimeters_thickness)), + offset(slice.expolygon, float(- perimeters_thickness - critical_area_depth)) + ); + // check whether a portion of the upper slices falls inside the critical area + const Polylines intersection = intersection_pl(to_polylines(upper_layerm_polygons), critical_area); + // only add an additional loop if at least 30% of the slice loop would benefit from it + if (total_length(intersection) <= total_loop_length*0.3) + break; + /* + if (0) { + require "Slic3r/SVG.pm"; + Slic3r::SVG::output( + "extra.svg", + no_arrows => 1, + expolygons => union_ex($critical_area), + polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ], + ); + } + */ + ++ slice.extra_perimeters; + } + #ifdef DEBUG + if (slice.extra_perimeters > 0) + printf(" adding %d more perimeter(s) at layer %zu\n", slice.extra_perimeters, layer_idx); + #endif + } + } + }); + BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - end"; + } + + BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start"; + tbb::parallel_for( + tbb::blocked_range(0, this->layers.size()), + [this](const tbb::blocked_range& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + PARALLEL_FOR_CANCEL; + this->layers[layer_idx]->make_perimeters(); + } + } + ); + BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end"; + + /* + simplify slices (both layer and region slices), + we only need the max resolution for perimeters + ### This makes this method not-idempotent, so we keep it disabled for now. + ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); + */ + + this->state.set_done(posPerimeters); +} + +void PrintObject::prepare_infill() +{ + if (this->state.is_done(posPrepareInfill)) + return; + + this->state.set_started(posPrepareInfill); + this->_print->set_status(30, "Preparing infill"); + + // This will assign a type (top/bottom/internal) to $layerm->slices. + // Then the classifcation of $layerm->slices is transfered onto + // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces + // by the cummulative area of the previous $layerm->fill_surfaces. + this->detect_surfaces_type(); + + // Decide what surfaces are to be filled. + // Here the S_TYPE_TOP / S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is turned to just S_TYPE_INTERNAL if zero top / bottom infill layers are configured. + // Also tiny S_TYPE_INTERNAL surfaces are turned to S_TYPE_INTERNAL_SOLID. + BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..."; + for (auto *layer : this->layers) + for (auto *region : layer->regions) + region->prepare_fill_surfaces(); + + // this will detect bridges and reverse bridges + // and rearrange top/bottom/internal surfaces + // It produces enlarged overlapping bridging areas. + // + // 1) S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is grown by 3mm and clipped by the total infill area. Bridges are detected. The areas may overlap. + // 2) S_TYPE_TOP is grown by 3mm and clipped by the grown bottom areas. The areas may overlap. + // 3) Clip the internal surfaces by the grown top/bottom surfaces. + // 4) Merge surfaces with the same style. This will mostly get rid of the overlaps. + //FIXME This does not likely merge surfaces, which are supported by a material with different colors, but same properties. + this->process_external_surfaces(); + + // Add solid fills to ensure the shell vertical thickness. + this->discover_vertical_shells(); + + // Debugging output. +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { + for (const Layer *layer : this->layers) { + LayerRegion *layerm = layer->regions[region_id]; + layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final"); + layerm->export_region_fill_surfaces_to_svg_debug("6_discover_vertical_shells-final"); + } // for each layer + } // for each region +#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + + // Detect, which fill surfaces are near external layers. + // They will be split in internal and internal-solid surfaces. + // The purpose is to add a configurable number of solid layers to support the TOP surfaces + // and to add a configurable number of solid layers above the BOTTOM / BOTTOMBRIDGE surfaces + // to close these surfaces reliably. + //FIXME Vojtech: Is this a good place to add supporting infills below sloping perimeters? + this->discover_horizontal_shells(); + +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { + for (const Layer *layer : this->layers) { + LayerRegion *layerm = layer->regions[region_id]; + layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final"); + layerm->export_region_fill_surfaces_to_svg_debug("7_discover_horizontal_shells-final"); + } // for each layer + } // for each region +#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + + // Only active if config->infill_only_where_needed. This step trims the sparse infill, + // so it acts as an internal support. It maintains all other infill types intact. + // Here the internal surfaces and perimeters have to be supported by the sparse infill. + //FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support. + // Likely the sparse infill will not be anchored correctly, so it will not work as intended. + // Also one wishes the perimeters to be supported by a full infill. + this->clip_fill_surfaces(); + +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { + for (const Layer *layer : this->layers) { + LayerRegion *layerm = layer->regions[region_id]; + layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final"); + layerm->export_region_fill_surfaces_to_svg_debug("8_clip_surfaces-final"); + } // for each layer + } // for each region +#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + + // the following step needs to be done before combination because it may need + // to remove only half of the combined infill + this->bridge_over_infill(); + + // combine fill surfaces to honor the "infill every N layers" option + this->combine_infill(); + +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { + for (const Layer *layer : this->layers) { + LayerRegion *layerm = layer->regions[region_id]; + layerm->export_region_slices_to_svg_debug("9_prepare_infill-final"); + layerm->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final"); + } // for each layer + } // for each region + for (const Layer *layer : this->layers) { + layer->export_region_slices_to_svg_debug("9_prepare_infill-final"); + layer->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final"); + } // for each layer +#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + + this->state.set_done(posPrepareInfill); +} + +void PrintObject::infill() +{ + // prerequisites + this->prepare_infill(); + + if (! this->state.is_done(posInfill)) { + this->state.set_started(posInfill); + BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; + tbb::parallel_for( + tbb::blocked_range(0, this->layers.size()), + [this](const tbb::blocked_range& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + PARALLEL_FOR_CANCEL; + this->layers[layer_idx]->make_fills(); + } + } + ); + BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - end"; + /* we could free memory now, but this would make this step not idempotent + ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; + */ + this->state.set_done(posInfill); + } +} + +void PrintObject::generate_support_material() +{ + if (! this->state.is_done(posSupportMaterial)) { + this->state.set_started(posSupportMaterial); + this->clear_support_layers(); + if ((this->config.support_material || this->config.raft_layers > 0) && this->layers.size() > 1) { + this->_print->set_status(85, "Generating support material"); + this->_generate_support_material(); + } + this->state.set_done(posSupportMaterial); + char stats[128]; + //FIXME this does not belong here! Why should the status bar be updated with the object weight + // at the end of object's support.? + sprintf(stats, "Weight: %.1lfg, Cost: %.1lf", this->_print->total_weight, this->_print->total_cost); + this->_print->set_status(85, stats); + } +} + void PrintObject::clear_layers() { for (Layer *l : this->layers) @@ -282,105 +583,6 @@ bool PrintObject::has_support_material() const || this->config.support_material_enforce_layers > 0; } -void PrintObject::_prepare_infill() -{ - // This will assign a type (top/bottom/internal) to $layerm->slices. - // Then the classifcation of $layerm->slices is transfered onto - // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces - // by the cummulative area of the previous $layerm->fill_surfaces. - this->detect_surfaces_type(); - - // Decide what surfaces are to be filled. - // Here the S_TYPE_TOP / S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is turned to just S_TYPE_INTERNAL if zero top / bottom infill layers are configured. - // Also tiny S_TYPE_INTERNAL surfaces are turned to S_TYPE_INTERNAL_SOLID. - BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..."; - for (auto *layer : this->layers) - for (auto *region : layer->regions) - region->prepare_fill_surfaces(); - - // this will detect bridges and reverse bridges - // and rearrange top/bottom/internal surfaces - // It produces enlarged overlapping bridging areas. - // - // 1) S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is grown by 3mm and clipped by the total infill area. Bridges are detected. The areas may overlap. - // 2) S_TYPE_TOP is grown by 3mm and clipped by the grown bottom areas. The areas may overlap. - // 3) Clip the internal surfaces by the grown top/bottom surfaces. - // 4) Merge surfaces with the same style. This will mostly get rid of the overlaps. - //FIXME This does not likely merge surfaces, which are supported by a material with different colors, but same properties. - this->process_external_surfaces(); - - // Add solid fills to ensure the shell vertical thickness. - this->discover_vertical_shells(); - - // Debugging output. -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (const Layer *layer : this->layers) { - LayerRegion *layerm = layer->regions[region_id]; - layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final"); - layerm->export_region_fill_surfaces_to_svg_debug("6_discover_vertical_shells-final"); - } // for each layer - } // for each region -#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - - // Detect, which fill surfaces are near external layers. - // They will be split in internal and internal-solid surfaces. - // The purpose is to add a configurable number of solid layers to support the TOP surfaces - // and to add a configurable number of solid layers above the BOTTOM / BOTTOMBRIDGE surfaces - // to close these surfaces reliably. - //FIXME Vojtech: Is this a good place to add supporting infills below sloping perimeters? - this->discover_horizontal_shells(); - -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (const Layer *layer : this->layers) { - LayerRegion *layerm = layer->regions[region_id]; - layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final"); - layerm->export_region_fill_surfaces_to_svg_debug("7_discover_horizontal_shells-final"); - } // for each layer - } // for each region -#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - - // Only active if config->infill_only_where_needed. This step trims the sparse infill, - // so it acts as an internal support. It maintains all other infill types intact. - // Here the internal surfaces and perimeters have to be supported by the sparse infill. - //FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support. - // Likely the sparse infill will not be anchored correctly, so it will not work as intended. - // Also one wishes the perimeters to be supported by a full infill. - this->clip_fill_surfaces(); - -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (const Layer *layer : this->layers) { - LayerRegion *layerm = layer->regions[region_id]; - layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final"); - layerm->export_region_fill_surfaces_to_svg_debug("8_clip_surfaces-final"); - } // for each layer - } // for each region -#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - - // the following step needs to be done before combination because it may need - // to remove only half of the combined infill - this->bridge_over_infill(); - - // combine fill surfaces to honor the "infill every N layers" option - this->combine_infill(); - -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (const Layer *layer : this->layers) { - LayerRegion *layerm = layer->regions[region_id]; - layerm->export_region_slices_to_svg_debug("9_prepare_infill-final"); - layerm->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final"); - } // for each layer - } // for each region - for (const Layer *layer : this->layers) { - layer->export_region_slices_to_svg_debug("9_prepare_infill-final"); - layer->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final"); - } // for each layer -#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ -} - // This function analyzes slices of a region (SurfaceCollection slices). // Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface. // Initially all slices are of type stInternal. @@ -427,6 +629,7 @@ void PrintObject::detect_surfaces_type() (this->config.support_material.value && this->config.support_material_contact_distance.value == 0) ? stBottom : stBottomBridge; for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { + PARALLEL_FOR_CANCEL; // BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z; Layer *layer = this->layers[idx_layer]; LayerRegion *layerm = layer->get_region(idx_region); @@ -564,6 +767,7 @@ void PrintObject::detect_surfaces_type() tbb::blocked_range(0, this->layers.size()), [this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range& range) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { + PARALLEL_FOR_CANCEL; LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region); layerm->slices_to_fill_surfaces_clipped(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -590,6 +794,7 @@ void PrintObject::process_external_surfaces() tbb::blocked_range(0, this->layers.size()), [this, region_id](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + PARALLEL_FOR_CANCEL; // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << this->layers[layer_idx]->print_z; this->layers[layer_idx]->get_region(region_id)->process_external_surfaces((layer_idx == 0) ? NULL : this->layers[layer_idx - 1]); } @@ -638,6 +843,7 @@ void PrintObject::discover_vertical_shells() const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; const size_t num_regions = this->_print->regions.size(); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { + PARALLEL_FOR_CANCEL; const Layer &layer = *this->layers[idx_layer]; DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[idx_layer]; // Simulate single set of perimeters over all merged regions. @@ -720,6 +926,7 @@ void PrintObject::discover_vertical_shells() [this, idx_region, &cache_top_botom_regions](const tbb::blocked_range& range) { const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { + PARALLEL_FOR_CANCEL; Layer &layer = *this->layers[idx_layer]; LayerRegion &layerm = *layer.regions[idx_region]; float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f; @@ -748,7 +955,7 @@ void PrintObject::discover_vertical_shells() // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { PROFILE_BLOCK(discover_vertical_shells_region_layer); - + PARALLEL_FOR_CANCEL; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING static size_t debug_idx = 0; ++ debug_idx; @@ -1265,6 +1472,7 @@ end: tbb::blocked_range(0, this->layers.size()), [this](const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { + PARALLEL_FOR_CANCEL; Layer *layer = this->layers[layer_id]; // Apply size compensation and perform clipping of multi-part objects. float delta = float(scale_(this->config.xy_size_compensation.value)); @@ -1348,6 +1556,7 @@ std::string PrintObject::_fix_slicing_errors() tbb::blocked_range(0, buggy_layers.size()), [this, &buggy_layers](const tbb::blocked_range& range) { for (size_t buggy_layer_idx = range.begin(); buggy_layer_idx < range.end(); ++ buggy_layer_idx) { + PARALLEL_FOR_CANCEL; size_t idx_layer = buggy_layers[buggy_layer_idx]; Layer *layer = this->layers[idx_layer]; assert(layer->slicing_errors); @@ -1424,6 +1633,7 @@ void PrintObject::_simplify_slices(double distance) tbb::blocked_range(0, this->layers.size()), [this, distance](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + PARALLEL_FOR_CANCEL; Layer *layer = this->layers[layer_idx]; for (size_t region_idx = 0; region_idx < layer->regions.size(); ++ region_idx) layer->regions[region_idx]->slices.simplify(distance); @@ -1433,137 +1643,6 @@ void PrintObject::_simplify_slices(double distance) BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - end"; } -void PrintObject::_make_perimeters() -{ - if (this->state.is_done(posPerimeters)) return; - this->state.set_started(posPerimeters); - - BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; - - // merge slices if they were split into types - if (this->typed_slices) { - FOREACH_LAYER(this, layer_it) - (*layer_it)->merge_slices(); - this->typed_slices = false; - this->state.invalidate(posPrepareInfill); - } - - // compare each layer to the one below, and mark those slices needing - // one additional inner perimeter, like the top of domed objects- - - // this algorithm makes sure that at least one perimeter is overlapping - // but we don't generate any extra perimeter if fill density is zero, as they would be floating - // inside the object - infill_only_where_needed should be the method of choice for printing - // hollow objects - FOREACH_REGION(this->_print, region_it) { - size_t region_id = region_it - this->_print->regions.begin(); - const PrintRegion ®ion = **region_it; - - - if (!region.config.extra_perimeters - || region.config.perimeters == 0 - || region.config.fill_density == 0 - || this->layer_count() < 2) - continue; - - BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start"; - tbb::parallel_for( - tbb::blocked_range(0, this->layers.size() - 1), - [this, ®ion, region_id](const tbb::blocked_range& range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - LayerRegion &layerm = *this->layers[layer_idx]->regions[region_id]; - const LayerRegion &upper_layerm = *this->layers[layer_idx+1]->regions[region_id]; - const Polygons upper_layerm_polygons = upper_layerm.slices; - // Filter upper layer polygons in intersection_ppl by their bounding boxes? - // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; - const double total_loop_length = total_length(upper_layerm_polygons); - const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing(); - const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter); - const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width(); - const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing(); - - for (Surface &slice : layerm.slices.surfaces) { - for (;;) { - // compute the total thickness of perimeters - const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2 - + (region.config.perimeters-1 + slice.extra_perimeters) * perimeter_spacing; - // define a critical area where we don't want the upper slice to fall into - // (it should either lay over our perimeters or outside this area) - const coord_t critical_area_depth = coord_t(perimeter_spacing * 1.5); - const Polygons critical_area = diff( - offset(slice.expolygon, float(- perimeters_thickness)), - offset(slice.expolygon, float(- perimeters_thickness - critical_area_depth)) - ); - // check whether a portion of the upper slices falls inside the critical area - const Polylines intersection = intersection_pl(to_polylines(upper_layerm_polygons), critical_area); - // only add an additional loop if at least 30% of the slice loop would benefit from it - if (total_length(intersection) <= total_loop_length*0.3) - break; - /* - if (0) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output( - "extra.svg", - no_arrows => 1, - expolygons => union_ex($critical_area), - polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ], - ); - } - */ - ++ slice.extra_perimeters; - } - #ifdef DEBUG - if (slice.extra_perimeters > 0) - printf(" adding %d more perimeter(s) at layer %zu\n", slice.extra_perimeters, layer_idx); - #endif - } - } - }); - BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - end"; - } - - BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start"; - tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), - [this](const tbb::blocked_range& range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) - this->layers[layer_idx]->make_perimeters(); - } - ); - BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end"; - - /* - simplify slices (both layer and region slices), - we only need the max resolution for perimeters - ### This makes this method not-idempotent, so we keep it disabled for now. - ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); - */ - - this->state.set_done(posPerimeters); -} - -void PrintObject::_infill() -{ - if (this->state.is_done(posInfill)) return; - this->state.set_started(posInfill); - - BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; - tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), - [this](const tbb::blocked_range& range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) - this->layers[layer_idx]->make_fills(); - } - ); - BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - end"; - - /* we could free memory now, but this would make this step not idempotent - ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; - */ - - this->state.set_done(posInfill); -} - // Only active if config->infill_only_where_needed. This step trims the sparse infill, // so it acts as an internal support. It maintains all other infill types intact. // Here the internal surfaces and perimeters have to be supported by the sparse infill. diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index d7c9a590a0..58d553966d 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -56,6 +56,7 @@ REGISTER_CLASS(SurfaceCollection, "Surface::Collection"); REGISTER_CLASS(PrintObjectSupportMaterial, "Print::SupportMaterial2"); REGISTER_CLASS(TriangleMesh, "TriangleMesh"); REGISTER_CLASS(AppConfig, "GUI::AppConfig"); +REGISTER_CLASS(BackgroundSlicingProcess, "GUI::BackgroundSlicingProcess"); REGISTER_CLASS(GLShader, "GUI::_3DScene::GLShader"); REGISTER_CLASS(GLVolume, "GUI::_3DScene::GLVolume"); REGISTER_CLASS(GLVolumeCollection, "GUI::_3DScene::GLVolume::Collection"); diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp new file mode 100644 index 0000000000..900fdfa43b --- /dev/null +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -0,0 +1,138 @@ +#include "BackgroundSlicingProcess.hpp" +#include "GUI.hpp" + +#include "../../libslic3r/Print.hpp" + +#include +#include + +//#undef NDEBUG +#include +#include + +namespace Slic3r { + +namespace GUI { + extern wxPanel *g_wxPlater; +}; + +void BackgroundSlicingProcess::thread_proc() +{ + std::unique_lock lck(m_mutex); + // Let the caller know we are ready to run the background processing task. + m_state = STATE_IDLE; + lck.unlock(); + m_condition.notify_one(); + for (;;) { + assert(m_state == STATE_IDLE); + // Wait until a new task is ready to be executed, or this thread should be finished. + lck.lock(); + m_condition.wait(lck, [this](){ return m_state == STATE_STARTED || m_state == STATE_EXIT; }); + if (m_state == STATE_EXIT) + // Exiting this thread. + break; + // Process the background slicing task. + m_state = STATE_RUNNING; + lck.unlock(); + std::string error; + try { + assert(m_print != nullptr); + m_print->process(); + if (m_print->canceled()) + return; + printf("PReparing m_event_sliced_id command\n"); + wxCommandEvent evt(m_event_sliced_id); + printf("Issuing m_event_sliced_id command\n"); + wxQueueEvent(GUI::g_wxPlater, evt.Clone()); + GUI::g_wxPlater->ProcessWindowEvent(evt); + //GUI::g_wxPlater->ProcessEvent(evt); + printf("Done with m_event_sliced_id command\n"); + m_print->export_gcode(m_output_path, m_gcode_preview_data); + } catch (std::exception &ex) { + error = ex.what(); + } catch (...) { + error = "Unknown C++ exception."; + } + lck.lock(); + m_state = m_print->canceled() ? STATE_CANCELED : STATE_FINISHED; + wxCommandEvent evt(m_event_finished_id); + evt.SetString(error); + evt.SetInt(m_print->canceled() ? -1 : (error.empty() ? 1 : 0)); + wxQueueEvent(GUI::g_wxPlater, evt.Clone()); + lck.unlock(); + // Let the UI thread wake up if it is waiting for the background task to finish. + m_condition.notify_one(); + // Let the UI thread see the result. + } + m_state = STATE_EXITED; + lck.unlock(); + // End of the background processing thread. The UI thread should join m_thread now. +} + +void BackgroundSlicingProcess::join_background_thread() +{ + std::unique_lock lck(m_mutex); + if (m_state == STATE_INITIAL) { + // Worker thread has not been started yet. + assert(! m_thread.joinable()); + } else { + assert(m_state == STATE_IDLE); + assert(m_thread.joinable()); + // Notify the worker thread to exit. + m_state = STATE_EXIT; + lck.unlock(); + m_condition.notify_one(); + // Wait until the worker thread exits. + m_thread.join(); + } +} + +bool BackgroundSlicingProcess::start() +{ + std::unique_lock lck(m_mutex); + if (m_state == STATE_INITIAL) { + // The worker thread is not running yet. Start it. + assert(! m_thread.joinable()); + m_thread = std::thread([this]{this->thread_proc();}); + // Wait until the worker thread is ready to execute the background processing task. + m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; }); + } + assert(m_state == STATE_IDLE || this->running()); + if (this->running()) + // The background processing thread is already running. + return false; + if (! this->idle()) + throw std::runtime_error("Cannot start a background task, the worker thread is not idle."); + m_state = STATE_STARTED; + lck.unlock(); + m_condition.notify_one(); + return true; +} + +bool BackgroundSlicingProcess::stop() +{ + std::unique_lock lck(m_mutex); + if (m_state == STATE_INITIAL) + return false; + assert(this->running()); + if (m_state == STATE_STARTED || m_state == STATE_RUNNING) { + m_print->cancel(); + // Wait until the background processing stops by being canceled. + lck.unlock(); + m_condition.wait(lck, [this](){ return m_state == STATE_CANCELED; }); + } + return true; +} + +// Apply config over the print. Returns false, if the new config values caused any of the already +// processed steps to be invalidated, therefore the task will need to be restarted. +bool BackgroundSlicingProcess::apply_config(DynamicPrintConfig *config) +{ + /* + // apply new config + my $invalidated = $self->{print}->apply_config(wxTheApp->{preset_bundle}->full_config); + */ + return true; +} + +}; // namespace Slic3r diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp new file mode 100644 index 0000000000..988138d2eb --- /dev/null +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -0,0 +1,82 @@ +#ifndef slic3r_GUI_BackgroundSlicingProcess_hpp_ +#define slic3r_GUI_BackgroundSlicingProcess_hpp_ + +#include +#include +#include +#include + +namespace Slic3r { + +class DynamicPrintConfig; +class GCodePreviewData; +class Print; + +// Support for the GUI background processing (Slicing and G-code generation). +// As of now this class is not declared in Slic3r::GUI due to the Perl bindings limits. +class BackgroundSlicingProcess +{ +public: + BackgroundSlicingProcess() {} + ~BackgroundSlicingProcess() { this->stop(); this->join_background_thread(); } + + void set_print(Print *print) { m_print = print; } + void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; } + void set_sliced_event(int event_id) { m_event_sliced_id = event_id; } + void set_finished_event(int event_id) { m_event_finished_id = event_id; } + + // Start the background processing. Returns false if the background processing was already running. + bool start(); + // Cancel the background processing. Returns false if the background processing was not running. + // A stopped background processing may be restarted with start(). + bool stop(); + + // Apply config over the print. Returns false, if the new config values caused any of the already + // processed steps to be invalidated, therefore the task will need to be restarted. + bool apply_config(DynamicPrintConfig *config); + + enum State { + // m_thread is not running yet, or it did not reach the STATE_IDLE yet (it does not wait on the condition yet). + STATE_INITIAL = 0, + // m_thread is waiting for the task to execute. + STATE_IDLE, + STATE_STARTED, + // m_thread is executing a task. + STATE_RUNNING, + // m_thread finished executing a task, and it is waiting until the UI thread picks up the results. + STATE_FINISHED, + // m_thread finished executing a task, the task has been canceled by the UI thread, therefore the UI thread will not be notified. + STATE_CANCELED, + // m_thread exited the loop and it is going to finish. The UI thread should join on m_thread. + STATE_EXIT, + STATE_EXITED, + }; + State state() const { return m_state; } + bool idle() const { return m_state == STATE_IDLE; } + bool running() const { return m_state == STATE_STARTED || m_state == STATE_RUNNING || m_state == STATE_FINISHED || m_state == STATE_CANCELED; } + +private: + void thread_proc(); + void start_background_thread(); + void join_background_thread(); + + Print *m_print = nullptr; + GCodePreviewData *m_gcode_preview_data = nullptr; + std::string m_output_path; + // Thread, on which the background processing is executed. The thread will always be present + // and ready to execute the slicing process. + std::thread m_thread; + // Mutex and condition variable to synchronize m_thread with the UI thread. + std::mutex m_mutex; + std::condition_variable m_condition; + State m_state = STATE_INITIAL; + + // wxWidgets command ID to be sent to the platter to inform that the slicing is finished, and the G-code export will continue. + int m_event_sliced_id = 0; + // wxWidgets command ID to be sent to the platter to inform that the task finished. + int m_event_finished_id = 0; +}; + +}; // namespace Slic3r + +#endif /* slic3r_GUI_BackgroundSlicingProcess_hpp_ */ diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 0410b7969c..270a5d1986 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -47,6 +47,8 @@ #include "Preferences.hpp" #include "PresetBundle.hpp" +#include "../../libslic3r/Print.hpp" + namespace Slic3r { namespace GUI { #if __APPLE__ @@ -172,6 +174,7 @@ void break_to_debugger() wxApp *g_wxApp = nullptr; wxFrame *g_wxMainFrame = nullptr; wxNotebook *g_wxTabPanel = nullptr; +wxPanel *g_wxPlater = nullptr; AppConfig *g_AppConfig = nullptr; PresetBundle *g_PresetBundle= nullptr; @@ -197,6 +200,11 @@ void set_tab_panel(wxNotebook *tab_panel) g_wxTabPanel = tab_panel; } +void set_plater(wxPanel *plater) +{ + g_wxPlater = plater; +} + void set_app_config(AppConfig *app_config) { g_AppConfig = app_config; @@ -507,6 +515,18 @@ void warning_catcher(wxWindow* parent, wxString message){ msg->ShowModal(); } +// Assign a Lambda to the print object to emit a wxWidgets Command with the provided ID +// to deliver a progress status message. +void set_print_callback_event(Print *print, int id) +{ + print->set_status_callback([id](int percent, const std::string &message){ + wxCommandEvent event(id); + event.SetInt(percent); + event.SetString(message); + wxQueueEvent(g_wxMainFrame, event.Clone()); + }); +} + wxApp* get_app(){ return g_wxApp; } diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 084b6de466..506454e56d 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -11,6 +11,7 @@ class wxFrame; class wxWindow; class wxMenuBar; class wxNotebook; +class wxPanel; class wxComboCtrl; class wxString; class wxArrayString; @@ -23,6 +24,7 @@ namespace Slic3r { class PresetBundle; class PresetCollection; +class Print; class AppConfig; class DynamicPrintConfig; class TabIface; @@ -73,6 +75,7 @@ void break_to_debugger(); void set_wxapp(wxApp *app); void set_main_frame(wxFrame *main_frame); void set_tab_panel(wxNotebook *tab_panel); +void set_plater(wxPanel *plater); void set_app_config(AppConfig *app_config); void set_preset_bundle(PresetBundle *preset_bundle); @@ -98,6 +101,10 @@ void show_error(wxWindow* parent, wxString message); void show_info(wxWindow* parent, wxString message, wxString title); void warning_catcher(wxWindow* parent, wxString message); +// Assign a Lambda to the print object to emit a wxWidgets Command with the provided ID +// to deliver a progress status message. +void set_print_callback_event(Print *print, int id); + // load language saved at application config bool load_language(); // save language at application config diff --git a/xs/t/20_print.t b/xs/t/20_print.t index e535cdd8cb..0ef194ecf5 100644 --- a/xs/t/20_print.t +++ b/xs/t/20_print.t @@ -10,8 +10,6 @@ use Test::More tests => 5; my $print = Slic3r::Print->new; isa_ok $print, 'Slic3r::Print'; isa_ok $print->config, 'Slic3r::Config::Static::Ref'; - isa_ok $print->default_object_config, 'Slic3r::Config::Static::Ref'; - isa_ok $print->default_region_config, 'Slic3r::Config::Static::Ref'; isa_ok $print->placeholder_parser, 'Slic3r::GCode::PlaceholderParser::Ref'; } diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index d306f12ce5..a7f3bd554b 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -32,6 +32,9 @@ void set_main_frame(SV *ui) void set_tab_panel(SV *ui) %code%{ Slic3r::GUI::set_tab_panel((wxNotebook*)wxPli_sv_2_object(aTHX_ ui, "Wx::Notebook")); %}; + +void set_plater(SV *ui) + %code%{ Slic3r::GUI::set_plater((wxPanel*)wxPli_sv_2_object(aTHX_ ui, "Wx::Panel")); %}; void add_debug_menu(SV *ui, int event_language_change) %code%{ Slic3r::GUI::add_debug_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_language_change); %}; @@ -65,5 +68,8 @@ void add_frequently_changed_parameters(SV *ui_parent, SV *ui_sizer, SV *ui_p_siz (wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"), (wxFlexGridSizer*)wxPli_sv_2_object(aTHX_ ui_p_sizer, "Wx::FlexGridSizer")); %}; +void set_print_callback_event(Print *print, int id) + %code%{ Slic3r::GUI::set_print_callback_event(print, id); %}; + std::string fold_utf8_to_ascii(const char *src) %code%{ RETVAL = Slic3r::fold_utf8_to_ascii(src); %}; diff --git a/xs/xsp/GUI_BackgroundSlicingProcess.xsp b/xs/xsp/GUI_BackgroundSlicingProcess.xsp new file mode 100644 index 0000000000..d12c7f8e3b --- /dev/null +++ b/xs/xsp/GUI_BackgroundSlicingProcess.xsp @@ -0,0 +1,23 @@ + +%module{Slic3r::XS}; + +%{ +#include +#include "slic3r/GUI/BackgroundSlicingProcess.hpp" +%} + +%name{Slic3r::GUI::BackgroundSlicingProcess} class BackgroundSlicingProcess { + BackgroundSlicingProcess(); + ~BackgroundSlicingProcess(); + + void set_print(Print *print); + void set_gcode_preview_data(GCodePreviewData *gpd); + void set_sliced_event(int event_id); + void set_finished_event(int event_id); + + bool start(); + bool stop(); + bool apply_config(DynamicPrintConfig *config); + + bool running(); +}; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index cbc04a804f..4ad4f74b61 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -27,7 +27,6 @@ _constant() %} - %name{Slic3r::Print::Region} class PrintRegion { // owned by Print, no constructor/destructor @@ -39,16 +38,9 @@ _constant() %code%{ RETVAL = THIS->flow(role, layer_height, bridge, first_layer, width, *object); %}; }; - %name{Slic3r::Print::Object} class PrintObject { // owned by Print, no constructor/destructor - void add_region_volume(int region_id, int volume_id); - std::vector get_region_volumes(int region_id) - %code%{ - if (0 <= region_id && region_id < THIS->region_volumes.size()) - RETVAL = THIS->region_volumes[region_id]; - %}; int region_count() %code%{ RETVAL = THIS->print()->regions.size(); %}; @@ -67,57 +59,22 @@ _constant() Points _shifted_copies() %code%{ RETVAL = THIS->_shifted_copies; %}; - void set_shifted_copies(Points value) - %code%{ THIS->_shifted_copies = value; %}; bool add_copy(Pointf* point) %code%{ RETVAL = THIS->add_copy(*point); %}; bool delete_last_copy(); - bool delete_all_copies(); - bool set_copies(Points copies); bool reload_model_instances(); void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges) %code%{ THIS->layer_height_ranges = layer_height_ranges; %}; - void set_layer_height_profile(std::vector profile) - %code%{ THIS->layer_height_profile = profile; %}; - size_t total_layer_count(); size_t layer_count(); - void clear_layers(); Ref get_layer(int idx); - Ref add_layer(int id, coordf_t height, coordf_t print_z, - coordf_t slice_z); size_t support_layer_count(); - void clear_support_layers(); Ref get_support_layer(int idx); bool step_done(PrintObjectStep step) %code%{ RETVAL = THIS->state.is_done(step); %}; - void set_step_done(PrintObjectStep step) - %code%{ THIS->state.set_done(step); %}; - void set_step_started(PrintObjectStep step) - %code%{ THIS->state.set_started(step); %}; - - void _slice(); - std::string _fix_slicing_errors(); - void _simplify_slices(double distance); - void _prepare_infill(); - void detect_surfaces_type(); - void process_external_surfaces(); - void _make_perimeters(); - void _infill(); - void _generate_support_material(); - - std::vector get_layer_height_min_max() - %code%{ - SlicingParameters slicing_params = THIS->slicing_parameters(); - RETVAL.push_back(slicing_params.min_layer_height); - RETVAL.push_back(slicing_params.max_layer_height); - RETVAL.push_back(slicing_params.first_print_layer_height); - RETVAL.push_back(slicing_params.first_object_layer_height); - RETVAL.push_back(slicing_params.layer_height); - %}; void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action) %code%{ @@ -129,25 +86,16 @@ _constant() %}; void reset_layer_height_profile(); - - int ptr() - %code%{ RETVAL = (int)(intptr_t)THIS; %}; }; - %name{Slic3r::Print} class Print { Print(); ~Print(); Ref config() %code%{ RETVAL = &THIS->config; %}; - Ref default_object_config() - %code%{ RETVAL = &THIS->default_object_config; %}; - Ref default_region_config() - %code%{ RETVAL = &THIS->default_region_config; %}; Ref placeholder_parser() %code%{ RETVAL = &THIS->placeholder_parser; %}; - // TODO: status_cb Ref skirt() %code%{ RETVAL = &THIS->skirt; %}; Ref brim() @@ -176,20 +124,7 @@ _constant() %code%{ RETVAL = THIS->state.is_done(step); %}; bool object_step_done(PrintObjectStep step) %code%{ RETVAL = THIS->step_done(step); %}; - void set_step_done(PrintStep step) - %code%{ THIS->state.set_done(step); %}; - void set_step_started(PrintStep step) - %code%{ THIS->state.set_started(step); %}; - void clear_filament_stats() - %code%{ - THIS->filament_stats.clear(); - %}; - void set_filament_stats(int extruder_id, float length) - %code%{ - THIS->filament_stats.insert(std::pair(extruder_id, 0)); - THIS->filament_stats[extruder_id] += length; - %}; SV* filament_stats() %code%{ HV* hv = newHV(); @@ -203,7 +138,6 @@ _constant() RETVAL = newRV_noinc((SV*)hv); } %}; - void _simplify_slices(double distance); double max_allowed_layer_height() const; bool has_support_material() const; void auto_assign_extruders(ModelObject* model_object); @@ -220,7 +154,6 @@ _constant() bool apply_config(DynamicPrintConfig* config) %code%{ RETVAL = THIS->apply_config(*config); %}; bool has_infinite_skirt(); - bool has_skirt(); std::vector extruders() const; int validate() %code%{ std::string err = THIS->validate(); @@ -230,16 +163,33 @@ _constant() %}; Clone bounding_box(); Clone total_bounding_box(); - double skirt_first_layer_height(); - Clone brim_flow(); - Clone skirt_flow(); - void _make_skirt(); - void _make_brim(); + void set_callback_event(int evt) %code%{ + %}; - bool has_wipe_tower(); - void _clear_wipe_tower(); - void _make_wipe_tower(); + void process() %code%{ + try { + THIS->process(); + } catch (std::exception& e) { + croak(e.what()); + } + %}; + + void export_gcode_with_preview_data(char *path_template, GCodePreviewData *preview_data) %code%{ + try { + THIS->export_gcode(path_template, preview_data); + } catch (std::exception& e) { + croak(e.what()); + } + %}; + + void export_gcode(char *path_template) %code%{ + try { + THIS->export_gcode(path_template, nullptr); + } catch (std::exception& e) { + croak(e.what()); + } + %}; %{ diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 87a8d8d866..67693685ad 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -217,6 +217,8 @@ Clone O_OBJECT_SLIC3R_T AppConfig* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T +BackgroundSlicingProcess* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T GLShader* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 0214a158d6..461a12e747 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -196,6 +196,8 @@ %typemap{Clone}{simple}; %typemap{AppConfig*}; %typemap{Ref}{simple}; +%typemap{BackgroundSlicingProcess*}; +%typemap{Ref}{simple}; %typemap{GLShader*}; %typemap{Ref}{simple}; %typemap{GLVolume*}; From 5063c15442ec93c3c2dad3786b4007b2a741c504 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 23 Mar 2018 16:00:00 +0100 Subject: [PATCH 02/36] Fixed automatic tests. --- lib/Slic3r/Test.pm | 3 ++- xs/src/libslic3r/Print.hpp | 12 +++++++++--- xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp | 8 +++++++- xs/t/20_print.t | 2 +- xs/xsp/Print.xsp | 3 +++ 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 6471ae1239..c0137f9e44 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -208,8 +208,9 @@ sub gcode { my $gcode_temp_path = abs_path($0) . '.gcode.temp'; # Remove the existing temp file. unlink $gcode_temp_path; + $print->set_status_silent; $print->process; - $print->export_gcode(output_file => $gcode_temp_path, quiet => 1); + $print->export_gcode($gcode_temp_path); # Read the temoprary G-code file. my $gcode; { diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index dfde2650ad..341e48e299 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -192,8 +192,10 @@ public: // (layer height, first layer height, raft settings, print nozzle diameter etc). SlicingParameters slicing_parameters() const; -private: + // Called when slicing to SVG (see Print.pm sub export_svg), and used by perimeters.t void slice(); + +private: void make_perimeters(); void prepare_infill(); void infill(); @@ -304,9 +306,13 @@ public: std::string output_filepath(const std::string &path); typedef std::function status_callback_type; + // Default status console print out in the form of percent => message. + void set_status_default() { m_status_callback = nullptr; } + // No status output or callback whatsoever. + void set_status_silent() { m_status_callback = [](int, const std::string&){}; } + // Register a custom status callback. void set_status_callback(status_callback_type cb) { m_status_callback = cb; } - void reset_status_callback() { m_status_callback = nullptr; } - // Calls a registered callback to update the status. + // Calls a registered callback to update the status, or print out the default message. void set_status(int percent, const std::string &message) { if (m_status_callback) m_status_callback(percent, message); else printf("%d => %s\n", percent, message.c_str()); diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 988138d2eb..9dcce0af1f 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -18,11 +18,17 @@ class BackgroundSlicingProcess { public: BackgroundSlicingProcess() {} + // Stop the background processing and finalize the bacgkround processing thread. ~BackgroundSlicingProcess() { this->stop(); this->join_background_thread(); } void set_print(Print *print) { m_print = print; } void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; } + // The following wxCommandEvent will be sent to the UI thread / Platter window, when the slicing is finished + // and the background processing will transition into G-code export. + // The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed. void set_sliced_event(int event_id) { m_event_sliced_id = event_id; } + // The following wxCommandEvent will be sent to the UI thread / Platter window, when the G-code export is finished. + // The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed. void set_finished_event(int event_id) { m_event_finished_id = event_id; } // Start the background processing. Returns false if the background processing was already running. @@ -57,10 +63,10 @@ public: private: void thread_proc(); - void start_background_thread(); void join_background_thread(); Print *m_print = nullptr; + // Data structure, to which the G-code export writes its annotations. GCodePreviewData *m_gcode_preview_data = nullptr; std::string m_output_path; // Thread, on which the background processing is executed. The thread will always be present diff --git a/xs/t/20_print.t b/xs/t/20_print.t index 0ef194ecf5..68ec1e7195 100644 --- a/xs/t/20_print.t +++ b/xs/t/20_print.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 5; +use Test::More tests => 3; { my $print = Slic3r::Print->new; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 4ad4f74b61..a9744c3123 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -86,6 +86,8 @@ _constant() %}; void reset_layer_height_profile(); + + void slice(); }; %name{Slic3r::Print} class Print { @@ -166,6 +168,7 @@ _constant() void set_callback_event(int evt) %code%{ %}; + void set_status_silent(); void process() %code%{ try { From eed15c459b58dd819ca7826a69019e491e7b0702 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 23 Mar 2018 17:39:19 +0100 Subject: [PATCH 03/36] Use asynchronous wxWidgets messages when posting between threads. --- xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 900fdfa43b..15da2eb0cd 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -40,13 +40,7 @@ void BackgroundSlicingProcess::thread_proc() m_print->process(); if (m_print->canceled()) return; - printf("PReparing m_event_sliced_id command\n"); - wxCommandEvent evt(m_event_sliced_id); - printf("Issuing m_event_sliced_id command\n"); - wxQueueEvent(GUI::g_wxPlater, evt.Clone()); - GUI::g_wxPlater->ProcessWindowEvent(evt); - //GUI::g_wxPlater->ProcessEvent(evt); - printf("Done with m_event_sliced_id command\n"); + wxQueueEvent(GUI::g_wxPlater, new wxCommandEvent(m_event_sliced_id)); m_print->export_gcode(m_output_path, m_gcode_preview_data); } catch (std::exception &ex) { error = ex.what(); From 8639df1cfd25d3023b5b71b03f69faa203fa53b6 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 26 Mar 2018 11:21:53 +0200 Subject: [PATCH 04/36] Removed an incorrectly placed status line update, causing stack overflow in unit tests. --- xs/src/libslic3r/PrintObject.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index dd2c4e3776..e64c129696 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -397,11 +397,6 @@ void PrintObject::generate_support_material() this->_generate_support_material(); } this->state.set_done(posSupportMaterial); - char stats[128]; - //FIXME this does not belong here! Why should the status bar be updated with the object weight - // at the end of object's support.? - sprintf(stats, "Weight: %.1lfg, Cost: %.1lf", this->_print->total_weight, this->_print->total_cost); - this->_print->set_status(85, stats); } } From 33e1108f65ff3dc893ea73e56774ba3d5cf031c1 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 28 Mar 2018 17:05:31 +0200 Subject: [PATCH 05/36] Background processing in C++, WIP. --- lib/Slic3r/GUI/Plater.pm | 8 +- xs/src/libslic3r/GCode.cpp | 40 +++++- xs/src/libslic3r/GCode.hpp | 3 +- xs/src/libslic3r/Layer.cpp | 9 +- xs/src/libslic3r/LayerRegion.cpp | 9 +- xs/src/libslic3r/Print.cpp | 47 +++++--- xs/src/libslic3r/Print.hpp | 78 ++++++++---- xs/src/libslic3r/PrintObject.cpp | 114 ++++++++++++------ xs/src/libslic3r/PrintRegion.cpp | 3 +- xs/src/libslic3r/TriangleMesh.cpp | 37 +++--- xs/src/libslic3r/TriangleMesh.hpp | 11 +- xs/src/slic3r/GUI/3DScene.cpp | 17 ++- .../slic3r/GUI/BackgroundSlicingProcess.cpp | 30 +++-- .../slic3r/GUI/BackgroundSlicingProcess.hpp | 2 +- xs/xsp/GUI_BackgroundSlicingProcess.xsp | 3 +- xs/xsp/Print.xsp | 6 +- xs/xsp/TriangleMesh.xsp | 2 +- 17 files changed, 279 insertions(+), 140 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index b6450fbdd8..ae31ca7646 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1162,7 +1162,7 @@ sub stop_background_process { $self->statusbar->SetCancelCallback(undef); $self->statusbar->StopBusy; $self->statusbar->SetStatusText(""); - # Stop the background task. + # Stop the background task, wait until the thread goes into the "Idle" state. $self->{background_slicing_process}->stop; # Update the UI with the slicing results. $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; @@ -1275,7 +1275,11 @@ sub on_update_print_preview { # This gets called also if we don't have threads. sub on_process_completed { my ($self, $result) = @_; - + + # Stop the background task, wait until the thread goes into the "Idle" state. + # At this point of time the thread should be either finished or canceled, + # so the following call just confirms, that the produced data were consumed. + $self->{background_slicing_process}->stop; $self->statusbar->SetCancelCallback(undef); $self->statusbar->StopBusy; $self->statusbar->SetStatusText(""); diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 8ad479532b..9c6143fd65 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -365,13 +365,21 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ if (file == nullptr) throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); - this->m_placeholder_parser_failed_templates.clear(); - this->_do_export(*print, file, preview_data); - fflush(file); - if (ferror(file)) { + try { + this->m_placeholder_parser_failed_templates.clear(); + this->_do_export(*print, file, preview_data); + fflush(file); + if (ferror(file)) { + fclose(file); + boost::nowide::remove(path_tmp.c_str()); + throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); + } + } catch (std::exception &ex) { + // Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown. + // Close and remove the file. fclose(file); boost::nowide::remove(path_tmp.c_str()); - throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); + throw; } fclose(file); if (! this->m_placeholder_parser_failed_templates.empty()) { @@ -403,6 +411,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) { PROFILE_FUNC(); + print.set_started(psGCodeExport); + // resets time estimator m_time_estimator.reset(); m_time_estimator.set_dialect(print.config.gcode_flavor); @@ -444,6 +454,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) std::sort(zs.begin(), zs.end()); m_layer_count = (unsigned int)(std::unique(zs.begin(), zs.end()) - zs.begin()); } + print.throw_if_canceled(); m_enable_cooling_markers = true; this->apply_print_config(print.config); @@ -475,6 +486,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) for (auto layer : object->support_layers) mm3_per_mm.push_back(layer->support_fills.min_mm3_per_mm()); } + print.throw_if_canceled(); // filter out 0-width segments mm3_per_mm.erase(std::remove_if(mm3_per_mm.begin(), mm3_per_mm.end(), [](double v) { return v < 0.000001; }), mm3_per_mm.end()); if (! mm3_per_mm.empty()) { @@ -489,6 +501,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) m_volumetric_speed = std::min(m_volumetric_speed, print.config.max_volumetric_speed.value); } } + print.throw_if_canceled(); m_cooling_buffer = make_unique(*this); if (print.config.spiral_vase.value) @@ -513,6 +526,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) if (! lines.empty()) _write(file, "\n"); } + print.throw_if_canceled(); + // 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; @@ -530,6 +545,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width); _write_format(file, "\n"); } + print.throw_if_canceled(); // Prepare the helper object for replacing placeholders in custom G-code and output filename. m_placeholder_parser = print.placeholder_parser; @@ -566,6 +582,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) final_extruder_id = tool_ordering.last_extruder(); assert(final_extruder_id != (unsigned int)-1); } + print.throw_if_canceled(); m_cooling_buffer->set_current_extruder(initial_extruder_id); @@ -611,7 +628,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _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); - + print.throw_if_canceled(); + // Set other general things. _write(file, this->preamble()); @@ -629,6 +647,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) } //FIXME Mege the islands in parallel. m_avoid_crossing_perimeters.init_external_mp(union_ex(islands)); + print.throw_if_canceled(); } // Calculate wiping points if needed @@ -659,6 +678,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) ); #endif } + print.throw_if_canceled(); } // Set initial extruder only after custom start G-code. @@ -686,6 +706,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) final_extruder_id = tool_ordering.last_extruder(); assert(final_extruder_id != (unsigned int)-1); } + print.throw_if_canceled(); this->set_origin(unscale(copy.x), unscale(copy.y)); if (finished_objects > 0) { // Move to the origin position for the copy we're going to print. @@ -716,6 +737,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) std::vector lrs; lrs.emplace_back(std::move(ltp)); this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), © - object._shifted_copies.data()); + print.throw_if_canceled(); } if (m_pressure_equalizer) _write(file, m_pressure_equalizer->process("", true)); @@ -761,6 +783,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix. _write(file, "M1 S10\n"); } + print.throw_if_canceled(); } // Extrude the layers. for (auto &layer : layers_to_print) { @@ -768,6 +791,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) if (m_wipe_tower && layer_tools.has_wipe_tower) m_wipe_tower->next_layer(); this->process_layer(file, print, layer.second, layer_tools, size_t(-1)); + print.throw_if_canceled(); } if (m_pressure_equalizer) _write(file, m_pressure_equalizer->process("", true)); @@ -799,6 +823,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _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()); + print.throw_if_canceled(); // calculates estimated printing time m_time_estimator.calculate_time(); @@ -839,10 +864,13 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) if (!full_config.empty()) _write(file, full_config); } + print.throw_if_canceled(); // starts analizer calculations if (preview_data != nullptr) m_analyzer.calc_gcode_preview_data(*preview_data); + + print.set_done(psGCodeExport); } std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 968cd14dec..5c3a6ffb3d 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -136,7 +136,8 @@ public: {} ~GCode() {} - // throws std::runtime_exception + // throws std::runtime_exception on error, + // throws CanceledException through print->throw_if_canceled(). void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr); // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests. diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp index 652bcdaa04..3dc2335d79 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/xs/src/libslic3r/Layer.cpp @@ -150,13 +150,12 @@ void Layer::make_fills() #ifdef SLIC3R_DEBUG printf("Making fills for layer " PRINTF_ZU "\n", this->id()); #endif - for (LayerRegionPtrs::iterator it_layerm = regions.begin(); it_layerm != regions.end(); ++ it_layerm) { - LayerRegion &layerm = *(*it_layerm); - layerm.fills.clear(); - make_fill(layerm, layerm.fills); + for (LayerRegion *layerm : regions) { + layerm->fills.clear(); + make_fill(*layerm, layerm->fills); #ifndef NDEBUG for (size_t i = 0; i < layerm.fills.entities.size(); ++ i) - assert(dynamic_cast(layerm.fills.entities[i]) != NULL); + assert(dynamic_cast(layerm->fills.entities[i]) != NULL); #endif } } diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index 68e17407e9..68d1c5829c 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -51,8 +51,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped() } } -void -LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces) +void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces) { this->perimeters.clear(); this->thin_fills.clear(); @@ -340,8 +339,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ } -void -LayerRegion::prepare_fill_surfaces() +void LayerRegion::prepare_fill_surfaces() { #ifdef SLIC3R_DEBUG_SLICE_PROCESSING export_region_slices_to_svg_debug("2_prepare_fill_surfaces-initial"); @@ -382,8 +380,7 @@ LayerRegion::prepare_fill_surfaces() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ } -double -LayerRegion::infill_area_threshold() const +double LayerRegion::infill_area_threshold() const { double ss = this->flow(frSolidInfill).scaled_spacing(); return ss*ss; diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index a74afdaec7..a5e8261613 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -83,7 +83,7 @@ bool Print::invalidate_state_by_config_options(const std::vector steps_ignore = { + static std::unordered_set steps_gcode = { "avoid_crossing_perimeters", "bed_shape", "bed_temperature", @@ -159,13 +159,18 @@ bool Print::invalidate_state_by_config_options(const std::vector steps_ignore = {}; + std::vector steps; std::vector osteps; bool invalidated = false; for (const t_config_option_key &opt_key : opt_keys) { - if (steps_ignore.find(opt_key) != steps_ignore.end()) { + if (steps_gcode.find(opt_key) != steps_gcode.end()) { // These options only affect G-code export or they are just notes without influence on the generated G-code, // so there is nothing to invalidate. + steps.emplace_back(psGCodeExport); + } else if (steps_ignore.find(opt_key) != steps_ignore.end()) { + // These steps have no influence on the G-code whatsoever. Just ignore them. } else if ( opt_key == "skirts" || opt_key == "skirt_height" @@ -226,22 +231,22 @@ bool Print::invalidate_state_by_config_options(const std::vectorstate.invalidate(step); + bool invalidated = m_state.invalidate(step); // Propagate to dependent steps. //FIXME Why should skirt invalidate brim? Shouldn't it be vice versa? if (step == psSkirt) - invalidated |= this->state.invalidate(psBrim); + invalidated |= m_state.invalidate(psBrim); return invalidated; } // returns true if an object step is done on all objects // and there's at least one object -bool Print::step_done(PrintObjectStep step) const +bool Print::is_step_done(PrintObjectStep step) const { if (this->objects.empty()) return false; for (const PrintObject *object : this->objects) - if (!object->state.is_done(step)) + if (!object->m_state.is_done(step)) return false; return true; } @@ -801,37 +806,42 @@ void Print::process() BOOST_LOG_TRIVIAL(info) << "Staring the slicing process."; for (PrintObject *obj : this->objects) obj->make_perimeters(); + this->throw_if_canceled(); this->set_status(70, "Infilling layers"); for (PrintObject *obj : this->objects) obj->infill(); + this->throw_if_canceled(); for (PrintObject *obj : this->objects) obj->generate_support_material(); - if (! this->state.is_done(psSkirt)) { - this->state.set_started(psSkirt); + this->throw_if_canceled(); + if (! m_state.is_done(psSkirt)) { + this->set_started(psSkirt); this->skirt.clear(); if (this->has_skirt()) { this->set_status(88, "Generating skirt"); this->_make_skirt(); } - this->state.set_done(psSkirt); + m_state.set_done(psSkirt); } - if (! this->state.is_done(psBrim)) { - this->state.set_started(psBrim); + this->throw_if_canceled(); + if (! m_state.is_done(psBrim)) { + this->set_started(psBrim); this->brim.clear(); if (this->config.brim_width > 0) { this->set_status(88, "Generating brim"); this->_make_brim(); } - this->state.set_done(psBrim); + m_state.set_done(psBrim); } - if (! this->state.is_done(psWipeTower)) { - this->state.set_started(psWipeTower); + this->throw_if_canceled(); + if (! m_state.is_done(psWipeTower)) { + this->set_started(psWipeTower); this->_clear_wipe_tower(); if (this->has_wipe_tower()) { //this->set_status(95, "Generating wipe tower"); this->_make_wipe_tower(); } - this->state.set_done(psWipeTower); + m_state.set_done(psWipeTower); } BOOST_LOG_TRIVIAL(info) << "Slicing process finished."; } @@ -883,6 +893,7 @@ void Print::_make_skirt() // Collect points from all layers contained in skirt height. Points points; for (const PrintObject *object : this->objects) { + this->throw_if_canceled(); Points object_points; // Get object layers up to skirt_height_z. for (const Layer *layer : object->layers) { @@ -912,6 +923,7 @@ void Print::_make_skirt() // At least three points required for a convex hull. return; + this->throw_if_canceled(); Polygon convex_hull = Slic3r::Geometry::convex_hull(points); // Skirt may be printed on several layers, having distinct layer heights, @@ -946,6 +958,7 @@ void Print::_make_skirt() // Loop while we have less skirts than required or any extruder hasn't reached the min length if any. std::vector extruded_length(extruders.size(), 0.); for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) { + this->throw_if_canceled(); // Offset the skirt outside. distance += coord_t(scale_(spacing)); // Generate the skirt centerline. @@ -994,6 +1007,7 @@ void Print::_make_brim() Flow flow = this->brim_flow(); Polygons islands; for (PrintObject *object : this->objects) { + this->throw_if_canceled(); Polygons object_islands; for (ExPolygon &expoly : object->layers.front()->slices.expolygons) object_islands.push_back(expoly.contour); @@ -1009,6 +1023,7 @@ void Print::_make_brim() Polygons loops; size_t num_loops = size_t(floor(this->config.brim_width.value / flow.width)); for (size_t i = 0; i < num_loops; ++ i) { + this->throw_if_canceled(); islands = offset(islands, float(flow.scaled_spacing()), jtSquare); for (Polygon &poly : islands) { // poly.simplify(SCALED_RESOLUTION); @@ -1088,6 +1103,7 @@ void Print::_make_wipe_tower() } } } + this->throw_if_canceled(); // Initialize the wipe tower. WipeTowerPrusaMM wipe_tower( @@ -1119,6 +1135,7 @@ void Print::_make_wipe_tower() // Set current_extruder_id to the last extruder primed. unsigned int current_extruder_id = m_tool_ordering.all_extruders().back(); for (const ToolOrdering::LayerTools &layer_tools : m_tool_ordering.layer_tools()) { + this->throw_if_canceled(); if (! layer_tools.has_wipe_tower) // This is a support only layer, or the wipe tower does not reach to this height. continue; diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 341e48e299..1162c9b0f1 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -18,56 +18,71 @@ #include "GCode/WipeTower.hpp" #include "tbb/atomic.h" +// tbb/mutex.h includes Windows, which in turn defines min/max macros. Convince Windows.h to not define these min/max macros. +#ifndef NOMINMAX + #define NOMINMAX +#endif +#include "tbb/mutex.h" namespace Slic3r { class Print; class PrintObject; class ModelObject; +class GCode; class GCodePreviewData; // Print step IDs for keeping track of the print state. enum PrintStep { - psSkirt, psBrim, psWipeTower, psCount, + psSkirt, psBrim, psWipeTower, psGCodeExport, psCount, }; enum PrintObjectStep { posSlice, posPerimeters, posPrepareInfill, posInfill, posSupportMaterial, posCount, }; +class CanceledException : public std::exception { +public: + const char* what() const throw() { return "Background processing has been canceled"; } +}; + // To be instantiated over PrintStep or PrintObjectStep enums. template class PrintState { public: - PrintState() { memset(state, 0, sizeof(state)); } + PrintState() { for (size_t i = 0; i < COUNT; ++ i) m_state[i] = INVALID; } enum State { INVALID, STARTED, DONE, }; - State state[COUNT]; - bool is_started(StepType step) const { return this->state[step] == STARTED; } - bool is_done(StepType step) const { return this->state[step] == DONE; } - void set_started(StepType step) { this->state[step] = STARTED; } - void set_done(StepType step) { this->state[step] = DONE; } + bool is_done(StepType step) const { return m_state[step] == DONE; } + // set_started() will lock the provided mutex before setting the state. + // This is necessary to block until the Print::apply_config() updates its state, which may + // influence the processing step being entered. + void set_started(StepType step, tbb::mutex &mtx) { mtx.lock(); m_state[step] = STARTED; mtx.unlock(); } + void set_done(StepType step) { m_state[step] = DONE; } bool invalidate(StepType step) { - bool invalidated = this->state[step] != INVALID; - this->state[step] = INVALID; + bool invalidated = m_state[step] != INVALID; + m_state[step] = INVALID; return invalidated; } bool invalidate_all() { bool invalidated = false; for (size_t i = 0; i < COUNT; ++ i) - if (this->state[i] != INVALID) { + if (m_state[i] != INVALID) { invalidated = true; + m_state[i] = INVALID; break; } - memset(state, 0, sizeof(state)); return invalidated; } + +private: + std::atomic m_state[COUNT]; }; // A PrintRegion object represents a group of volumes to print @@ -131,7 +146,6 @@ public: LayerPtrs layers; SupportLayerPtrs support_layers; - PrintState state; Print* print() { return this->_print; } const Print* print() const { return this->_print; } @@ -174,7 +188,8 @@ public: // methods for handling state bool invalidate_state_by_config_options(const std::vector &opt_keys); bool invalidate_step(PrintObjectStep step); - bool invalidate_all_steps() { return this->state.invalidate_all(); } + bool invalidate_all_steps() { return m_state.invalidate_all(); } + bool is_step_done(PrintObjectStep step) const { return m_state.is_done(step); } // To be used over the layer_height_profile of both the PrintObject and ModelObject // to initialize the height profile with the height ranges. @@ -218,11 +233,18 @@ private: ModelObject* _model_object; Points _copies; // Slic3r::Point objects in scaled G-code coordinates + PrintState m_state; + // Mutex used for synchronization of the worker thread with the UI thread: + // The mutex will be used to guard the worker thread against entering a stage + // while the data influencing the stage is modified. + tbb::mutex m_mutex; + // TODO: call model_object->get_bounding_box() instead of accepting // parameter PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); ~PrintObject() {} + void set_started(PrintObjectStep step) { m_state.set_started(step, m_mutex); } std::vector _slice_region(size_t region_id, const std::vector &z, bool modifier); }; @@ -242,7 +264,6 @@ public: std::string estimated_print_time; double total_used_filament, total_extruded_volume, total_cost, total_weight; std::map filament_stats; - PrintState state; // ordered collections of extrusion paths to build skirt loops and brim ExtrusionEntityCollection skirt, brim; @@ -266,9 +287,10 @@ public: // methods for handling state bool invalidate_step(PrintStep step); - bool invalidate_all_steps() { return this->state.invalidate_all(); } - bool step_done(PrintObjectStep step) const; - + bool invalidate_all_steps() { return m_state.invalidate_all(); } + bool is_step_done(PrintStep step) const { return m_state.is_done(step); } + bool is_step_done(PrintObjectStep step) const; + void add_model_object(ModelObject* model_object, int idx = -1); bool apply_config(DynamicPrintConfig config); bool has_infinite_skirt() const; @@ -308,7 +330,7 @@ public: typedef std::function status_callback_type; // Default status console print out in the form of percent => message. void set_status_default() { m_status_callback = nullptr; } - // No status output or callback whatsoever. + // No status output or callback whatsoever, useful mostly for automatic tests. void set_status_silent() { m_status_callback = [](int, const std::string&){}; } // Register a custom status callback. void set_status_callback(status_callback_type cb) { m_status_callback = cb; } @@ -323,7 +345,12 @@ public: void restart() { m_canceled = false; } // Has the calculation been canceled? bool canceled() { return m_canceled; } - + void throw_if_canceled() { if (m_canceled) throw CanceledException(); } + +protected: + void set_started(PrintStep step) { m_state.set_started(step, m_mutex); } + void set_done(PrintStep step) { m_state.set_done(step); } + private: bool invalidate_state_by_config_options(const std::vector &opt_keys); PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume); @@ -333,9 +360,18 @@ private: void _clear_wipe_tower(); void _make_wipe_tower(); + PrintState m_state; + // Mutex used for synchronization of the worker thread with the UI thread: + // The mutex will be used to guard the worker thread against entering a stage + // while the data influencing the stage is modified. + tbb::mutex m_mutex; // Has the calculation been canceled? - tbb::atomic m_canceled; - status_callback_type m_status_callback; + tbb::atomic m_canceled; + // Callback to be evoked regularly to update state of the UI thread. + status_callback_type m_status_callback; + + // To allow GCode to set the Print's GCodeExport step status. + friend class GCode; }; #define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator) diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index e64c129696..ff1e983fd0 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -31,8 +31,6 @@ #include #endif -#define PARALLEL_FOR_CANCEL do { if (this->print()->canceled()) return; } while (0) - namespace Slic3r { PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) : @@ -117,14 +115,16 @@ bool PrintObject::reload_model_instances() // this should be idempotent void PrintObject::slice() { - if (this->state.is_done(posSlice)) + if (m_state.is_done(posSlice)) return; - this->state.set_started(posSlice); + this->set_started(posSlice); this->_print->set_status(10, "Processing triangulated mesh"); this->_slice(); + this->_print->throw_if_canceled(); // Fix the model. //FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend. std::string warning = this->_fix_slicing_errors(); + this->_print->throw_if_canceled(); if (! warning.empty()) BOOST_LOG_TRIVIAL(info) << warning; // Simplify slices if required. @@ -132,7 +132,7 @@ void PrintObject::slice() this->_simplify_slices(scale_(this->_print->config.resolution)); if (this->layers.empty()) throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); - this->state.set_done(posSlice); + m_state.set_done(posSlice); } // 1) Merges typed region slices into stInternal type. @@ -143,19 +143,21 @@ void PrintObject::make_perimeters() // prerequisites this->slice(); - if (this->state.is_done(posPerimeters)) + if (m_state.is_done(posPerimeters)) return; - this->state.set_started(posPerimeters); + this->set_started(posPerimeters); this->_print->set_status(20, "Generating perimeters"); BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; // merge slices if they were split into types if (this->typed_slices) { - FOREACH_LAYER(this, layer_it) + FOREACH_LAYER(this, layer_it) { (*layer_it)->merge_slices(); + this->_print->throw_if_canceled(); + } this->typed_slices = false; - this->state.invalidate(posPrepareInfill); + m_state.invalidate(posPrepareInfill); } // compare each layer to the one below, and mark those slices needing @@ -168,8 +170,7 @@ void PrintObject::make_perimeters() FOREACH_REGION(this->_print, region_it) { size_t region_id = region_it - this->_print->regions.begin(); const PrintRegion ®ion = **region_it; - - + if (!region.config.extra_perimeters || region.config.perimeters == 0 || region.config.fill_density == 0 @@ -181,7 +182,7 @@ void PrintObject::make_perimeters() tbb::blocked_range(0, this->layers.size() - 1), [this, ®ion, region_id](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); LayerRegion &layerm = *this->layers[layer_idx]->regions[region_id]; const LayerRegion &upper_layerm = *this->layers[layer_idx+1]->regions[region_id]; const Polygons upper_layerm_polygons = upper_layerm.slices; @@ -230,6 +231,7 @@ void PrintObject::make_perimeters() } } }); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - end"; } @@ -238,11 +240,12 @@ void PrintObject::make_perimeters() tbb::blocked_range(0, this->layers.size()), [this](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); this->layers[layer_idx]->make_perimeters(); } } ); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end"; /* @@ -252,15 +255,15 @@ void PrintObject::make_perimeters() ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); */ - this->state.set_done(posPerimeters); + m_state.set_done(posPerimeters); } void PrintObject::prepare_infill() { - if (this->state.is_done(posPrepareInfill)) + if (m_state.is_done(posPrepareInfill)) return; - this->state.set_started(posPrepareInfill); + this->set_started(posPrepareInfill); this->_print->set_status(30, "Preparing infill"); // This will assign a type (top/bottom/internal) to $layerm->slices. @@ -268,14 +271,17 @@ void PrintObject::prepare_infill() // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces // by the cummulative area of the previous $layerm->fill_surfaces. this->detect_surfaces_type(); + this->_print->throw_if_canceled(); // Decide what surfaces are to be filled. // Here the S_TYPE_TOP / S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is turned to just S_TYPE_INTERNAL if zero top / bottom infill layers are configured. // Also tiny S_TYPE_INTERNAL surfaces are turned to S_TYPE_INTERNAL_SOLID. BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..."; for (auto *layer : this->layers) - for (auto *region : layer->regions) + for (auto *region : layer->regions) { region->prepare_fill_surfaces(); + this->_print->throw_if_canceled(); + } // this will detect bridges and reverse bridges // and rearrange top/bottom/internal surfaces @@ -287,9 +293,11 @@ void PrintObject::prepare_infill() // 4) Merge surfaces with the same style. This will mostly get rid of the overlaps. //FIXME This does not likely merge surfaces, which are supported by a material with different colors, but same properties. this->process_external_surfaces(); + this->_print->throw_if_canceled(); // Add solid fills to ensure the shell vertical thickness. this->discover_vertical_shells(); + this->_print->throw_if_canceled(); // Debugging output. #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -309,6 +317,7 @@ void PrintObject::prepare_infill() // to close these surfaces reliably. //FIXME Vojtech: Is this a good place to add supporting infills below sloping perimeters? this->discover_horizontal_shells(); + this->_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { @@ -327,6 +336,7 @@ void PrintObject::prepare_infill() // Likely the sparse infill will not be anchored correctly, so it will not work as intended. // Also one wishes the perimeters to be supported by a full infill. this->clip_fill_surfaces(); + this->_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { @@ -341,9 +351,11 @@ void PrintObject::prepare_infill() // the following step needs to be done before combination because it may need // to remove only half of the combined infill this->bridge_over_infill(); + this->_print->throw_if_canceled(); // combine fill surfaces to honor the "infill every N layers" option this->combine_infill(); + this->_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { @@ -359,7 +371,7 @@ void PrintObject::prepare_infill() } // for each layer #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - this->state.set_done(posPrepareInfill); + m_state.set_done(posPrepareInfill); } void PrintObject::infill() @@ -367,36 +379,38 @@ void PrintObject::infill() // prerequisites this->prepare_infill(); - if (! this->state.is_done(posInfill)) { - this->state.set_started(posInfill); + if (! m_state.is_done(posInfill)) { + this->set_started(posInfill); BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, this->layers.size()), [this](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); this->layers[layer_idx]->make_fills(); } } ); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - end"; /* we could free memory now, but this would make this step not idempotent ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; */ - this->state.set_done(posInfill); + m_state.set_done(posInfill); } } void PrintObject::generate_support_material() { - if (! this->state.is_done(posSupportMaterial)) { - this->state.set_started(posSupportMaterial); + if (! m_state.is_done(posSupportMaterial)) { + this->set_started(posSupportMaterial); this->clear_support_layers(); if ((this->config.support_material || this->config.raft_layers > 0) && this->layers.size() > 1) { this->_print->set_status(85, "Generating support material"); this->_generate_support_material(); + this->_print->throw_if_canceled(); } - this->state.set_done(posSupportMaterial); + m_state.set_done(posSupportMaterial); } } @@ -545,7 +559,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vectorstate.invalidate(step); + bool invalidated = m_state.invalidate(step); // propagate to dependent steps if (step == posPerimeters) { @@ -624,7 +638,7 @@ void PrintObject::detect_surfaces_type() (this->config.support_material.value && this->config.support_material_contact_distance.value == 0) ? stBottom : stBottomBridge; for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); // BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z; Layer *layer = this->layers[idx_layer]; LayerRegion *layerm = layer->get_region(idx_region); @@ -749,6 +763,7 @@ void PrintObject::detect_surfaces_type() } } ); // for each layer of a region + this->_print->throw_if_canceled(); if (interface_shells) { // Move surfaces_new to layerm->slices.surfaces @@ -762,7 +777,7 @@ void PrintObject::detect_surfaces_type() tbb::blocked_range(0, this->layers.size()), [this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range& range) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region); layerm->slices_to_fill_surfaces_clipped(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -770,6 +785,7 @@ void PrintObject::detect_surfaces_type() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ } // for each layer of a region }); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - end"; } // for each this->print->region_count @@ -789,12 +805,13 @@ void PrintObject::process_external_surfaces() tbb::blocked_range(0, this->layers.size()), [this, region_id](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << this->layers[layer_idx]->print_z; this->layers[layer_idx]->get_region(region_id)->process_external_surfaces((layer_idx == 0) ? NULL : this->layers[layer_idx - 1]); } } ); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - end"; } } @@ -838,7 +855,7 @@ void PrintObject::discover_vertical_shells() const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; const size_t num_regions = this->_print->regions.size(); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); const Layer &layer = *this->layers[idx_layer]; DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[idx_layer]; // Simulate single set of perimeters over all merged regions. @@ -893,6 +910,7 @@ void PrintObject::discover_vertical_shells() cache.holes = union_(cache.holes, false); } }); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom"; } @@ -921,7 +939,7 @@ void PrintObject::discover_vertical_shells() [this, idx_region, &cache_top_botom_regions](const tbb::blocked_range& range) { const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); Layer &layer = *this->layers[idx_layer]; LayerRegion &layerm = *layer.regions[idx_region]; float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f; @@ -939,6 +957,7 @@ void PrintObject::discover_vertical_shells() } } }); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end : cache top / bottom"; } @@ -950,7 +969,7 @@ void PrintObject::discover_vertical_shells() // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { PROFILE_BLOCK(discover_vertical_shells_region_layer); - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING static size_t debug_idx = 0; ++ debug_idx; @@ -1163,7 +1182,8 @@ void PrintObject::discover_vertical_shells() layerm->fill_surfaces.append(new_internal_void, stInternalVoid); layerm->fill_surfaces.append(new_internal_solid, stInternalSolid); } // for each layer - }); + }); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end"; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -1303,6 +1323,7 @@ void PrintObject::bridge_over_infill() layerm->export_region_slices_to_svg_debug("7_bridge_over_infill"); layerm->export_region_fill_surfaces_to_svg_debug("7_bridge_over_infill"); #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + this->_print->throw_if_canceled(); } } } @@ -1411,9 +1432,11 @@ void PrintObject::_slice() for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; std::vector expolygons_by_layer = this->_slice_region(region_id, slice_zs, false); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start"; for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) this->layers[layer_id]->regions[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " end"; } @@ -1422,6 +1445,7 @@ void PrintObject::_slice() for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id; std::vector expolygons_by_layer = this->_slice_region(region_id, slice_zs, true); + this->_print->throw_if_canceled(); // loop through the other regions and 'steal' the slices belonging to this one BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " start"; for (size_t other_region_id = 0; other_region_id < this->print()->regions.size(); ++ other_region_id) { @@ -1443,6 +1467,7 @@ void PrintObject::_slice() layerm->slices.append(std::move(my_parts), stInternal); } } + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " end"; } } @@ -1459,6 +1484,7 @@ void PrintObject::_slice() if (! this->layers.empty()) this->layers.back()->upper_layer = nullptr; } + this->_print->throw_if_canceled(); end: ; @@ -1467,7 +1493,7 @@ end: tbb::blocked_range(0, this->layers.size()), [this](const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); Layer *layer = this->layers[layer_id]; // Apply size compensation and perform clipping of multi-part objects. float delta = float(scale_(this->config.xy_size_compensation.value)); @@ -1503,6 +1529,7 @@ end: layer->make_slices(); } }); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end"; } @@ -1519,6 +1546,7 @@ std::vector PrintObject::_slice_region(size_t region_id, const std:: ModelVolume *volume = this->model_object()->volumes[volume_id]; if (volume->modifier == modifier) mesh.merge(volume->mesh); + this->_print->throw_if_canceled(); } if (mesh.stl.stats.number_of_facets > 0) { // transform mesh @@ -1528,8 +1556,12 @@ std::vector PrintObject::_slice_region(size_t region_id, const std:: // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift mesh.translate(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z)); // perform actual slicing - TriangleMeshSlicer mslicer(&mesh); - mslicer.slice(z, &layers); + TriangleMeshSlicer mslicer; + Print *print = this->print(); + auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); + mslicer.init(&mesh, callback); + mslicer.slice(z, &layers, callback); + this->_print->throw_if_canceled(); } } } @@ -1551,7 +1583,7 @@ std::string PrintObject::_fix_slicing_errors() tbb::blocked_range(0, buggy_layers.size()), [this, &buggy_layers](const tbb::blocked_range& range) { for (size_t buggy_layer_idx = range.begin(); buggy_layer_idx < range.end(); ++ buggy_layer_idx) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); size_t idx_layer = buggy_layers[buggy_layer_idx]; Layer *layer = this->layers[idx_layer]; assert(layer->slicing_errors); @@ -1602,6 +1634,7 @@ std::string PrintObject::_fix_slicing_errors() layer->make_slices(); } }); + this->_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - end"; // remove empty layers from bottom @@ -1628,7 +1661,7 @@ void PrintObject::_simplify_slices(double distance) tbb::blocked_range(0, this->layers.size()), [this, distance](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - PARALLEL_FOR_CANCEL; + this->_print->throw_if_canceled(); Layer *layer = this->layers[layer_idx]; for (size_t region_idx = 0; region_idx < layer->regions.size(); ++ region_idx) layer->regions[region_idx]->slices.simplify(distance); @@ -1721,6 +1754,7 @@ void PrintObject::clip_fill_surfaces() layerm->export_region_fill_surfaces_to_svg_debug("6_clip_fill_surfaces"); #endif } + this->_print->throw_if_canceled(); } } @@ -1730,6 +1764,7 @@ void PrintObject::discover_horizontal_shells() for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { for (int i = 0; i < int(this->layers.size()); ++ i) { + this->_print->throw_if_canceled(); LayerRegion *layerm = this->layers[i]->regions[region_id]; PrintRegionConfig ®ion_config = layerm->region()->config; if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 && @@ -1746,6 +1781,7 @@ void PrintObject::discover_horizontal_shells() continue; for (int idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) { + this->_print->throw_if_canceled(); SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge; // Find slices of current type for current layer. // Use slices instead of fill_surfaces, because they also include the perimeter area, @@ -1932,6 +1968,7 @@ void PrintObject::combine_infill() double current_height = 0.; size_t num_layers = 0; for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++ layer_idx) { + this->_print->throw_if_canceled(); const Layer *layer = this->layers[layer_idx]; if (layer->id() == 0) // Skip first print layer (which may not be first layer in array because of raft). @@ -1954,6 +1991,7 @@ void PrintObject::combine_infill() // loop through layers to which we have assigned layers to combine for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++ layer_idx) { + this->_print->throw_if_canceled(); size_t num_layers = combine[layer_idx]; if (num_layers <= 1) continue; diff --git a/xs/src/libslic3r/PrintRegion.cpp b/xs/src/libslic3r/PrintRegion.cpp index 4874c71bcd..2fcf691663 100644 --- a/xs/src/libslic3r/PrintRegion.cpp +++ b/xs/src/libslic3r/PrintRegion.cpp @@ -2,8 +2,7 @@ namespace Slic3r { -Flow -PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const +Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const { ConfigOptionFloatOrPercent config_width; if (width != -1) { diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index aecb39662e..17b3d98f93 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -606,11 +606,11 @@ TriangleMesh::require_shared_vertices() BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end"; } - -TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : - mesh(_mesh) +void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type throw_on_cancel) { + mesh = _mesh; _mesh->require_shared_vertices(); + throw_on_cancel(); facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1); v_scaled_shared.assign(_mesh->stl.v_shared, _mesh->stl.v_shared + _mesh->stl.stats.shared_vertices); // Scale the copied vertices. @@ -650,6 +650,7 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : e2f.face_edge = - e2f.face_edge; } } + throw_on_cancel(); std::sort(edges_map.begin(), edges_map.end()); // Assign a unique common edge id to touching triangle edges. @@ -689,11 +690,12 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : edge_j.face = -1; } ++ num_edges; + if ((i & 0x0ffff) == 0) + throw_on_cancel(); } } -void -TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const +void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const { BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice"; @@ -730,13 +732,17 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la boost::mutex lines_mutex; tbb::parallel_for( tbb::blocked_range(0,this->mesh->stl.stats.number_of_facets), - [&lines, &lines_mutex, &z, this](const tbb::blocked_range& range) { - for (int facet_idx = range.begin(); facet_idx < range.end(); ++ facet_idx) + [&lines, &lines_mutex, &z, throw_on_cancel, this](const tbb::blocked_range& range) { + for (int facet_idx = range.begin(); facet_idx < range.end(); ++ facet_idx) { + if ((facet_idx & 0x0ffff) == 0) + throw_on_cancel(); this->_slice_do(facet_idx, &lines, &lines_mutex, z); + } } ); } - + throw_on_cancel(); + // v_scaled_shared could be freed here // build loops @@ -744,9 +750,12 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la layers->resize(z.size()); tbb::parallel_for( tbb::blocked_range(0, z.size()), - [&lines, &layers, this](const tbb::blocked_range& range) { - for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx) + [&lines, &layers, throw_on_cancel, this](const tbb::blocked_range& range) { + for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx) { + if ((line_idx & 0x0ffff) == 0) + throw_on_cancel(); this->make_loops(lines[line_idx], &(*layers)[line_idx]); + } } ); BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice finished"; @@ -823,21 +832,21 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector &z, std::vector* layers) const +void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const { std::vector layers_p; - this->slice(z, &layers_p); + this->slice(z, &layers_p, throw_on_cancel); BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - start"; layers->resize(z.size()); tbb::parallel_for( tbb::blocked_range(0, z.size()), - [&layers_p, layers, this](const tbb::blocked_range& range) { + [&layers_p, layers, throw_on_cancel, this](const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { #ifdef SLIC3R_TRIANGLEMESH_DEBUG printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]); #endif + throw_on_cancel(); this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]); } }); diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index c700784a51..55900656b8 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -3,6 +3,7 @@ #include "libslic3r.h" #include +#include #include #include #include "BoundingBox.hpp" @@ -130,9 +131,13 @@ typedef std::vector IntersectionLinePtrs; class TriangleMeshSlicer { public: - TriangleMeshSlicer(TriangleMesh* _mesh); - void slice(const std::vector &z, std::vector* layers) const; - void slice(const std::vector &z, std::vector* layers) const; + typedef std::function throw_on_cancel_callback_type; + TriangleMeshSlicer() : mesh(nullptr) {} + // Not quite nice, but the constructor and init() methods require non-const mesh pointer to be able to call mesh->require_shared_vertices() + TriangleMeshSlicer(TriangleMesh* mesh) { this->init(mesh, throw_on_cancel_callback_type()); } + void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel); + void slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; + void slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; bool slice_facet(float slice_z, const stl_facet &facet, const int facet_idx, const float min_z, const float max_z, IntersectionLine *line_out) const; void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index f6be96a783..03154c47eb 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -647,14 +647,10 @@ std::vector GLVolumeCollection::get_current_print_zs() const for (GLVolume *vol : this->volumes) { for (coordf_t z : vol->print_zs) - { - double round_z = (double)round(z * 100000.0f) / 100000.0f; - if (std::find(print_zs.begin(), print_zs.end(), round_z) == print_zs.end()) - print_zs.push_back(round_z); - } + print_zs.emplace_back((double)round(z * 100000.0f) / 100000.0f); } - std::sort(print_zs.begin(), print_zs.end()); + sort_remove_duplicates(print_zs); return print_zs; } @@ -1671,6 +1667,9 @@ void _3DScene::_load_print_toolpaths( const std::vector &tool_colors, bool use_VBOs) { + // The skirt and brim steps should be marked as done, so their paths are valid. + assert(print->is_step_done(psSkirt) && print->is_step_done(psBrim)); + if (!print->has_skirt() && print->config.brim_width.value == 0) return; @@ -1762,9 +1761,9 @@ void _3DScene::_load_print_object_toolpaths( std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; }); // Maximum size of an allocation block: 32MB / sizeof(float) - ctxt.has_perimeters = print_object->state.is_done(posPerimeters); - ctxt.has_infill = print_object->state.is_done(posInfill); - ctxt.has_support = print_object->state.is_done(posSupportMaterial); + ctxt.has_perimeters = print_object->is_step_done(posPerimeters); + ctxt.has_infill = print_object->is_step_done(posInfill); + ctxt.has_support = print_object->is_step_done(posSupportMaterial); ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start"; diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 15da2eb0cd..d774ef65cb 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -24,7 +24,7 @@ void BackgroundSlicingProcess::thread_proc() lck.unlock(); m_condition.notify_one(); for (;;) { - assert(m_state == STATE_IDLE); + assert(m_state == STATE_IDLE || m_state == STATE_CANCELED || m_state == STATE_FINISHED); // Wait until a new task is ready to be executed, or this thread should be finished. lck.lock(); m_condition.wait(lck, [this](){ return m_state == STATE_STARTED || m_state == STATE_EXIT; }); @@ -38,10 +38,13 @@ void BackgroundSlicingProcess::thread_proc() try { assert(m_print != nullptr); m_print->process(); - if (m_print->canceled()) - return; - wxQueueEvent(GUI::g_wxPlater, new wxCommandEvent(m_event_sliced_id)); - m_print->export_gcode(m_output_path, m_gcode_preview_data); + if (! m_print->canceled()) { + wxQueueEvent(GUI::g_wxPlater, new wxCommandEvent(m_event_sliced_id)); + m_print->export_gcode(m_output_path, m_gcode_preview_data); + } + } catch (CanceledException &ex) { + // Canceled, this is all right. + assert(m_print->canceled()); } catch (std::exception &ex) { error = ex.what(); } catch (...) { @@ -53,6 +56,7 @@ void BackgroundSlicingProcess::thread_proc() evt.SetString(error); evt.SetInt(m_print->canceled() ? -1 : (error.empty() ? 1 : 0)); wxQueueEvent(GUI::g_wxPlater, evt.Clone()); + m_print->restart(); lck.unlock(); // Let the UI thread wake up if it is waiting for the background task to finish. m_condition.notify_one(); @@ -112,21 +116,23 @@ bool BackgroundSlicingProcess::stop() if (m_state == STATE_STARTED || m_state == STATE_RUNNING) { m_print->cancel(); // Wait until the background processing stops by being canceled. - lck.unlock(); m_condition.wait(lck, [this](){ return m_state == STATE_CANCELED; }); + // In the "Canceled" state. Reset the state to "Idle". + m_state = STATE_IDLE; + } else if (m_state == STATE_FINISHED || m_state == STATE_CANCELED) { + // In the "Finished" or "Canceled" state. Reset the state to "Idle". + m_state = STATE_IDLE; } return true; } // Apply config over the print. Returns false, if the new config values caused any of the already // processed steps to be invalidated, therefore the task will need to be restarted. -bool BackgroundSlicingProcess::apply_config(DynamicPrintConfig *config) +bool BackgroundSlicingProcess::apply_config(const DynamicPrintConfig &config) { - /* - // apply new config - my $invalidated = $self->{print}->apply_config(wxTheApp->{preset_bundle}->full_config); - */ - return true; + this->stop(); + bool invalidated = this->m_print->apply_config(config); + return invalidated; } }; // namespace Slic3r diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 9dcce0af1f..358e00ca97 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -39,7 +39,7 @@ public: // Apply config over the print. Returns false, if the new config values caused any of the already // processed steps to be invalidated, therefore the task will need to be restarted. - bool apply_config(DynamicPrintConfig *config); + bool apply_config(const DynamicPrintConfig &config); enum State { // m_thread is not running yet, or it did not reach the STATE_IDLE yet (it does not wait on the condition yet). diff --git a/xs/xsp/GUI_BackgroundSlicingProcess.xsp b/xs/xsp/GUI_BackgroundSlicingProcess.xsp index d12c7f8e3b..cef9f6d58a 100644 --- a/xs/xsp/GUI_BackgroundSlicingProcess.xsp +++ b/xs/xsp/GUI_BackgroundSlicingProcess.xsp @@ -17,7 +17,8 @@ bool start(); bool stop(); - bool apply_config(DynamicPrintConfig *config); + bool apply_config(DynamicPrintConfig *config) + %code%{ RETVAL = THIS->apply_config(*config); %}; bool running(); }; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index a9744c3123..16e2d84aa3 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -74,7 +74,7 @@ _constant() Ref get_support_layer(int idx); bool step_done(PrintObjectStep step) - %code%{ RETVAL = THIS->state.is_done(step); %}; + %code%{ RETVAL = THIS->is_step_done(step); %}; void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action) %code%{ @@ -123,9 +123,9 @@ _constant() %code%{ RETVAL = THIS->regions.size(); %}; bool step_done(PrintStep step) - %code%{ RETVAL = THIS->state.is_done(step); %}; + %code%{ RETVAL = THIS->is_step_done(step); %}; bool object_step_done(PrintObjectStep step) - %code%{ RETVAL = THIS->step_done(step); %}; + %code%{ RETVAL = THIS->is_step_done(step); %}; SV* filament_stats() %code%{ diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index d4578303b1..690d871202 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -185,7 +185,7 @@ TriangleMesh::slice(z) std::vector layers; TriangleMeshSlicer mslicer(THIS); - mslicer.slice(z_f, &layers); + mslicer.slice(z_f, &layers, [](){}); AV* layers_av = newAV(); size_t len = layers.size(); From fca7c8abfeb08a4c3e1108cd19d4713dccbd8d2e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 28 Mar 2018 19:47:26 +0200 Subject: [PATCH 06/36] Fixed compilation and test cases. --- xs/src/libslic3r/Print.cpp | 2 +- xs/src/libslic3r/TriangleMesh.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index a5e8261613..d67fe5884d 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -159,7 +159,7 @@ bool Print::invalidate_state_by_config_options(const std::vector steps_ignore = {}; + static std::unordered_set steps_ignore; std::vector steps; std::vector osteps; diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 55900656b8..9a09975cd0 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -134,7 +134,7 @@ public: typedef std::function throw_on_cancel_callback_type; TriangleMeshSlicer() : mesh(nullptr) {} // Not quite nice, but the constructor and init() methods require non-const mesh pointer to be able to call mesh->require_shared_vertices() - TriangleMeshSlicer(TriangleMesh* mesh) { this->init(mesh, throw_on_cancel_callback_type()); } + TriangleMeshSlicer(TriangleMesh* mesh) { this->init(mesh, [](){}); } void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel); void slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; void slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; From 49697ed6aaa5155461be37be82f3b6bf58635a08 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 10 Sep 2018 09:11:49 +0200 Subject: [PATCH 07/36] Replaced this->m_xxx with just m_xxx --- xs/src/libslic3r/EdgeGrid.cpp | 2 +- xs/src/libslic3r/ExtrusionEntity.hpp | 4 ++-- xs/src/libslic3r/GCode.hpp | 1 + xs/src/libslic3r/GCode/PressureEqualizer.cpp | 10 +++++----- xs/src/libslic3r/GCodeReader.cpp | 2 +- xs/src/libslic3r/GCodeWriter.cpp | 4 ++-- xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp | 2 +- xs/src/slic3r/GUI/OptionsGroup.cpp | 6 +++--- xs/src/slic3r/GUI/Preset.cpp | 16 ++++++++-------- xs/src/slic3r/GUI/Preset.hpp | 2 +- 10 files changed, 25 insertions(+), 24 deletions(-) diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp index 733ff2ad74..7522434074 100644 --- a/xs/src/libslic3r/EdgeGrid.cpp +++ b/xs/src/libslic3r/EdgeGrid.cpp @@ -614,7 +614,7 @@ bool EdgeGrid::Grid::inside(const Point &pt_src) return false; coord_t ix = p.x / m_resolution; coord_t iy = p.y / m_resolution; - if (ix >= this->m_cols || iy >= this->m_rows) + if (ix >= m_cols || iy >= m_rows) return false; size_t i_closest = (size_t)-1; diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index 16ef51c1fb..9d34aced84 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -117,8 +117,8 @@ public: ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), m_role(rhs.m_role) {} // ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f), extruder_id(0) {}; - ExtrusionPath& operator=(const ExtrusionPath &rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = rhs.polyline; return *this; } - ExtrusionPath& operator=(ExtrusionPath &&rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = std::move(rhs.polyline); return *this; } + ExtrusionPath& operator=(const ExtrusionPath &rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = rhs.polyline; return *this; } + ExtrusionPath& operator=(ExtrusionPath &&rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = std::move(rhs.polyline); return *this; } ExtrusionPath* clone() const { return new ExtrusionPath (*this); } void reverse() { this->polyline.reverse(); } diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 5c3a6ffb3d..0e5305cb57 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -151,6 +151,7 @@ public: const Layer* layer() const { return m_layer; } GCodeWriter& writer() { return m_writer; } PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } + const PlaceholderParser& placeholder_parser() const { 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); diff --git a/xs/src/libslic3r/GCode/PressureEqualizer.cpp b/xs/src/libslic3r/GCode/PressureEqualizer.cpp index 68e642f3c0..3b2a58a884 100644 --- a/xs/src/libslic3r/GCode/PressureEqualizer.cpp +++ b/xs/src/libslic3r/GCode/PressureEqualizer.cpp @@ -49,10 +49,10 @@ void PressureEqualizer::reset() // Volumetric rate of a 0.45mm x 0.2mm extrusion at 60mm/s XY movement: 0.45*0.2*60*60=5.4*60 = 324 mm^3/min // Volumetric rate of a 0.45mm x 0.2mm extrusion at 20mm/s XY movement: 0.45*0.2*20*60=1.8*60 = 108 mm^3/min // Slope of the volumetric rate, changing from 20mm/s to 60mm/s over 2 seconds: (5.4-1.8)*60*60/2=60*60*1.8 = 6480 mm^3/min^2 = 1.8 mm^3/s^2 - m_max_volumetric_extrusion_rate_slope_positive = (this->m_config == NULL) ? 6480.f : - this->m_config->max_volumetric_extrusion_rate_slope_positive.value * 60.f * 60.f; - m_max_volumetric_extrusion_rate_slope_negative = (this->m_config == NULL) ? 6480.f : - this->m_config->max_volumetric_extrusion_rate_slope_negative.value * 60.f * 60.f; + m_max_volumetric_extrusion_rate_slope_positive = (m_config == NULL) ? 6480.f : + m_config->max_volumetric_extrusion_rate_slope_positive.value * 60.f * 60.f; + m_max_volumetric_extrusion_rate_slope_negative = (m_config == NULL) ? 6480.f : + m_config->max_volumetric_extrusion_rate_slope_negative.value * 60.f * 60.f; for (size_t i = 0; i < numExtrusionRoles; ++ i) { m_max_volumetric_extrusion_rate_slopes[i].negative = m_max_volumetric_extrusion_rate_slope_negative; @@ -171,7 +171,7 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi if (strncmp(line, EXTRUSION_ROLE_TAG, strlen(EXTRUSION_ROLE_TAG)) == 0) { line += strlen(EXTRUSION_ROLE_TAG); int role = atoi(line); - this->m_current_extrusion_role = ExtrusionRole(role); + m_current_extrusion_role = ExtrusionRole(role); ++ line_idx; return false; } diff --git a/xs/src/libslic3r/GCodeReader.cpp b/xs/src/libslic3r/GCodeReader.cpp index 965b7ef8ec..c1e1e98be0 100644 --- a/xs/src/libslic3r/GCodeReader.cpp +++ b/xs/src/libslic3r/GCodeReader.cpp @@ -101,7 +101,7 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pairm_position[i] = gline.value(Axis(i)); + m_position[i] = gline.value(Axis(i)); } } } diff --git a/xs/src/libslic3r/GCodeWriter.cpp b/xs/src/libslic3r/GCodeWriter.cpp index cbe94f3179..9876a3fac7 100644 --- a/xs/src/libslic3r/GCodeWriter.cpp +++ b/xs/src/libslic3r/GCodeWriter.cpp @@ -18,7 +18,7 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config) { this->config.apply(print_config, true); m_extrusion_axis = this->config.get_extrusion_axis(); - this->m_single_extruder_multi_material = print_config.single_extruder_multi_material.value; + m_single_extruder_multi_material = print_config.single_extruder_multi_material.value; } void GCodeWriter::set_extruders(const std::vector &extruder_ids) @@ -85,7 +85,7 @@ std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, in } gcode << temperature; if (tool != -1 && - ( (this->multiple_extruders && ! this->m_single_extruder_multi_material) || + ( (this->multiple_extruders && ! m_single_extruder_multi_material) || FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)) ) { gcode << " T" << tool; } diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp index d774ef65cb..32cc07f156 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -131,7 +131,7 @@ bool BackgroundSlicingProcess::stop() bool BackgroundSlicingProcess::apply_config(const DynamicPrintConfig &config) { this->stop(); - bool invalidated = this->m_print->apply_config(config); + bool invalidated = m_print->apply_config(config); return invalidated; } diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp index 0be24824c4..892d0332ab 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.cpp +++ b/xs/src/slic3r/GUI/OptionsGroup.cpp @@ -66,12 +66,12 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co field->m_on_change = [this](std::string opt_id, boost::any value){ //! This function will be called from Field. //! Call OptionGroup._on_change(...) - if (!this->m_disabled) + if (!m_disabled) this->on_change_OG(opt_id, value); }; field->m_on_kill_focus = [this](){ //! This function will be called from Field. - if (!this->m_disabled) + if (!m_disabled) this->on_kill_focus(); }; field->m_parent = parent(); @@ -79,7 +79,7 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co //! Label to change background color, when option is modified field->m_Label = label; field->m_back_to_initial_value = [this](std::string opt_id){ - if (!this->m_disabled) + if (!m_disabled) this->back_to_initial_value(opt_id); }; if (!m_is_tab_opt) field->m_Undo_btn->Hide(); diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index aa1917bd57..59dcabb399 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -399,10 +399,10 @@ Preset* PresetCollection::find_preset(const std::string &name, bool first_visibl size_t PresetCollection::first_visible_idx() const { size_t idx = m_default_suppressed ? 1 : 0; - for (; idx < this->m_presets.size(); ++ idx) + for (; idx < m_presets.size(); ++ idx) if (m_presets[idx].is_visible) break; - if (idx == this->m_presets.size()) + if (idx == m_presets.size()) idx = 0; return idx; } @@ -411,10 +411,10 @@ size_t PresetCollection::first_visible_idx() const size_t PresetCollection::first_compatible_idx() const { size_t idx = m_default_suppressed ? 1 : 0; - for (; idx < this->m_presets.size(); ++ idx) + for (; idx < m_presets.size(); ++ idx) if (m_presets[idx].is_compatible) break; - if (idx == this->m_presets.size()) + if (idx == m_presets.size()) idx = 0; return idx; } @@ -465,8 +465,8 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) // Otherwise fill in the list from scratch. ui->Freeze(); ui->Clear(); - for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { - const Preset &preset = this->m_presets[i]; + for (size_t i = m_presets.front().is_visible ? 0 : 1; i < m_presets.size(); ++ i) { + const Preset &preset = m_presets[i]; if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected)) continue; const wxBitmap *bmp = (i == 0 || preset.is_compatible) ? m_bitmap_main_frame : m_bitmap_incompatible; @@ -484,8 +484,8 @@ void PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatibl return; ui->Freeze(); ui->Clear(); - for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { - const Preset &preset = this->m_presets[i]; + for (size_t i = m_presets.front().is_visible ? 0 : 1; i < m_presets.size(); ++ i) { + const Preset &preset = m_presets[i]; if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected)) continue; const wxBitmap *bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible; diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp index c1d85833bf..f5c1383472 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -181,7 +181,7 @@ public: const Preset& first_compatible() const { return this->preset(this->first_compatible_idx()); } // Return number of presets including the "- default -" preset. - size_t size() const { return this->m_presets.size(); } + size_t size() const { return m_presets.size(); } // For Print / Filament presets, disable those, which are not compatible with the printer. void update_compatible_with_printer(const Preset &active_printer, bool select_other_if_incompatible); From 41ce69f327a2fe3324dc47756b64a6fdb001cfa3 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 11 Sep 2018 14:04:47 +0200 Subject: [PATCH 08/36] Improved constness of the Print / PrintObject / Layer ... Split the wipe tower and time statistics data into separate objects. Initial work in synchronizing the Model with the Print. --- xs/src/libslic3r/Fill/Fill.cpp | 14 +- xs/src/libslic3r/Flow.cpp | 24 +- xs/src/libslic3r/GCode.cpp | 272 +++++----- xs/src/libslic3r/GCode/PrintExtents.cpp | 14 +- xs/src/libslic3r/GCode/ToolOrdering.cpp | 50 +- xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 26 +- xs/src/libslic3r/Layer.cpp | 52 +- xs/src/libslic3r/Layer.hpp | 145 +++--- xs/src/libslic3r/LayerRegion.cpp | 30 +- xs/src/libslic3r/PerimeterGenerator.cpp | 3 +- xs/src/libslic3r/PerimeterGenerator.hpp | 48 +- xs/src/libslic3r/PlaceholderParser.cpp | 16 +- xs/src/libslic3r/PlaceholderParser.hpp | 6 +- xs/src/libslic3r/Print.cpp | 401 ++++++++------- xs/src/libslic3r/Print.hpp | 344 ++++++++----- xs/src/libslic3r/PrintObject.cpp | 541 ++++++++++---------- xs/src/libslic3r/PrintRegion.cpp | 32 +- xs/src/libslic3r/SupportMaterial.cpp | 130 ++--- xs/src/libslic3r/SupportMaterial.hpp | 2 +- xs/src/slic3r/GUI/3DScene.cpp | 64 +-- xs/xsp/Print.xsp | 84 +-- 21 files changed, 1197 insertions(+), 1101 deletions(-) diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/xs/src/libslic3r/Fill/Fill.cpp index 5333fcfecd..f1436c9315 100644 --- a/xs/src/libslic3r/Fill/Fill.cpp +++ b/xs/src/libslic3r/Fill/Fill.cpp @@ -34,7 +34,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) { // Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id; - double fill_density = layerm.region()->config.fill_density; + double fill_density = layerm.region()->config().fill_density; Flow infill_flow = layerm.flow(frInfill); Flow solid_infill_flow = layerm.flow(frSolidInfill); Flow top_solid_infill_flow = layerm.flow(frTopSolidInfill); @@ -69,7 +69,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) { group_attrib[i].is_solid = true; group_attrib[i].flow_width = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width; - group_attrib[i].pattern = surface.is_external() ? layerm.region()->config.external_fill_pattern.value : ipRectilinear; + group_attrib[i].pattern = surface.is_external() ? layerm.region()->config().external_fill_pattern.value : ipRectilinear; } } // Loop through solid groups, find compatible groups and append them to this one. @@ -152,7 +152,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) for (const Surface &surface : surfaces) { if (surface.surface_type == stInternalVoid) continue; - InfillPattern fill_pattern = layerm.region()->config.fill_pattern.value; + InfillPattern fill_pattern = layerm.region()->config().fill_pattern.value; double density = fill_density; FlowRole role = (surface.surface_type == stTop) ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill); @@ -161,7 +161,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) if (surface.is_solid()) { density = 100.; fill_pattern = (surface.is_external() && ! is_bridge) ? - layerm.region()->config.external_fill_pattern.value : + layerm.region()->config().external_fill_pattern.value : ipRectilinear; } else if (density <= 0) continue; @@ -190,7 +190,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) // layer height Flow internal_flow = layerm.region()->flow( frInfill, - layerm.layer()->object()->config.layer_height.value, // TODO: handle infill_every_layers? + layerm.layer()->object()->config().layer_height.value, // TODO: handle infill_every_layers? false, // no bridge false, // no first layer -1, // auto width @@ -205,7 +205,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) double link_max_length = 0.; if (! is_bridge) { #if 0 - link_max_length = layerm.region()->config.get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing()); + link_max_length = layerm.region()->config().get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing()); // printf("flow spacing: %f, is_external: %d, link_max_length: %lf\n", flow.spacing(), int(surface.is_external()), link_max_length); #else if (density > 80.) // 80% @@ -215,7 +215,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) f->layer_id = layerm.layer()->id(); f->z = layerm.layer()->print_z; - f->angle = float(Geometry::deg2rad(layerm.region()->config.fill_angle.value)); + f->angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value)); // Maximum length of the perimeter segment linking two infill lines. f->link_max_length = scale_(link_max_length); // Used by the concentric infill pattern to clip the loops to create extrusion paths. diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp index b60e26dcc2..c33de8b6e7 100644 --- a/xs/src/libslic3r/Flow.cpp +++ b/xs/src/libslic3r/Flow.cpp @@ -111,22 +111,22 @@ Flow support_material_flow(const PrintObject *object, float layer_height) return Flow::new_from_config_width( frSupportMaterial, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. - (object->config.support_material_extrusion_width.value > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width, - // if object->config.support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. - float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)), - (layer_height > 0.f) ? layer_height : float(object->config.layer_height.value), + (object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width, + // if object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. + float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)), + (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value), false); } Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) { - const auto &width = (object->print()->config.first_layer_extrusion_width.value > 0) ? object->print()->config.first_layer_extrusion_width : object->config.support_material_extrusion_width; + const auto &width = (object->print()->config().first_layer_extrusion_width.value > 0) ? object->print()->config().first_layer_extrusion_width : object->config().support_material_extrusion_width; return Flow::new_from_config_width( frSupportMaterial, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. - (width.value > 0) ? width : object->config.extrusion_width, - float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)), - (layer_height > 0.f) ? layer_height : float(object->config.first_layer_height.get_abs_value(object->config.layer_height.value)), + (width.value > 0) ? width : object->config().extrusion_width, + float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)), + (layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value)), false); } @@ -135,10 +135,10 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig return Flow::new_from_config_width( frSupportMaterialInterface, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. - (object->config.support_material_extrusion_width > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width, - // if object->config.support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. - float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)), - (layer_height > 0.f) ? layer_height : float(object->config.layer_height.value), + (object->config().support_material_extrusion_width > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width, + // if object->config().support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. + float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_interface_extruder-1)), + (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value), false); } diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 9c6143fd65..e55404845c 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -275,15 +275,15 @@ std::string WipeTowerIntegration::finalize(GCode &gcodegen) std::vector GCode::collect_layers_to_print(const PrintObject &object) { std::vector layers_to_print; - layers_to_print.reserve(object.layers.size() + object.support_layers.size()); + layers_to_print.reserve(object.layers().size() + object.support_layers().size()); // Pair the object layers with the support layers by z. size_t idx_object_layer = 0; size_t idx_support_layer = 0; - while (idx_object_layer < object.layers.size() || idx_support_layer < object.support_layers.size()) { + while (idx_object_layer < object.layers().size() || idx_support_layer < object.support_layers().size()) { LayerToPrint layer_to_print; - layer_to_print.object_layer = (idx_object_layer < object.layers.size()) ? object.layers[idx_object_layer ++] : nullptr; - layer_to_print.support_layer = (idx_support_layer < object.support_layers.size()) ? object.support_layers[idx_support_layer ++] : nullptr; + layer_to_print.object_layer = (idx_object_layer < object.layers().size()) ? object.layers()[idx_object_layer ++] : nullptr; + layer_to_print.support_layer = (idx_support_layer < object.support_layers().size()) ? object.support_layers()[idx_support_layer ++] : nullptr; if (layer_to_print.object_layer && layer_to_print.support_layer) { if (layer_to_print.object_layer->print_z < layer_to_print.support_layer->print_z - EPSILON) { layer_to_print.support_layer = nullptr; @@ -309,10 +309,10 @@ std::vector>> GCode::collec size_t object_idx; size_t layer_idx; }; - std::vector> per_object(print.objects.size(), std::vector()); + std::vector> per_object(print.objects().size(), std::vector()); std::vector ordering; - for (size_t i = 0; i < print.objects.size(); ++ i) { - per_object[i] = collect_layers_to_print(*print.objects[i]); + for (size_t i = 0; i < print.objects().size(); ++ i) { + per_object[i] = collect_layers_to_print(*print.objects()[i]); OrderingItem ordering_item; ordering_item.object_idx = i; ordering.reserve(ordering.size() + per_object[i].size()); @@ -337,7 +337,7 @@ std::vector>> GCode::collec std::pair> merged; // Assign an average print_z to the set of layers with nearly equal print_z. merged.first = 0.5 * (ordering[i].print_z + ordering[j-1].print_z); - merged.second.assign(print.objects.size(), LayerToPrint()); + merged.second.assign(print.objects().size(), LayerToPrint()); for (; i < j; ++ i) { const OrderingItem &oi = ordering[i]; assert(merged.second[oi.object_idx].layer() == nullptr); @@ -366,7 +366,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); try { - this->m_placeholder_parser_failed_templates.clear(); + m_placeholder_parser_failed_templates.clear(); this->_do_export(*print, file, preview_data); fflush(file); if (ferror(file)) { @@ -382,10 +382,10 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ throw; } fclose(file); - if (! this->m_placeholder_parser_failed_templates.empty()) { + if (! 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) + for (const std::string &name : 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"; @@ -415,7 +415,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // resets time estimator m_time_estimator.reset(); - m_time_estimator.set_dialect(print.config.gcode_flavor); + m_time_estimator.set_dialect(print.config().gcode_flavor); // resets analyzer m_analyzer.reset(); @@ -429,14 +429,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // How many times will be change_layer() called? // change_layer() in turn increments the progress bar status. m_layer_count = 0; - if (print.config.complete_objects.value) { + if (print.config().complete_objects.value) { // Add each of the object's layers separately. - for (auto object : print.objects) { + for (auto object : print.objects()) { std::vector zs; - zs.reserve(object->layers.size() + object->support_layers.size()); - for (auto layer : object->layers) + zs.reserve(object->layers().size() + object->support_layers().size()); + for (auto layer : object->layers()) zs.push_back(layer->print_z); - for (auto layer : object->support_layers) + for (auto layer : object->support_layers()) zs.push_back(layer->print_z); std::sort(zs.begin(), zs.end()); m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin())); @@ -444,11 +444,11 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) } else { // Print all objects with the same print_z together. std::vector zs; - for (auto object : print.objects) { - zs.reserve(zs.size() + object->layers.size() + object->support_layers.size()); - for (auto layer : object->layers) + for (auto object : print.objects()) { + zs.reserve(zs.size() + object->layers().size() + object->support_layers().size()); + for (auto layer : object->layers()) zs.push_back(layer->print_z); - for (auto layer : object->support_layers) + for (auto layer : object->support_layers()) zs.push_back(layer->print_z); } std::sort(zs.begin(), zs.end()); @@ -457,33 +457,33 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) print.throw_if_canceled(); m_enable_cooling_markers = true; - this->apply_print_config(print.config); + this->apply_print_config(print.config()); this->set_extruders(print.extruders()); // Initialize autospeed. { // get the minimum cross-section used in the print std::vector mm3_per_mm; - for (auto object : print.objects) { - for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) { - auto region = print.regions[region_id]; - for (auto layer : object->layers) { - auto layerm = layer->regions[region_id]; - if (region->config.get_abs_value("perimeter_speed" ) == 0 || - region->config.get_abs_value("small_perimeter_speed" ) == 0 || - region->config.get_abs_value("external_perimeter_speed" ) == 0 || - region->config.get_abs_value("bridge_speed" ) == 0) + for (auto object : print.objects()) { + for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) { + auto region = print.regions()[region_id]; + for (auto layer : object->layers()) { + auto layerm = layer->regions()[region_id]; + if (region->config().get_abs_value("perimeter_speed" ) == 0 || + region->config().get_abs_value("small_perimeter_speed" ) == 0 || + region->config().get_abs_value("external_perimeter_speed" ) == 0 || + region->config().get_abs_value("bridge_speed" ) == 0) mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm()); - if (region->config.get_abs_value("infill_speed" ) == 0 || - region->config.get_abs_value("solid_infill_speed" ) == 0 || - region->config.get_abs_value("top_solid_infill_speed" ) == 0 || - region->config.get_abs_value("bridge_speed" ) == 0) + if (region->config().get_abs_value("infill_speed" ) == 0 || + region->config().get_abs_value("solid_infill_speed" ) == 0 || + region->config().get_abs_value("top_solid_infill_speed" ) == 0 || + region->config().get_abs_value("bridge_speed" ) == 0) mm3_per_mm.push_back(layerm->fills.min_mm3_per_mm()); } } - if (object->config.get_abs_value("support_material_speed" ) == 0 || - object->config.get_abs_value("support_material_interface_speed" ) == 0) - for (auto layer : object->support_layers) + if (object->config().get_abs_value("support_material_speed" ) == 0 || + object->config().get_abs_value("support_material_interface_speed" ) == 0) + for (auto layer : object->support_layers()) mm3_per_mm.push_back(layer->support_fills.min_mm3_per_mm()); } print.throw_if_canceled(); @@ -495,20 +495,20 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // volumetric speed as the volumetric speed produced by printing the // smallest cross-section at the maximum speed: any larger cross-section // will need slower feedrates. - m_volumetric_speed = *std::min_element(mm3_per_mm.begin(), mm3_per_mm.end()) * print.config.max_print_speed.value; + m_volumetric_speed = *std::min_element(mm3_per_mm.begin(), mm3_per_mm.end()) * print.config().max_print_speed.value; // limit such volumetric speed with max_volumetric_speed if set - if (print.config.max_volumetric_speed.value > 0) - m_volumetric_speed = std::min(m_volumetric_speed, print.config.max_volumetric_speed.value); + if (print.config().max_volumetric_speed.value > 0) + m_volumetric_speed = std::min(m_volumetric_speed, print.config().max_volumetric_speed.value); } } print.throw_if_canceled(); m_cooling_buffer = make_unique(*this); - if (print.config.spiral_vase.value) - m_spiral_vase = make_unique(print.config); - if (print.config.max_volumetric_extrusion_rate_slope_positive.value > 0 || - print.config.max_volumetric_extrusion_rate_slope_negative.value > 0) - m_pressure_equalizer = make_unique(&print.config); + if (print.config().spiral_vase.value) + m_spiral_vase = make_unique(print.config()); + if (print.config().max_volumetric_extrusion_rate_slope_positive.value > 0 || + print.config().max_volumetric_extrusion_rate_slope_negative.value > 0) + m_pressure_equalizer = make_unique(&print.config()); m_enable_extrusion_role_markers = (bool)m_pressure_equalizer; // Write information on the generator. @@ -516,7 +516,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // Write notes (content of the Print Settings tab -> Notes) { std::list lines; - boost::split(lines, print.config.notes.value, boost::is_any_of("\n"), boost::token_compress_off); + boost::split(lines, print.config().notes.value, boost::is_any_of("\n"), boost::token_compress_off); for (auto line : lines) { // Remove the trailing '\r' from the '\r\n' sequence. if (! line.empty() && line.back() == '\r') @@ -529,11 +529,11 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) print.throw_if_canceled(); // 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]; + 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]; _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width); _write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width); _write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width); @@ -541,14 +541,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width); if (print.has_support_material()) _write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width); - if (print.config.first_layer_extrusion_width.value > 0) + if (print.config().first_layer_extrusion_width.value > 0) _write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width); _write_format(file, "\n"); } print.throw_if_canceled(); // Prepare the helper object for replacing placeholders in custom G-code and output filename. - m_placeholder_parser = print.placeholder_parser; + m_placeholder_parser = print.placeholder_parser(); m_placeholder_parser.update_timestamp(); // Get optimal tool ordering to minimize tool switches of a multi-exruder print. @@ -558,19 +558,19 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) 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) { + 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) { - tool_ordering = ToolOrdering(*print.objects[initial_print_object_id], initial_extruder_id); + for (; initial_print_object_id < print.objects().size(); ++initial_print_object_id) { + tool_ordering = ToolOrdering(*print.objects()[initial_print_object_id], initial_extruder_id); if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1) break; } } else { // Find tool ordering for all the objects at once, and the initial extruder ID. // If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it. - tool_ordering = print.m_tool_ordering.empty() ? + tool_ordering = print.wipe_tower_data().tool_ordering.empty() ? ToolOrdering(print, initial_extruder_id) : - print.m_tool_ordering; + print.wipe_tower_data().tool_ordering; initial_extruder_id = tool_ordering.first_extruder(); has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower(); } @@ -587,7 +587,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) m_cooling_buffer->set_current_extruder(initial_extruder_id); // Disable fan. - if (! print.config.cooling.get_at(initial_extruder_id) || print.config.disable_fan_first_layers.get_at(initial_extruder_id)) + if (! print.config().cooling.get_at(initial_extruder_id) || print.config().disable_fan_first_layers.get_at(initial_extruder_id)) _write(file, m_writer.set_fan(0, true)); // Let the start-up script prime the 1st printing tool. @@ -598,7 +598,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) m_placeholder_parser.set("current_object_idx", 0); // For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided. m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); - std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config.start_gcode.value, initial_extruder_id); + std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id); // Set bed temperature if the start G-code does not contain any bed temp control G-codes. this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true); @@ -616,16 +616,16 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // Write the custom start G-code _writeln(file, start_gcode); // Process filament-specific gcode in extruder order. - if (print.config.single_extruder_multi_material) { + if (print.config().single_extruder_multi_material) { if (has_wipe_tower) { // Wipe tower will control the extruder switching, it will call the start_filament_gcode. } else { // Only initialize the initial extruder. - _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id)); + _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id)); } } else { - for (const std::string &start_gcode : print.config.start_filament_gcode.values) - _writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front()))); + 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); print.throw_if_canceled(); @@ -634,12 +634,12 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _write(file, this->preamble()); // Initialize a motion planner for object-to-object travel moves. - if (print.config.avoid_crossing_perimeters.value) { + if (print.config().avoid_crossing_perimeters.value) { // Collect outer contours of all objects over all layers. // Discard objects only containing thin walls (offset would fail on an empty polygon). Polygons islands; - for (const PrintObject *object : print.objects) - for (const Layer *layer : object->layers) + for (const PrintObject *object : print.objects()) + for (const Layer *layer : object->layers()) for (const ExPolygon &expoly : layer->slices.expolygons) for (const Point © : object->_shifted_copies) { islands.emplace_back(expoly.contour); @@ -651,16 +651,16 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) } // Calculate wiping points if needed - if (print.config.ooze_prevention.value && ! print.config.single_extruder_multi_material) { + if (print.config().ooze_prevention.value && ! print.config().single_extruder_multi_material) { Points skirt_points; - for (const ExtrusionEntity *ee : print.skirt.entities) + for (const ExtrusionEntity *ee : print.skirt().entities) for (const ExtrusionPath &path : dynamic_cast(ee)->paths) append(skirt_points, path.polyline.points); if (! skirt_points.empty()) { Polygon outer_skirt = Slic3r::Geometry::convex_hull(skirt_points); Polygons skirts; for (unsigned int extruder_id : print.extruders()) { - const Pointf &extruder_offset = print.config.extruder_offset.get_at(extruder_id); + const Pointf &extruder_offset = print.config().extruder_offset.get_at(extruder_id); Polygon s(outer_skirt); s.translate(-scale_(extruder_offset.x), -scale_(extruder_offset.y)); skirts.emplace_back(std::move(s)); @@ -685,10 +685,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _write(file, this->set_extruder(initial_extruder_id)); // Do all objects for each layer. - if (print.config.complete_objects.value) { + if (print.config().complete_objects.value) { // Print objects from the smallest to the tallest to avoid collisions // when moving onto next object starting point. - std::vector objects(print.objects); + std::vector objects(print.objects()); std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size.z < po2->size.z; }); size_t finished_objects = 0; for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) { @@ -722,7 +722,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // 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 = this->placeholder_parser_process("between_objects_gcode", 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); @@ -751,7 +751,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // Order objects using a nearest neighbor search. std::vector object_indices; Points object_reference_points; - for (PrintObject *object : print.objects) + for (PrintObject *object : print.objects()) object_reference_points.push_back(object->_shifted_copies.front()); Slic3r::Geometry::chained_path(object_reference_points, object_indices); // Sort layers by Z. @@ -759,13 +759,13 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) std::vector>> layers_to_print = collect_layers_to_print(print); // Prusa Multi-Material wipe tower. 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())); + m_wipe_tower.reset(new WipeTowerIntegration(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().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) + 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)); @@ -813,14 +813,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) } // Process filament-specific gcode in extruder order. - if (print.config.single_extruder_multi_material) { + 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())); + _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_filament_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front()))); + for (const std::string &end_gcode : print.config().end_filament_gcode.values) + _writeln(file, this->placeholder_parser_process("end_filament_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())); + _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()); print.throw_if_canceled(); @@ -829,31 +829,27 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) m_time_estimator.calculate_time(); // Get filament stats. - print.filament_stats.clear(); - print.total_used_filament = 0.; - print.total_extruded_volume = 0.; - print.total_weight = 0.; - print.total_cost = 0.; - print.estimated_print_time = m_time_estimator.get_time_hms(); + print.m_print_statistics.clear(); + print.m_print_statistics.estimated_print_time = m_time_estimator.get_time_hms(); for (const Extruder &extruder : m_writer.extruders()) { double used_filament = extruder.used_filament(); double extruded_volume = extruder.extruded_volume(); double filament_weight = extruded_volume * extruder.filament_density() * 0.001; double filament_cost = filament_weight * extruder.filament_cost() * 0.001; - print.filament_stats.insert(std::pair(extruder.id(), used_filament)); + print.m_print_statistics.filament_stats.insert(std::pair(extruder.id(), used_filament)); _write_format(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001); if (filament_weight > 0.) { - print.total_weight = print.total_weight + filament_weight; + print.m_print_statistics.total_weight = print.m_print_statistics.total_weight + filament_weight; _write_format(file, "; filament used = %.1lf\n", filament_weight); if (filament_cost > 0.) { - print.total_cost = print.total_cost + filament_cost; + print.m_print_statistics.total_cost = print.m_print_statistics.total_cost + filament_cost; _write_format(file, "; filament cost = %.1lf\n", filament_cost); } } - print.total_used_filament = print.total_used_filament + used_filament; - print.total_extruded_volume = print.total_extruded_volume + extruded_volume; + print.m_print_statistics.total_used_filament = print.m_print_statistics.total_used_filament + used_filament; + print.m_print_statistics.total_extruded_volume = print.m_print_statistics.total_extruded_volume + extruded_volume; } - _write_format(file, "; total filament cost = %.1lf\n", print.total_cost); + _write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost); _write_format(file, "; estimated printing time = %s\n", m_time_estimator.get_time_hms().c_str()); // Append full config. @@ -879,7 +875,7 @@ std::string GCode::placeholder_parser_process(const std::string &name, const std 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); + 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" + @@ -949,7 +945,7 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { // Initial bed temperature based on the first extruder. - int temp = print.config.first_layer_bed_temperature.get_at(first_printing_extruder_id); + int temp = print.config().first_layer_bed_temperature.get_at(first_printing_extruder_id); // Is the bed temperature set by the provided custom G-code? int temp_by_gcode = -1; bool temp_set_by_gcode = custom_gcode_sets_temperature(gcode, 140, 190, temp_by_gcode); @@ -972,23 +968,23 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c int temp_by_gcode = -1; if (custom_gcode_sets_temperature(gcode, 104, 109, temp_by_gcode)) { // Set the extruder temperature at m_writer, but throw away the generated G-code as it will be written with the custom G-code. - int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id); + int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id); if (temp_by_gcode >= 0 && temp_by_gcode < 1000) temp = temp_by_gcode; m_writer.set_temperature(temp_by_gcode, wait, first_printing_extruder_id); } else { // Custom G-code does not set the extruder temperature. Do it now. - if (print.config.single_extruder_multi_material.value) { + if (print.config().single_extruder_multi_material.value) { // Set temperature of the first printing extruder only. - int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id); + int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id); if (temp > 0) _write(file, m_writer.set_temperature(temp, wait, first_printing_extruder_id)); } else { // Set temperatures of all the printing extruders. for (unsigned int tool_id : print.extruders()) { - int temp = print.config.first_layer_temperature.get_at(tool_id); - if (print.config.ooze_prevention.value) - temp += print.config.standby_temperature_delta.value; + int temp = print.config().first_layer_temperature.get_at(tool_id); + if (print.config().ooze_prevention.value) + temp += print.config().standby_temperature_delta.value; if (temp > 0) _write(file, m_writer.set_temperature(temp, wait, tool_id)); } @@ -1061,15 +1057,15 @@ void GCode::process_layer( unsigned int first_extruder_id = layer_tools.extruders.front(); // Initialize config with the 1st object to be printed at this layer. - m_config.apply(layer.object()->config, true); + m_config.apply(layer.object()->config(), true); // Check whether it is possible to apply the spiral vase logic for this layer. // Just a reminder: A spiral vase mode is allowed for a single object, single material print only. if (m_spiral_vase && layers.size() == 1 && support_layer == nullptr) { - bool enable = (layer.id() > 0 || print.config.brim_width.value == 0.) && (layer.id() >= print.config.skirt_height.value && ! print.has_infinite_skirt()); + bool enable = (layer.id() > 0 || print.config().brim_width.value == 0.) && (layer.id() >= print.config().skirt_height.value && ! print.has_infinite_skirt()); if (enable) { - for (const LayerRegion *layer_region : layer.regions) - if (layer_region->region()->config.bottom_solid_layers.value > layer.id() || + for (const LayerRegion *layer_region : layer.regions()) + if (layer_region->region()->config().bottom_solid_layers.value > layer.id() || layer_region->perimeters.items_count() > 1 || layer_region->fills.items_count() > 0) { enable = false; @@ -1084,22 +1080,22 @@ void GCode::process_layer( std::string gcode; // Set new layer - this will change Z and force a retraction if retract_layer_change is enabled. - if (! print.config.before_layer_gcode.value.empty()) { + if (! print.config().before_layer_gcode.value.empty()) { 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 += this->placeholder_parser_process("before_layer_gcode", - print.config.before_layer_gcode.value, m_writer.extruder()->id(), &config) + print.config().before_layer_gcode.value, m_writer.extruder()->id(), &config) + "\n"; } gcode += this->change_layer(print_z); // this will increase m_layer_index m_layer = &layer; - if (! print.config.layer_gcode.value.empty()) { + if (! print.config().layer_gcode.value.empty()) { DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); gcode += this->placeholder_parser_process("layer_gcode", - print.config.layer_gcode.value, m_writer.extruder()->id(), &config) + print.config().layer_gcode.value, m_writer.extruder()->id(), &config) + "\n"; } @@ -1107,14 +1103,14 @@ void GCode::process_layer( // Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent // first_layer_temperature vs. temperature settings. for (const Extruder &extruder : m_writer.extruders()) { - if (print.config.single_extruder_multi_material.value && extruder.id() != m_writer.extruder()->id()) + if (print.config().single_extruder_multi_material.value && extruder.id() != m_writer.extruder()->id()) // In single extruder multi material mode, set the temperature for the current extruder only. continue; - int temperature = print.config.temperature.get_at(extruder.id()); - if (temperature > 0 && temperature != print.config.first_layer_temperature.get_at(extruder.id())) + int temperature = print.config().temperature.get_at(extruder.id()); + if (temperature > 0 && temperature != print.config().first_layer_temperature.get_at(extruder.id())) gcode += m_writer.set_temperature(temperature, false, extruder.id()); } - gcode += m_writer.set_bed_temperature(print.config.bed_temperature.get_at(first_extruder_id)); + gcode += m_writer.set_bed_temperature(print.config().bed_temperature.get_at(first_extruder_id)); // Mark the temperature transition from 1st to 2nd layer to be finished. m_second_layer_things_done = true; } @@ -1122,9 +1118,9 @@ void GCode::process_layer( // Extrude skirt at the print_z of the raft layers and normal object layers // not at the print_z of the interlaced support material layers. bool extrude_skirt = - ! print.skirt.entities.empty() && + ! print.skirt().entities.empty() && // Not enough skirt layers printed yet. - (m_skirt_done.size() < print.config.skirt_height.value || print.has_infinite_skirt()) && + (m_skirt_done.size() < print.config().skirt_height.value || print.has_infinite_skirt()) && // This print_z has not been extruded yet (m_skirt_done.empty() ? 0. : m_skirt_done.back()) < print_z - EPSILON && // and this layer is the 1st layer, or it is an object layer, or it is a raft layer. @@ -1146,7 +1142,7 @@ void GCode::process_layer( extruder_ids.front() = first_extruder_id; break; } - size_t n_loops = print.skirt.entities.size(); + size_t n_loops = print.skirt().entities.size(); if (n_loops <= extruder_ids.size()) { for (size_t i = 0; i < n_loops; ++i) skirt_loops_per_extruder[extruder_ids[i]] = std::pair(i, i + 1); @@ -1165,7 +1161,7 @@ void GCode::process_layer( } } else // Extrude all skirts with the current extruder. - skirt_loops_per_extruder[first_extruder_id] = std::pair(0, print.config.skirts.value); + skirt_loops_per_extruder[first_extruder_id] = std::pair(0, print.config().skirts.value); } // Group extrusions by an extruder, then by an object, an island and a region. @@ -1180,22 +1176,22 @@ void GCode::process_layer( bool has_support = role == erMixed || role == erSupportMaterial; bool has_interface = role == erMixed || role == erSupportMaterialInterface; // Extruder ID of the support base. -1 if "don't care". - unsigned int support_extruder = object.config.support_material_extruder.value - 1; + unsigned int support_extruder = object.config().support_material_extruder.value - 1; // Shall the support be printed with the active extruder, preferably with non-soluble, to avoid tool changes? - bool support_dontcare = object.config.support_material_extruder.value == 0; + bool support_dontcare = object.config().support_material_extruder.value == 0; // Extruder ID of the support interface. -1 if "don't care". - unsigned int interface_extruder = object.config.support_material_interface_extruder.value - 1; + unsigned int interface_extruder = object.config().support_material_interface_extruder.value - 1; // Shall the support interface be printed with the active extruder, preferably with non-soluble, to avoid tool changes? - bool interface_dontcare = object.config.support_material_interface_extruder.value == 0; + bool interface_dontcare = object.config().support_material_interface_extruder.value == 0; if (support_dontcare || interface_dontcare) { // Some support will be printed with "don't care" material, preferably non-soluble. // Is the current extruder assigned a soluble filament? unsigned int dontcare_extruder = first_extruder_id; - if (print.config.filament_soluble.get_at(dontcare_extruder)) { + if (print.config().filament_soluble.get_at(dontcare_extruder)) { // The last extruder printed on the previous layer extrudes soluble filament. // Try to find a non-soluble extruder on the same layer. for (unsigned int extruder_id : layer_tools.extruders) - if (! print.config.filament_soluble.get_at(extruder_id)) { + if (! print.config().filament_soluble.get_at(extruder_id)) { dontcare_extruder = extruder_id; break; } @@ -1242,11 +1238,11 @@ void GCode::process_layer( layer.slices.expolygons[i].contour.contains(point); }; - for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) { - const LayerRegion *layerm = layer.regions[region_id]; + for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) { + const LayerRegion *layerm = layer.regions()[region_id]; if (layerm == nullptr) continue; - const PrintRegion ®ion = *print.regions[region_id]; + const PrintRegion ®ion = *print.regions()[region_id]; // process perimeters for (const ExtrusionEntity *ee : layerm->perimeters.entities) { @@ -1258,7 +1254,7 @@ void GCode::process_layer( // Init by_extruder item only if we actually use the extruder. std::vector &islands = object_islands_by_extruder( by_extruder, - std::max(region.config.perimeter_extruder.value - 1, 0), + std::max(region.config().perimeter_extruder.value - 1, 0), &layer_to_print - layers.data(), layers.size(), n_slices+1); for (size_t i = 0; i <= n_slices; ++ i) @@ -1267,7 +1263,7 @@ void GCode::process_layer( // perimeter_coll->first_point fits inside ith slice point_inside_surface(i, perimeter_coll->first_point())) { if (islands[i].by_region.empty()) - islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region()); + islands[i].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); islands[i].by_region[region_id].perimeters.append(perimeter_coll->entities); break; } @@ -1285,7 +1281,7 @@ void GCode::process_layer( // This shouldn't happen but first_point() would fail. continue; // init by_extruder item only if we actually use the extruder - int extruder_id = std::max(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1); + int extruder_id = std::max(0, (is_solid_infill(fill->entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1); // Init by_extruder item only if we actually use the extruder. std::vector &islands = object_islands_by_extruder( by_extruder, @@ -1298,7 +1294,7 @@ void GCode::process_layer( // fill->first_point fits inside ith slice point_inside_surface(i, fill->first_point())) { if (islands[i].by_region.empty()) - islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region()); + islands[i].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); islands[i].by_region[region_id].infills.append(fill->entities); break; } @@ -1324,7 +1320,7 @@ void GCode::process_layer( Flow skirt_flow = print.skirt_flow(); for (size_t i = loops.first; i < loops.second; ++ i) { // Adjust flow according to this layer's layer height. - ExtrusionLoop loop = *dynamic_cast(print.skirt.entities[i]); + ExtrusionLoop loop = *dynamic_cast(print.skirt().entities[i]); Flow layer_skirt_flow(skirt_flow); layer_skirt_flow.height = (float)skirt_height; double mm3_per_mm = layer_skirt_flow.mm3_per_mm(); @@ -1345,7 +1341,7 @@ void GCode::process_layer( if (! m_brim_done) { this->set_origin(0., 0.); m_avoid_crossing_perimeters.use_external_mp = true; - for (const ExtrusionEntity *ee : print.brim.entities) + for (const ExtrusionEntity *ee : print.brim().entities) gcode += this->extrude_loop(*dynamic_cast(ee), "brim", m_config.support_material_speed.value); m_brim_done = true; m_avoid_crossing_perimeters.use_external_mp = false; @@ -1363,7 +1359,7 @@ void GCode::process_layer( // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z. continue; - m_config.apply(print_object->config, true); + m_config.apply(print_object->config(), true); m_layer = layers[layer_id].layer(); if (m_config.avoid_crossing_perimeters) m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true)); @@ -1389,7 +1385,7 @@ void GCode::process_layer( m_layer = layers[layer_id].layer(); } for (const ObjectByExtruder::Island &island : object_by_extruder.islands) { - if (print.config.infill_first) { + if (print.config().infill_first) { gcode += this->extrude_infill(print, island.by_region); gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]); } else { @@ -1430,7 +1426,7 @@ void GCode::apply_print_config(const PrintConfig &print_config) void GCode::append_full_config(const Print& print, std::string& str) { - const StaticPrintConfig *configs[] = { &print.config, &print.default_object_config, &print.default_region_config }; + const StaticPrintConfig *configs[] = { &print.config(), &print.default_object_config(), &print.default_region_config() }; for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) { const StaticPrintConfig *cfg = configs[i]; for (const std::string &key : cfg->keys()) @@ -2015,7 +2011,7 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vectorconfig); + m_config.apply(print.regions()[®ion - &by_region.front()]->config()); for (ExtrusionEntity *ee : region.perimeters.entities) gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid); } @@ -2027,7 +2023,7 @@ std::string GCode::extrude_infill(const Print &print, const std::vectorconfig); + m_config.apply(print.regions()[®ion - &by_region.front()]->config()); ExtrusionEntityCollection chained = region.infills.chained_path_from(m_last_pos, false); for (ExtrusionEntity *fill : chained.entities) { auto *eec = dynamic_cast(fill); diff --git a/xs/src/libslic3r/GCode/PrintExtents.cpp b/xs/src/libslic3r/GCode/PrintExtents.cpp index 3c3f0f8d5c..43352ee823 100644 --- a/xs/src/libslic3r/GCode/PrintExtents.cpp +++ b/xs/src/libslic3r/GCode/PrintExtents.cpp @@ -99,19 +99,19 @@ static BoundingBoxf extrusionentity_extents(const ExtrusionEntity *extrusion_ent BoundingBoxf get_print_extrusions_extents(const Print &print) { - BoundingBoxf bbox(extrusionentity_extents(print.brim)); - bbox.merge(extrusionentity_extents(print.skirt)); + BoundingBoxf bbox(extrusionentity_extents(print.brim())); + bbox.merge(extrusionentity_extents(print.skirt())); return bbox; } BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object, const coordf_t max_print_z) { BoundingBoxf bbox; - for (const Layer *layer : print_object.layers) { + for (const Layer *layer : print_object.layers()) { if (layer->print_z > max_print_z) break; BoundingBoxf bbox_this; - for (const LayerRegion *layerm : layer->regions) { + for (const LayerRegion *layerm : layer->regions()) { bbox_this.merge(extrusionentity_extents(layerm->perimeters)); for (const ExtrusionEntity *ee : layerm->fills.entities) // fill represents infill extrusions of a single island. @@ -135,7 +135,7 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_t max_print_z) { BoundingBoxf bbox; - for (const std::vector &tool_changes : print.m_wipe_tower_tool_changes) { + for (const std::vector &tool_changes : print.wipe_tower_data().tool_changes) { if (! tool_changes.empty() && tool_changes.front().print_z > max_print_z) break; for (const WipeTower::ToolChangeResult &tcr : tool_changes) { @@ -161,8 +161,8 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_ BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print) { BoundingBoxf bbox; - if (print.m_wipe_tower_priming) { - const WipeTower::ToolChangeResult &tcr = *print.m_wipe_tower_priming.get(); + if (print.wipe_tower_data().priming != nullptr) { + const WipeTower::ToolChangeResult &tcr = *print.wipe_tower_data().priming; for (size_t i = 1; i < tcr.extrusions.size(); ++ i) { const WipeTower::Extrusion &e = tcr.extrusions[i]; if (e.width > 0) { diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp index 271b75ef32..6e03d89c9a 100644 --- a/xs/src/libslic3r/GCode/ToolOrdering.cpp +++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp @@ -16,19 +16,19 @@ namespace Slic3r { // For the use case when each object is printed separately -// (print.config.complete_objects is true). +// (print.config().complete_objects is true). ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material) { - if (object.layers.empty()) + if (object.layers().empty()) return; // Initialize the print layers for just a single object. { std::vector zs; - zs.reserve(zs.size() + object.layers.size() + object.support_layers.size()); - for (auto layer : object.layers) + zs.reserve(zs.size() + object.layers().size() + object.support_layers().size()); + for (auto layer : object.layers()) zs.emplace_back(layer->print_z); - for (auto layer : object.support_layers) + for (auto layer : object.support_layers()) zs.emplace_back(layer->print_z); this->initialize_layers(zs); } @@ -39,39 +39,39 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); - this->fill_wipe_tower_partitions(object.print()->config, object.layers.front()->print_z - object.layers.front()->height); + this->fill_wipe_tower_partitions(object.print()->config(), object.layers().front()->print_z - object.layers().front()->height); this->collect_extruder_statistics(prime_multi_material); } // For the use case when all objects are printed at once. -// (print.config.complete_objects is false). +// (print.config().complete_objects is false). ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material) { // Initialize the print layers for all objects and all layers. coordf_t object_bottom_z = 0.; { std::vector zs; - for (auto object : print.objects) { - zs.reserve(zs.size() + object->layers.size() + object->support_layers.size()); - for (auto layer : object->layers) + for (auto object : print.objects()) { + zs.reserve(zs.size() + object->layers().size() + object->support_layers().size()); + for (auto layer : object->layers()) zs.emplace_back(layer->print_z); - for (auto layer : object->support_layers) + for (auto layer : object->support_layers()) zs.emplace_back(layer->print_z); - if (! object->layers.empty()) - object_bottom_z = object->layers.front()->print_z - object->layers.front()->height; + if (! object->layers().empty()) + object_bottom_z = object->layers().front()->print_z - object->layers().front()->height; } this->initialize_layers(zs); } // Collect extruders reuqired to print the layers. - for (auto object : print.objects) + for (auto object : print.objects()) this->collect_extruders(*object); // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); - this->fill_wipe_tower_partitions(print.config, object_bottom_z); + this->fill_wipe_tower_partitions(print.config(), object_bottom_z); this->collect_extruder_statistics(prime_multi_material); } @@ -111,13 +111,13 @@ void ToolOrdering::initialize_layers(std::vector &zs) void ToolOrdering::collect_extruders(const PrintObject &object) { // Collect the support extruders. - for (auto support_layer : object.support_layers) { + for (auto support_layer : object.support_layers()) { LayerTools &layer_tools = this->tools_for_layer(support_layer->print_z); ExtrusionRole role = support_layer->support_fills.role(); bool has_support = role == erMixed || role == erSupportMaterial; bool has_interface = role == erMixed || role == erSupportMaterialInterface; - unsigned int extruder_support = object.config.support_material_extruder.value; - unsigned int extruder_interface = object.config.support_material_interface_extruder.value; + unsigned int extruder_support = object.config().support_material_extruder.value; + unsigned int extruder_interface = object.config().support_material_interface_extruder.value; if (has_support) layer_tools.extruders.push_back(extruder_support); if (has_interface) @@ -126,16 +126,16 @@ void ToolOrdering::collect_extruders(const PrintObject &object) layer_tools.has_support = true; } // Collect the object extruders. - for (auto layer : object.layers) { + for (auto layer : object.layers()) { LayerTools &layer_tools = this->tools_for_layer(layer->print_z); // What extruders are required to print this object layer? - for (size_t region_id = 0; region_id < object.print()->regions.size(); ++ region_id) { - const LayerRegion *layerm = (region_id < layer->regions.size()) ? layer->regions[region_id] : nullptr; + for (size_t region_id = 0; region_id < object.print()->regions().size(); ++ region_id) { + const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr; if (layerm == nullptr) continue; - const PrintRegion ®ion = *object.print()->regions[region_id]; + const PrintRegion ®ion = *object.print()->regions()[region_id]; if (! layerm->perimeters.entities.empty()) { - layer_tools.extruders.push_back(region.config.perimeter_extruder.value); + layer_tools.extruders.push_back(region.config().perimeter_extruder.value); layer_tools.has_object = true; } bool has_infill = false; @@ -150,9 +150,9 @@ void ToolOrdering::collect_extruders(const PrintObject &object) has_infill = true; } if (has_solid_infill) - layer_tools.extruders.push_back(region.config.solid_infill_extruder); + layer_tools.extruders.push_back(region.config().solid_infill_extruder); if (has_infill) - layer_tools.extruders.push_back(region.config.infill_extruder); + layer_tools.extruders.push_back(region.config().infill_extruder); if (has_solid_infill || has_infill) layer_tools.has_object = true; } diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index 99c6c757fa..069f975dd9 100644 --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -396,8 +396,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( } this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false); - this->m_num_layer_changes = 0; - this->m_current_tool = tools.front(); + m_num_layer_changes = 0; + m_current_tool = tools.front(); // The Prusa i3 MK2 has a working space of [0, -2.2] to [250, 210]. // Due to the XYZ calibration, this working space may shrink slightly from all directions, @@ -439,9 +439,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( if (last_wipe_inside_wipe_tower) { // Shrink the last wipe area to the area of the other purge areas, // remember the last initial wipe width to be purged into the 1st layer of the wipe tower. - this->m_initial_extra_wipe = std::max(0.f, wipe_area - (y_end + 0.5f * 0.85f * m_perimeter_width - cleaning_box.ld.y)); - cleaning_box.lu.y -= this->m_initial_extra_wipe; - cleaning_box.ru.y -= this->m_initial_extra_wipe; + m_initial_extra_wipe = std::max(0.f, wipe_area - (y_end + 0.5f * 0.85f * m_perimeter_width - cleaning_box.ld.y)); + cleaning_box.lu.y -= m_initial_extra_wipe; + cleaning_box.ru.y -= m_initial_extra_wipe; } toolchange_Wipe(writer, cleaning_box, false); } else { @@ -471,8 +471,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( m_idx_tool_change_in_layer = (unsigned int)(-1); ToolChangeResult result; - result.print_z = this->m_z_pos; - result.layer_height = this->m_layer_height; + result.print_z = m_z_pos; + result.layer_height = m_layer_height; result.gcode = writer.gcode(); result.elapsed_time = writer.elapsed_time(); result.extrusions = writer.extrusions(); @@ -612,8 +612,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo } ToolChangeResult result; - result.print_z = this->m_z_pos; - result.layer_height = this->m_layer_height; + result.print_z = m_z_pos; + result.layer_height = m_layer_height; result.gcode = writer.gcode(); result.elapsed_time = writer.elapsed_time(); result.extrusions = writer.extrusions(); @@ -718,8 +718,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b } ToolChangeResult result; - result.print_z = this->m_z_pos; - result.layer_height = this->m_layer_height; + result.print_z = m_z_pos; + result.layer_height = m_layer_height; result.gcode = writer.gcode(); result.elapsed_time = writer.elapsed_time(); result.extrusions = writer.extrusions(); @@ -1040,8 +1040,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose) } ToolChangeResult result; - result.print_z = this->m_z_pos; - result.layer_height = this->m_layer_height; + result.print_z = m_z_pos; + result.layer_height = m_layer_height; result.gcode = writer.gcode(); result.elapsed_time = writer.elapsed_time(); result.extrusions = writer.extrusions(); diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp index 3dc2335d79..72ac371a2c 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/xs/src/libslic3r/Layer.cpp @@ -12,24 +12,24 @@ namespace Slic3r { Layer::~Layer() { this->lower_layer = this->upper_layer = nullptr; - for (LayerRegion *region : this->regions) + for (LayerRegion *region : m_regions) delete region; - this->regions.clear(); + m_regions.clear(); } LayerRegion* Layer::add_region(PrintRegion* print_region) { - this->regions.emplace_back(new LayerRegion(this, print_region)); - return this->regions.back(); + m_regions.emplace_back(new LayerRegion(this, print_region)); + return m_regions.back(); } // merge all regions' slices to get islands void Layer::make_slices() { ExPolygons slices; - if (this->regions.size() == 1) { + if (m_regions.size() == 1) { // optimization: if we only have one region, take its slices - slices = this->regions.front()->slices; + slices = m_regions.front()->slices; } else { Polygons slices_p; FOREACH_LAYERREGION(this, layerm) { @@ -58,10 +58,10 @@ void Layer::make_slices() void Layer::merge_slices() { - if (this->regions.size() == 1) { + if (m_regions.size() == 1) { // Optimization, also more robust. Don't merge classified pieces of layerm->slices, // but use the non-split islands of a layer. For a single region print, these shall be equal. - this->regions.front()->slices.set(this->slices.expolygons, stInternal); + m_regions.front()->slices.set(this->slices.expolygons, stInternal); } else { FOREACH_LAYERREGION(this, layerm) { // without safety offset, artifacts are generated (GH #2494) @@ -81,18 +81,18 @@ void Layer::make_perimeters() std::set done; FOREACH_LAYERREGION(this, layerm) { - size_t region_id = layerm - this->regions.begin(); + size_t region_id = layerm - m_regions.begin(); if (done.find(region_id) != done.end()) continue; BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id; done.insert(region_id); - const PrintRegionConfig &config = (*layerm)->region()->config; + const PrintRegionConfig &config = (*layerm)->region()->config(); // find compatible regions LayerRegionPtrs layerms; layerms.push_back(*layerm); - for (LayerRegionPtrs::const_iterator it = layerm + 1; it != this->regions.end(); ++it) { + for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it) { LayerRegion* other_layerm = *it; - const PrintRegionConfig &other_config = other_layerm->region()->config; + const PrintRegionConfig &other_config = other_layerm->region()->config(); if (config.perimeter_extruder == other_config.perimeter_extruder && config.perimeters == other_config.perimeters @@ -104,7 +104,7 @@ void Layer::make_perimeters() && config.thin_walls == other_config.thin_walls && config.external_perimeters_first == other_config.external_perimeters_first) { layerms.push_back(other_layerm); - done.insert(it - this->regions.begin()); + done.insert(it - m_regions.begin()); } } @@ -150,7 +150,7 @@ void Layer::make_fills() #ifdef SLIC3R_DEBUG printf("Making fills for layer " PRINTF_ZU "\n", this->id()); #endif - for (LayerRegion *layerm : regions) { + for (LayerRegion *layerm : m_regions) { layerm->fills.clear(); make_fill(*layerm, layerm->fills); #ifndef NDEBUG @@ -163,18 +163,18 @@ void Layer::make_fills() void Layer::export_region_slices_to_svg(const char *path) const { BoundingBox bbox; - for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region) - for (Surfaces::const_iterator surface = (*region)->slices.surfaces.begin(); surface != (*region)->slices.surfaces.end(); ++surface) - bbox.merge(get_extents(surface->expolygon)); + for (const auto *region : m_regions) + for (const auto &surface : region->slices.surfaces) + bbox.merge(get_extents(surface.expolygon)); Point legend_size = export_surface_type_legend_to_svg_box_size(); Point legend_pos(bbox.min.x, bbox.max.y); bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y)); SVG svg(path, bbox); const float transparency = 0.5f; - for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region) - for (Surfaces::const_iterator surface = (*region)->slices.surfaces.begin(); surface != (*region)->slices.surfaces.end(); ++surface) - svg.draw(surface->expolygon, surface_type_to_color_name(surface->surface_type), transparency); + for (const auto *region : m_regions) + for (const auto &surface : region->slices.surfaces) + svg.draw(surface.expolygon, surface_type_to_color_name(surface.surface_type), transparency); export_surface_type_legend_to_svg(svg, legend_pos); svg.Close(); } @@ -189,18 +189,18 @@ void Layer::export_region_slices_to_svg_debug(const char *name) const void Layer::export_region_fill_surfaces_to_svg(const char *path) const { BoundingBox bbox; - for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region) - for (Surfaces::const_iterator surface = (*region)->fill_surfaces.surfaces.begin(); surface != (*region)->fill_surfaces.surfaces.end(); ++surface) - bbox.merge(get_extents(surface->expolygon)); + for (const auto *region : m_regions) + for (const auto &surface : region->slices.surfaces) + bbox.merge(get_extents(surface.expolygon)); Point legend_size = export_surface_type_legend_to_svg_box_size(); Point legend_pos(bbox.min.x, bbox.max.y); bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y)); SVG svg(path, bbox); const float transparency = 0.5f; - for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region) - for (Surfaces::const_iterator surface = (*region)->fill_surfaces.surfaces.begin(); surface != (*region)->fill_surfaces.surfaces.end(); ++surface) - svg.draw(surface->expolygon, surface_type_to_color_name(surface->surface_type), transparency); + for (const auto *region : m_regions) + for (const auto &surface : region->slices.surfaces) + svg.draw(surface.expolygon, surface_type_to_color_name(surface.surface_type), transparency); export_surface_type_legend_to_svg(svg, legend_pos); svg.Close(); } diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp index f3b4604430..8dbe850ccc 100644 --- a/xs/src/libslic3r/Layer.hpp +++ b/xs/src/libslic3r/Layer.hpp @@ -15,95 +15,93 @@ class Layer; class PrintRegion; class PrintObject; -// TODO: make stuff private class LayerRegion { - friend class Layer; - public: - Layer* layer() { return this->_layer; } - const Layer* layer() const { return this->_layer; } - PrintRegion* region() { return this->_region; } - const PrintRegion* region() const { return this->_region; } + Layer* layer() { return m_layer; } + const Layer* layer() const { return m_layer; } + PrintRegion* region() { return m_region; } + const PrintRegion* region() const { return m_region; } // collection of surfaces generated by slicing the original geometry // divided by type top/bottom/internal - SurfaceCollection slices; + SurfaceCollection slices; // collection of extrusion paths/loops filling gaps // These fills are generated by the perimeter generator. // They are not printed on their own, but they are copied to this->fills during infill generation. - ExtrusionEntityCollection thin_fills; + ExtrusionEntityCollection thin_fills; // Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature") // and for re-starting of infills. - ExPolygons fill_expolygons; + ExPolygons fill_expolygons; // collection of surfaces for infill generation - SurfaceCollection fill_surfaces; + SurfaceCollection fill_surfaces; // Collection of perimeter surfaces. This is a cached result of diff(slices, fill_surfaces). // While not necessary, the memory consumption is meager and it speeds up calculation. // The perimeter_surfaces keep the IDs of the slices (top/bottom/) - SurfaceCollection perimeter_surfaces; + SurfaceCollection perimeter_surfaces; // collection of expolygons representing the bridged areas (thus not // needing support material) - Polygons bridged; + Polygons bridged; // collection of polylines representing the unsupported bridge edges - PolylineCollection unsupported_bridge_edges; + PolylineCollection unsupported_bridge_edges; // ordered collection of extrusion paths/loops to build all perimeters // (this collection contains only ExtrusionEntityCollection objects) - ExtrusionEntityCollection perimeters; + ExtrusionEntityCollection perimeters; // ordered collection of extrusion paths to fill surfaces // (this collection contains only ExtrusionEntityCollection objects) - ExtrusionEntityCollection fills; + ExtrusionEntityCollection fills; - Flow flow(FlowRole role, bool bridge = false, double width = -1) const; - void slices_to_fill_surfaces_clipped(); - void prepare_fill_surfaces(); - void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); - void process_external_surfaces(const Layer* lower_layer); + Flow flow(FlowRole role, bool bridge = false, double width = -1) const; + void slices_to_fill_surfaces_clipped(); + void prepare_fill_surfaces(); + void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); + void process_external_surfaces(const Layer* lower_layer); double infill_area_threshold() const; - void export_region_slices_to_svg(const char *path) const; - void export_region_fill_surfaces_to_svg(const char *path) const; + void export_region_slices_to_svg(const char *path) const; + void export_region_fill_surfaces_to_svg(const char *path) const; // Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export. - void export_region_slices_to_svg_debug(const char *name) const; - void export_region_fill_surfaces_to_svg_debug(const char *name) const; + void export_region_slices_to_svg_debug(const char *name) const; + void export_region_fill_surfaces_to_svg_debug(const char *name) const; // Is there any valid extrusion assigned to this LayerRegion? - bool has_extrusions() const { return ! this->perimeters.entities.empty() || ! this->fills.entities.empty(); } + bool has_extrusions() const { return ! this->perimeters.entities.empty() || ! this->fills.entities.empty(); } + +protected: + friend class Layer; + + LayerRegion(Layer *layer, PrintRegion *region) : m_layer(layer), m_region(region) {} + ~LayerRegion() {} private: - Layer *_layer; - PrintRegion *_region; - - LayerRegion(Layer *layer, PrintRegion *region) : _layer(layer), _region(region) {} - ~LayerRegion() {} + Layer *m_layer; + PrintRegion *m_region; }; typedef std::vector LayerRegionPtrs; -class Layer { - friend class PrintObject; - +class Layer +{ public: - size_t id() const { return this->_id; } - void set_id(size_t id) { this->_id = id; } - PrintObject* object() { return this->_object; } - const PrintObject* object() const { return this->_object; } + size_t id() const { return m_id; } + void set_id(size_t id) { m_id = id; } + PrintObject* object() { return m_object; } + const PrintObject* object() const { return m_object; } - Layer *upper_layer; - Layer *lower_layer; - LayerRegionPtrs regions; - bool slicing_errors; - coordf_t slice_z; // Z used for slicing in unscaled coordinates - coordf_t print_z; // Z used for printing in unscaled coordinates - coordf_t height; // layer height in unscaled coordinates + Layer *upper_layer; + Layer *lower_layer; + bool slicing_errors; + coordf_t slice_z; // Z used for slicing in unscaled coordinates + coordf_t print_z; // Z used for printing in unscaled coordinates + coordf_t height; // layer height in unscaled coordinates // collection of expolygons generated by slicing the original geometry; // also known as 'islands' (all regions and surface types are merged here) @@ -111,57 +109,64 @@ public: // order will be recovered by the G-code generator. ExPolygonCollection slices; - size_t region_count() const { return this->regions.size(); } - const LayerRegion* get_region(int idx) const { return this->regions.at(idx); } - LayerRegion* get_region(int idx) { return this->regions.at(idx); } - LayerRegion* add_region(PrintRegion* print_region); + size_t region_count() const { return m_regions.size(); } + const LayerRegion* get_region(int idx) const { return m_regions.at(idx); } + LayerRegion* get_region(int idx) { return m_regions[idx]; } + LayerRegion* add_region(PrintRegion* print_region); + const LayerRegionPtrs& regions() const { return m_regions; } - void make_slices(); - void merge_slices(); + void make_slices(); + void merge_slices(); template bool any_internal_region_slice_contains(const T &item) const { - for (const LayerRegion *layerm : this->regions) if (layerm->slices.any_internal_contains(item)) return true; + for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_internal_contains(item)) return true; return false; } template bool any_bottom_region_slice_contains(const T &item) const { - for (const LayerRegion *layerm : this->regions) if (layerm->slices.any_bottom_contains(item)) return true; + for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_bottom_contains(item)) return true; return false; } - void make_perimeters(); - void make_fills(); + void make_perimeters(); + void make_fills(); - void export_region_slices_to_svg(const char *path) const; - void export_region_fill_surfaces_to_svg(const char *path) const; + void export_region_slices_to_svg(const char *path) const; + void export_region_fill_surfaces_to_svg(const char *path) const; // Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export. - void export_region_slices_to_svg_debug(const char *name) const; - void export_region_fill_surfaces_to_svg_debug(const char *name) const; + void export_region_slices_to_svg_debug(const char *name) const; + void export_region_fill_surfaces_to_svg_debug(const char *name) const; // Is there any valid extrusion assigned to this LayerRegion? - virtual bool has_extrusions() const { for (auto layerm : this->regions) if (layerm->has_extrusions()) return true; return false; } + virtual bool has_extrusions() const { for (auto layerm : m_regions) if (layerm->has_extrusions()) return true; return false; } protected: - size_t _id; // sequential number of layer, 0-based - PrintObject *_object; + friend class PrintObject; Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : upper_layer(nullptr), lower_layer(nullptr), slicing_errors(false), slice_z(slice_z), print_z(print_z), height(height), - _id(id), _object(object) {} + m_id(id), m_object(object) {} virtual ~Layer(); + +private: + // sequential number of layer, 0-based + size_t m_id; + PrintObject *m_object; + LayerRegionPtrs m_regions; }; -class SupportLayer : public Layer { - friend class PrintObject; - +class SupportLayer : public Layer +{ public: // Polygons covered by the supports: base, interface and contact areas. - ExPolygonCollection support_islands; + ExPolygonCollection support_islands; // Extrusion paths for the support base and for the support interface and contacts. - ExtrusionEntityCollection support_fills; + ExtrusionEntityCollection support_fills; // Is there any valid extrusion assigned to this LayerRegion? - virtual bool has_extrusions() const { return ! support_fills.empty(); } + virtual bool has_extrusions() const { return ! support_fills.empty(); } + +protected: + friend class PrintObject; -//protected: // The constructor has been made public to be able to insert additional support layers for the skirt or a wipe tower // between the raft and the object first layer. SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index 68d1c5829c..c576d7eec7 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -18,13 +18,13 @@ namespace Slic3r { Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const { - return this->_region->flow( + return m_region->flow( role, - this->_layer->height, + m_layer->height, bridge, - this->_layer->id() == 0, + m_layer->id() == 0, width, - *this->_layer->object() + *m_layer->object() ); } @@ -61,9 +61,9 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec &slices, this->layer()->height, this->flow(frPerimeter), - &this->region()->config, - &this->layer()->object()->config, - &this->layer()->object()->print()->config, + &this->region()->config(), + &this->layer()->object()->config(), + &this->layer()->object()->print()->config(), // output: &this->perimeters, @@ -116,7 +116,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) { // bottom_polygons are used to trim inflated top surfaces. fill_boundaries.reserve(number_polygons(surfaces)); - bool has_infill = this->region()->config.fill_density.value > 0.; + bool has_infill = this->region()->config().fill_density.value > 0.; for (const Surface &surface : this->fill_surfaces.surfaces) { if (surface.surface_type == stTop) { // Collect the top surfaces, inflate them and trim them by the bottom surfaces. @@ -259,9 +259,9 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) #ifdef SLIC3R_DEBUG printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id()); #endif - if (bd.detect_angle(Geometry::deg2rad(this->region()->config.bridge_angle.value))) { + if (bd.detect_angle(Geometry::deg2rad(this->region()->config().bridge_angle.value))) { bridges[idx_last].bridge_angle = bd.angle; - if (this->layer()->object()->config.support_material) { + if (this->layer()->object()->config().support_material) { polygons_append(this->bridged, bd.coverage()); this->unsupported_bridge_edges.append(bd.unsupported_edges()); } @@ -351,13 +351,13 @@ void LayerRegion::prepare_fill_surfaces() the only meaningful information returned by psPerimeters. */ // if no solid layers are requested, turn top/bottom surfaces to internal - if (this->region()->config.top_solid_layers == 0) { + if (this->region()->config().top_solid_layers == 0) { for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) if (surface->surface_type == stTop) - surface->surface_type = (this->layer()->object()->config.infill_only_where_needed) ? + surface->surface_type = (this->layer()->object()->config().infill_only_where_needed) ? stInternalVoid : stInternal; } - if (this->region()->config.bottom_solid_layers == 0) { + if (this->region()->config().bottom_solid_layers == 0) { for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { if (surface->surface_type == stBottom || surface->surface_type == stBottomBridge) surface->surface_type = stInternal; @@ -365,9 +365,9 @@ void LayerRegion::prepare_fill_surfaces() } // turn too small internal regions into solid regions according to the user setting - if (this->region()->config.fill_density.value > 0) { + if (this->region()->config().fill_density.value > 0) { // scaling an area requires two calls! - double min_area = scale_(scale_(this->region()->config.solid_infill_below_area.value)); + double min_area = scale_(scale_(this->region()->config().solid_infill_below_area.value)); for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { if (surface->surface_type == stInternal && surface->area() <= min_area) surface->surface_type = stInternalSolid; diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index ea9c73fa4f..176f289265 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -6,8 +6,7 @@ namespace Slic3r { -void -PerimeterGenerator::process() +void PerimeterGenerator::process() { // other perimeters this->_mm3_per_mm = this->perimeter_flow.mm3_per_mm(); diff --git a/xs/src/libslic3r/PerimeterGenerator.hpp b/xs/src/libslic3r/PerimeterGenerator.hpp index 04557a2c0b..dd1f9b0ecd 100644 --- a/xs/src/libslic3r/PerimeterGenerator.hpp +++ b/xs/src/libslic3r/PerimeterGenerator.hpp @@ -38,30 +38,30 @@ typedef std::vector PerimeterGeneratorLoops; class PerimeterGenerator { public: // Inputs: - const SurfaceCollection* slices; - const ExPolygonCollection* lower_slices; - double layer_height; - int layer_id; - Flow perimeter_flow; - Flow ext_perimeter_flow; - Flow overhang_flow; - Flow solid_infill_flow; - PrintRegionConfig* config; - PrintObjectConfig* object_config; - PrintConfig* print_config; + const SurfaceCollection *slices; + const ExPolygonCollection *lower_slices; + double layer_height; + int layer_id; + Flow perimeter_flow; + Flow ext_perimeter_flow; + Flow overhang_flow; + Flow solid_infill_flow; + const PrintRegionConfig *config; + const PrintObjectConfig *object_config; + const PrintConfig *print_config; // Outputs: - ExtrusionEntityCollection* loops; - ExtrusionEntityCollection* gap_fill; - SurfaceCollection* fill_surfaces; + ExtrusionEntityCollection *loops; + ExtrusionEntityCollection *gap_fill; + SurfaceCollection *fill_surfaces; PerimeterGenerator( // Input: const SurfaceCollection* slices, double layer_height, Flow flow, - PrintRegionConfig* config, - PrintObjectConfig* object_config, - PrintConfig* print_config, + const PrintRegionConfig* config, + const PrintObjectConfig* object_config, + const PrintConfig* print_config, // Output: // Loops with the external thin walls ExtrusionEntityCollection* loops, @@ -79,15 +79,13 @@ public: void process(); private: - double _ext_mm3_per_mm; - double _mm3_per_mm; - double _mm3_per_mm_overhang; - Polygons _lower_slices_p; + double _ext_mm3_per_mm; + double _mm3_per_mm; + double _mm3_per_mm_overhang; + Polygons _lower_slices_p; - ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops, - ThickPolylines &thin_walls) const; - ExtrusionEntityCollection _variable_width - (const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const; + ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) const; + ExtrusionEntityCollection _variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const; }; } diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index 62b5169356..848e7fd2a2 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -69,7 +69,7 @@ PlaceholderParser::PlaceholderParser() this->update_timestamp(); } -void PlaceholderParser::update_timestamp() +void PlaceholderParser::update_timestamp(DynamicConfig &config) { time_t rawtime; time(&rawtime); @@ -84,14 +84,14 @@ void PlaceholderParser::update_timestamp() ss << std::setw(2) << std::setfill('0') << timeinfo->tm_hour; ss << std::setw(2) << std::setfill('0') << timeinfo->tm_min; ss << std::setw(2) << std::setfill('0') << timeinfo->tm_sec; - this->set("timestamp", ss.str()); + config.set_key_value("timestamp", new ConfigOptionString(ss.str())); } - this->set("year", 1900 + timeinfo->tm_year); - this->set("month", 1 + timeinfo->tm_mon); - this->set("day", timeinfo->tm_mday); - this->set("hour", timeinfo->tm_hour); - this->set("minute", timeinfo->tm_min); - this->set("second", timeinfo->tm_sec); + config.set_key_value("year", new ConfigOptionInt(1900 + timeinfo->tm_year)); + config.set_key_value("month", new ConfigOptionInt(1 + timeinfo->tm_mon)); + config.set_key_value("day", new ConfigOptionInt(timeinfo->tm_mday)); + config.set_key_value("hour", new ConfigOptionInt(timeinfo->tm_hour)); + config.set_key_value("minute", new ConfigOptionInt(timeinfo->tm_min)); + config.set_key_value("second", new ConfigOptionInt(timeinfo->tm_sec)); } // Scalar configuration values are stored into m_single, diff --git a/xs/src/libslic3r/PlaceholderParser.hpp b/xs/src/libslic3r/PlaceholderParser.hpp index 4e0aa9ee28..49d53ec9ec 100644 --- a/xs/src/libslic3r/PlaceholderParser.hpp +++ b/xs/src/libslic3r/PlaceholderParser.hpp @@ -14,7 +14,6 @@ class PlaceholderParser public: PlaceholderParser(); - void update_timestamp(); void apply_config(const DynamicPrintConfig &config); void apply_env_variables(); @@ -37,6 +36,11 @@ public: // Throws std::runtime_error on syntax or runtime error. static bool evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override = nullptr); + // Update timestamp, year, month, day, hour, minute, second variables at the provided config. + static void update_timestamp(DynamicConfig &config); + // Update timestamp, year, month, day, hour, minute, second variables at m_config. + void update_timestamp() { update_timestamp(m_config); } + private: DynamicConfig m_config; }; diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index d67fe5884d..e8a010a79c 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -20,24 +20,27 @@ template class PrintState; void Print::clear_objects() { - for (int i = int(this->objects.size())-1; i >= 0; --i) + tbb::mutex::scoped_lock lock(m_mutex); + for (int i = int(m_objects.size())-1; i >= 0; --i) this->delete_object(i); - for (PrintRegion *region : this->regions) + for (PrintRegion *region : m_regions) delete region; - this->regions.clear(); + m_regions.clear(); } void Print::delete_object(size_t idx) { + tbb::mutex::scoped_lock lock(m_mutex); // destroy object and remove it from our container - delete this->objects[idx]; - this->objects.erase(this->objects.begin() + idx); + delete m_objects[idx]; + m_objects.erase(m_objects.begin() + idx); this->invalidate_all_steps(); // TODO: purge unused regions } void Print::reload_object(size_t /* idx */) { + tbb::mutex::scoped_lock lock(m_mutex); /* TODO: this method should check whether the per-object config and per-material configs have changed in such a way that regions need to be rearranged or we can just apply the diff and invalidate something. Same logic as apply_config() @@ -46,8 +49,8 @@ void Print::reload_object(size_t /* idx */) // collect all current model objects ModelObjectPtrs model_objects; - model_objects.reserve(this->objects.size()); - for (PrintObject *object : this->objects) + model_objects.reserve(m_objects.size()); + for (PrintObject *object : m_objects) model_objects.push_back(object->model_object()); // remove our print objects this->clear_objects(); @@ -63,15 +66,21 @@ void Print::reload_object(size_t /* idx */) bool Print::reload_model_instances() { bool invalidated = false; - for (PrintObject *object : this->objects) + for (PrintObject *object : m_objects) invalidated |= object->reload_model_instances(); return invalidated; } PrintRegion* Print::add_region() { - regions.push_back(new PrintRegion(this)); - return regions.back(); + m_regions.emplace_back(new PrintRegion(this)); + return m_regions.back(); +} + +PrintRegion* Print::add_region(const PrintRegionConfig &config) +{ + m_regions.emplace_back(new PrintRegion(this, config)); + return m_regions.back(); } // Called by Print::apply_config(). @@ -224,18 +233,18 @@ bool Print::invalidate_state_by_config_options(const std::vectorinvalidate_step(step); sort_remove_duplicates(osteps); for (PrintObjectStep ostep : osteps) - for (PrintObject *object : this->objects) + for (PrintObject *object : m_objects) invalidated |= object->invalidate_step(ostep); return invalidated; } bool Print::invalidate_step(PrintStep step) { - bool invalidated = m_state.invalidate(step); + bool invalidated = m_state.invalidate(step, m_mutex, m_cancel_callback); // Propagate to dependent steps. //FIXME Why should skirt invalidate brim? Shouldn't it be vice versa? if (step == psSkirt) - invalidated |= m_state.invalidate(psBrim); + invalidated |= m_state.invalidate(psBrim, m_mutex, m_cancel_callback); return invalidated; } @@ -243,9 +252,9 @@ bool Print::invalidate_step(PrintStep step) // and there's at least one object bool Print::is_step_done(PrintObjectStep step) const { - if (this->objects.empty()) + if (m_objects.empty()) return false; - for (const PrintObject *object : this->objects) + for (const PrintObject *object : m_objects) if (!object->m_state.is_done(step)) return false; return true; @@ -256,15 +265,15 @@ std::vector Print::object_extruders() const { std::vector extruders; - for (PrintRegion* region : this->regions) { + for (PrintRegion* region : m_regions) { // these checks reflect the same logic used in the GUI for enabling/disabling // extruder selection fields - if (region->config.perimeters.value > 0 || this->config.brim_width.value > 0) - extruders.push_back(region->config.perimeter_extruder - 1); - if (region->config.fill_density.value > 0) - extruders.push_back(region->config.infill_extruder - 1); - if (region->config.top_solid_layers.value > 0 || region->config.bottom_solid_layers.value > 0) - extruders.push_back(region->config.solid_infill_extruder - 1); + if (region->config().perimeters.value > 0 || m_config.brim_width.value > 0) + extruders.push_back(region->config().perimeter_extruder - 1); + if (region->config().fill_density.value > 0) + extruders.push_back(region->config().infill_extruder - 1); + if (region->config().top_solid_layers.value > 0 || region->config().bottom_solid_layers.value > 0) + extruders.push_back(region->config().solid_infill_extruder - 1); } sort_remove_duplicates(extruders); @@ -277,16 +286,16 @@ std::vector Print::support_material_extruders() const std::vector extruders; bool support_uses_current_extruder = false; - for (PrintObject *object : this->objects) { + for (PrintObject *object : m_objects) { if (object->has_support_material()) { - if (object->config.support_material_extruder == 0) + if (object->config().support_material_extruder == 0) support_uses_current_extruder = true; else - extruders.push_back(object->config.support_material_extruder - 1); - if (object->config.support_material_interface_extruder == 0) + extruders.push_back(object->config().support_material_extruder - 1); + if (object->config().support_material_interface_extruder == 0) support_uses_current_extruder = true; else - extruders.push_back(object->config.support_material_interface_extruder - 1); + extruders.push_back(object->config().support_material_interface_extruder - 1); } } @@ -309,10 +318,10 @@ std::vector Print::extruders() const void Print::_simplify_slices(double distance) { - for (PrintObject *object : this->objects) { - for (Layer *layer : object->layers) { + for (PrintObject *object : m_objects) { + for (Layer *layer : object->m_layers) { layer->slices.simplify(distance); - for (LayerRegion *layerm : layer->regions) + for (LayerRegion *layerm : layer->regions()) layerm->slices.simplify(distance); } } @@ -322,7 +331,7 @@ double Print::max_allowed_layer_height() const { double nozzle_diameter_max = 0.; for (unsigned int extruder_id : this->extruders()) - nozzle_diameter_max = std::max(nozzle_diameter_max, this->config.nozzle_diameter.get_at(extruder_id)); + nozzle_diameter_max = std::max(nozzle_diameter_max, m_config.nozzle_diameter.get_at(extruder_id)); return nozzle_diameter_max; } @@ -333,10 +342,10 @@ void Print::add_model_object(ModelObject* model_object, int idx) // Initialize a new print object and store it at the given position. PrintObject *object = new PrintObject(this, model_object, model_object->raw_bounding_box()); if (idx != -1) { - delete this->objects[idx]; - this->objects[idx] = object; + delete m_objects[idx]; + m_objects[idx] = object; } else - this->objects.emplace_back(object); + m_objects.emplace_back(object); // Invalidate all print steps. this->invalidate_all_steps(); @@ -345,37 +354,42 @@ void Print::add_model_object(ModelObject* model_object, int idx) PrintRegionConfig config = this->_region_config_from_model_volume(*model_object->volumes[volume_id]); // Find an existing print region with the same config. size_t region_id = size_t(-1); - for (size_t i = 0; i < this->regions.size(); ++ i) - if (config.equals(this->regions[i]->config)) { + for (size_t i = 0; i < m_regions.size(); ++ i) + if (config.equals(m_regions[i]->config())) { region_id = i; break; } // If no region exists with the same config, create a new one. if (region_id == size_t(-1)) { - region_id = this->regions.size(); - this->add_region()->config.apply(config); + region_id = this->regions().size(); + this->add_region(config); } // Assign volume to a region. object->add_region_volume(region_id, volume_id); } // Apply config to print object. - object->config.apply(this->default_object_config); - normalize_and_apply_config(object->config, model_object->config); + object->config_apply(this->default_object_config()); + { + //normalize_and_apply_config(object->config(), model_object->config); + DynamicPrintConfig src_normalized(model_object->config); + src_normalized.normalize(); + object->config_apply(src_normalized, true); + } // update placeholders { // get the first input file name std::string input_file; std::vector v_scale; - for (const PrintObject *object : this->objects) { + for (const PrintObject *object : m_objects) { const ModelObject &mobj = *object->model_object(); v_scale.push_back(boost::lexical_cast(mobj.instances[0]->scaling_factor*100) + "%"); if (input_file.empty()) input_file = mobj.input_file; } - PlaceholderParser &pp = this->placeholder_parser; + PlaceholderParser &pp = m_placeholder_parser; pp.set("scale", v_scale); if (! input_file.empty()) { // get basename with and without suffix @@ -393,19 +407,19 @@ bool Print::apply_config(DynamicPrintConfig config) config.normalize(); // apply variables to placeholder parser - this->placeholder_parser.apply_config(config); + m_placeholder_parser.apply_config(config); // handle changes to print config - t_config_option_keys print_diff = this->config.diff(config); - this->config.apply_only(config, print_diff, true); + t_config_option_keys print_diff = m_config.diff(config); + m_config.apply_only(config, print_diff, true); bool invalidated = this->invalidate_state_by_config_options(print_diff); // handle changes to object config defaults - this->default_object_config.apply(config, true); - for (PrintObject *object : this->objects) { + m_default_object_config.apply(config, true); + for (PrintObject *object : m_objects) { // we don't assume that config contains a full ObjectConfig, // so we base it on the current print-wise default - PrintObjectConfig new_config = this->default_object_config; + PrintObjectConfig new_config = this->default_object_config(); // we override the new config with object-specific options normalize_and_apply_config(new_config, object->model_object()->config); // Force a refresh of a variable layer height profile at the PrintObject if it is not valid. @@ -417,13 +431,13 @@ bool Print::apply_config(DynamicPrintConfig config) invalidated = true; } // check whether the new config is different from the current one - t_config_option_keys diff = object->config.diff(new_config); - object->config.apply_only(new_config, diff, true); + t_config_option_keys diff = object->config().diff(new_config); + object->config_apply_only(new_config, diff, true); invalidated |= object->invalidate_state_by_config_options(diff); } // handle changes to regions config defaults - this->default_region_config.apply(config, true); + m_default_region_config.apply(config, true); // All regions now have distinct settings. // Check whether applying the new region config defaults we'd get different regions. @@ -432,11 +446,11 @@ bool Print::apply_config(DynamicPrintConfig config) // Collect the already visited region configs into other_region_configs, // so one may check for duplicates. std::vector other_region_configs; - for (size_t region_id = 0; region_id < this->regions.size(); ++ region_id) { - PrintRegion ®ion = *this->regions[region_id]; + for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) { + PrintRegion ®ion = *m_regions[region_id]; PrintRegionConfig this_region_config; bool this_region_config_set = false; - for (PrintObject *object : this->objects) { + for (PrintObject *object : m_objects) { if (region_id < object->region_volumes.size()) { for (int volume_id : object->region_volumes[region_id]) { const ModelVolume &volume = *object->model_object()->volumes[volume_id]; @@ -466,10 +480,10 @@ bool Print::apply_config(DynamicPrintConfig config) } } if (this_region_config_set) { - t_config_option_keys diff = region.config.diff(this_region_config); + t_config_option_keys diff = region.config().diff(this_region_config); if (! diff.empty()) { - region.config.apply_only(this_region_config, diff); - for (PrintObject *object : this->objects) + region.config_apply_only(this_region_config, diff, false); + for (PrintObject *object : m_objects) if (region_id < object->region_volumes.size() && ! object->region_volumes[region_id].empty()) invalidated |= object->invalidate_state_by_config_options(diff); } @@ -484,8 +498,8 @@ exit_for_rearrange_regions: // The current subdivision of regions does not make sense anymore. // We need to remove all objects and re-add them. ModelObjectPtrs model_objects; - model_objects.reserve(this->objects.size()); - for (PrintObject *object : this->objects) + model_objects.reserve(m_objects.size()); + for (PrintObject *object : m_objects) model_objects.push_back(object->model_object()); this->clear_objects(); for (ModelObject *mo : model_objects) @@ -494,7 +508,7 @@ exit_for_rearrange_regions: } // Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads. - for (PrintObject *object : this->objects) + for (PrintObject *object : m_objects) if (! object->layer_height_profile_valid) object->update_layer_height_profile(); @@ -503,32 +517,32 @@ exit_for_rearrange_regions: bool Print::has_infinite_skirt() const { - return (this->config.skirt_height == -1 && this->config.skirts > 0) - || (this->config.ooze_prevention && this->extruders().size() > 1); + return (m_config.skirt_height == -1 && m_config.skirts > 0) + || (m_config.ooze_prevention && this->extruders().size() > 1); } bool Print::has_skirt() const { - return (this->config.skirt_height > 0 && this->config.skirts > 0) + return (m_config.skirt_height > 0 && m_config.skirts > 0) || this->has_infinite_skirt(); } std::string Print::validate() const { - BoundingBox bed_box_2D = get_extents(Polygon::new_scale(config.bed_shape.values)); - BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config.max_print_height)); + BoundingBox bed_box_2D = get_extents(Polygon::new_scale(m_config.bed_shape.values)); + BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), m_config.max_print_height)); // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced. print_volume.min.z = -1e10; - for (PrintObject *po : this->objects) { + for (PrintObject *po : m_objects) { if (! print_volume.contains(po->model_object()->tight_bounding_box(false))) return "Some objects are outside of the print volume."; } - if (this->config.complete_objects) { + if (m_config.complete_objects) { // Check horizontal clearance. { Polygons convex_hulls_other; - for (PrintObject *object : this->objects) { + for (PrintObject *object : m_objects) { // Get convex hull of all meshes assigned to this print object. Polygon convex_hull; { @@ -542,7 +556,7 @@ std::string Print::validate() const // Apply the same transformations we apply to the actual meshes when slicing them. object->model_object()->instances.front()->transform_polygon(&convex_hull); // Grow convex hull with the clearance margin. - convex_hull = offset(convex_hull, scale_(this->config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front(); + convex_hull = offset(convex_hull, scale_(m_config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front(); // Now we check that no instance of convex_hull intersects any of the previously checked object instances. for (const Point © : object->_shifted_copies) { Polygon p = convex_hull; @@ -556,47 +570,47 @@ std::string Print::validate() const // Check vertical clearance. { std::vector object_height; - for (const PrintObject *object : this->objects) + for (const PrintObject *object : m_objects) object_height.insert(object_height.end(), object->copies().size(), object->size.z); std::sort(object_height.begin(), object_height.end()); // Ignore the tallest *copy* (this is why we repeat height for all of them): // it will be printed as last one so its height doesn't matter. object_height.pop_back(); - if (! object_height.empty() && object_height.back() > scale_(this->config.extruder_clearance_height.value)) + if (! object_height.empty() && object_height.back() > scale_(m_config.extruder_clearance_height.value)) return "Some objects are too tall and cannot be printed without extruder collisions."; } - } // end if (this->config.complete_objects) + } // end if (m_config.complete_objects) - if (this->config.spiral_vase) { + if (m_config.spiral_vase) { size_t total_copies_count = 0; - for (const PrintObject *object : this->objects) + for (const PrintObject *object : m_objects) total_copies_count += object->copies().size(); // #4043 - if (total_copies_count > 1 && ! this->config.complete_objects.value) + if (total_copies_count > 1 && ! m_config.complete_objects.value) return "The Spiral Vase option can only be used when printing a single object."; - if (this->regions.size() > 1) + if (m_regions.size() > 1) return "The Spiral Vase option can only be used when printing single material objects."; } - if (this->has_wipe_tower() && ! this->objects.empty()) { + if (this->has_wipe_tower() && ! m_objects.empty()) { #if 0 - for (auto dmr : this->config.nozzle_diameter.values) + for (auto dmr : m_config.nozzle_diameter.values) if (std::abs(dmr - 0.4) > EPSILON) return "The Wipe Tower is currently only supported for the 0.4mm nozzle diameter."; #endif - if (this->config.gcode_flavor != gcfRepRap && this->config.gcode_flavor != gcfMarlin) + if (m_config.gcode_flavor != gcfRepRap && m_config.gcode_flavor != gcfMarlin) return "The Wipe Tower is currently only supported for the Marlin and RepRap/Sprinter G-code flavors."; - if (! this->config.use_relative_e_distances) + if (! m_config.use_relative_e_distances) return "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1)."; - SlicingParameters slicing_params0 = this->objects.front()->slicing_parameters(); - for (PrintObject *object : this->objects) { + SlicingParameters slicing_params0 = m_objects.front()->slicing_parameters(); + for (PrintObject *object : m_objects) { SlicingParameters slicing_params = object->slicing_parameters(); if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON || std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON) return "The Wipe Tower is only supported for multiple objects if they have equal layer heigths"; if (slicing_params.raft_layers() != slicing_params0.raft_layers()) return "The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers"; - if (object->config.support_material_contact_distance != this->objects.front()->config.support_material_contact_distance) + if (object->config().support_material_contact_distance != m_objects.front()->config().support_material_contact_distance) return "The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance"; if (! equal_layering(slicing_params, slicing_params0)) return "The Wipe Tower is only supported for multiple objects if they are sliced equally."; @@ -605,7 +619,7 @@ std::string Print::validate() const object->layer_height_profile_valid = was_layer_height_profile_valid; for (size_t i = 5; i < object->layer_height_profile.size(); i += 2) if (object->layer_height_profile[i-1] > slicing_params.object_print_z_min + EPSILON && - std::abs(object->layer_height_profile[i] - object->config.layer_height) > EPSILON) + std::abs(object->layer_height_profile[i] - object->config().layer_height) > EPSILON) return "The Wipe Tower is currently only supported with constant Z layer spacing. Layer editing is not allowed."; } } @@ -618,12 +632,12 @@ std::string Print::validate() const std::vector nozzle_diameters; for (unsigned int extruder_id : extruders) - nozzle_diameters.push_back(this->config.nozzle_diameter.get_at(extruder_id)); + nozzle_diameters.push_back(m_config.nozzle_diameter.get_at(extruder_id)); double min_nozzle_diameter = *std::min_element(nozzle_diameters.begin(), nozzle_diameters.end()); - for (PrintObject *object : this->objects) { - if ((object->config.support_material_extruder == -1 || object->config.support_material_interface_extruder == -1) && - (object->config.raft_layers > 0 || object->config.support_material.value)) { + for (PrintObject *object : m_objects) { + if ((object->config().support_material_extruder == -1 || object->config().support_material_interface_extruder == -1) && + (object->config().raft_layers > 0 || object->config().support_material.value)) { // The object has some form of support and either support_material_extruder or support_material_interface_extruder // will be printed with the current tool without a forced tool change. Play safe, assert that all object nozzles // are of the same diameter. @@ -634,16 +648,16 @@ std::string Print::validate() const } // validate first_layer_height - double first_layer_height = object->config.get_abs_value("first_layer_height"); + double first_layer_height = object->config().get_abs_value("first_layer_height"); double first_layer_min_nozzle_diameter; - if (object->config.raft_layers > 0) { + if (object->config().raft_layers > 0) { // if we have raft layers, only support material extruder is used on first layer - size_t first_layer_extruder = object->config.raft_layers == 1 - ? object->config.support_material_interface_extruder-1 - : object->config.support_material_extruder-1; + size_t first_layer_extruder = object->config().raft_layers == 1 + ? object->config().support_material_interface_extruder-1 + : object->config().support_material_extruder-1; first_layer_min_nozzle_diameter = (first_layer_extruder == size_t(-1)) ? min_nozzle_diameter : - this->config.nozzle_diameter.get_at(first_layer_extruder); + m_config.nozzle_diameter.get_at(first_layer_extruder); } else { // if we don't have raft layers, any nozzle diameter is potentially used in first layer first_layer_min_nozzle_diameter = min_nozzle_diameter; @@ -652,7 +666,7 @@ std::string Print::validate() const return "First layer height can't be greater than nozzle diameter"; // validate layer_height - if (object->config.layer_height.value > min_nozzle_diameter) + if (object->config().layer_height.value > min_nozzle_diameter) return "Layer height can't be greater than nozzle diameter"; } } @@ -665,7 +679,7 @@ std::string Print::validate() const BoundingBox Print::bounding_box() const { BoundingBox bb; - for (const PrintObject *object : this->objects) + for (const PrintObject *object : m_objects) for (Point copy : object->_shifted_copies) { bb.merge(copy); copy.translate(object->size); @@ -683,7 +697,7 @@ BoundingBox Print::total_bounding_box() const BoundingBox bb = this->bounding_box(); // we need to offset the objects bounding box by at least half the perimeters extrusion width - Flow perimeter_flow = this->objects.front()->get_layer(0)->get_region(0)->flow(frPerimeter); + Flow perimeter_flow = m_objects.front()->get_layer(0)->get_region(0)->flow(frPerimeter); double extra = perimeter_flow.width/2; // consider support material @@ -692,18 +706,18 @@ BoundingBox Print::total_bounding_box() const } // consider brim and skirt - if (this->config.brim_width.value > 0) { + if (m_config.brim_width.value > 0) { Flow brim_flow = this->brim_flow(); - extra = std::max(extra, this->config.brim_width.value + brim_flow.width/2); + extra = std::max(extra, m_config.brim_width.value + brim_flow.width/2); } if (this->has_skirt()) { - int skirts = this->config.skirts.value; + int skirts = m_config.skirts.value; if (skirts == 0 && this->has_infinite_skirt()) skirts = 1; Flow skirt_flow = this->skirt_flow(); extra = std::max( extra, - this->config.brim_width.value - + this->config.skirt_distance.value + m_config.brim_width.value + + m_config.skirt_distance.value + skirts * skirt_flow.spacing() + skirt_flow.width/2 ); @@ -717,17 +731,17 @@ BoundingBox Print::total_bounding_box() const double Print::skirt_first_layer_height() const { - if (this->objects.empty()) CONFESS("skirt_first_layer_height() can't be called without PrintObjects"); - return this->objects.front()->config.get_abs_value("first_layer_height"); + if (m_objects.empty()) CONFESS("skirt_first_layer_height() can't be called without PrintObjects"); + return m_objects.front()->config().get_abs_value("first_layer_height"); } Flow Print::brim_flow() const { - ConfigOptionFloatOrPercent width = this->config.first_layer_extrusion_width; + ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width; if (width.value == 0) - width = this->regions.front()->config.perimeter_extrusion_width; + width = m_regions.front()->config().perimeter_extrusion_width; if (width.value == 0) - width = this->objects.front()->config.extrusion_width; + width = m_objects.front()->config().extrusion_width; /* We currently use a random region's perimeter extruder. While this works for most cases, we should probably consider all of the perimeter @@ -737,7 +751,7 @@ Flow Print::brim_flow() const return Flow::new_from_config_width( frPerimeter, width, - this->config.nozzle_diameter.get_at(this->regions.front()->config.perimeter_extruder-1), + m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1), this->skirt_first_layer_height(), 0 ); @@ -745,11 +759,11 @@ Flow Print::brim_flow() const Flow Print::skirt_flow() const { - ConfigOptionFloatOrPercent width = this->config.first_layer_extrusion_width; + ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width; if (width.value == 0) - width = this->regions.front()->config.perimeter_extrusion_width; + width = m_regions.front()->config().perimeter_extrusion_width; if (width.value == 0) - width = this->objects.front()->config.extrusion_width; + width = m_objects.front()->config().extrusion_width; /* We currently use a random object's support material extruder. While this works for most cases, we should probably consider all of the support material @@ -759,7 +773,7 @@ Flow Print::skirt_flow() const return Flow::new_from_config_width( frPerimeter, width, - this->config.nozzle_diameter.get_at(this->objects.front()->config.support_material_extruder-1), + m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1), this->skirt_first_layer_height(), 0 ); @@ -767,7 +781,7 @@ Flow Print::skirt_flow() const PrintRegionConfig Print::_region_config_from_model_volume(const ModelVolume &volume) { - PrintRegionConfig config = this->default_region_config; + PrintRegionConfig config = this->default_region_config(); normalize_and_apply_config(config, volume.get_object()->config); normalize_and_apply_config(config, volume.config); if (! volume.material_id().empty()) @@ -777,7 +791,7 @@ PrintRegionConfig Print::_region_config_from_model_volume(const ModelVolume &vol bool Print::has_support_material() const { - for (const PrintObject *object : this->objects) + for (const PrintObject *object : m_objects) if (object->has_support_material()) return true; return false; @@ -791,7 +805,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const if (model_object->volumes.size() < 2) return; -// size_t extruders = this->config.nozzle_diameter.values.size(); +// size_t extruders = m_config.nozzle_diameter.values.size(); for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) { ModelVolume *volume = model_object->volumes[volume_id]; //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned. @@ -804,44 +818,44 @@ void Print::auto_assign_extruders(ModelObject* model_object) const void Print::process() { BOOST_LOG_TRIVIAL(info) << "Staring the slicing process."; - for (PrintObject *obj : this->objects) + for (PrintObject *obj : m_objects) obj->make_perimeters(); this->throw_if_canceled(); this->set_status(70, "Infilling layers"); - for (PrintObject *obj : this->objects) + for (PrintObject *obj : m_objects) obj->infill(); this->throw_if_canceled(); - for (PrintObject *obj : this->objects) + for (PrintObject *obj : m_objects) obj->generate_support_material(); this->throw_if_canceled(); if (! m_state.is_done(psSkirt)) { this->set_started(psSkirt); - this->skirt.clear(); + m_skirt.clear(); if (this->has_skirt()) { this->set_status(88, "Generating skirt"); this->_make_skirt(); } - m_state.set_done(psSkirt); + this->set_done(psSkirt); } this->throw_if_canceled(); if (! m_state.is_done(psBrim)) { this->set_started(psBrim); - this->brim.clear(); - if (this->config.brim_width > 0) { + m_brim.clear(); + if (m_config.brim_width > 0) { this->set_status(88, "Generating brim"); this->_make_brim(); } - m_state.set_done(psBrim); + this->set_done(psBrim); } this->throw_if_canceled(); if (! m_state.is_done(psWipeTower)) { this->set_started(psWipeTower); - this->_clear_wipe_tower(); + m_wipe_tower_data.clear(); if (this->has_wipe_tower()) { //this->set_status(95, "Generating wipe tower"); this->_make_wipe_tower(); } - m_state.set_done(psWipeTower); + this->set_done(psWipeTower); } BOOST_LOG_TRIVIAL(info) << "Slicing process finished."; } @@ -883,20 +897,20 @@ void Print::_make_skirt() // prepended to the first 'n' layers (with 'n' = skirt_height). // $skirt_height_z in this case is the highest possible skirt height for safety. coordf_t skirt_height_z = 0.; - for (const PrintObject *object : this->objects) { + for (const PrintObject *object : m_objects) { size_t skirt_layers = this->has_infinite_skirt() ? object->layer_count() : - std::min(size_t(this->config.skirt_height.value), object->layer_count()); - skirt_height_z = std::max(skirt_height_z, object->layers[skirt_layers-1]->print_z); + std::min(size_t(m_config.skirt_height.value), object->layer_count()); + skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z); } // Collect points from all layers contained in skirt height. Points points; - for (const PrintObject *object : this->objects) { + for (const PrintObject *object : m_objects) { this->throw_if_canceled(); Points object_points; // Get object layers up to skirt_height_z. - for (const Layer *layer : object->layers) { + for (const Layer *layer : object->m_layers) { if (layer->print_z > skirt_height_z) break; for (const ExPolygon &expoly : layer->slices.expolygons) @@ -904,7 +918,7 @@ void Print::_make_skirt() append(object_points, expoly.contour.points); } // Get support layers up to skirt_height_z. - for (const SupportLayer *layer : object->support_layers) { + for (const SupportLayer *layer : object->support_layers()) { if (layer->print_z > skirt_height_z) break; for (const ExtrusionEntity *extrusion_entity : layer->support_fills.entities) @@ -942,18 +956,18 @@ void Print::_make_skirt() extruders_e_per_mm.reserve(set_extruders.size()); for (auto &extruder_id : set_extruders) { extruders.push_back(extruder_id); - extruders_e_per_mm.push_back(Extruder((unsigned int)extruder_id, &this->config).e_per_mm(mm3_per_mm)); + extruders_e_per_mm.push_back(Extruder((unsigned int)extruder_id, &m_config).e_per_mm(mm3_per_mm)); } } // Number of skirt loops per skirt layer. - int n_skirts = this->config.skirts.value; + int n_skirts = m_config.skirts.value; if (this->has_infinite_skirt() && n_skirts == 0) n_skirts = 1; // Initial offset of the brim inner edge from the object (possible with a support & raft). // The skirt will touch the brim if the brim is extruded. - coord_t distance = scale_(std::max(this->config.skirt_distance.value, this->config.brim_width.value)); + coord_t distance = scale_(std::max(m_config.skirt_distance.value, m_config.brim_width.value)); // Draw outlines from outside to inside. // Loop while we have less skirts than required or any extruder hasn't reached the min length if any. std::vector extruded_length(extruders.size(), 0.); @@ -978,16 +992,16 @@ void Print::_make_skirt() first_layer_height // this will be overridden at G-code export time ))); eloop.paths.back().polyline = loop.split_at_first_point(); - this->skirt.append(eloop); - if (this->config.min_skirt_length.value > 0) { + m_skirt.append(eloop); + if (m_config.min_skirt_length.value > 0) { // The skirt length is limited. Sum the total amount of filament length extruded, in mm. extruded_length[extruder_idx] += unscale(loop.length()) * extruders_e_per_mm[extruder_idx]; - if (extruded_length[extruder_idx] < this->config.min_skirt_length.value) { + if (extruded_length[extruder_idx] < m_config.min_skirt_length.value) { // Not extruded enough yet with the current extruder. Add another loop. if (i == 1) ++ i; } else { - assert(extruded_length[extruder_idx] >= this->config.min_skirt_length.value); + assert(extruded_length[extruder_idx] >= m_config.min_skirt_length.value); // Enough extruded with the current extruder. Extrude with the next one, // until the prescribed number of skirt loops is extruded. if (extruder_idx + 1 < extruders.size()) @@ -998,7 +1012,7 @@ void Print::_make_skirt() } } // Brims were generated inside out, reverse to print the outmost contour first. - this->skirt.reverse(); + m_skirt.reverse(); } void Print::_make_brim() @@ -1006,13 +1020,13 @@ void Print::_make_brim() // Brim is only printed on first layer and uses perimeter extruder. Flow flow = this->brim_flow(); Polygons islands; - for (PrintObject *object : this->objects) { + for (PrintObject *object : m_objects) { this->throw_if_canceled(); Polygons object_islands; - for (ExPolygon &expoly : object->layers.front()->slices.expolygons) + for (ExPolygon &expoly : object->m_layers.front()->slices.expolygons) object_islands.push_back(expoly.contour); - if (! object->support_layers.empty()) - object->support_layers.front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); + if (! object->support_layers().empty()) + object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); islands.reserve(islands.size() + object_islands.size() * object->_shifted_copies.size()); for (const Point &pt : object->_shifted_copies) for (Polygon &poly : object_islands) { @@ -1021,7 +1035,7 @@ void Print::_make_brim() } } Polygons loops; - size_t num_loops = size_t(floor(this->config.brim_width.value / flow.width)); + size_t num_loops = size_t(floor(m_config.brim_width.value / flow.width)); for (size_t i = 0; i < num_loops; ++ i) { this->throw_if_canceled(); islands = offset(islands, float(flow.scaled_spacing()), jtSquare); @@ -1037,32 +1051,24 @@ void Print::_make_brim() loops = union_pt_chained(loops, false); std::reverse(loops.begin(), loops.end()); - extrusion_entities_append_loops(this->brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height())); + extrusion_entities_append_loops(m_brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height())); } // Wipe tower support. bool Print::has_wipe_tower() const { return - this->config.single_extruder_multi_material.value && - ! this->config.spiral_vase.value && - this->config.wipe_tower.value && - this->config.nozzle_diameter.values.size() > 1; -} - -void Print::_clear_wipe_tower() -{ - m_tool_ordering.clear(); - m_wipe_tower_priming.reset(nullptr); - m_wipe_tower_tool_changes.clear(); - m_wipe_tower_final_purge.reset(nullptr); + m_config.single_extruder_multi_material.value && + ! m_config.spiral_vase.value && + m_config.wipe_tower.value && + m_config.nozzle_diameter.values.size() > 1; } void Print::_make_wipe_tower() { // Let the ToolOrdering class know there will be initial priming extrusions at the start of the print. - m_tool_ordering = ToolOrdering(*this, (unsigned int)-1, true); - if (! m_tool_ordering.has_wipe_tower()) + m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, true); + if (! m_wipe_tower_data.tool_ordering.has_wipe_tower()) // Don't generate any wipe tower. return; @@ -1072,33 +1078,31 @@ void Print::_make_wipe_tower() // see https://github.com/prusa3d/Slic3r/issues/607 { size_t idx_begin = size_t(-1); - size_t idx_end = m_tool_ordering.layer_tools().size(); + size_t idx_end = m_wipe_tower_data.tool_ordering.layer_tools().size(); // Find the first wipe tower layer, which does not have a counterpart in an object or a support layer. for (size_t i = 0; i < idx_end; ++ i) { - const ToolOrdering::LayerTools < = m_tool_ordering.layer_tools()[i]; + const ToolOrdering::LayerTools < = m_wipe_tower_data.tool_ordering.layer_tools()[i]; if (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support) { idx_begin = i; break; } } if (idx_begin != size_t(-1)) { - // Find the position in this->objects.first()->support_layers to insert these new support layers. - double wipe_tower_new_layer_print_z_first = m_tool_ordering.layer_tools()[idx_begin].print_z; - SupportLayerPtrs::iterator it_layer = this->objects.front()->support_layers.begin(); - SupportLayerPtrs::iterator it_end = this->objects.front()->support_layers.end(); + // Find the position in m_objects.first()->support_layers to insert these new support layers. + double wipe_tower_new_layer_print_z_first = m_wipe_tower_data.tool_ordering.layer_tools()[idx_begin].print_z; + SupportLayerPtrs::const_iterator it_layer = m_objects.front()->support_layers().begin(); + SupportLayerPtrs::const_iterator it_end = m_objects.front()->support_layers().end(); for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer); // Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer. for (size_t i = idx_begin; i < idx_end; ++ i) { - ToolOrdering::LayerTools < = const_cast(m_tool_ordering.layer_tools()[i]); + ToolOrdering::LayerTools < = const_cast(m_wipe_tower_data.tool_ordering.layer_tools()[i]); if (! (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support)) break; lt.has_support = true; // Insert the new support layer. - double height = lt.print_z - m_tool_ordering.layer_tools()[i-1].print_z; + double height = lt.print_z - m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z; //FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway. - auto *new_layer = new SupportLayer(size_t(-1), this->objects.front(), - height, lt.print_z, lt.print_z - 0.5 * height); - it_layer = this->objects.front()->support_layers.insert(it_layer, new_layer); + it_layer = m_objects.front()->insert_support_layer(it_layer, size_t(-1), height, lt.print_z, lt.print_z - 0.5 * height); ++ it_layer; } } @@ -1107,9 +1111,9 @@ void Print::_make_wipe_tower() // Initialize the wipe tower. WipeTowerPrusaMM wipe_tower( - float(this->config.wipe_tower_x.value), float(this->config.wipe_tower_y.value), - float(this->config.wipe_tower_width.value), float(this->config.wipe_tower_per_color_wipe.value), - m_tool_ordering.first_extruder()); + float(m_config.wipe_tower_x.value), float(m_config.wipe_tower_y.value), + float(m_config.wipe_tower_width.value), float(m_config.wipe_tower_per_color_wipe.value), + m_wipe_tower_data.tool_ordering.first_extruder()); //wipe_tower.set_retract(); //wipe_tower.set_zhop(); @@ -1118,29 +1122,29 @@ void Print::_make_wipe_tower() for (size_t i = 0; i < 4; ++ i) wipe_tower.set_extruder( i, - WipeTowerPrusaMM::parse_material(this->config.filament_type.get_at(i).c_str()), - this->config.temperature.get_at(i), - this->config.first_layer_temperature.get_at(i)); + WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()), + m_config.temperature.get_at(i), + m_config.first_layer_temperature.get_at(i)); // When printing the first layer's wipe tower, the first extruder is expected to be active and primed. - // Therefore the number of wipe sections at the wipe tower will be (m_tool_ordering.front().extruders-1) at the 1st layer. + // Therefore the number of wipe sections at the wipe tower will be (m_wipe_tower_data.tool_ordering.front().extruders-1) at the 1st layer. // The following variable is true if the last priming section cannot be squeezed inside the wipe tower. - bool last_priming_wipe_full = m_tool_ordering.front().extruders.size() > m_tool_ordering.front().wipe_tower_partitions; + bool last_priming_wipe_full = m_wipe_tower_data.tool_ordering.front().extruders.size() > m_wipe_tower_data.tool_ordering.front().wipe_tower_partitions; - m_wipe_tower_priming = Slic3r::make_unique( - wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), ! last_priming_wipe_full, WipeTower::PURPOSE_EXTRUDE)); + m_wipe_tower_data.priming = Slic3r::make_unique( + wipe_tower.prime(this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), ! last_priming_wipe_full, WipeTower::PURPOSE_EXTRUDE)); // Generate the wipe tower layers. - m_wipe_tower_tool_changes.reserve(m_tool_ordering.layer_tools().size()); + m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size()); // Set current_extruder_id to the last extruder primed. - unsigned int current_extruder_id = m_tool_ordering.all_extruders().back(); - for (const ToolOrdering::LayerTools &layer_tools : m_tool_ordering.layer_tools()) { + unsigned int current_extruder_id = m_wipe_tower_data.tool_ordering.all_extruders().back(); + for (const ToolOrdering::LayerTools &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { this->throw_if_canceled(); if (! layer_tools.has_wipe_tower) // This is a support only layer, or the wipe tower does not reach to this height. continue; - bool first_layer = &layer_tools == &m_tool_ordering.front(); - bool last_layer = &layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0; + bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front(); + bool last_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0; wipe_tower.set_layer( float(layer_tools.print_z), float(layer_tools.wipe_tower_layer_height), @@ -1151,7 +1155,7 @@ void Print::_make_wipe_tower() for (unsigned int extruder_id : layer_tools.extruders) // Call the wipe_tower.tool_change() at the first layer for the initial extruder // to extrude the wipe tower brim, - if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || + if ((first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || // or when an extruder shall be switched. extruder_id != current_extruder_id) { tool_changes.emplace_back(wipe_tower.tool_change(extruder_id, extruder_id == layer_tools.extruders.back(), WipeTower::PURPOSE_EXTRUDE)); @@ -1175,48 +1179,49 @@ void Print::_make_wipe_tower() tool_changes.pop_back(); } } - m_wipe_tower_tool_changes.emplace_back(std::move(tool_changes)); + m_wipe_tower_data.tool_changes.emplace_back(std::move(tool_changes)); if (last_layer) break; } // Unload the current filament over the purge tower. - coordf_t layer_height = this->objects.front()->config.layer_height.value; - if (m_tool_ordering.back().wipe_tower_partitions > 0) { + coordf_t layer_height = m_objects.front()->config().layer_height.value; + if (m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions > 0) { // The wipe tower goes up to the last layer of the print. if (wipe_tower.layer_finished()) { // The wipe tower is printed to the top of the print and it has no space left for the final extruder purge. // Lift Z to the next layer. - wipe_tower.set_layer(float(m_tool_ordering.back().print_z + layer_height), float(layer_height), 0, false, true); + wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z + layer_height), float(layer_height), 0, false, true); } else { // There is yet enough space at this layer of the wipe tower for the final purge. } } else { // The wipe tower does not reach the last print layer, perform the pruge at the last print layer. - assert(m_tool_ordering.back().wipe_tower_partitions == 0); - wipe_tower.set_layer(float(m_tool_ordering.back().print_z), float(layer_height), 0, false, true); + assert(m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions == 0); + wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z), float(layer_height), 0, false, true); } - m_wipe_tower_final_purge = Slic3r::make_unique( + m_wipe_tower_data.final_purge = Slic3r::make_unique( wipe_tower.tool_change((unsigned int)-1, false, WipeTower::PURPOSE_EXTRUDE)); } -std::string Print::output_filename() +std::string Print::output_filename() const { - this->placeholder_parser.update_timestamp(); + DynamicConfig cfg_timestamp; + PlaceholderParser::update_timestamp(cfg_timestamp); try { - return this->placeholder_parser.process(this->config.output_filename_format.value, 0); + return this->placeholder_parser().process(m_config.output_filename_format.value, 0, &cfg_timestamp); } catch (std::runtime_error &err) { throw std::runtime_error(std::string("Failed processing of the output_filename_format template.\n") + err.what()); } } -std::string Print::output_filepath(const std::string &path) +std::string Print::output_filepath(const std::string &path) const { // if we were supplied no path, generate an automatic one based on our first object's input file if (path.empty()) { // get the first input file name std::string input_file; - for (const PrintObject *object : this->objects) { + for (const PrintObject *object : m_objects) { input_file = object->model_object()->input_file; if (! input_file.empty()) break; diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 1162c9b0f1..71711b31ed 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -51,7 +51,7 @@ template class PrintState { public: - PrintState() { for (size_t i = 0; i < COUNT; ++ i) m_state[i] = INVALID; } + PrintState() { for (size_t i = 0; i < COUNT; ++ i) m_state[i].store(INVALID, std::memory_order_relaxed); } enum State { INVALID, @@ -59,24 +59,58 @@ public: DONE, }; + // With full memory barrier. bool is_done(StepType step) const { return m_state[step] == DONE; } - // set_started() will lock the provided mutex before setting the state. + + // Set the step as started. Block on mutex while the Print / PrintObject / PrintRegion objects are being + // modified by the UI thread. // This is necessary to block until the Print::apply_config() updates its state, which may // influence the processing step being entered. - void set_started(StepType step, tbb::mutex &mtx) { mtx.lock(); m_state[step] = STARTED; mtx.unlock(); } - void set_done(StepType step) { m_state[step] = DONE; } - bool invalidate(StepType step) { - bool invalidated = m_state[step] != INVALID; - m_state[step] = INVALID; + void set_started(StepType step, tbb::mutex &mtx) { + mtx.lock(); + m_state[step].store(STARTED, std::memory_order_relaxed); + mtx.unlock(); + } + + // Set the step as done. Block on mutex while the Print / PrintObject / PrintRegion objects are being + // modified by the UI thread. + void set_done(StepType step, tbb::mutex &mtx) { + mtx.lock(); + m_state[step].store(DONE, std::memory_order_relaxed); + mtx.unlock(); + } + + // Make the step invalid. + // The provided mutex should be locked at this point, guarding access to m_state. + // In case the step has already been entered or finished, cancel the background + // processing by calling the cancel callback. + template + bool invalidate(StepType step, tbb::mutex &mtx, CancelationCallback &cancel) { + bool invalidated = m_state[step].load(std::memory_order_relaxed) != INVALID; + if (invalidated) { + mtx.unlock(); + cancel(); + mtx.lock(); + } return invalidated; } - bool invalidate_all() { + + // Make all steps invalid. + // The provided mutex should be locked at this point, guarding access to m_state. + // In case any step has already been entered or finished, cancel the background + // processing by calling the cancel callback. + template + bool invalidate_all(tbb::mutex &mtx, CancelationCallback &cancel) { bool invalidated = false; for (size_t i = 0; i < COUNT; ++ i) - if (m_state[i] != INVALID) { - invalidated = true; - m_state[i] = INVALID; - break; + if (m_state[i].load(std::memory_order_relaxed) != INVALID) { + if (! invalidated) { + mtx.unlock(); + cancel(); + mtx.lock(); + invalidated = true; + } + m_state[i].store(INVALID, std::memory_order_relaxed); } return invalidated; } @@ -91,17 +125,24 @@ class PrintRegion { friend class Print; +// Methods NOT modifying the PrintRegion's state: public: - PrintRegionConfig config; + const Print* print() const { return m_print; } + const PrintRegionConfig& config() const { return m_config; } + Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const; + coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const; - Print* print() { return this->_print; } - Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const; - coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const; +// Methods modifying the PrintRegion's state: +public: + Print* print() { return m_print; } + void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); } private: - Print* _print; + Print *m_print; + PrintRegionConfig m_config; - PrintRegion(Print* print) : _print(print) {} + PrintRegion(Print* print) : m_print(print) {} + PrintRegion(Print* print, const PrintRegionConfig &config) : m_print(print), m_config(config) {} ~PrintRegion() {} }; @@ -117,7 +158,6 @@ class PrintObject public: // vector of (vectors of volume ids), indexed by region_id std::vector> region_volumes; - PrintObjectConfig config; t_layer_height_ranges layer_height_ranges; // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. @@ -144,15 +184,17 @@ public: // Slic3r::Point objects in scaled G-code coordinates in our coordinates Points _shifted_copies; - LayerPtrs layers; - SupportLayerPtrs support_layers; + Print* print() { return m_print; } + const Print* print() const { return m_print; } + ModelObject* model_object() { return m_model_object; } + const ModelObject* model_object() const { return m_model_object; } + const PrintObjectConfig& config() const { return m_config; } + void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); } + void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); } + const LayerPtrs& layers() const { return m_layers; } + const SupportLayerPtrs& support_layers() const { return m_support_layers; } - Print* print() { return this->_print; } - const Print* print() const { return this->_print; } - ModelObject* model_object() { return this->_model_object; } - const ModelObject* model_object() const { return this->_model_object; } - - const Points& copies() const { return this->_copies; } + const Points& copies() const { return m_copies; } bool add_copy(const Pointf &point); bool delete_last_copy(); bool delete_all_copies() { return this->set_copies(Points()); } @@ -171,24 +213,25 @@ public: // this value is not supposed to be compared with Layer::id // since they have different semantics. size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); } - size_t layer_count() const { return this->layers.size(); } + size_t layer_count() const { return m_layers.size(); } void clear_layers(); - Layer* get_layer(int idx) { return this->layers.at(idx); } - const Layer* get_layer(int idx) const { return this->layers.at(idx); } + Layer* get_layer(int idx) { return m_layers[idx]; } + const Layer* get_layer(int idx) const { return m_layers[idx]; } // print_z: top of the layer; slice_z: center of the layer. Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); - size_t support_layer_count() const { return this->support_layers.size(); } + size_t support_layer_count() const { return m_support_layers.size(); } void clear_support_layers(); - SupportLayer* get_support_layer(int idx) { return this->support_layers.at(idx); } + SupportLayer* get_support_layer(int idx) { return m_support_layers[idx]; } SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z); + SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z); void delete_support_layer(int idx); // methods for handling state bool invalidate_state_by_config_options(const std::vector &opt_keys); bool invalidate_step(PrintObjectStep step); - bool invalidate_all_steps() { return m_state.invalidate_all(); } + bool invalidate_all_steps(); bool is_step_done(PrintObjectStep step) const { return m_state.is_done(step); } // To be used over the layer_height_profile of both the PrintObject and ModelObject @@ -229,9 +272,14 @@ private: void combine_infill(); void _generate_support_material(); - Print* _print; - ModelObject* _model_object; - Points _copies; // Slic3r::Point objects in scaled G-code coordinates + Print *m_print; + ModelObject *m_model_object; + PrintObjectConfig m_config; + // Slic3r::Point objects in scaled G-code coordinates + Points m_copies; + + LayerPtrs m_layers; + SupportLayerPtrs m_support_layers; PrintState m_state; // Mutex used for synchronization of the worker thread with the UI thread: @@ -245,9 +293,49 @@ private: ~PrintObject() {} void set_started(PrintObjectStep step) { m_state.set_started(step, m_mutex); } + void set_done(PrintObjectStep step) { m_state.set_done(step, m_mutex); } std::vector _slice_region(size_t region_id, const std::vector &z, bool modifier); }; +struct WipeTowerData +{ + // Following section will be consumed by the GCodeGenerator. + // Tool ordering of a non-sequential print has to be known to calculate the wipe tower. + // Cache it here, so it does not need to be recalculated during the G-code generation. + ToolOrdering tool_ordering; + // Cache of tool changes per print layer. + std::unique_ptr priming; + std::vector> tool_changes; + std::unique_ptr final_purge; + + void clear() { + tool_ordering.clear(); + priming.reset(nullptr); + tool_changes.clear(); + final_purge.reset(nullptr); + } +}; + +struct PrintStatistics +{ + PrintStatistics() { clear(); } + std::string estimated_print_time; + double total_used_filament; + double total_extruded_volume; + double total_cost; + double total_weight; + std::map filament_stats; + + void clear() { + estimated_print_time.clear(); + total_used_filament = 0.; + total_extruded_volume = 0.; + total_weight = 0.; + total_cost = 0.; + filament_stats.clear(); + } +}; + typedef std::vector PrintObjectPtrs; typedef std::vector PrintRegionPtrs; @@ -255,110 +343,115 @@ typedef std::vector PrintRegionPtrs; class Print { public: - PrintConfig config; - PrintObjectConfig default_object_config; - PrintRegionConfig default_region_config; - PrintObjectPtrs objects; - PrintRegionPtrs regions; - PlaceholderParser placeholder_parser; - std::string estimated_print_time; - double total_used_filament, total_extruded_volume, total_cost, total_weight; - std::map filament_stats; - - // ordered collections of extrusion paths to build skirt loops and brim - ExtrusionEntityCollection skirt, brim; - - Print() : total_used_filament(0), total_extruded_volume(0) { restart(); } + Print() { restart(); } ~Print() { clear_objects(); } - // methods for handling objects - void clear_objects(); - PrintObject* get_object(size_t idx) { return objects.at(idx); } - const PrintObject* get_object(size_t idx) const { return objects.at(idx); } + // Methods, which change the state of Print / PrintObject / PrintRegion. + // The following methods are synchronized with process() and export_gcode(), + // so that process() and export_gcode() may be called from a background thread. + // In case the following methods need to modify data processed by process() or export_gcode(), + // a cancellation callback is executed to stop the background processing before the operation. + void clear_objects(); + void delete_object(size_t idx); + void reload_object(size_t idx); + bool reload_model_instances(); + void add_model_object(ModelObject* model_object, int idx = -1); + bool apply_config(DynamicPrintConfig config); + void process(); + void export_gcode(const std::string &path_template, GCodePreviewData *preview_data); - void delete_object(size_t idx); - void reload_object(size_t idx); - bool reload_model_instances(); - - // methods for handling regions - PrintRegion* get_region(size_t idx) { return regions.at(idx); } - const PrintRegion* get_region(size_t idx) const { return regions.at(idx); } - PrintRegion* add_region(); - // methods for handling state - bool invalidate_step(PrintStep step); - bool invalidate_all_steps() { return m_state.invalidate_all(); } - bool is_step_done(PrintStep step) const { return m_state.is_done(step); } - bool is_step_done(PrintObjectStep step) const; + bool is_step_done(PrintStep step) const { return m_state.is_done(step); } + bool is_step_done(PrintObjectStep step) const; - void add_model_object(ModelObject* model_object, int idx = -1); - bool apply_config(DynamicPrintConfig config); - bool has_infinite_skirt() const; - bool has_skirt() const; + bool has_infinite_skirt() const; + bool has_skirt() const; // Returns an empty string if valid, otherwise returns an error message. - std::string validate() const; - BoundingBox bounding_box() const; - BoundingBox total_bounding_box() const; - double skirt_first_layer_height() const; - Flow brim_flow() const; - Flow skirt_flow() const; + std::string validate() const; + BoundingBox bounding_box() const; + BoundingBox total_bounding_box() const; + double skirt_first_layer_height() const; + Flow brim_flow() const; + Flow skirt_flow() const; std::vector object_extruders() const; std::vector support_material_extruders() const; std::vector extruders() const; - void _simplify_slices(double distance); - double max_allowed_layer_height() const; - bool has_support_material() const; - void auto_assign_extruders(ModelObject* model_object) const; + double max_allowed_layer_height() const; + bool has_support_material() const; + // Make sure the background processing has no access to this model_object during this call! + void auto_assign_extruders(ModelObject* model_object) const; - void process(); - void export_gcode(const std::string &path_template, GCodePreviewData *preview_data); + const PrintConfig& config() const { return m_config; } + const PrintObjectConfig& default_object_config() const { return m_default_object_config; } + const PrintRegionConfig& default_region_config() const { return m_default_region_config; } + const PrintObjectPtrs& objects() const { return m_objects; } + const PrintRegionPtrs& regions() const { return m_regions; } + const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; } + + const ExtrusionEntityCollection& skirt() const { return m_skirt; } + const ExtrusionEntityCollection& brim() const { return m_brim; } + + const PrintStatistics& print_statistics() const { return m_print_statistics; } // Wipe tower support. - bool has_wipe_tower() const; - // Tool ordering of a non-sequential print has to be known to calculate the wipe tower. - // Cache it here, so it does not need to be recalculated during the G-code generation. - ToolOrdering m_tool_ordering; - // Cache of tool changes per print layer. - std::unique_ptr m_wipe_tower_priming; - std::vector> m_wipe_tower_tool_changes; - std::unique_ptr m_wipe_tower_final_purge; + bool has_wipe_tower() const; + const WipeTowerData& wipe_tower_data() const { return m_wipe_tower_data; } - std::string output_filename(); - std::string output_filepath(const std::string &path); + std::string output_filename() const; + std::string output_filepath(const std::string &path) const; typedef std::function status_callback_type; // Default status console print out in the form of percent => message. - void set_status_default() { m_status_callback = nullptr; } + void set_status_default() { m_status_callback = nullptr; } // No status output or callback whatsoever, useful mostly for automatic tests. - void set_status_silent() { m_status_callback = [](int, const std::string&){}; } + void set_status_silent() { m_status_callback = [](int, const std::string&){}; } // Register a custom status callback. - void set_status_callback(status_callback_type cb) { m_status_callback = cb; } + void set_status_callback(status_callback_type cb) { m_status_callback = cb; } // Calls a registered callback to update the status, or print out the default message. - void set_status(int percent, const std::string &message) { + void set_status(int percent, const std::string &message) { if (m_status_callback) m_status_callback(percent, message); else printf("%d => %s\n", percent, message.c_str()); } - // Cancel the running computation. Stop execution of all the background threads. - void cancel() { m_canceled = true; } - // Cancel the running computation. Stop execution of all the background threads. - void restart() { m_canceled = false; } + + typedef std::function cancel_callback_type; + // Various methods will call this callback to stop the background processing (the Print::process() call) + // in case a successive change of the Print / PrintObject / PrintRegion instances changed + // the state of the finished or running calculations. + void set_cancel_callback(cancel_callback_type cancel_callback) { m_cancel_callback = cancel_callback; } // Has the calculation been canceled? - bool canceled() { return m_canceled; } - void throw_if_canceled() { if (m_canceled) throw CanceledException(); } + bool canceled() const { return m_canceled; } + // Cancel the running computation. Stop execution of all the background threads. + void cancel() { m_canceled = true; } + // Cancel the running computation. Stop execution of all the background threads. + void restart() { m_canceled = false; } + + // Accessed by SupportMaterial + const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; } protected: - void set_started(PrintStep step) { m_state.set_started(step, m_mutex); } - void set_done(PrintStep step) { m_state.set_done(step); } + void set_started(PrintStep step) { m_state.set_started(step, m_mutex); } + void set_done(PrintStep step) { m_state.set_done(step, m_mutex); } + bool invalidate_step(PrintStep step); + bool invalidate_all_steps() { return m_state.invalidate_all(m_mutex, m_cancel_callback); } + + // methods for handling regions + PrintRegion* get_region(size_t idx) { return m_regions[idx]; } + PrintRegion* add_region(); + PrintRegion* add_region(const PrintRegionConfig &config); private: - bool invalidate_state_by_config_options(const std::vector &opt_keys); - PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume); + bool invalidate_state_by_config_options(const std::vector &opt_keys); + PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume); - void _make_skirt(); - void _make_brim(); - void _clear_wipe_tower(); - void _make_wipe_tower(); + // If the background processing stop was requested, throw CanceledException. + // To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly. + void throw_if_canceled() { if (m_canceled) throw CanceledException(); } + + void _make_skirt(); + void _make_brim(); + void _make_wipe_tower(); + void _simplify_slices(double distance); PrintState m_state; // Mutex used for synchronization of the worker thread with the UI thread: @@ -370,15 +463,36 @@ private: // Callback to be evoked regularly to update state of the UI thread. status_callback_type m_status_callback; + // Callback to be evoked to stop the background processing before a state is updated. + cancel_callback_type m_cancel_callback = [](){}; + + PrintConfig m_config; + PrintObjectConfig m_default_object_config; + PrintRegionConfig m_default_region_config; + PrintObjectPtrs m_objects; + PrintRegionPtrs m_regions; + PlaceholderParser m_placeholder_parser; + + // Ordered collections of extrusion paths to build skirt loops and brim. + ExtrusionEntityCollection m_skirt; + ExtrusionEntityCollection m_brim; + + // Following section will be consumed by the GCodeGenerator. + WipeTowerData m_wipe_tower_data; + + // Estimated print time, filament consumed. + PrintStatistics m_print_statistics; + // To allow GCode to set the Print's GCodeExport step status. friend class GCode; + // Allow PrintObject to access m_mutex and m_cancel_callback. + friend class PrintObject; }; #define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator) -#define FOREACH_REGION(print, region) FOREACH_BASE(PrintRegionPtrs, (print)->regions, region) -#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->objects, object) -#define FOREACH_LAYER(object, layer) FOREACH_BASE(LayerPtrs, (object)->layers, layer) -#define FOREACH_LAYERREGION(layer, layerm) FOREACH_BASE(LayerRegionPtrs, (layer)->regions, layerm) +#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->m_objects, object) +#define FOREACH_LAYER(object, layer) FOREACH_BASE(LayerPtrs, (object)->m_layers, layer) +#define FOREACH_LAYERREGION(layer, layerm) FOREACH_BASE(LayerRegionPtrs, (layer)->m_regions, layerm) } diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index ff1e983fd0..ba2521cd7a 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -35,8 +35,8 @@ namespace Slic3r { PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) : typed_slices(false), - _print(print), - _model_object(model_object), + m_print(print), + m_model_object(model_object), layer_height_profile_valid(false) { // Compute the translation to be applied to our meshes so that we work with smaller coordinates @@ -60,21 +60,21 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Bounding bool PrintObject::add_copy(const Pointf &point) { - Points points = this->_copies; + Points points = m_copies; points.push_back(Point::new_scale(point.x, point.y)); return this->set_copies(points); } bool PrintObject::delete_last_copy() { - Points points = this->_copies; + Points points = m_copies; points.pop_back(); return this->set_copies(points); } bool PrintObject::set_copies(const Points &points) { - this->_copies = points; + m_copies = points; // order copies with a nearest neighbor search and translate them by _copies_shift this->_shifted_copies.clear(); @@ -90,16 +90,16 @@ bool PrintObject::set_copies(const Points &points) this->_shifted_copies.push_back(copy); } - bool invalidated = this->_print->invalidate_step(psSkirt); - invalidated |= this->_print->invalidate_step(psBrim); + bool invalidated = m_print->invalidate_step(psSkirt); + invalidated |= m_print->invalidate_step(psBrim); return invalidated; } bool PrintObject::reload_model_instances() { Points copies; - copies.reserve(this->_model_object->instances.size()); - for (const ModelInstance *mi : this->_model_object->instances) + copies.reserve(m_model_object->instances.size()); + for (const ModelInstance *mi : m_model_object->instances) copies.emplace_back(Point::new_scale(mi->offset.x, mi->offset.y)); return this->set_copies(copies); } @@ -118,21 +118,21 @@ void PrintObject::slice() if (m_state.is_done(posSlice)) return; this->set_started(posSlice); - this->_print->set_status(10, "Processing triangulated mesh"); + m_print->set_status(10, "Processing triangulated mesh"); this->_slice(); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); // Fix the model. //FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend. std::string warning = this->_fix_slicing_errors(); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); if (! warning.empty()) BOOST_LOG_TRIVIAL(info) << warning; // Simplify slices if required. - if (this->_print->config.resolution) - this->_simplify_slices(scale_(this->_print->config.resolution)); - if (this->layers.empty()) + if (m_print->config().resolution) + this->_simplify_slices(scale_(this->print()->config().resolution)); + if (m_layers.empty()) throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); - m_state.set_done(posSlice); + this->set_done(posSlice); } // 1) Merges typed region slices into stInternal type. @@ -147,17 +147,17 @@ void PrintObject::make_perimeters() return; this->set_started(posPerimeters); - this->_print->set_status(20, "Generating perimeters"); + m_print->set_status(20, "Generating perimeters"); BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; // merge slices if they were split into types if (this->typed_slices) { FOREACH_LAYER(this, layer_it) { (*layer_it)->merge_slices(); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); } this->typed_slices = false; - m_state.invalidate(posPrepareInfill); +// m_state.invalidate(posPrepareInfill); } // compare each layer to the one below, and mark those slices needing @@ -167,24 +167,23 @@ void PrintObject::make_perimeters() // but we don't generate any extra perimeter if fill density is zero, as they would be floating // inside the object - infill_only_where_needed should be the method of choice for printing // hollow objects - FOREACH_REGION(this->_print, region_it) { - size_t region_id = region_it - this->_print->regions.begin(); - const PrintRegion ®ion = **region_it; + for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) { + const PrintRegion ®ion = *m_print->regions()[region_id]; - if (!region.config.extra_perimeters - || region.config.perimeters == 0 - || region.config.fill_density == 0 + if (!region.config().extra_perimeters + || region.config().perimeters == 0 + || region.config().fill_density == 0 || this->layer_count() < 2) continue; BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size() - 1), + tbb::blocked_range(0, m_layers.size() - 1), [this, ®ion, region_id](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - this->_print->throw_if_canceled(); - LayerRegion &layerm = *this->layers[layer_idx]->regions[region_id]; - const LayerRegion &upper_layerm = *this->layers[layer_idx+1]->regions[region_id]; + m_print->throw_if_canceled(); + LayerRegion &layerm = *m_layers[layer_idx]->m_regions[region_id]; + const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->m_regions[region_id]; const Polygons upper_layerm_polygons = upper_layerm.slices; // Filter upper layer polygons in intersection_ppl by their bounding boxes? // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; @@ -198,7 +197,7 @@ void PrintObject::make_perimeters() for (;;) { // compute the total thickness of perimeters const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2 - + (region.config.perimeters-1 + slice.extra_perimeters) * perimeter_spacing; + + (region.config().perimeters-1 + slice.extra_perimeters) * perimeter_spacing; // define a critical area where we don't want the upper slice to fall into // (it should either lay over our perimeters or outside this area) const coord_t critical_area_depth = coord_t(perimeter_spacing * 1.5); @@ -231,21 +230,21 @@ void PrintObject::make_perimeters() } } }); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - end"; } BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), + tbb::blocked_range(0, m_layers.size()), [this](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - this->_print->throw_if_canceled(); - this->layers[layer_idx]->make_perimeters(); + m_print->throw_if_canceled(); + m_layers[layer_idx]->make_perimeters(); } } ); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end"; /* @@ -255,7 +254,7 @@ void PrintObject::make_perimeters() ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); */ - m_state.set_done(posPerimeters); + this->set_done(posPerimeters); } void PrintObject::prepare_infill() @@ -264,23 +263,23 @@ void PrintObject::prepare_infill() return; this->set_started(posPrepareInfill); - this->_print->set_status(30, "Preparing infill"); + m_print->set_status(30, "Preparing infill"); // This will assign a type (top/bottom/internal) to $layerm->slices. // Then the classifcation of $layerm->slices is transfered onto // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces // by the cummulative area of the previous $layerm->fill_surfaces. this->detect_surfaces_type(); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); // Decide what surfaces are to be filled. // Here the S_TYPE_TOP / S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is turned to just S_TYPE_INTERNAL if zero top / bottom infill layers are configured. // Also tiny S_TYPE_INTERNAL surfaces are turned to S_TYPE_INTERNAL_SOLID. BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..."; - for (auto *layer : this->layers) - for (auto *region : layer->regions) { + for (auto *layer : m_layers) + for (auto *region : layer->m_regions) { region->prepare_fill_surfaces(); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); } // this will detect bridges and reverse bridges @@ -293,17 +292,17 @@ void PrintObject::prepare_infill() // 4) Merge surfaces with the same style. This will mostly get rid of the overlaps. //FIXME This does not likely merge surfaces, which are supported by a material with different colors, but same properties. this->process_external_surfaces(); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); // Add solid fills to ensure the shell vertical thickness. this->discover_vertical_shells(); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); // Debugging output. #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (const Layer *layer : this->layers) { - LayerRegion *layerm = layer->regions[region_id]; + for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { + for (const Layer *layer : m_layers) { + LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final"); layerm->export_region_fill_surfaces_to_svg_debug("6_discover_vertical_shells-final"); } // for each layer @@ -317,12 +316,12 @@ void PrintObject::prepare_infill() // to close these surfaces reliably. //FIXME Vojtech: Is this a good place to add supporting infills below sloping perimeters? this->discover_horizontal_shells(); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (const Layer *layer : this->layers) { - LayerRegion *layerm = layer->regions[region_id]; + for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { + for (const Layer *layer : m_layers) { + LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final"); layerm->export_region_fill_surfaces_to_svg_debug("7_discover_horizontal_shells-final"); } // for each layer @@ -336,12 +335,12 @@ void PrintObject::prepare_infill() // Likely the sparse infill will not be anchored correctly, so it will not work as intended. // Also one wishes the perimeters to be supported by a full infill. this->clip_fill_surfaces(); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (const Layer *layer : this->layers) { - LayerRegion *layerm = layer->regions[region_id]; + for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { + for (const Layer *layer : m_layers) { + LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final"); layerm->export_region_fill_surfaces_to_svg_debug("8_clip_surfaces-final"); } // for each layer @@ -351,27 +350,27 @@ void PrintObject::prepare_infill() // the following step needs to be done before combination because it may need // to remove only half of the combined infill this->bridge_over_infill(); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); // combine fill surfaces to honor the "infill every N layers" option this->combine_infill(); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (const Layer *layer : this->layers) { - LayerRegion *layerm = layer->regions[region_id]; + for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { + for (const Layer *layer : m_layers) { + LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("9_prepare_infill-final"); layerm->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final"); } // for each layer } // for each region - for (const Layer *layer : this->layers) { + for (const Layer *layer : m_layers) { layer->export_region_slices_to_svg_debug("9_prepare_infill-final"); layer->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final"); } // for each layer #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - m_state.set_done(posPrepareInfill); + this->set_done(posPrepareInfill); } void PrintObject::infill() @@ -383,20 +382,20 @@ void PrintObject::infill() this->set_started(posInfill); BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), + tbb::blocked_range(0, m_layers.size()), [this](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - this->_print->throw_if_canceled(); - this->layers[layer_idx]->make_fills(); + m_print->throw_if_canceled(); + m_layers[layer_idx]->make_fills(); } } ); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - end"; /* we could free memory now, but this would make this step not idempotent ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; */ - m_state.set_done(posInfill); + this->set_done(posInfill); } } @@ -405,39 +404,44 @@ void PrintObject::generate_support_material() if (! m_state.is_done(posSupportMaterial)) { this->set_started(posSupportMaterial); this->clear_support_layers(); - if ((this->config.support_material || this->config.raft_layers > 0) && this->layers.size() > 1) { - this->_print->set_status(85, "Generating support material"); + if ((m_config.support_material || m_config.raft_layers > 0) && m_layers.size() > 1) { + m_print->set_status(85, "Generating support material"); this->_generate_support_material(); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); } - m_state.set_done(posSupportMaterial); + this->set_done(posSupportMaterial); } } void PrintObject::clear_layers() { - for (Layer *l : this->layers) + for (Layer *l : m_layers) delete l; - this->layers.clear(); + m_layers.clear(); } Layer* PrintObject::add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z) { - layers.push_back(new Layer(id, this, height, print_z, slice_z)); - return layers.back(); + m_layers.emplace_back(new Layer(id, this, height, print_z, slice_z)); + return m_layers.back(); } void PrintObject::clear_support_layers() { - for (Layer *l : this->support_layers) + for (Layer *l : m_support_layers) delete l; - this->support_layers.clear(); + m_support_layers.clear(); } SupportLayer* PrintObject::add_support_layer(int id, coordf_t height, coordf_t print_z) { - support_layers.emplace_back(new SupportLayer(id, this, height, print_z, -1)); - return support_layers.back(); + m_support_layers.emplace_back(new SupportLayer(id, this, height, print_z, -1)); + return m_support_layers.back(); +} + +SupportLayerPtrs::const_iterator PrintObject::insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z) +{ + return m_support_layers.insert(pos, new SupportLayer(id, this, height, print_z, slice_z)); } // Called by Print::apply_config(). @@ -545,8 +549,8 @@ bool PrintObject::invalidate_state_by_config_options(const std::vectorinvalidate_all_steps(); this->reset_layer_height_profile(); - this->invalidate_all_steps(); invalidated = true; } } @@ -559,37 +563,42 @@ bool PrintObject::invalidate_state_by_config_options(const std::vectorm_mutex, m_print->m_cancel_callback); // propagate to dependent steps if (step == posPerimeters) { invalidated |= this->invalidate_step(posPrepareInfill); - invalidated |= this->_print->invalidate_step(psSkirt); - invalidated |= this->_print->invalidate_step(psBrim); + invalidated |= m_print->invalidate_step(psSkirt); + invalidated |= m_print->invalidate_step(psBrim); } else if (step == posPrepareInfill) { invalidated |= this->invalidate_step(posInfill); } else if (step == posInfill) { - invalidated |= this->_print->invalidate_step(psSkirt); - invalidated |= this->_print->invalidate_step(psBrim); + invalidated |= m_print->invalidate_step(psSkirt); + invalidated |= m_print->invalidate_step(psBrim); } else if (step == posSlice) { invalidated |= this->invalidate_step(posPerimeters); invalidated |= this->invalidate_step(posSupportMaterial); - invalidated |= this->_print->invalidate_step(psWipeTower); + invalidated |= m_print->invalidate_step(psWipeTower); } else if (step == posSupportMaterial) { - invalidated |= this->_print->invalidate_step(psSkirt); - invalidated |= this->_print->invalidate_step(psBrim); + invalidated |= m_print->invalidate_step(psSkirt); + invalidated |= m_print->invalidate_step(psBrim); } // Wipe tower depends on the ordering of extruders, which in turn depends on everything. - invalidated |= this->_print->invalidate_step(psWipeTower); + invalidated |= m_print->invalidate_step(psWipeTower); return invalidated; } +bool PrintObject::invalidate_all_steps() +{ + return m_state.invalidate_all(m_print->m_mutex, m_print->m_cancel_callback); +} + bool PrintObject::has_support_material() const { - return this->config.support_material - || this->config.raft_layers > 0 - || this->config.support_material_enforce_layers > 0; + return m_config.support_material + || m_config.raft_layers > 0 + || m_config.support_material_enforce_layers > 0; } // This function analyzes slices of a region (SurfaceCollection slices). @@ -610,42 +619,42 @@ void PrintObject::detect_surfaces_type() // are completely hidden inside a collective body of intersecting parts. // This is useful if one of the parts is to be dissolved, or if it is transparent and the internal shells // should be visible. - bool interface_shells = this->config.interface_shells.value; + bool interface_shells = m_config.interface_shells.value; - for (int idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) { + for (int idx_region = 0; idx_region < m_print->m_regions.size(); ++ idx_region) { BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start"; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (Layer *layer : this->layers) - layer->regions[idx_region]->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial"); + for (Layer *layer : m_layers) + layer->m_regions[idx_region]->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial"); #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ // If interface shells are allowed, the region->surfaces cannot be overwritten as they may be used by other threads. // Cache the result of the following parallel_loop. std::vector surfaces_new; if (interface_shells) - surfaces_new.assign(this->layers.size(), Surfaces()); + surfaces_new.assign(m_layers.size(), Surfaces()); tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), + tbb::blocked_range(0, m_layers.size()), [this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range& range) { // If we have raft layers, consider bottom layer as a bridge just like any other bottom surface lying on the void. SurfaceType surface_type_bottom_1st = - (this->config.raft_layers.value > 0 && this->config.support_material_contact_distance.value > 0) ? + (m_config.raft_layers.value > 0 && m_config.support_material_contact_distance.value > 0) ? stBottomBridge : stBottom; // If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating // the support from the print. SurfaceType surface_type_bottom_other = - (this->config.support_material.value && this->config.support_material_contact_distance.value == 0) ? + (m_config.support_material.value && m_config.support_material_contact_distance.value == 0) ? stBottom : stBottomBridge; for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); // BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z; - Layer *layer = this->layers[idx_layer]; + Layer *layer = m_layers[idx_layer]; LayerRegion *layerm = layer->get_region(idx_region); // comparison happens against the *full* slices (considering all regions) // unless internal shells are requested - Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? this->layers[idx_layer + 1] : nullptr; - Layer *lower_layer = (idx_layer > 0) ? this->layers[idx_layer - 1] : nullptr; + Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr; + Layer *lower_layer = (idx_layer > 0) ? m_layers[idx_layer - 1] : nullptr; // collapse very narrow parts (using the safety offset in the diff is not enough) float offset = layerm->flow(frExternalPerimeter).scaled_width() / 10.f; @@ -763,29 +772,29 @@ void PrintObject::detect_surfaces_type() } } ); // for each layer of a region - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); if (interface_shells) { // Move surfaces_new to layerm->slices.surfaces - for (size_t idx_layer = 0; idx_layer < this->layers.size(); ++ idx_layer) - this->layers[idx_layer]->get_region(idx_region)->slices.surfaces = std::move(surfaces_new[idx_layer]); + for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer) + m_layers[idx_layer]->get_region(idx_region)->slices.surfaces = std::move(surfaces_new[idx_layer]); } BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - start"; // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), + tbb::blocked_range(0, m_layers.size()), [this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range& range) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - this->_print->throw_if_canceled(); - LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region); + m_print->throw_if_canceled(); + LayerRegion *layerm = m_layers[idx_layer]->get_region(idx_region); layerm->slices_to_fill_surfaces_clipped(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final"); #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ } // for each layer of a region }); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - end"; } // for each this->print->region_count @@ -797,21 +806,21 @@ void PrintObject::process_external_surfaces() { BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..."; - FOREACH_REGION(this->_print, region) { - int region_id = int(region - this->_print->regions.begin()); + for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) { + const PrintRegion ®ion = *m_print->regions()[region_id]; BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), + tbb::blocked_range(0, m_layers.size()), [this, region_id](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - this->_print->throw_if_canceled(); - // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << this->layers[layer_idx]->print_z; - this->layers[layer_idx]->get_region(region_id)->process_external_surfaces((layer_idx == 0) ? NULL : this->layers[layer_idx - 1]); + m_print->throw_if_canceled(); + // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z; + m_layers[layer_idx]->get_region(region_id)->process_external_surfaces((layer_idx == 0) ? NULL : m_layers[layer_idx - 1]); } } ); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - end"; } } @@ -829,17 +838,17 @@ void PrintObject::discover_vertical_shells() Polygons bottom_surfaces; Polygons holes; }; - std::vector cache_top_botom_regions(this->layers.size(), DiscoverVerticalShellsCacheEntry()); - bool top_bottom_surfaces_all_regions = this->_print->regions.size() > 1 && ! this->config.interface_shells.value; + std::vector cache_top_botom_regions(m_layers.size(), DiscoverVerticalShellsCacheEntry()); + bool top_bottom_surfaces_all_regions = m_print->regions().size() > 1 && ! m_config.interface_shells.value; if (top_bottom_surfaces_all_regions) { // This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness // is calculated over all materials. // Is the "ensure vertical wall thickness" applicable to any region? bool has_extra_layers = false; - for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) { - const PrintRegion ®ion = *this->_print->get_region(idx_region); - if (region.config.ensure_vertical_shell_thickness.value && - (region.config.top_solid_layers.value > 1 || region.config.bottom_solid_layers.value > 1)) { + for (size_t idx_region = 0; idx_region < m_print->regions().size(); ++ idx_region) { + const PrintRegion ®ion = *m_print->get_region(idx_region); + if (region.config().ensure_vertical_shell_thickness.value && + (region.config().top_solid_layers.value > 1 || region.config().bottom_solid_layers.value > 1)) { has_extra_layers = true; } } @@ -848,15 +857,15 @@ void PrintObject::discover_vertical_shells() return; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - start : cache top / bottom"; //FIXME Improve the heuristics for a grain size. - size_t grain_size = std::max(this->layers.size() / 16, size_t(1)); + size_t grain_size = std::max(m_layers.size() / 16, size_t(1)); tbb::parallel_for( - tbb::blocked_range(0, this->layers.size(), grain_size), + tbb::blocked_range(0, m_layers.size(), grain_size), [this, &cache_top_botom_regions](const tbb::blocked_range& range) { const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; - const size_t num_regions = this->_print->regions.size(); + const size_t num_regions = m_print->regions().size(); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - this->_print->throw_if_canceled(); - const Layer &layer = *this->layers[idx_layer]; + m_print->throw_if_canceled(); + const Layer &layer = *m_layers[idx_layer]; DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[idx_layer]; // Simulate single set of perimeters over all merged regions. float perimeter_offset = 0.f; @@ -866,7 +875,7 @@ void PrintObject::discover_vertical_shells() ++ debug_idx; #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ for (size_t idx_region = 0; idx_region < num_regions; ++ idx_region) { - LayerRegion &layerm = *layer.regions[idx_region]; + LayerRegion &layerm = *layer.m_regions[idx_region]; float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f; // Top surfaces. append(cache.top_surfaces, offset(to_expolygons(layerm.slices.filter_by_type(stTop)), min_perimeter_infill_spacing)); @@ -879,7 +888,7 @@ void PrintObject::discover_vertical_shells() unsigned int perimeters = 0; for (Surface &s : layerm.slices.surfaces) perimeters = std::max(perimeters, s.extra_perimeters); - perimeters += layerm.region()->config.perimeters.value; + perimeters += layerm.region()->config().perimeters.value; // Then calculate the infill offset. if (perimeters > 0) { Flow extflow = layerm.flow(frExternalPerimeter); @@ -910,38 +919,38 @@ void PrintObject::discover_vertical_shells() cache.holes = union_(cache.holes, false); } }); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom"; } - for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) { + for (size_t idx_region = 0; idx_region < m_print->regions().size(); ++ idx_region) { PROFILE_BLOCK(discover_vertical_shells_region); - const PrintRegion ®ion = *this->_print->get_region(idx_region); - if (! region.config.ensure_vertical_shell_thickness.value) + const PrintRegion ®ion = *m_print->get_region(idx_region); + if (! region.config().ensure_vertical_shell_thickness.value) // This region will be handled by discover_horizontal_shells(). continue; - int n_extra_top_layers = std::max(0, region.config.top_solid_layers.value - 1); - int n_extra_bottom_layers = std::max(0, region.config.bottom_solid_layers.value - 1); + int n_extra_top_layers = std::max(0, region.config().top_solid_layers.value - 1); + int n_extra_bottom_layers = std::max(0, region.config().bottom_solid_layers.value - 1); if (n_extra_top_layers + n_extra_bottom_layers == 0) // Zero or 1 layer, there is no additional vertical wall thickness enforced. continue; //FIXME Improve the heuristics for a grain size. - size_t grain_size = std::max(this->layers.size() / 16, size_t(1)); + size_t grain_size = std::max(m_layers.size() / 16, size_t(1)); if (! top_bottom_surfaces_all_regions) { // This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness // is calculated over a single material. BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : cache top / bottom"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size(), grain_size), + tbb::blocked_range(0, m_layers.size(), grain_size), [this, idx_region, &cache_top_botom_regions](const tbb::blocked_range& range) { const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - this->_print->throw_if_canceled(); - Layer &layer = *this->layers[idx_layer]; - LayerRegion &layerm = *layer.regions[idx_region]; + m_print->throw_if_canceled(); + Layer &layer = *m_layers[idx_layer]; + LayerRegion &layerm = *layer.m_regions[idx_region]; float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f; // Top surfaces. auto &cache = cache_top_botom_regions[idx_layer]; @@ -952,31 +961,31 @@ void PrintObject::discover_vertical_shells() append(cache.bottom_surfaces, offset(to_expolygons(layerm.fill_surfaces.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing)); // Holes over all regions. Only collect them once, they are valid for all idx_region iterations. if (cache.holes.empty()) { - for (size_t idx_region = 0; idx_region < layer.regions.size(); ++ idx_region) - polygons_append(cache.holes, to_polygons(layer.regions[idx_region]->fill_expolygons)); + for (size_t idx_region = 0; idx_region < layer.regions().size(); ++ idx_region) + polygons_append(cache.holes, to_polygons(layer.regions()[idx_region]->fill_expolygons)); } } }); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end : cache top / bottom"; } BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : ensure vertical wall thickness"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size(), grain_size), + tbb::blocked_range(0, m_layers.size(), grain_size), [this, idx_region, n_extra_top_layers, n_extra_bottom_layers, &cache_top_botom_regions] (const tbb::blocked_range& range) { // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { PROFILE_BLOCK(discover_vertical_shells_region_layer); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING static size_t debug_idx = 0; ++ debug_idx; #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - Layer *layer = this->layers[idx_layer]; - LayerRegion *layerm = layer->regions[idx_region]; + Layer *layer = m_layers[idx_layer]; + LayerRegion *layerm = layer->m_regions[idx_region]; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial"); @@ -999,9 +1008,9 @@ void PrintObject::discover_vertical_shells() { Slic3r::SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", debug_idx), this->bounding_box()); for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) { - if (n < 0 || n >= (int)this->layers.size()) + if (n < 0 || n >= (int)m_layers.size()) continue; - ExPolygons &expolys = this->layers[n]->perimeter_expolygons; + ExPolygons &expolys = m_layers[n]->perimeter_expolygons; for (size_t i = 0; i < expolys.size(); ++ i) { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg", debug_idx, n, i), get_extents(expolys[i])); svg.draw(expolys[i]); @@ -1019,8 +1028,8 @@ void PrintObject::discover_vertical_shells() // Reset the top / bottom inflated regions caches of entries, which are out of the moving window. bool hole_first = true; for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) - if (n >= 0 && n < (int)this->layers.size()) { - Layer &neighbor_layer = *this->layers[n]; + if (n >= 0 && n < (int)m_layers.size()) { + Layer &neighbor_layer = *m_layers[n]; const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[n]; if (hole_first) { hole_first = false; @@ -1183,12 +1192,12 @@ void PrintObject::discover_vertical_shells() layerm->fill_surfaces.append(new_internal_solid, stInternalSolid); } // for each layer }); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end"; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t idx_layer = 0; idx_layer < this->layers.size(); ++idx_layer) { - LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region); + for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++idx_layer) { + LayerRegion *layerm = m_layers[idx_layer]->get_region(idx_region); layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-final"); layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-final"); } @@ -1206,14 +1215,14 @@ void PrintObject::bridge_over_infill() { BOOST_LOG_TRIVIAL(info) << "Bridge over infill..."; - FOREACH_REGION(this->_print, region) { - size_t region_id = region - this->_print->regions.begin(); + for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) { + const PrintRegion ®ion = *m_print->regions()[region_id]; // skip bridging in case there are no voids - if ((*region)->config.fill_density.value == 100) continue; + if (region.config().fill_density.value == 100) continue; // get bridge flow - Flow bridge_flow = (*region)->flow( + Flow bridge_flow = region.flow( frSolidInfill, -1, // layer height, not relevant for bridge flow true, // bridge @@ -1224,10 +1233,10 @@ void PrintObject::bridge_over_infill() FOREACH_LAYER(this, layer_it) { // skip first layer - if (layer_it == this->layers.begin()) continue; + if (layer_it == m_layers.begin()) continue; Layer* layer = *layer_it; - LayerRegion* layerm = layer->regions[region_id]; + LayerRegion* layerm = layer->m_regions[region_id]; // extract the stInternalSolid surfaces that might be transformed into bridges Polygons internal_solid; @@ -1242,8 +1251,8 @@ void PrintObject::bridge_over_infill() // iterate through lower layers spanned by bridge_flow double bottom_z = layer->print_z - bridge_flow.height; - for (int i = int(layer_it - this->layers.begin()) - 1; i >= 0; --i) { - const Layer* lower_layer = this->layers[i]; + for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) { + const Layer* lower_layer = m_layers[i]; // stop iterating if layer is lower than bottom_z if (lower_layer->print_z < bottom_z) break; @@ -1323,7 +1332,7 @@ void PrintObject::bridge_over_infill() layerm->export_region_slices_to_svg_debug("7_bridge_over_infill"); layerm->export_region_fill_surfaces_to_svg_debug("7_bridge_over_infill"); #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); } } } @@ -1331,7 +1340,7 @@ void PrintObject::bridge_over_infill() SlicingParameters PrintObject::slicing_parameters() const { return SlicingParameters::create_from_config( - this->print()->config, this->config, + this->print()->config(), m_config, unscale(this->size.z), this->print()->object_extruders()); } @@ -1422,39 +1431,39 @@ void PrintObject::_slice() layer->lower_layer = prev; } // Make sure all layers contain layer region objects for all regions. - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) - layer->add_region(this->print()->regions[region_id]); + for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) + layer->add_region(this->print()->regions()[region_id]); prev = layer; } } // Slice all non-modifier volumes. - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; std::vector expolygons_by_layer = this->_slice_region(region_id, slice_zs, false); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start"; for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) - this->layers[layer_id]->regions[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal); - this->_print->throw_if_canceled(); + m_layers[layer_id]->regions()[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " end"; } // Slice all modifier volumes. - if (this->print()->regions.size() > 1) { - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { + if (this->print()->regions().size() > 1) { + for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id; std::vector expolygons_by_layer = this->_slice_region(region_id, slice_zs, true); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); // loop through the other regions and 'steal' the slices belonging to this one BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " start"; - for (size_t other_region_id = 0; other_region_id < this->print()->regions.size(); ++ other_region_id) { + for (size_t other_region_id = 0; other_region_id < this->print()->regions().size(); ++ other_region_id) { if (region_id == other_region_id) continue; for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) { - Layer *layer = layers[layer_id]; - LayerRegion *layerm = layer->regions[region_id]; - LayerRegion *other_layerm = layer->regions[other_region_id]; + Layer *layer = this->layers()[layer_id]; + LayerRegion *layerm = layer->m_regions[region_id]; + LayerRegion *other_layerm = layer->m_regions[other_region_id]; if (layerm == nullptr || other_layerm == nullptr) continue; Polygons other_slices = to_polygons(other_layerm->slices); @@ -1467,59 +1476,59 @@ void PrintObject::_slice() layerm->slices.append(std::move(my_parts), stInternal); } } - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " end"; } } BOOST_LOG_TRIVIAL(debug) << "Slicing objects - removing top empty layers"; - while (! this->layers.empty()) { - const Layer *layer = this->layers.back(); - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) - if (layer->regions[region_id] != nullptr && ! layer->regions[region_id]->slices.empty()) + while (! m_layers.empty()) { + const Layer *layer = m_layers.back(); + for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) + if (layer->m_regions[region_id] != nullptr && ! layer->m_regions[region_id]->slices.empty()) // Non empty layer. goto end; delete layer; - this->layers.pop_back(); - if (! this->layers.empty()) - this->layers.back()->upper_layer = nullptr; + m_layers.pop_back(); + if (! m_layers.empty()) + m_layers.back()->upper_layer = nullptr; } - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); end: ; BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - begin"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), + tbb::blocked_range(0, m_layers.size()), [this](const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - this->_print->throw_if_canceled(); - Layer *layer = this->layers[layer_id]; + m_print->throw_if_canceled(); + Layer *layer = m_layers[layer_id]; // Apply size compensation and perform clipping of multi-part objects. - float delta = float(scale_(this->config.xy_size_compensation.value)); + float delta = float(scale_(m_config.xy_size_compensation.value)); if (layer_id == 0) - delta -= float(scale_(this->config.elefant_foot_compensation.value)); + delta -= float(scale_(m_config.elefant_foot_compensation.value)); bool scale = delta != 0.f; - bool clip = this->config.clip_multipart_objects.value || delta > 0.f; - if (layer->regions.size() == 1) { + bool clip = m_config.clip_multipart_objects.value || delta > 0.f; + if (layer->m_regions.size() == 1) { if (scale) { // Single region, growing or shrinking. - LayerRegion *layerm = layer->regions.front(); + LayerRegion *layerm = layer->m_regions.front(); layerm->slices.set(offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta), stInternal); } } else if (scale || clip) { // Multiple regions, growing, shrinking or just clipping one region by the other. // When clipping the regions, priority is given to the first regions. Polygons processed; - for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id) { - LayerRegion *layerm = layer->regions[region_id]; + for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) { + LayerRegion *layerm = layer->m_regions[region_id]; ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces)); if (scale) slices = offset_ex(slices, delta); if (region_id > 0 && clip) // Trim by the slices of already processed regions. slices = diff_ex(to_polygons(std::move(slices)), processed); - if (clip && region_id + 1 < layer->regions.size()) + if (clip && region_id + 1 < layer->m_regions.size()) // Collect the already processed regions to trim the to be processed regions. polygons_append(processed, slices); layerm->slices.set(std::move(slices), stInternal); @@ -1529,7 +1538,7 @@ end: layer->make_slices(); } }); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end"; } @@ -1546,7 +1555,7 @@ std::vector PrintObject::_slice_region(size_t region_id, const std:: ModelVolume *volume = this->model_object()->volumes[volume_id]; if (volume->modifier == modifier) mesh.merge(volume->mesh); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); } if (mesh.stl.stats.number_of_facets > 0) { // transform mesh @@ -1561,7 +1570,7 @@ std::vector PrintObject::_slice_region(size_t region_id, const std:: auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); mslicer.init(&mesh, callback); mslicer.slice(z, &layers, callback); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); } } } @@ -1573,9 +1582,9 @@ std::string PrintObject::_fix_slicing_errors() // Collect layers with slicing errors. // These layers will be fixed in parallel. std::vector buggy_layers; - buggy_layers.reserve(this->layers.size()); - for (size_t idx_layer = 0; idx_layer < this->layers.size(); ++ idx_layer) - if (this->layers[idx_layer]->slicing_errors) + buggy_layers.reserve(m_layers.size()); + for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer) + if (m_layers[idx_layer]->slicing_errors) buggy_layers.push_back(idx_layer); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - begin"; @@ -1583,25 +1592,25 @@ std::string PrintObject::_fix_slicing_errors() tbb::blocked_range(0, buggy_layers.size()), [this, &buggy_layers](const tbb::blocked_range& range) { for (size_t buggy_layer_idx = range.begin(); buggy_layer_idx < range.end(); ++ buggy_layer_idx) { - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); size_t idx_layer = buggy_layers[buggy_layer_idx]; - Layer *layer = this->layers[idx_layer]; + Layer *layer = m_layers[idx_layer]; assert(layer->slicing_errors); // Try to repair the layer surfaces by merging all contours and all holes from neighbor layers. // BOOST_LOG_TRIVIAL(trace) << "Attempting to repair layer" << idx_layer; - for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id) { - LayerRegion *layerm = layer->regions[region_id]; + for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) { + LayerRegion *layerm = layer->m_regions[region_id]; // Find the first valid layer below / above the current layer. const Surfaces *upper_surfaces = nullptr; const Surfaces *lower_surfaces = nullptr; - for (size_t j = idx_layer + 1; j < this->layers.size(); ++ j) - if (! this->layers[j]->slicing_errors) { - upper_surfaces = &this->layers[j]->regions[region_id]->slices.surfaces; + for (size_t j = idx_layer + 1; j < m_layers.size(); ++ j) + if (! m_layers[j]->slicing_errors) { + upper_surfaces = &m_layers[j]->regions()[region_id]->slices.surfaces; break; } for (int j = int(idx_layer) - 1; j >= 0; -- j) - if (! this->layers[j]->slicing_errors) { - lower_surfaces = &this->layers[j]->regions[region_id]->slices.surfaces; + if (! m_layers[j]->slicing_errors) { + lower_surfaces = &m_layers[j]->regions()[region_id]->slices.surfaces; break; } // Collect outer contours and holes from the valid layers above & below. @@ -1634,16 +1643,16 @@ std::string PrintObject::_fix_slicing_errors() layer->make_slices(); } }); - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - end"; // remove empty layers from bottom - while (! this->layers.empty() && this->layers.front()->slices.expolygons.empty()) { - delete this->layers.front(); - this->layers.erase(this->layers.begin()); - this->layers.front()->lower_layer = nullptr; - for (size_t i = 0; i < this->layers.size(); ++ i) - this->layers[i]->set_id(this->layers[i]->id() - 1); + while (! m_layers.empty() && m_layers.front()->slices.expolygons.empty()) { + delete m_layers.front(); + m_layers.erase(m_layers.begin()); + m_layers.front()->lower_layer = nullptr; + for (size_t i = 0; i < m_layers.size(); ++ i) + m_layers[i]->set_id(m_layers[i]->id() - 1); } return buggy_layers.empty() ? "" : @@ -1658,13 +1667,13 @@ void PrintObject::_simplify_slices(double distance) { BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - begin"; tbb::parallel_for( - tbb::blocked_range(0, this->layers.size()), + tbb::blocked_range(0, m_layers.size()), [this, distance](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - this->_print->throw_if_canceled(); - Layer *layer = this->layers[layer_idx]; - for (size_t region_idx = 0; region_idx < layer->regions.size(); ++ region_idx) - layer->regions[region_idx]->slices.simplify(distance); + m_print->throw_if_canceled(); + Layer *layer = m_layers[layer_idx]; + for (size_t region_idx = 0; region_idx < layer->m_regions.size(); ++ region_idx) + layer->m_regions[region_idx]->slices.simplify(distance); layer->slices.simplify(distance); } }); @@ -1681,18 +1690,18 @@ void PrintObject::_simplify_slices(double distance) // fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries. void PrintObject::clip_fill_surfaces() { - if (! this->config.infill_only_where_needed.value || - ! std::any_of(this->print()->regions.begin(), this->print()->regions.end(), - [](const PrintRegion *region) { return region->config.fill_density > 0; })) + if (! m_config.infill_only_where_needed.value || + ! std::any_of(this->print()->regions().begin(), this->print()->regions().end(), + [](const PrintRegion *region) { return region->config().fill_density > 0; })) return; // We only want infill under ceilings; this is almost like an // internal support material. // Proceed top-down, skipping the bottom layer. Polygons upper_internal; - for (int layer_id = int(this->layers.size()) - 1; layer_id > 0; -- layer_id) { - Layer *layer = this->layers[layer_id]; - Layer *lower_layer = this->layers[layer_id - 1]; + for (int layer_id = int(m_layers.size()) - 1; layer_id > 0; -- layer_id) { + Layer *layer = m_layers[layer_id]; + Layer *lower_layer = m_layers[layer_id - 1]; // Detect things that we need to support. // Cummulative slices. Polygons slices; @@ -1702,7 +1711,7 @@ void PrintObject::clip_fill_surfaces() Polygons fill_surfaces; // Solid surfaces to be supported. Polygons overhangs; - for (const LayerRegion *layerm : layer->regions) + for (const LayerRegion *layerm : layer->m_regions) for (const Surface &surface : layerm->fill_surfaces.surfaces) { Polygons polygons = to_polygons(surface.expolygon); if (surface.is_solid()) @@ -1711,7 +1720,7 @@ void PrintObject::clip_fill_surfaces() } Polygons lower_layer_fill_surfaces; Polygons lower_layer_internal_surfaces; - for (const LayerRegion *layerm : lower_layer->regions) + for (const LayerRegion *layerm : lower_layer->m_regions) for (const Surface &surface : layerm->fill_surfaces.surfaces) { Polygons polygons = to_polygons(surface.expolygon); if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) @@ -1727,7 +1736,7 @@ void PrintObject::clip_fill_surfaces() //FIXME Offset2 eats out from both sides, while the perimeters are create outside in. //Should the pw not be half of the current value? float pw = FLT_MAX; - for (const LayerRegion *layerm : layer->regions) + for (const LayerRegion *layerm : layer->m_regions) pw = std::min(pw, layerm->flow(frPerimeter).scaled_width()); // Append such thick perimeters to the areas that need support polygons_append(overhangs, offset2(perimeters, -pw, +pw)); @@ -1736,8 +1745,8 @@ void PrintObject::clip_fill_surfaces() polygons_append(overhangs, std::move(upper_internal)); upper_internal = intersection(overhangs, lower_layer_internal_surfaces); // Apply new internal infill to regions. - for (LayerRegion *layerm : lower_layer->regions) { - if (layerm->region()->config.fill_density.value == 0) + for (LayerRegion *layerm : lower_layer->m_regions) { + if (layerm->region()->config().fill_density.value == 0) continue; SurfaceType internal_surface_types[] = { stInternal, stInternalVoid }; Polygons internal; @@ -1754,7 +1763,7 @@ void PrintObject::clip_fill_surfaces() layerm->export_region_fill_surfaces_to_svg_debug("6_clip_fill_surfaces"); #endif } - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); } } @@ -1762,11 +1771,11 @@ void PrintObject::discover_horizontal_shells() { BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()"; - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (int i = 0; i < int(this->layers.size()); ++ i) { - this->_print->throw_if_canceled(); - LayerRegion *layerm = this->layers[i]->regions[region_id]; - PrintRegionConfig ®ion_config = layerm->region()->config; + for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { + for (int i = 0; i < int(m_layers.size()); ++ i) { + m_print->throw_if_canceled(); + LayerRegion *layerm = m_layers[i]->regions()[region_id]; + const PrintRegionConfig ®ion_config = layerm->region()->config(); if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 && (i % region_config.solid_infill_every_layers) == 0) { // Insert a solid internal layer. Mark stInternal surfaces as stInternalSolid or stInternalBridge. @@ -1781,7 +1790,7 @@ void PrintObject::discover_horizontal_shells() continue; for (int idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) { - this->_print->throw_if_canceled(); + m_print->throw_if_canceled(); SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge; // Find slices of current type for current layer. // Use slices instead of fill_surfaces, because they also include the perimeter area, @@ -1810,11 +1819,11 @@ void PrintObject::discover_horizontal_shells() size_t solid_layers = (type == stTop) ? region_config.top_solid_layers.value : region_config.bottom_solid_layers.value; for (int n = (type == stTop) ? i-1 : i+1; std::abs(n - i) < solid_layers; (type == stTop) ? -- n : ++ n) { - if (n < 0 || n >= int(this->layers.size())) + if (n < 0 || n >= int(m_layers.size())) continue; // Slic3r::debugf " looking for neighbors on layer %d...\n", $n; // Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface. - LayerRegion *neighbor_layerm = this->layers[n]->regions[region_id]; + LayerRegion *neighbor_layerm = m_layers[n]->regions()[region_id]; // find intersection between neighbor and current layer's surfaces // intersections have contours and holes @@ -1936,9 +1945,9 @@ void PrintObject::discover_horizontal_shells() } // for each region #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - for (const Layer *layer : this->layers) { - const LayerRegion *layerm = layer->regions[region_id]; + for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { + for (const Layer *layer : m_layers) { + const LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells"); layerm->export_region_fill_surfaces_to_svg_debug("5_discover_horizontal_shells"); } // for each layer @@ -1952,24 +1961,24 @@ void PrintObject::discover_horizontal_shells() void PrintObject::combine_infill() { // Work on each region separately. - for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { - const PrintRegion *region = this->print()->regions[region_id]; - const int every = region->config.infill_every_layers.value; - if (every < 2 || region->config.fill_density == 0.) + for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { + const PrintRegion *region = this->print()->regions()[region_id]; + const int every = region->config().infill_every_layers.value; + if (every < 2 || region->config().fill_density == 0.) continue; // Limit the number of combined layers to the maximum height allowed by this regions' nozzle. //FIXME limit the layer height to max_layer_height double nozzle_diameter = std::min( - this->print()->config.nozzle_diameter.get_at(region->config.infill_extruder.value - 1), - this->print()->config.nozzle_diameter.get_at(region->config.solid_infill_extruder.value - 1)); + this->print()->config().nozzle_diameter.get_at(region->config().infill_extruder.value - 1), + this->print()->config().nozzle_diameter.get_at(region->config().solid_infill_extruder.value - 1)); // define the combinations - std::vector combine(this->layers.size(), 0); + std::vector combine(m_layers.size(), 0); { double current_height = 0.; size_t num_layers = 0; - for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++ layer_idx) { - this->_print->throw_if_canceled(); - const Layer *layer = this->layers[layer_idx]; + for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) { + m_print->throw_if_canceled(); + const Layer *layer = m_layers[layer_idx]; if (layer->id() == 0) // Skip first print layer (which may not be first layer in array because of raft). continue; @@ -1986,12 +1995,12 @@ void PrintObject::combine_infill() } // Append lower layers (if any) to uppermost layer. - combine[this->layers.size() - 1] = num_layers; + combine[m_layers.size() - 1] = num_layers; } // loop through layers to which we have assigned layers to combine - for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++ layer_idx) { - this->_print->throw_if_canceled(); + for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) { + m_print->throw_if_canceled(); size_t num_layers = combine[layer_idx]; if (num_layers <= 1) continue; @@ -1999,7 +2008,7 @@ void PrintObject::combine_infill() std::vector layerms; layerms.reserve(num_layers); for (size_t i = layer_idx + 1 - num_layers; i <= layer_idx; ++ i) - layerms.emplace_back(this->layers[i]->regions[region_id]); + layerms.emplace_back(m_layers[i]->regions()[region_id]); // We need to perform a multi-layer intersection, so let's split it in pairs. // Initialize the intersection with the candidates of the lowest layer. ExPolygons intersection = to_expolygons(layerms.front()->fill_surfaces.filter_by_type(stInternal)); @@ -2028,10 +2037,10 @@ void PrintObject::combine_infill() 0.5f * layerms.back()->flow(frPerimeter).scaled_width() + // Because fill areas for rectilinear and honeycomb are grown // later to overlap perimeters, we need to counteract that too. - ((region->config.fill_pattern == ipRectilinear || - region->config.fill_pattern == ipGrid || - region->config.fill_pattern == ipLine || - region->config.fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * + ((region->config().fill_pattern == ipRectilinear || + region->config().fill_pattern == ipGrid || + region->config().fill_pattern == ipLine || + region->config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * layerms.back()->flow(frSolidInfill).scaled_width(); for (ExPolygon &expoly : intersection) polygons_append(intersection_with_clearance, offset(expoly, clearance_offset)); diff --git a/xs/src/libslic3r/PrintRegion.cpp b/xs/src/libslic3r/PrintRegion.cpp index 2fcf691663..cdc8c532ae 100644 --- a/xs/src/libslic3r/PrintRegion.cpp +++ b/xs/src/libslic3r/PrintRegion.cpp @@ -12,48 +12,48 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir } else { // otherwise, get extrusion width from configuration // (might be an absolute value, or a percent value, or zero for auto) - if (first_layer && this->_print->config.first_layer_extrusion_width.value > 0) { - config_width = this->_print->config.first_layer_extrusion_width; + if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) { + config_width = m_print->config().first_layer_extrusion_width; } else if (role == frExternalPerimeter) { - config_width = this->config.external_perimeter_extrusion_width; + config_width = m_config.external_perimeter_extrusion_width; } else if (role == frPerimeter) { - config_width = this->config.perimeter_extrusion_width; + config_width = m_config.perimeter_extrusion_width; } else if (role == frInfill) { - config_width = this->config.infill_extrusion_width; + config_width = m_config.infill_extrusion_width; } else if (role == frSolidInfill) { - config_width = this->config.solid_infill_extrusion_width; + config_width = m_config.solid_infill_extrusion_width; } else if (role == frTopSolidInfill) { - config_width = this->config.top_infill_extrusion_width; + config_width = m_config.top_infill_extrusion_width; } else { CONFESS("Unknown role"); } } if (config_width.value == 0) { - config_width = object.config.extrusion_width; + config_width = object.config().extrusion_width; } // get the configured nozzle_diameter for the extruder associated // to the flow role requested size_t extruder = 0; // 1-based if (role == frPerimeter || role == frExternalPerimeter) { - extruder = this->config.perimeter_extruder; + extruder = m_config.perimeter_extruder; } else if (role == frInfill) { - extruder = this->config.infill_extruder; + extruder = m_config.infill_extruder; } else if (role == frSolidInfill || role == frTopSolidInfill) { - extruder = this->config.solid_infill_extruder; + extruder = m_config.solid_infill_extruder; } else { CONFESS("Unknown role $role"); } - double nozzle_diameter = this->_print->config.nozzle_diameter.get_at(extruder-1); + double nozzle_diameter = m_print->config().nozzle_diameter.get_at(extruder-1); - return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? (float)this->config.bridge_flow_ratio : 0.0); + return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? (float)m_config.bridge_flow_ratio : 0.0); } coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const { - return (print_config.nozzle_diameter.get_at(this->config.perimeter_extruder.value - 1) + - print_config.nozzle_diameter.get_at(this->config.infill_extruder.value - 1) + - print_config.nozzle_diameter.get_at(this->config.solid_infill_extruder.value - 1)) / 3.; + return (print_config.nozzle_diameter.get_at(m_config.perimeter_extruder.value - 1) + + print_config.nozzle_diameter.get_at(m_config.infill_extruder.value - 1) + + print_config.nozzle_diameter.get_at(m_config.solid_infill_extruder.value - 1)) / 3.; } } diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 0cecf0014b..d18f9d710a 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -142,8 +142,8 @@ void export_print_z_polygons_and_extrusions_to_svg( PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) : m_object (object), - m_print_config (&object->print()->config), - m_object_config (&object->config), + m_print_config (&object->print()->config()), + m_object_config (&object->config()), m_slicing_params (slicing_params), m_first_layer_flow (support_material_1st_layer_flow(object, float(slicing_params.first_print_layer_height))), m_support_material_flow (support_material_flow(object, float(slicing_params.layer_height))), @@ -164,7 +164,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object coordf_t external_perimeter_width = 0.; for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { if (! object->region_volumes[region_id].empty()) { - const PrintRegionConfig &config = object->print()->get_region(region_id)->config; + const PrintRegionConfig &config = object->print()->get_region(region_id)->config(); coordf_t width = config.external_perimeter_extrusion_width.get_abs_value(slicing_params.layer_height); if (width <= 0.) width = m_print_config->nozzle_diameter.get_at(config.perimeter_extruder-1); @@ -226,7 +226,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) coordf_t max_object_layer_height = 0.; for (size_t i = 0; i < object.layer_count(); ++ i) - max_object_layer_height = std::max(max_object_layer_height, object.layers[i]->height); + max_object_layer_height = std::max(max_object_layer_height, object.layers()[i]->height); // Layer instances will be allocated by std::deque and they will be kept until the end of this function call. // The layers will be referenced by various LayersPtr (of type std::vector) @@ -266,9 +266,9 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) layer_support_areas); #ifdef SLIC3R_DEBUG - for (size_t layer_id = 0; layer_id < object.layers.size(); ++ layer_id) + for (size_t layer_id = 0; layer_id < object.layers().size(); ++ layer_id) Slic3r::SVG::export_expolygons( - debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers[layer_id]->print_z), + debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers()[layer_id]->print_z), union_ex(layer_support_areas[layer_id], false)); #endif /* SLIC3R_DEBUG */ @@ -354,7 +354,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // Sort the layers lexicographically by a raising print_z and a decreasing height. std::sort(layers_sorted.begin(), layers_sorted.end(), MyLayersPtrCompare()); int layer_id = 0; - assert(object.support_layers.empty()); + assert(object.support_layers().empty()); for (int i = 0; i < int(layers_sorted.size());) { // Find the last layer with roughly the same print_z, find the minimum layer height of all. // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should. @@ -420,8 +420,8 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t { // 1) Count the new polygons first. size_t n_polygons_new = 0; - for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) { - const LayerRegion ®ion = *(*it_region); + for (const LayerRegion* pregion : layer.regions()) { + const LayerRegion ®ion = *pregion; const SurfaceCollection &slices = region.slices; for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) { const Surface &surface = *it; @@ -433,8 +433,8 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t // 2) Collect the new polygons. Polygons out; out.reserve(n_polygons_new); - for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) { - const LayerRegion ®ion = *(*it_region); + for (const LayerRegion *pregion : layer.regions()) { + const LayerRegion ®ion = *pregion; const SurfaceCollection &slices = region.slices; for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) { const Surface &surface = *it; @@ -658,9 +658,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ std::vector buildplate_covered; if (buildplate_only) { BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() - collecting regions covering the print bed."; - buildplate_covered.assign(object.layers.size(), Polygons()); - for (size_t layer_id = 1; layer_id < object.layers.size(); ++ layer_id) { - const Layer &lower_layer = *object.layers[layer_id-1]; + buildplate_covered.assign(object.layers().size(), Polygons()); + for (size_t layer_id = 1; layer_id < object.layers().size(); ++ layer_id) { + const Layer &lower_layer = *object.layers()[layer_id-1]; // Merge the new slices with the preceding slices. // Apply the safety offset to the newly added polygons, so they will connect // with the polygons collected before, @@ -686,7 +686,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ [this, &object, &buildplate_covered, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out](const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - const Layer &layer = *object.layers[layer_id]; + const Layer &layer = *object.layers()[layer_id]; // Detect overhangs and contact areas needed to support them. // Collect overhangs and contacts of all regions of this layer supported by the layer immediately below. @@ -703,13 +703,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ contact_polygons = offset(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN)); } else { // Generate overhang / contact_polygons for non-raft layers. - const Layer &lower_layer = *object.layers[layer_id-1]; - for (LayerRegion *layerm : layer.regions) { + const Layer &lower_layer = *object.layers()[layer_id-1]; + for (LayerRegion *layerm : layer.regions()) { // Extrusion width accounts for the roundings of the extrudates. // It is the maximum widh of the extrudate. float fw = float(layerm->flow(frExternalPerimeter).scaled_width()); float lower_layer_offset = - (layer_id < this->m_object_config->support_material_enforce_layers.value) ? + (layer_id < m_object_config->support_material_enforce_layers.value) ? // Enforce a full possible support, ignore the overhang angle. 0.f : (threshold_rad > 0. ? @@ -762,14 +762,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ } #endif /* SLIC3R_DEBUG */ - if (this->m_object_config->dont_support_bridges) { + if (m_object_config->dont_support_bridges) { // compute the area of bridging perimeters // Note: this is duplicate code from GCode.pm, we need to refactor if (true) { Polygons bridged_perimeters; { Flow bridge_flow = layerm->flow(frPerimeter, true); - coordf_t nozzle_diameter = m_print_config->nozzle_diameter.get_at(layerm->region()->config.perimeter_extruder-1); + coordf_t nozzle_diameter = m_print_config->nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder-1); Polygons lower_grown_slices = offset(lower_layer_polygons, 0.5f*float(scale_(nozzle_diameter)), SUPPORT_SURFACES_OFFSET_PARAMETERS); // Collect perimeters of this layer. @@ -894,8 +894,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ } else { // Interface layer will be synchronized with the object. assert(layer_id > 0); - new_layer.height = object.layers[layer_id - 1]->height; - new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers[layer_id - 2]->print_z; + new_layer.height = object.layers()[layer_id - 1]->height; + new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers()[layer_id - 2]->print_z; } } else { // Contact layer will be printed with a normal flow, but @@ -904,9 +904,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // In the future we may switch to a normal extrusion flow for the supported bridges. // Get the average nozzle diameter used on this layer. coordf_t nozzle_dmr = 0.; - for (const LayerRegion *region : layer.regions) + for (const LayerRegion *region : layer.regions()) nozzle_dmr += region->region()->nozzle_dmr_avg(*m_print_config); - nozzle_dmr /= coordf_t(layer.regions.size()); + nozzle_dmr /= coordf_t(layer.regions().size()); new_layer.print_z = layer.print_z - nozzle_dmr - m_object_config->support_material_contact_distance; new_layer.bottom_z = new_layer.print_z; new_layer.height = 0.; @@ -1048,10 +1048,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // top shapes so this can be done here layer_new.height = m_slicing_params.soluble_interface ? // Align the interface layer with the object's layer height. - object.layers[layer_id + 1]->height : + object.layers()[layer_id + 1]->height : // Place a bridge flow interface layer over the top surface. m_support_material_interface_flow.nozzle_diameter; - layer_new.print_z = m_slicing_params.soluble_interface ? object.layers[layer_id + 1]->print_z : + layer_new.print_z = m_slicing_params.soluble_interface ? object.layers()[layer_id + 1]->print_z : layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value; layer_new.bottom_z = layer.print_z; layer_new.idx_object_layer_below = layer_id; @@ -1062,16 +1062,16 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // Walk the top surfaces, snap the top of the new bottom surface to the closest top of the top surface, // so there will be no support surfaces generated with thickness lower than m_support_layer_height_min. for (size_t top_idx = size_t(std::max(0, contact_idx)); - top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min; + top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + m_support_layer_height_min; ++ top_idx) { - if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min) { + if (top_contacts[top_idx]->print_z > layer_new.print_z - m_support_layer_height_min) { // A top layer has been found, which is close to the new bottom layer. coordf_t diff = layer_new.print_z - top_contacts[top_idx]->print_z; - assert(std::abs(diff) <= this->m_support_layer_height_min); + assert(std::abs(diff) <= m_support_layer_height_min); if (diff > 0.) { // The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer. assert(diff < layer_new.height + EPSILON); - assert(layer_new.height - diff >= this->m_support_layer_height_min - EPSILON); + assert(layer_new.height - diff >= m_support_layer_height_min - EPSILON); layer_new.print_z = top_contacts[top_idx]->print_z; layer_new.height -= diff; } else { @@ -1093,7 +1093,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // Trim the already created base layers above the current layer intersecting with the new bottom contacts layer. touching = offset(touching, float(SCALED_EPSILON)); for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++ layer_id_above) { - const Layer &layer_above = *object.layers[layer_id_above]; + const Layer &layer_above = *object.layers()[layer_id_above]; if (layer_above.print_z > layer_new.print_z + EPSILON) break; if (! layer_support_areas[layer_id_above].empty()) { @@ -1320,7 +1320,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int // Verify that the extremes are separated by m_support_layer_height_min. for (size_t i = 1; i < extremes.size(); ++ i) { assert(extremes[i]->extreme_z() - extremes[i-1]->extreme_z() == 0. || - extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > this->m_support_layer_height_min - EPSILON); + extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > m_support_layer_height_min - EPSILON); assert(extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > 0. || extremes[i]->layer_type == extremes[i-1]->layer_type || (extremes[i]->layer_type == sltBottomContact && extremes[i - 1]->layer_type == sltTopContact)); @@ -1344,7 +1344,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int // This is a bottom of a synchronized (or soluble) top contact layer, its height has been decided in this->top_contact_layers(). assert(extr2->layer_type == sltTopContact); assert(extr2->bottom_z == m_slicing_params.first_print_layer_height); - assert(extr2->print_z >= m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON); + assert(extr2->print_z >= m_slicing_params.first_print_layer_height + m_support_layer_height_min - EPSILON); if (intermediate_layers.empty() || intermediate_layers.back()->print_z < m_slicing_params.first_print_layer_height) { MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); layer_new.bottom_z = 0.; @@ -1384,7 +1384,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int if (synchronize) { // Emit support layers synchronized with the object layers. // Find the first object layer, which has its print_z in this support Z range. - while (idx_layer_object < object.layers.size() && object.layers[idx_layer_object]->print_z < extr1z + EPSILON) + while (idx_layer_object < object.layers().size() && object.layers()[idx_layer_object]->print_z < extr1z + EPSILON) ++ idx_layer_object; if (idx_layer_object == 0 && extr1z == m_slicing_params.raft_interface_top_z) { // Insert one base support layer below the object. @@ -1395,11 +1395,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int intermediate_layers.push_back(&layer_new); } // Emit all intermediate support layers synchronized with object layers up to extr2z. - for (; idx_layer_object < object.layers.size() && object.layers[idx_layer_object]->print_z < extr2z + EPSILON; ++ idx_layer_object) { + for (; idx_layer_object < object.layers().size() && object.layers()[idx_layer_object]->print_z < extr2z + EPSILON; ++ idx_layer_object) { MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); - layer_new.print_z = object.layers[idx_layer_object]->print_z; - layer_new.height = object.layers[idx_layer_object]->height; - layer_new.bottom_z = (idx_layer_object > 0) ? object.layers[idx_layer_object - 1]->print_z : (layer_new.print_z - layer_new.height); + layer_new.print_z = object.layers()[idx_layer_object]->print_z; + layer_new.height = object.layers()[idx_layer_object]->height; + layer_new.bottom_z = (idx_layer_object > 0) ? object.layers()[idx_layer_object - 1]->print_z : (layer_new.print_z - layer_new.height); assert(intermediate_layers.empty() || intermediate_layers.back()->print_z < layer_new.print_z + EPSILON); intermediate_layers.push_back(&layer_new); } @@ -1409,10 +1409,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int assert(n_layers_extra > 0); coordf_t step = dist / coordf_t(n_layers_extra); if (extr1 != nullptr && extr1->layer_type == sltTopContact && - extr1->print_z + this->m_support_layer_height_min > extr1->bottom_z + step) { + extr1->print_z + m_support_layer_height_min > extr1->bottom_z + step) { // The bottom extreme is a bottom of a top surface. Ensure that the gap // between the 1st intermediate layer print_z and extr1->print_z is not too small. - assert(extr1->bottom_z + this->m_support_layer_height_min < extr1->print_z + EPSILON); + assert(extr1->bottom_z + m_support_layer_height_min < extr1->print_z + EPSILON); // Generate the first intermediate layer. MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); layer_new.bottom_z = extr1->bottom_z; @@ -1509,7 +1509,7 @@ void PrintObjectSupportMaterial::generate_base_layers( Polygons polygons_new; // Use the precomputed layer_support_areas. - idx_object_layer_above = std::max(0, idx_lower_or_equal(object.layers, idx_object_layer_above, + idx_object_layer_above = std::max(0, idx_lower_or_equal(object.layers(), idx_object_layer_above, [&layer_intermediate](const Layer *layer){ return layer->print_z <= layer_intermediate.print_z + EPSILON; })); polygons_new = layer_support_areas[idx_object_layer_above]; @@ -1644,24 +1644,24 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( // Find the overlapping object layers including the extra above / below gap. coordf_t z_threshold = support_layer.print_z - support_layer.height - gap_extra_below + EPSILON; idx_object_layer_overlapping = idx_higher_or_equal( - object.layers, idx_object_layer_overlapping, + object.layers(), idx_object_layer_overlapping, [z_threshold](const Layer *layer){ return layer->print_z >= z_threshold; }); // Collect all the object layers intersecting with this layer. Polygons polygons_trimming; size_t i = idx_object_layer_overlapping; - for (; i < object.layers.size(); ++ i) { - const Layer &object_layer = *object.layers[i]; + for (; i < object.layers().size(); ++ i) { + const Layer &object_layer = *object.layers()[i]; if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON) break; polygons_append(polygons_trimming, (Polygons)object_layer.slices); } - if (! this->m_slicing_params.soluble_interface) { + if (! m_slicing_params.soluble_interface) { // Collect all bottom surfaces, which will be extruded with a bridging flow. - for (; i < object.layers.size(); ++ i) { - const Layer &object_layer = *object.layers[i]; + for (; i < object.layers().size(); ++ i) { + const Layer &object_layer = *object.layers()[i]; bool some_region_overlaps = false; - for (LayerRegion* region : object_layer.regions) { - coordf_t nozzle_dmr = region->region()->nozzle_dmr_avg(*this->m_print_config); + for (LayerRegion* region : object_layer.regions()) { + coordf_t nozzle_dmr = region->region()->nozzle_dmr_avg(*m_print_config); if (object_layer.print_z - nozzle_dmr > support_layer.print_z + gap_extra_above - EPSILON) break; some_region_overlaps = true; @@ -1764,7 +1764,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf // Expand the bases of the support columns in the 1st layer. columns_base->polygons = diff( offset(columns_base->polygons, inflate_factor_1st_layer), - offset(m_object->layers.front()->slices.expolygons, scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS)); + offset(m_object->layers().front()->slices.expolygons, scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (contacts != nullptr) columns_base->polygons = diff(columns_base->polygons, interface_polygons); } @@ -1909,8 +1909,8 @@ struct MyLayerExtruded else *m_polygons_to_extrude = std::move(polygons); } - Polygons& polygons_to_extrude() { return (this->m_polygons_to_extrude == nullptr) ? layer->polygons : *this->m_polygons_to_extrude; } - const Polygons& polygons_to_extrude() const { return (this->m_polygons_to_extrude == nullptr) ? layer->polygons : *this->m_polygons_to_extrude; } + Polygons& polygons_to_extrude() { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } + const Polygons& polygons_to_extrude() const { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } bool could_merge(const MyLayerExtruded &other) const { return ! this->empty() && ! other.empty() && @@ -1923,21 +1923,21 @@ struct MyLayerExtruded assert(this->could_merge(other)); // 1) Merge the rest polygons to extrude, if there are any. if (other.m_polygons_to_extrude != nullptr) { - if (this->m_polygons_to_extrude == nullptr) { + if (m_polygons_to_extrude == nullptr) { // This layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet). assert(this->extrusions.empty()); - this->m_polygons_to_extrude = new Polygons(this->layer->polygons); + m_polygons_to_extrude = new Polygons(this->layer->polygons); } - Slic3r::polygons_append(*this->m_polygons_to_extrude, std::move(*other.m_polygons_to_extrude)); - *this->m_polygons_to_extrude = union_(*this->m_polygons_to_extrude, true); + Slic3r::polygons_append(*m_polygons_to_extrude, std::move(*other.m_polygons_to_extrude)); + *m_polygons_to_extrude = union_(*m_polygons_to_extrude, true); delete other.m_polygons_to_extrude; other.m_polygons_to_extrude = nullptr; - } else if (this->m_polygons_to_extrude != nullptr) { + } else if (m_polygons_to_extrude != nullptr) { assert(other.m_polygons_to_extrude == nullptr); // The other layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet). assert(other.extrusions.empty()); - Slic3r::polygons_append(*this->m_polygons_to_extrude, other.layer->polygons); - *this->m_polygons_to_extrude = union_(*this->m_polygons_to_extrude, true); + Slic3r::polygons_append(*m_polygons_to_extrude, other.layer->polygons); + *m_polygons_to_extrude = union_(*m_polygons_to_extrude, true); } // 2) Merge the extrusions. this->extrusions.insert(this->extrusions.end(), other.extrusions.begin(), other.extrusions.end()); @@ -2535,7 +2535,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { assert(support_layer_id < raft_layers.size()); - SupportLayer &support_layer = *object.support_layers[support_layer_id]; + SupportLayer &support_layer = *object.support_layers()[support_layer_id]; assert(support_layer.support_fills.entities.empty()); MyLayer &raft_layer = *raft_layers[support_layer_id]; @@ -2631,9 +2631,9 @@ void PrintObjectSupportMaterial::generate_toolpaths( MyLayerExtruded interface_layer; std::vector overlaps; }; - std::vector layer_caches(object.support_layers.size(), LayerCache()); + std::vector layer_caches(object.support_layers().size(), LayerCache()); - tbb::parallel_for(tbb::blocked_range(n_raft_layers, object.support_layers.size()), + tbb::parallel_for(tbb::blocked_range(n_raft_layers, object.support_layers().size()), [this, &object, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &layer_caches, &loop_interface_processor, infill_pattern, &bbox_object, support_density, interface_density, interface_angle, &angles, link_max_length_factor, with_sheath] (const tbb::blocked_range& range) { @@ -2648,7 +2648,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( filler_support->set_bounding_box(bbox_object); for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { - SupportLayer &support_layer = *object.support_layers[support_layer_id]; + SupportLayer &support_layer = *object.support_layers()[support_layer_id]; LayerCache &layer_cache = layer_caches[support_layer_id]; // Find polygons with the same print_z. @@ -2844,11 +2844,11 @@ void PrintObjectSupportMaterial::generate_toolpaths( }); // Now modulate the support layer height in parallel. - tbb::parallel_for(tbb::blocked_range(n_raft_layers, object.support_layers.size()), + tbb::parallel_for(tbb::blocked_range(n_raft_layers, object.support_layers().size()), [this, &object, &layer_caches] (const tbb::blocked_range& range) { for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { - SupportLayer &support_layer = *object.support_layers[support_layer_id]; + SupportLayer &support_layer = *object.support_layers()[support_layer_id]; LayerCache &layer_cache = layer_caches[support_layer_id]; for (LayerCacheItem &layer_cache_item : layer_cache.overlaps) { modulate_extrusion_by_overlapping_layers(layer_cache_item.layer_extruded->extrusions, *layer_cache_item.layer_extruded->layer, layer_cache_item.overlapping); diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 968763446b..e20d7c69fb 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -208,7 +208,7 @@ private: // Produce the actual G-code. void generate_toolpaths( - const PrintObject &object, + const PrintObject &object, const MyLayersPtr &raft_layers, const MyLayersPtr &bottom_contacts, const MyLayersPtr &top_contacts, diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 03154c47eb..356d27b961 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1670,30 +1670,30 @@ void _3DScene::_load_print_toolpaths( // The skirt and brim steps should be marked as done, so their paths are valid. assert(print->is_step_done(psSkirt) && print->is_step_done(psBrim)); - if (!print->has_skirt() && print->config.brim_width.value == 0) + if (!print->has_skirt() && print->config().brim_width.value == 0) return; const float color[] = { 0.5f, 1.0f, 0.5f, 1.f }; // greenish // number of skirt layers size_t total_layer_count = 0; - for (const PrintObject *print_object : print->objects) + for (const PrintObject *print_object : print->objects()) total_layer_count = std::max(total_layer_count, print_object->total_layer_count()); size_t skirt_height = print->has_infinite_skirt() ? total_layer_count : - std::min(print->config.skirt_height.value, total_layer_count); - if (skirt_height == 0 && print->config.brim_width.value > 0) + std::min(print->config().skirt_height.value, total_layer_count); + if (skirt_height == 0 && print->config().brim_width.value > 0) skirt_height = 1; // get first skirt_height layers (maybe this should be moved to a PrintObject method?) - const PrintObject *object0 = print->objects.front(); + const PrintObject *object0 = print->objects().front(); std::vector print_zs; print_zs.reserve(skirt_height * 2); - for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++ i) - print_zs.push_back(float(object0->layers[i]->print_z)); + for (size_t i = 0; i < std::min(skirt_height, object0->layers().size()); ++ i) + print_zs.push_back(float(object0->layers()[i]->print_z)); //FIXME why there are support layers? - for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++ i) - print_zs.push_back(float(object0->support_layers[i]->print_z)); + for (size_t i = 0; i < std::min(skirt_height, object0->support_layers().size()); ++ i) + print_zs.push_back(float(object0->support_layers()[i]->print_z)); sort_remove_duplicates(print_zs); if (print_zs.size() > skirt_height) print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); @@ -1705,8 +1705,8 @@ void _3DScene::_load_print_toolpaths( volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size()); volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size()); if (i == 0) - extrusionentity_to_verts(print->brim, print_zs[i], Point(0, 0), volume); - extrusionentity_to_verts(print->skirt, print_zs[i], Point(0, 0), volume); + extrusionentity_to_verts(print->brim(), print_zs[i], Point(0, 0), volume); + extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), volume); } volume.bounding_box = volume.indexed_vertex_array.bounding_box(); volume.indexed_vertex_array.finalize_geometry(use_VBOs); @@ -1753,10 +1753,10 @@ void _3DScene::_load_print_object_toolpaths( ctxt.shifted_copies = &print_object->_shifted_copies; // order layers by print_z - ctxt.layers.reserve(print_object->layers.size() + print_object->support_layers.size()); - for (const Layer *layer : print_object->layers) + ctxt.layers.reserve(print_object->layers().size() + print_object->support_layers().size()); + for (const Layer *layer : print_object->layers()) ctxt.layers.push_back(layer); - for (const Layer *layer : print_object->support_layers) + for (const Layer *layer : print_object->support_layers()) ctxt.layers.push_back(layer); std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; }); @@ -1803,10 +1803,10 @@ void _3DScene::_load_print_object_toolpaths( } } for (const Point ©: *ctxt.shifted_copies) { - for (const LayerRegion *layerm : layer->regions) { + for (const LayerRegion *layerm : layer->regions()) { if (ctxt.has_perimeters) extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, - *vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]); + *vols[ctxt.volume_idx(layerm->region()->config().perimeter_extruder.value, 0)]); if (ctxt.has_infill) { for (const ExtrusionEntity *ee : layerm->fills.entities) { // fill represents infill extrusions of a single island. @@ -1815,8 +1815,8 @@ void _3DScene::_load_print_object_toolpaths( extrusionentity_to_verts(*fill, float(layer->print_z), copy, *vols[ctxt.volume_idx( is_solid_infill(fill->entities.front()->role()) ? - layerm->region()->config.solid_infill_extruder : - layerm->region()->config.infill_extruder, + layerm->region()->config().solid_infill_extruder : + layerm->region()->config().infill_extruder, 1)]); } } @@ -1828,8 +1828,8 @@ void _3DScene::_load_print_object_toolpaths( extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, *vols[ctxt.volume_idx( (extrusion_entity->role() == erSupportMaterial) ? - support_layer->object()->config.support_material_extruder : - support_layer->object()->config.support_material_interface_extruder, + support_layer->object()->config().support_material_extruder : + support_layer->object()->config().support_material_interface_extruder, 2)]); } } @@ -1877,7 +1877,7 @@ void _3DScene::_load_wipe_tower_toolpaths( const std::vector &tool_colors_str, bool use_VBOs) { - if (print->m_wipe_tower_tool_changes.empty()) + if (print->wipe_tower_data().tool_changes.empty()) return; std::vector tool_colors = parse_colors(tool_colors_str); @@ -1902,8 +1902,8 @@ void _3DScene::_load_wipe_tower_toolpaths( const std::vector& tool_change(size_t idx) { return priming.empty() ? - ((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) : - ((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]); + ((idx == print->wipe_tower_data().tool_changes.size()) ? final : print->wipe_tower_data().tool_changes[idx]) : + ((idx == 0) ? priming : (idx == print->wipe_tower_data().tool_changes.size() + 1) ? final : print->wipe_tower_data().tool_changes[idx - 1]); } std::vector priming; std::vector final; @@ -1911,15 +1911,15 @@ void _3DScene::_load_wipe_tower_toolpaths( ctxt.print = print; ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; - if (print->m_wipe_tower_priming) - ctxt.priming.emplace_back(*print->m_wipe_tower_priming.get()); - if (print->m_wipe_tower_final_purge) - ctxt.final.emplace_back(*print->m_wipe_tower_final_purge.get()); + if (print->wipe_tower_data().priming != nullptr) + ctxt.priming.emplace_back(*print->wipe_tower_data().priming); + if (print->wipe_tower_data().final_purge) + ctxt.final.emplace_back(*print->wipe_tower_data().final_purge); BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start"; //FIXME Improve the heuristics for a grain size. - size_t n_items = print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1); + size_t n_items = print->wipe_tower_data().tool_changes.size() + (ctxt.priming.empty() ? 0 : 1); size_t grain_size = std::max(n_items / 128, size_t(1)); tbb::spin_mutex new_volume_mutex; auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* { @@ -2554,13 +2554,13 @@ void _3DScene::_load_shells(const Print& print, GLVolumeCollection& volumes, boo size_t initial_volumes_count = volumes.volumes.size(); s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Shell, 0, (unsigned int)initial_volumes_count); - if (print.objects.empty()) + if (print.objects().empty()) // nothing to render, return return; // adds objects' volumes unsigned int object_id = 0; - for (PrintObject* obj : print.objects) + for (PrintObject* obj : print.objects()) { ModelObject* model_obj = obj->model_object(); @@ -2579,8 +2579,8 @@ void _3DScene::_load_shells(const Print& print, GLVolumeCollection& volumes, boo } // adds wipe tower's volume - coordf_t max_z = print.objects[0]->model_object()->get_model()->bounding_box().max.z; - const PrintConfig& config = print.config; + coordf_t max_z = print.objects().front()->model_object()->get_model()->bounding_box().max.z; + const PrintConfig& config = print.config(); unsigned int extruders_count = config.nozzle_diameter.size(); if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, config.wipe_tower_per_color_wipe * (extruders_count - 1), max_z, use_VBOs); diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 16e2d84aa3..94c9d707d8 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -31,7 +31,7 @@ _constant() // owned by Print, no constructor/destructor Ref config() - %code%{ RETVAL = &THIS->config; %}; + %code%{ RETVAL = &THIS->config(); %}; Ref print(); Clone flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, PrintObject* object) @@ -42,12 +42,12 @@ _constant() // owned by Print, no constructor/destructor int region_count() - %code%{ RETVAL = THIS->print()->regions.size(); %}; + %code%{ RETVAL = THIS->print()->regions().size(); %}; Ref print(); Ref model_object(); Ref config() - %code%{ RETVAL = &THIS->config; %}; + %code%{ RETVAL = &THIS->config(); %}; Points copies(); t_layer_height_ranges layer_height_ranges() %code%{ RETVAL = THIS->layer_height_ranges; %}; @@ -95,32 +95,41 @@ _constant() ~Print(); Ref config() - %code%{ RETVAL = &THIS->config; %}; + %code%{ RETVAL = &THIS->config(); %}; Ref placeholder_parser() - %code%{ RETVAL = &THIS->placeholder_parser; %}; + %code%{ RETVAL = &THIS->placeholder_parser(); %}; Ref skirt() - %code%{ RETVAL = &THIS->skirt; %}; + %code%{ RETVAL = const_cast(&THIS->skirt()); %}; Ref brim() - %code%{ RETVAL = &THIS->brim; %}; + %code%{ RETVAL = const_cast(&THIS->brim()); %}; std::string estimated_print_time() - %code%{ RETVAL = THIS->estimated_print_time; %}; + %code%{ RETVAL = THIS->print_statistics().estimated_print_time; %}; + double total_used_filament() + %code%{ RETVAL = THIS->print_statistics().total_used_filament; %}; + double total_extruded_volume() + %code%{ RETVAL = THIS->print_statistics().total_extruded_volume; %}; + double total_weight() + %code%{ RETVAL = THIS->print_statistics().total_weight; %}; + double total_cost() + %code%{ RETVAL = THIS->print_statistics().total_cost; %}; PrintObjectPtrs* objects() - %code%{ RETVAL = &THIS->objects; %}; + %code%{ RETVAL = const_cast(&THIS->objects()); %}; void clear_objects(); - Ref get_object(int idx); + Ref get_object(int idx) + %code%{ RETVAL = THIS->objects()[idx]; %}; void delete_object(int idx); void reload_object(int idx); bool reload_model_instances(); size_t object_count() - %code%{ RETVAL = THIS->objects.size(); %}; + %code%{ RETVAL = THIS->objects().size(); %}; PrintRegionPtrs* regions() - %code%{ RETVAL = &THIS->regions; %}; - Ref get_region(int idx); - Ref add_region(); + %code%{ RETVAL = const_cast(&THIS->regions()); %}; + Ref get_region(int idx) + %code%{ RETVAL = THIS->regions()[idx]; %}; size_t region_count() - %code%{ RETVAL = THIS->regions.size(); %}; + %code%{ RETVAL = THIS->regions().size(); %}; bool step_done(PrintStep step) %code%{ RETVAL = THIS->is_step_done(step); %}; @@ -130,7 +139,7 @@ _constant() SV* filament_stats() %code%{ HV* hv = newHV(); - for (std::map::const_iterator it = THIS->filament_stats.begin(); it != THIS->filament_stats.end(); ++it) { + for (std::map::const_iterator it = THIS->print_statistics().filament_stats.begin(); it != THIS->print_statistics().filament_stats.end(); ++it) { // stringify extruder_id std::ostringstream ss; ss << it->first; @@ -194,47 +203,4 @@ _constant() } %}; -%{ - -double -Print::total_used_filament(...) - CODE: - if (items > 1) { - THIS->total_used_filament = (double)SvNV(ST(1)); - } - RETVAL = THIS->total_used_filament; - OUTPUT: - RETVAL - -double -Print::total_extruded_volume(...) - CODE: - if (items > 1) { - THIS->total_extruded_volume = (double)SvNV(ST(1)); - } - RETVAL = THIS->total_extruded_volume; - OUTPUT: - RETVAL - - -double -Print::total_weight(...) - CODE: - if (items > 1) { - THIS->total_weight = (double)SvNV(ST(1)); - } - RETVAL = THIS->total_weight; - OUTPUT: - RETVAL - -double -Print::total_cost(...) - CODE: - if (items > 1) { - THIS->total_cost = (double)SvNV(ST(1)); - } - RETVAL = THIS->total_cost; - OUTPUT: - RETVAL -%} }; From bb70ad609025a259f35f4f21e2fc9587c7a3fc2d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 12 Sep 2018 13:17:47 +0200 Subject: [PATCH 09/36] Removed Strings.hpp, replaced with wxString Fixed UTF8 rendering of status messages. --- lib/Slic3r/GUI/MainFrame.pm | 1 + xs/CMakeLists.txt | 3 +- xs/src/libslic3r/Print.cpp | 1 - xs/src/slic3r/AppController.cpp | 3 +- xs/src/slic3r/AppController.hpp | 24 +++++---- xs/src/slic3r/AppControllerWx.cpp | 34 ++++++------ xs/src/slic3r/GUI/GLCanvas3D.cpp | 1 + xs/src/slic3r/GUI/GUI.cpp | 9 ++++ xs/src/slic3r/GUI/GUI.hpp | 3 ++ xs/src/slic3r/GUI/ProgressStatusBar.cpp | 2 +- xs/src/slic3r/ProgressIndicator.hpp | 71 ------------------------- xs/src/slic3r/Strings.hpp | 10 ---- xs/xsp/GUI.xsp | 3 ++ xs/xsp/ProgressStatusBar.xsp | 2 +- 14 files changed, 51 insertions(+), 116 deletions(-) delete mode 100644 xs/src/slic3r/ProgressIndicator.hpp delete mode 100644 xs/src/slic3r/Strings.hpp diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 6ecfa68603..59c39dc8b8 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -78,6 +78,7 @@ sub new { $self->{statusbar}->Embed; $self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases")); # Make the global status bar and its progress indicator available in C++ + Slic3r::GUI::set_progress_status_bar($self->{statusbar}); $appController->set_global_progress_indicator($self->{statusbar}); $appController->set_model($self->{plater}->{model}); diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 755856dde2..272a1ad71a 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -263,6 +263,7 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/UpdateDialogs.hpp ${LIBDIR}/slic3r/GUI/FirmwareDialog.cpp ${LIBDIR}/slic3r/GUI/FirmwareDialog.hpp + ${LIBDIR}/slic3r/GUI/ProgressIndicator.hpp ${LIBDIR}/slic3r/GUI/ProgressStatusBar.hpp ${LIBDIR}/slic3r/GUI/ProgressStatusBar.cpp ${LIBDIR}/slic3r/Utils/Http.cpp @@ -285,11 +286,9 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/Utils/Time.hpp ${LIBDIR}/slic3r/Utils/HexFile.cpp ${LIBDIR}/slic3r/Utils/HexFile.hpp - ${LIBDIR}/slic3r/ProgressIndicator.hpp ${LIBDIR}/slic3r/AppController.hpp ${LIBDIR}/slic3r/AppController.cpp ${LIBDIR}/slic3r/AppControllerWx.cpp - ${LIBDIR}/slic3r/Strings.hpp ) add_library(admesh STATIC diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index ae02216dfc..c8a50a6bb5 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -14,7 +14,6 @@ #include #include -//#include "slic3r/ProgressIndicator.hpp" #include "PrintExport.hpp" //! macro used to mark string used at localization, diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index faecf9ecd3..5e259c3672 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -409,7 +408,7 @@ const PrintConfig &PrintController::config() const #endif void ProgressIndicator::message_fmt( - const string &fmtstr, ...) { + const wxString &fmtstr, ...) { std::stringstream ss; va_list args; va_start(args, fmtstr); diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp index b9af3bbd97..740f9a6301 100644 --- a/xs/src/slic3r/AppController.hpp +++ b/xs/src/slic3r/AppController.hpp @@ -7,7 +7,9 @@ #include #include -#include "ProgressIndicator.hpp" +#include "GUI/ProgressIndicator.hpp" + +#include namespace Slic3r { @@ -46,7 +48,7 @@ public: AppControllerBoilerplate(); ~AppControllerBoilerplate(); - using Path = string; + using Path = wxString; using PathList = std::vector; /// Common runtime issue types @@ -67,20 +69,20 @@ public: * @return Returns a list of paths choosed by the user. */ PathList query_destination_paths( - const string& title, + const wxString& title, const std::string& extensions) const; /** * @brief Same as query_destination_paths but works for directories only. */ PathList query_destination_dirs( - const string& title) const; + const wxString& title) const; /** * @brief Same as query_destination_paths but returns only one path. */ Path query_destination_path( - const string& title, + const wxString& title, const std::string& extensions, const std::string& hint = "") const; @@ -95,11 +97,11 @@ public: * title. */ bool report_issue(IssueType issuetype, - const string& description, - const string& brief); + const wxString& description, + const wxString& brief); bool report_issue(IssueType issuetype, - const string& description); + const wxString& description); /** * @brief Return the global progress indicator for the current controller. @@ -150,12 +152,12 @@ protected: */ ProgresIndicatorPtr create_progress_indicator( unsigned statenum, - const string& title, - const string& firstmsg) const; + const wxString& title, + const wxString& firstmsg) const; ProgresIndicatorPtr create_progress_indicator( unsigned statenum, - const string& title) const; + const wxString& title) const; // This is a global progress indicator placeholder. In the Slic3r UI it can // contain the progress indicator on the statusbar. diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp index 39192e7d37..bd2859fceb 100644 --- a/xs/src/slic3r/AppControllerWx.cpp +++ b/xs/src/slic3r/AppControllerWx.cpp @@ -33,7 +33,7 @@ void AppControllerBoilerplate::process_events() AppControllerBoilerplate::PathList AppControllerBoilerplate::query_destination_paths( - const string &title, + const wxString &title, const std::string &extensions) const { @@ -53,7 +53,7 @@ AppControllerBoilerplate::query_destination_paths( AppControllerBoilerplate::Path AppControllerBoilerplate::query_destination_path( - const string &title, + const wxString &title, const std::string &extensions, const std::string& hint) const { @@ -72,8 +72,8 @@ AppControllerBoilerplate::query_destination_path( } bool AppControllerBoilerplate::report_issue(IssueType issuetype, - const string &description, - const string &brief) + const wxString &description, + const wxString &brief) { auto icon = wxICON_INFORMATION; auto style = wxOK|wxCENTRE; @@ -91,9 +91,9 @@ bool AppControllerBoilerplate::report_issue(IssueType issuetype, bool AppControllerBoilerplate::report_issue( AppControllerBoilerplate::IssueType issuetype, - const string &description) + const wxString &description) { - return report_issue(issuetype, description, string()); + return report_issue(issuetype, description, wxString()); } wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent); @@ -137,8 +137,8 @@ public: /// Get the mode of parallel operation. inline bool asynch() const { return is_asynch_; } - inline GuiProgressIndicator(int range, const string& title, - const string& firstmsg) : + inline GuiProgressIndicator(int range, const wxString& title, + const wxString& firstmsg) : gauge_(title, firstmsg, range, wxTheApp->GetTopWindow(), wxPD_APP_MODAL | wxPD_AUTO_HIDE), message_(firstmsg), @@ -171,18 +171,18 @@ public: } else _state(st); } - virtual void message(const string & msg) override { + virtual void message(const wxString & msg) override { message_ = msg; } - virtual void messageFmt(const string& fmt, ...) { + virtual void messageFmt(const wxString& fmt, ...) { va_list arglist; va_start(arglist, fmt); message_ = wxString::Format(wxString(fmt), arglist); va_end(arglist); } - virtual void title(const string & title) override { + virtual void title(const wxString & title) override { title_ = title; } }; @@ -190,7 +190,7 @@ public: AppControllerBoilerplate::ProgresIndicatorPtr AppControllerBoilerplate::create_progress_indicator( - unsigned statenum, const string& title, const string& firstmsg) const + unsigned statenum, const wxString& title, const wxString& firstmsg) const { auto pri = std::make_shared(statenum, title, firstmsg); @@ -204,9 +204,9 @@ AppControllerBoilerplate::create_progress_indicator( AppControllerBoilerplate::ProgresIndicatorPtr AppControllerBoilerplate::create_progress_indicator(unsigned statenum, - const string &title) const + const wxString &title) const { - return create_progress_indicator(statenum, title, string()); + return create_progress_indicator(statenum, title, wxString()); } namespace { @@ -271,18 +271,18 @@ public: } } - virtual void message(const string & msg) override { + virtual void message(const wxString & msg) override { message_ = msg; } - virtual void message_fmt(const string& fmt, ...) override { + virtual void message_fmt(const wxString& fmt, ...) override { va_list arglist; va_start(arglist, fmt); message_ = wxString::Format(fmt, arglist); va_end(arglist); } - virtual void title(const string & /*title*/) override {} + virtual void title(const wxString & /*title*/) override {} virtual void on_cancel(CancelFn fn) override { sbar_->set_cancel_callback(fn); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index eebaffc2a4..2aaa4aed9c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -20,6 +20,7 @@ #include #include +// Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx. #include "../../libslic3r/Print.hpp" #include diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index dda2cb9530..e5ab22e8eb 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -48,6 +48,7 @@ #include "AboutDialog.hpp" #include "AppConfig.hpp" #include "ConfigSnapshotDialog.hpp" +#include "ProgressStatusBar.hpp" #include "Utils.hpp" #include "MsgDialog.hpp" #include "ConfigWizard.hpp" @@ -114,6 +115,7 @@ void break_to_debugger() // Passing the wxWidgets GUI classes instantiated by the Perl part to C++. wxApp *g_wxApp = nullptr; wxFrame *g_wxMainFrame = nullptr; +ProgressStatusBar *g_progress_status_bar = nullptr; wxNotebook *g_wxTabPanel = nullptr; wxPanel *g_wxPlater = nullptr; AppConfig *g_AppConfig = nullptr; @@ -207,6 +209,13 @@ void set_main_frame(wxFrame *main_frame) wxFrame* get_main_frame() { return g_wxMainFrame; } +void set_progress_status_bar(ProgressStatusBar *prsb) +{ + g_progress_status_bar = prsb; +} + +ProgressStatusBar* get_progress_status_bar() { return g_progress_status_bar; } + void set_tab_panel(wxNotebook *tab_panel) { g_wxTabPanel = tab_panel; diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 1f5805bb64..7105b8b4e4 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -33,6 +33,7 @@ namespace Slic3r { class PresetBundle; class PresetCollection; class Print; +class ProgressStatusBar; class AppConfig; class PresetUpdater; class DynamicPrintConfig; @@ -99,6 +100,7 @@ void break_to_debugger(); // Passing the wxWidgets GUI classes instantiated by the Perl part to C++. void set_wxapp(wxApp *app); void set_main_frame(wxFrame *main_frame); +void set_progress_status_bar(ProgressStatusBar *prsb); void set_tab_panel(wxNotebook *tab_panel); void set_plater(wxPanel *plater); void set_app_config(AppConfig *app_config); @@ -123,6 +125,7 @@ AppConfig* get_app_config(); wxApp* get_app(); PresetBundle* get_preset_bundle(); wxFrame* get_main_frame(); +ProgressStatusBar* get_progress_status_bar(); wxNotebook * get_tab_panel(); wxNotebook* get_tab_panel(); diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.cpp b/xs/src/slic3r/GUI/ProgressStatusBar.cpp index 224ad8af52..3c2bdee6a0 100644 --- a/xs/src/slic3r/GUI/ProgressStatusBar.cpp +++ b/xs/src/slic3r/GUI/ProgressStatusBar.cpp @@ -135,7 +135,7 @@ void ProgressStatusBar::embed(wxFrame *frame) void ProgressStatusBar::set_status_text(const std::string& txt) { - self->SetStatusText(txt); + self->SetStatusText(wxString::FromUTF8(txt.c_str())); } void ProgressStatusBar::show_cancel_button() diff --git a/xs/src/slic3r/ProgressIndicator.hpp b/xs/src/slic3r/ProgressIndicator.hpp deleted file mode 100644 index 4a39e84abc..0000000000 --- a/xs/src/slic3r/ProgressIndicator.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef IPROGRESSINDICATOR_HPP -#define IPROGRESSINDICATOR_HPP - -#include -#include -#include "Strings.hpp" - -namespace Slic3r { - -/** - * @brief Generic progress indication interface. - */ -class ProgressIndicator { -public: - using CancelFn = std::function; // Cancel function signature. - -private: - float state_ = .0f, max_ = 1.f, step_; - CancelFn cancelfunc_ = [](){}; - -public: - - inline virtual ~ProgressIndicator() {} - - /// Get the maximum of the progress range. - float max() const { return max_; } - - /// Get the current progress state - float state() const { return state_; } - - /// Set the maximum of the progress range - virtual void max(float maxval) { max_ = maxval; } - - /// Set the current state of the progress. - virtual void state(float val) { state_ = val; } - - /** - * @brief Number of states int the progress. Can be used instead of giving a - * maximum value. - */ - virtual void states(unsigned statenum) { - step_ = max_ / statenum; - } - - /// Message shown on the next status update. - virtual void message(const string&) = 0; - - /// Title of the operation. - virtual void title(const string&) = 0; - - /// Formatted message for the next status update. Works just like sprintf. - virtual void message_fmt(const string& fmt, ...); - - /// Set up a cancel callback for the operation if feasible. - virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; } - - /** - * Explicitly shut down the progress indicator and call the associated - * callback. - */ - virtual void cancel() { cancelfunc_(); } - - /// Convenience function to call message and status update in one function. - void update(float st, const string& msg) { - message(msg); state(st); - } -}; - -} - -#endif // IPROGRESSINDICATOR_HPP diff --git a/xs/src/slic3r/Strings.hpp b/xs/src/slic3r/Strings.hpp deleted file mode 100644 index b267fe0641..0000000000 --- a/xs/src/slic3r/Strings.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef STRINGS_HPP -#define STRINGS_HPP - -#include "GUI/GUI.hpp" - -namespace Slic3r { -using string = wxString; -} - -#endif // STRINGS_HPP diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index f8769d3585..3aee4035e8 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -35,6 +35,9 @@ bool is_windows10() void set_wxapp(SV *ui) %code%{ Slic3r::GUI::set_wxapp((wxApp*)wxPli_sv_2_object(aTHX_ ui, "Wx::App")); %}; +void set_progress_status_bar(ProgressStatusBar *prs) + %code%{ Slic3r::GUI::set_progress_status_bar(prs); %}; + void set_main_frame(SV *ui) %code%{ Slic3r::GUI::set_main_frame((wxFrame*)wxPli_sv_2_object(aTHX_ ui, "Wx::Frame")); %}; diff --git a/xs/xsp/ProgressStatusBar.xsp b/xs/xsp/ProgressStatusBar.xsp index f59895581f..6f41b2d2b3 100644 --- a/xs/xsp/ProgressStatusBar.xsp +++ b/xs/xsp/ProgressStatusBar.xsp @@ -36,7 +36,7 @@ void Embed() %code%{ THIS->embed(); %}; - void SetStatusText(std::string txt) + void SetStatusText(const char *txt) %code%{ THIS->set_status_text(txt); %}; void ShowCancelButton() From 9d9e4a0f7b2d9d3440c2a05bc65b2cab707d148a Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 14 Sep 2018 09:28:00 +0200 Subject: [PATCH 10/36] WIP: Background processing. --- lib/Slic3r/GUI/Plater.pm | 29 +++++-- xs/CMakeLists.txt | 12 +-- xs/src/libslic3r/GCode.cpp | 18 ++-- xs/src/libslic3r/GCodeTimeEstimator.cpp | 4 +- xs/src/libslic3r/Model.cpp | 1 + xs/src/libslic3r/Model.hpp | 1 + xs/src/libslic3r/Print.cpp | 23 ++++-- xs/src/libslic3r/Print.hpp | 19 +++-- xs/src/libslic3r/PrintObject.cpp | 13 ++- xs/src/libslic3r/Utils.hpp | 8 ++ xs/src/libslic3r/utils.cpp | 82 +++++++++++++++++++ .../slic3r/GUI/BackgroundSlicingProcess.cpp | 27 +++++- .../slic3r/GUI/BackgroundSlicingProcess.hpp | 9 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 4 +- xs/src/slic3r/GUI/GUI_ObjectParts.cpp | 9 +- xs/src/slic3r/GUI/ProgressIndicator.hpp | 72 ++++++++++++++++ xs/xsp/GUI_BackgroundSlicingProcess.xsp | 1 + 17 files changed, 280 insertions(+), 52 deletions(-) create mode 100644 xs/src/slic3r/GUI/ProgressIndicator.hpp diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 4d70076eff..de26578941 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1417,9 +1417,9 @@ sub start_background_process { # Stop the background processing sub stop_background_process { my ($self) = @_; + $self->{background_slicing_process}->stop(); $self->{toolpaths2D}->reload_print if $self->{canvas3D}; $self->{preview3D}->reload_print if $self->{preview3D}; - $self->schedule_background_process; } # Called by the "Slice now" button, which is visible only if the background processing is disabled. @@ -1467,6 +1467,7 @@ sub export_gcode { eval { # this will throw errors if config is not valid $config->validate; + #FIXME it shall use the background processing! $self->{print}->apply_config($config); $self->{print}->validate; }; @@ -1508,6 +1509,8 @@ sub export_gcode { # this updates buttons status $self->object_list_changed; }); + + $self->{background_slicing_process}->set_output_path($self->{export_gcode_output_file}); # start background process, whose completion event handler # will detect $self->{export_gcode_output_file} and proceed with export @@ -1557,7 +1560,10 @@ sub on_process_completed { my $message; my $send_gcode = 0; my $do_print = 0; - if ($result) { +# print "Process completed, message: ", $message, "\n"; + if (defined($result)) { + $message = L("Export failed"); + } else { # G-code file exported successfully. if ($self->{print_file}) { $message = L("File added to print queue"); @@ -1565,14 +1571,13 @@ sub on_process_completed { } elsif ($self->{send_gcode_file}) { $message = L("Sending G-code file to the Printer Host ..."); $send_gcode = 1; - } else { + } elsif (defined $self->{export_gcode_output_file}) { $message = L("G-code file exported to ") . $self->{export_gcode_output_file}; + } else { + $message = L("Slicing complete"); } - } else { - $message = L("Export failed"); } $self->{export_gcode_output_file} = undef; - $self->statusbar->SetStatusText($message); wxTheApp->notify($message); $self->do_print if $do_print; @@ -1580,14 +1585,20 @@ sub on_process_completed { # Send $self->{send_gcode_file} to OctoPrint. if ($send_gcode) { my $host = Slic3r::PrintHost::get_print_host($self->{config}); - if ($host->send_gcode($self->{send_gcode_file})) { - $self->statusbar->SetStatusText(L("Upload to host finished.")); + $message = L("Upload to host finished."); } else { - $self->statusbar->SetStatusText(""); + $message = ""; } } + # As of now, the BackgroundProcessing thread posts status bar update messages to a queue on the MainFrame.pm, + # but the "Processing finished" message is posted to this window. + # Delay the following status bar update, so it will be called later than what is received by MainFrame.pm. + wxTheApp->CallAfter(sub { + $self->statusbar->SetStatusText($message); + }); + $self->{print_file} = undef; $self->{send_gcode_file} = undef; $self->print_info_box_show(1); diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 272a1ad71a..7e35a27b15 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -602,12 +602,12 @@ if (WIN32 AND ";${PerlEmbed_CCFLAGS};" MATCHES ";[-/]Od;") message("Old CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}") message("Old CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELEASE}") message("Old CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS_RELEASE}") - set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32") - set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG /DWIN32") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG /DWIN32") - set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32") - set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG /DWIN32") + set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") + set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") + set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") + set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") endif() # The following line will add -fPIC on Linux to make the XS.so rellocable. add_definitions(${PerlEmbed_CCCDLFLAGS}) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 0ade0de612..91c9d6b0fa 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -418,6 +418,17 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ { PROFILE_CLEAR(); + if (print->is_step_done(psGCodeExport)) { + // Does the file exist? If so, we hope that it is still valid. + FILE *f = boost::nowide::fopen(path, "r"); + if (f != nullptr) { + ::fclose(f); + return; + } + } + + print->set_started(psGCodeExport); + BOOST_LOG_TRIVIAL(info) << "Exporting G-code..."; // Remove the old g-code if it exists. @@ -467,12 +478,13 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ throw std::runtime_error(msg); } - if (boost::nowide::rename(path_tmp.c_str(), path) != 0) + if (rename_file(path_tmp, 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'); BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished"; + print->set_done(psGCodeExport); // Write the profiler measurements to file PROFILE_UPDATE(); @@ -483,8 +495,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) { PROFILE_FUNC(); - print.set_started(psGCodeExport); - // resets time estimators m_normal_time_estimator.reset(); m_normal_time_estimator.set_dialect(print.config().gcode_flavor); @@ -1029,8 +1039,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // starts analizer calculations if (preview_data != nullptr) m_analyzer.calc_gcode_preview_data(*preview_data); - - print.set_done(psGCodeExport); } std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/xs/src/libslic3r/GCodeTimeEstimator.cpp index 7471367fe5..f97265ee3c 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.cpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.cpp @@ -1,4 +1,5 @@ #include "GCodeTimeEstimator.hpp" +#include "Utils.hpp" #include #include @@ -367,8 +368,7 @@ namespace Slic3r { fclose(out); in.close(); - boost::nowide::remove(filename.c_str()); - if (boost::nowide::rename(path_tmp.c_str(), filename.c_str()) != 0) + if (rename_file(path_tmp, filename) != 0) throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' + "Is " + path_tmp + " locked?" + '\n'); diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 19c474cad9..dc21ab4ae0 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -849,6 +849,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects) return; } +// Called by Print::validate() from the UI thread. void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume) { for (const ModelVolume* vol : this->volumes) diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 8a8af481c5..8ca1d8ea94 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -129,6 +129,7 @@ public: void cut(coordf_t z, Model* model) const; void split(ModelObjectPtrs* new_objects); + // Called by Print::validate() from the UI thread. void check_instances_print_volume_state(const BoundingBoxf3& print_volume); // Print object statistics to console. diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index c8a50a6bb5..a677d9e5aa 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -72,6 +72,7 @@ void Print::reload_object(size_t /* idx */) // Returns true if the brim or skirt have been invalidated. bool Print::reload_model_instances() { + tbb::mutex::scoped_lock lock(m_mutex); bool invalidated = false; for (PrintObject *object : m_objects) invalidated |= object->reload_model_instances(); @@ -370,6 +371,7 @@ double Print::max_allowed_layer_height() const // and have explicit instance positions. void Print::add_model_object(ModelObject* model_object, int idx) { + tbb::mutex::scoped_lock lock(m_mutex); // Initialize a new print object and store it at the given position. PrintObject *object = new PrintObject(this, model_object, model_object->raw_bounding_box()); if (idx != -1) { @@ -378,6 +380,7 @@ void Print::add_model_object(ModelObject* model_object, int idx) } else m_objects.emplace_back(object); // Invalidate all print steps. + //FIXME lock mutex! this->invalidate_all_steps(); for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) { @@ -434,6 +437,8 @@ void Print::add_model_object(ModelObject* model_object, int idx) bool Print::apply_config(DynamicPrintConfig config) { + tbb::mutex::scoped_lock lock(m_mutex); + // we get a copy of the config object so we can modify it safely config.normalize(); @@ -564,13 +569,17 @@ std::string Print::validate() const BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(m_config.max_print_height))); // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced. print_volume.min(2) = -1e10; - unsigned int printable_count = 0; - for (PrintObject *po : m_objects) { - po->model_object()->check_instances_print_volume_state(print_volume); - po->reload_model_instances(); - if (po->is_printable()) - ++printable_count; - } + unsigned int printable_count = 0; + { + // Lock due to the po->reload_model_instances() + tbb::mutex::scoped_lock lock(m_mutex); + for (PrintObject *po : m_objects) { + po->model_object()->check_instances_print_volume_state(print_volume); + po->reload_model_instances(); + if (po->is_printable()) + ++ printable_count; + } + } if (printable_count == 0) return L("All objects are outside of the print volume."); diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index d72c1fef52..106640c4d9 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -88,6 +88,11 @@ public: bool invalidate(StepType step, tbb::mutex &mtx, CancelationCallback &cancel) { bool invalidated = m_state[step].load(std::memory_order_relaxed) != INVALID; if (invalidated) { +#if 0 + if (mtx.state != mtx.HELD) { + printf("Not held!\n"); + } +#endif mtx.unlock(); cancel(); mtx.lock(); @@ -283,18 +288,14 @@ private: SupportLayerPtrs m_support_layers; PrintState m_state; - // Mutex used for synchronization of the worker thread with the UI thread: - // The mutex will be used to guard the worker thread against entering a stage - // while the data influencing the stage is modified. - tbb::mutex m_mutex; // TODO: call model_object->get_bounding_box() instead of accepting // parameter PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); ~PrintObject() {} - void set_started(PrintObjectStep step) { m_state.set_started(step, m_mutex); } - void set_done(PrintObjectStep step) { m_state.set_done(step, m_mutex); } + void set_started(PrintObjectStep step); + void set_done(PrintObjectStep step); std::vector _slice_region(size_t region_id, const std::vector &z, bool modifier); }; @@ -446,8 +447,8 @@ public: const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; } protected: - void set_started(PrintStep step) { m_state.set_started(step, m_mutex); } - void set_done(PrintStep step) { m_state.set_done(step, m_mutex); } + void set_started(PrintStep step) { m_state.set_started(step, m_mutex); throw_if_canceled(); } + void set_done(PrintStep step) { m_state.set_done(step, m_mutex); throw_if_canceled(); } bool invalidate_step(PrintStep step); bool invalidate_all_steps() { return m_state.invalidate_all(m_mutex, m_cancel_callback); } @@ -473,7 +474,7 @@ private: // Mutex used for synchronization of the worker thread with the UI thread: // The mutex will be used to guard the worker thread against entering a stage // while the data influencing the stage is modified. - tbb::mutex m_mutex; + mutable tbb::mutex m_mutex; // Has the calculation been canceled? tbb::atomic m_canceled; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 96c831157e..7882c58a89 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -59,8 +59,19 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Bounding this->layer_height_profile = model_object->layer_height_profile; } +void PrintObject::set_started(PrintObjectStep step) +{ + m_state.set_started(step, m_print->m_mutex); +} + +void PrintObject::set_done(PrintObjectStep step) +{ + m_state.set_done(step, m_print->m_mutex); +} + bool PrintObject::add_copy(const Vec2d &point) { + tbb::mutex::scoped_lock lock(m_print->m_mutex); Points points = m_copies; points.push_back(Point::new_scale(point(0), point(1))); return this->set_copies(points); @@ -68,6 +79,7 @@ bool PrintObject::add_copy(const Vec2d &point) bool PrintObject::delete_last_copy() { + tbb::mutex::scoped_lock lock(m_print->m_mutex); Points points = m_copies; points.pop_back(); return this->set_copies(points); @@ -158,7 +170,6 @@ void PrintObject::make_perimeters() m_print->throw_if_canceled(); } this->typed_slices = false; -// m_state.invalidate(posPrepareInfill); } // compare each layer to the one below, and mark those slices needing diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index f1390b8a29..dd05891dde 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -43,6 +43,14 @@ extern local_encoded_string encode_path(const char *src); extern std::string decode_path(const char *src); extern std::string normalize_utf8_nfc(const char *src); +// Safely rename a file even if the target exists. +// On Windows, the file explorer (or anti-virus or whatever else) often locks the file +// for a short while, so the file may not be movable. Retry while we see recoverable errors. +extern int rename_file(const std::string &from, const std::string &to); + +// Copy a file, adjust the access attributes, so that the target is writable. +extern int copy_file(const std::string &from, const std::string &to); + // File path / name / extension splitting utilities, working with UTF-8, // to be published to Perl. namespace PerlUtils { diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 95aaf5453c..4c2d65605d 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace Slic3r { @@ -139,6 +140,87 @@ const std::string& data_dir() return g_data_dir; } + +// borrowed from LVVM lib/Support/Windows/Path.inc +int rename_file(const std::string &from, const std::string &to) +{ + int ec = 0; + +#ifdef _WIN32 + + // Convert to utf-16. + std::wstring wide_from = boost::nowide::widen(from); + std::wstring wide_to = boost::nowide::widen(to); + + // Retry while we see recoverable errors. + // System scanners (eg. indexer) might open the source file when it is written + // and closed. + bool TryReplace = true; + + // This loop may take more than 2000 x 1ms to finish. + for (int i = 0; i < 2000; ++ i) { + if (i > 0) + // Sleep 1ms + ::Sleep(1); + if (TryReplace) { + // Try ReplaceFile first, as it is able to associate a new data stream + // with the destination even if the destination file is currently open. + if (::ReplaceFileW(wide_to.data(), wide_from.data(), NULL, 0, NULL, NULL)) + return 0; + DWORD ReplaceError = ::GetLastError(); + ec = -1; // ReplaceError + // If ReplaceFileW returned ERROR_UNABLE_TO_MOVE_REPLACEMENT or + // ERROR_UNABLE_TO_MOVE_REPLACEMENT_2, retry but only use MoveFileExW(). + if (ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT || + ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2) { + TryReplace = false; + continue; + } + // If ReplaceFileW returned ERROR_UNABLE_TO_REMOVE_REPLACED, retry + // using ReplaceFileW(). + if (ReplaceError == ERROR_UNABLE_TO_REMOVE_REPLACED) + continue; + // We get ERROR_FILE_NOT_FOUND if the destination file is missing. + // MoveFileEx can handle this case. + if (ReplaceError != ERROR_ACCESS_DENIED && ReplaceError != ERROR_FILE_NOT_FOUND && ReplaceError != ERROR_SHARING_VIOLATION) + break; + } + if (::MoveFileExW(wide_from.c_str(), wide_to.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) + return 0; + DWORD MoveError = ::GetLastError(); + ec = -1; // MoveError + if (MoveError != ERROR_ACCESS_DENIED && MoveError != ERROR_SHARING_VIOLATION) + break; + } + +#else + + boost::nowide::remove(from.c_str()); + ec = boost::nowide::rename(from.c_str(), to.c_str()); + +#endif + + return ec; +} + +int copy_file(const std::string &from, const std::string &to) +{ + const boost::filesystem::path source(from); + const boost::filesystem::path target(to); + static const auto perms = boost::filesystem::owner_read | boost::filesystem::owner_write | boost::filesystem::group_read | boost::filesystem::others_read; // aka 644 + + // Make sure the file has correct permission both before and after we copy over it. + try { + if (boost::filesystem::exists(target)) + boost::filesystem::permissions(target, perms); + boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists); + boost::filesystem::permissions(target, perms); + } catch (std::exception & /* ex */) { + return -1; + } + return 0; +} + } // namespace Slic3r #include diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 47da3367cf..2f88b25cab 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -3,6 +3,7 @@ #include #include +#include // Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx. #include "../../libslic3r/Print.hpp" @@ -11,12 +12,28 @@ #include #include +#include +#include + namespace Slic3r { namespace GUI { extern wxPanel *g_wxPlater; }; +BackgroundSlicingProcess::BackgroundSlicingProcess() +{ + m_temp_output_path = wxStandardPaths::Get().GetTempDir().utf8_str().data(); + m_temp_output_path += (boost::format(".%1%.gcode") % get_current_pid()).str(); +} + +BackgroundSlicingProcess::~BackgroundSlicingProcess() +{ + this->stop(); + this->join_background_thread(); + boost::nowide::remove(m_temp_output_path.c_str()); +} + void BackgroundSlicingProcess::thread_proc() { std::unique_lock lck(m_mutex); @@ -41,7 +58,10 @@ void BackgroundSlicingProcess::thread_proc() m_print->process(); if (! m_print->canceled()) { wxQueueEvent(GUI::g_wxPlater, new wxCommandEvent(m_event_sliced_id)); - m_print->export_gcode(m_output_path, m_gcode_preview_data); + m_print->export_gcode(m_temp_output_path, m_gcode_preview_data); + if (! m_print->canceled() && ! m_output_path.empty() && + copy_file(m_temp_output_path, m_output_path) != 0) + throw std::runtime_error("Copying of the temporary G-code to the output G-code failed"); } } catch (CanceledException &ex) { // Canceled, this is all right. @@ -111,8 +131,10 @@ bool BackgroundSlicingProcess::start() bool BackgroundSlicingProcess::stop() { std::unique_lock lck(m_mutex); - if (m_state == STATE_INITIAL) + if (m_state == STATE_INITIAL) { + this->m_output_path.clear(); return false; + } assert(this->running()); if (m_state == STATE_STARTED || m_state == STATE_RUNNING) { m_print->cancel(); @@ -124,6 +146,7 @@ bool BackgroundSlicingProcess::stop() // In the "Finished" or "Canceled" state. Reset the state to "Idle". m_state = STATE_IDLE; } + this->m_output_path.clear(); return true; } diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 358e00ca97..cc7a6db307 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -17,9 +17,9 @@ class Print; class BackgroundSlicingProcess { public: - BackgroundSlicingProcess() {} - // Stop the background processing and finalize the bacgkround processing thread. - ~BackgroundSlicingProcess() { this->stop(); this->join_background_thread(); } + BackgroundSlicingProcess(); + // Stop the background processing and finalize the bacgkround processing thread, remove temp files. + ~BackgroundSlicingProcess(); void set_print(Print *print) { m_print = print; } void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; } @@ -31,6 +31,8 @@ public: // The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed. void set_finished_event(int event_id) { m_event_finished_id = event_id; } + // Set the output path of the G-code. + void set_output_path(const std::string &path) { m_output_path = path; } // Start the background processing. Returns false if the background processing was already running. bool start(); // Cancel the background processing. Returns false if the background processing was not running. @@ -68,6 +70,7 @@ private: Print *m_print = nullptr; // Data structure, to which the G-code export writes its annotations. GCodePreviewData *m_gcode_preview_data = nullptr; + std::string m_temp_output_path; std::string m_output_path; // Thread, on which the background processing is executed. The thread will always be present // and ready to execute the slicing process. diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 2aaa4aed9c..ae42f014b9 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -5208,9 +5208,9 @@ void GLCanvas3D::_load_shells() // adds objects' volumes unsigned int object_id = 0; - for (PrintObject* obj : m_print->objects()) + for (const PrintObject* obj : m_print->objects()) { - ModelObject* model_obj = obj->model_object(); + const ModelObject* model_obj = obj->model_object(); std::vector instance_ids(model_obj->instances.size()); for (int i = 0; i < (int)model_obj->instances.size(); ++i) diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp index af57db8ed8..0c7b7a5d8f 100644 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp +++ b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp @@ -383,13 +383,10 @@ void update_after_moving() if (volume_id < 0) return; - Vec3d m = m_move_options; - Vec3d l = m_last_coords; - - auto d = Vec3d(m(0) - l(0), m(1) - l(1), m(2) - l(2)); - auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; + auto d = m_move_options - m_last_coords; + auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; volume->mesh.translate(d(0), d(1), d(2)); - m_last_coords = m; + m_last_coords = m_move_options; m_parts_changed = true; parts_changed(m_selected_object_id); diff --git a/xs/src/slic3r/GUI/ProgressIndicator.hpp b/xs/src/slic3r/GUI/ProgressIndicator.hpp new file mode 100644 index 0000000000..106f5e7ead --- /dev/null +++ b/xs/src/slic3r/GUI/ProgressIndicator.hpp @@ -0,0 +1,72 @@ +#ifndef IPROGRESSINDICATOR_HPP +#define IPROGRESSINDICATOR_HPP + +#include +#include + +#include + +namespace Slic3r { + +/** + * @brief Generic progress indication interface. + */ +class ProgressIndicator { +public: + using CancelFn = std::function; // Cancel function signature. + +private: + float state_ = .0f, max_ = 1.f, step_; + CancelFn cancelfunc_ = [](){}; + +public: + + inline virtual ~ProgressIndicator() {} + + /// Get the maximum of the progress range. + float max() const { return max_; } + + /// Get the current progress state + float state() const { return state_; } + + /// Set the maximum of the progress range + virtual void max(float maxval) { max_ = maxval; } + + /// Set the current state of the progress. + virtual void state(float val) { state_ = val; } + + /** + * @brief Number of states int the progress. Can be used instead of giving a + * maximum value. + */ + virtual void states(unsigned statenum) { + step_ = max_ / statenum; + } + + /// Message shown on the next status update. + virtual void message(const wxString&) = 0; + + /// Title of the operation. + virtual void title(const wxString&) = 0; + + /// Formatted message for the next status update. Works just like sprintf. + virtual void message_fmt(const wxString& fmt, ...); + + /// Set up a cancel callback for the operation if feasible. + virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; } + + /** + * Explicitly shut down the progress indicator and call the associated + * callback. + */ + virtual void cancel() { cancelfunc_(); } + + /// Convenience function to call message and status update in one function. + void update(float st, const wxString& msg) { + message(msg); state(st); + } +}; + +} + +#endif // IPROGRESSINDICATOR_HPP diff --git a/xs/xsp/GUI_BackgroundSlicingProcess.xsp b/xs/xsp/GUI_BackgroundSlicingProcess.xsp index cef9f6d58a..8452b8c314 100644 --- a/xs/xsp/GUI_BackgroundSlicingProcess.xsp +++ b/xs/xsp/GUI_BackgroundSlicingProcess.xsp @@ -15,6 +15,7 @@ void set_sliced_event(int event_id); void set_finished_event(int event_id); + void set_output_path(const char *path); bool start(); bool stop(); bool apply_config(DynamicPrintConfig *config) From 36faa090fcf57bdb7aceddfaa7148141165cb073 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 14 Sep 2018 10:25:20 +0200 Subject: [PATCH 11/36] Removed the Controller, Layer View, System Info, ObjectCutDialog, removed unused Perl modules. --- lib/Slic3r/GUI.pm | 33 +- lib/Slic3r/GUI/2DBed.pm | 217 ----- lib/Slic3r/GUI/Controller.pm | 190 ---- .../GUI/Controller/ManualControlDialog.pm | 190 ---- lib/Slic3r/GUI/Controller/PrinterPanel.pm | 707 -------------- lib/Slic3r/GUI/MainFrame.pm | 3 - lib/Slic3r/GUI/OptionsGroup.pm | 498 ---------- lib/Slic3r/GUI/OptionsGroup/Field.pm | 605 ------------ lib/Slic3r/GUI/Plater.pm | 70 +- lib/Slic3r/GUI/Plater/2D.pm | 372 ------- lib/Slic3r/GUI/Plater/2DToolpaths.pm | 910 ------------------ lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 221 ----- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 284 ------ lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 588 ----------- lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 224 ----- .../GUI/Plater/OverrideSettingsPanel.pm | 182 ---- lib/Slic3r/GUI/SystemInfo.pm | 70 -- lib/Slic3r/Geometry.pm | 4 + lib/Slic3r/Print.pm | 10 +- lib/Slic3r/SVG.pm | 142 --- utils/view-mesh.pl | 78 -- utils/view-toolpaths.pl | 106 -- utils/wireframe.pl | 172 ---- xs/src/libslic3r/Print.cpp | 43 +- xs/xsp/Print.xsp | 1 + 25 files changed, 70 insertions(+), 5850 deletions(-) delete mode 100644 lib/Slic3r/GUI/2DBed.pm delete mode 100644 lib/Slic3r/GUI/Controller.pm delete mode 100644 lib/Slic3r/GUI/Controller/ManualControlDialog.pm delete mode 100644 lib/Slic3r/GUI/Controller/PrinterPanel.pm delete mode 100644 lib/Slic3r/GUI/OptionsGroup.pm delete mode 100644 lib/Slic3r/GUI/OptionsGroup/Field.pm delete mode 100644 lib/Slic3r/GUI/Plater/2D.pm delete mode 100644 lib/Slic3r/GUI/Plater/2DToolpaths.pm delete mode 100644 lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm delete mode 100644 lib/Slic3r/GUI/Plater/ObjectCutDialog.pm delete mode 100644 lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm delete mode 100644 lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm delete mode 100644 lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm delete mode 100644 lib/Slic3r/GUI/SystemInfo.pm delete mode 100644 lib/Slic3r/SVG.pm delete mode 100644 utils/view-mesh.pl delete mode 100755 utils/view-toolpaths.pl delete mode 100644 utils/wireframe.pl diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 483fd36f95..d25f1a8aff 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -6,25 +6,11 @@ use utf8; use File::Basename qw(basename); use FindBin; use List::Util qw(first); -use Slic3r::GUI::2DBed; -use Slic3r::GUI::Controller; -use Slic3r::GUI::Controller::ManualControlDialog; -use Slic3r::GUI::Controller::PrinterPanel; use Slic3r::GUI::MainFrame; use Slic3r::GUI::Plater; -use Slic3r::GUI::Plater::2D; -use Slic3r::GUI::Plater::2DToolpaths; use Slic3r::GUI::Plater::3D; use Slic3r::GUI::Plater::3DPreview; -use Slic3r::GUI::Plater::ObjectPartsPanel; -use Slic3r::GUI::Plater::ObjectCutDialog; -use Slic3r::GUI::Plater::ObjectSettingsDialog; -use Slic3r::GUI::Plater::LambdaObjectDialog; -use Slic3r::GUI::Plater::OverrideSettingsPanel; use Slic3r::GUI::ProgressStatusBar; -use Slic3r::GUI::OptionsGroup; -use Slic3r::GUI::OptionsGroup::Field; -use Slic3r::GUI::SystemInfo; use Wx::Locale gettext => 'L'; @@ -226,16 +212,15 @@ sub system_info { $opengl_info = Slic3r::GUI::_3DScene::get_gl_info(1, 1); $opengl_info_txt = Slic3r::GUI::_3DScene::get_gl_info(0, 1); } - my $about = Slic3r::GUI::SystemInfo->new( - parent => undef, - slic3r_info => $slic3r_info, -# copyright_info => $copyright_info, - system_info => $system_info, - opengl_info => $opengl_info, - text_info => Slic3r::slic3r_info . Slic3r::system_info . $opengl_info_txt, - ); - $about->ShowModal; - $about->Destroy; +# my $about = Slic3r::GUI::SystemInfo->new( +# parent => undef, +# slic3r_info => $slic3r_info, +# system_info => $system_info, +# opengl_info => $opengl_info, +# text_info => Slic3r::slic3r_info . Slic3r::system_info . $opengl_info_txt, +# ); +# $about->ShowModal; +# $about->Destroy; } # static method accepting a wxWindow object as first parameter diff --git a/lib/Slic3r/GUI/2DBed.pm b/lib/Slic3r/GUI/2DBed.pm deleted file mode 100644 index 0891a4836c..0000000000 --- a/lib/Slic3r/GUI/2DBed.pm +++ /dev/null @@ -1,217 +0,0 @@ -# Bed shape dialog -# still used by the Slic3r::GUI::Controller::ManualControlDialog Perl module. - -package Slic3r::GUI::2DBed; -use strict; -use warnings; - -use List::Util qw(min max); -use Slic3r::Geometry qw(X Y unscale deg2rad); -use Slic3r::Geometry::Clipper qw(intersection_pl); -use Wx qw(:misc :pen :brush :font :systemsettings wxTAB_TRAVERSAL wxSOLID); -use Wx::Event qw(EVT_PAINT EVT_ERASE_BACKGROUND EVT_MOUSE_EVENTS EVT_SIZE); -use base qw(Wx::Panel Class::Accessor); - -__PACKAGE__->mk_accessors(qw(bed_shape interactive pos _scale_factor _shift on_move _painted)); - -sub new { - my ($class, $parent, $bed_shape) = @_; - - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [250,-1], wxTAB_TRAVERSAL); - $self->{user_drawn_background} = $^O ne 'darwin'; - $self->bed_shape($bed_shape // []); - EVT_PAINT($self, \&_repaint); - EVT_ERASE_BACKGROUND($self, sub {}) if $self->{user_drawn_background}; - EVT_MOUSE_EVENTS($self, \&_mouse_event); - EVT_SIZE($self, sub { $self->Refresh; }); - return $self; -} - -sub _repaint { - my ($self, $event) = @_; - - my $dc = Wx::AutoBufferedPaintDC->new($self); - my ($cw, $ch) = $self->GetSizeWH; - return if $cw == 0; # when canvas is not rendered yet, size is 0,0 - - if ($self->{user_drawn_background}) { - # On all systems the AutoBufferedPaintDC() achieves double buffering. - # On MacOS the background is erased, on Windows the background is not erased - # and on Linux/GTK the background is erased to gray color. - # Fill DC with the background on Windows & Linux/GTK. - my $color = Wx::SystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT); - $dc->SetPen(Wx::Pen->new($color, 1, wxSOLID)); - $dc->SetBrush(Wx::Brush->new($color, wxSOLID)); - my $rect = $self->GetUpdateRegion()->GetBox(); - $dc->DrawRectangle($rect->GetLeft(), $rect->GetTop(), $rect->GetWidth(), $rect->GetHeight()); - } - - # turn $cw and $ch from sizes to max coordinates - $cw--; - $ch--; - - my $cbb = Slic3r::Geometry::BoundingBoxf->new_from_points([ - Slic3r::Pointf->new(0, 0), - Slic3r::Pointf->new($cw, $ch), - ]); - - # leave space for origin point - $cbb->set_x_min($cbb->x_min + 4); - $cbb->set_x_max($cbb->x_max - 4); - $cbb->set_y_max($cbb->y_max - 4); - - # leave space for origin label - $cbb->set_y_max($cbb->y_max - 13); - - # read new size - ($cw, $ch) = @{$cbb->size}; - my $ccenter = $cbb->center; - - # get bounding box of bed shape in G-code coordinates - my $bed_shape = $self->bed_shape; - my $bed_polygon = Slic3r::Polygon->new_scale(@$bed_shape); - my $bb = Slic3r::Geometry::BoundingBoxf->new_from_points($bed_shape); - $bb->merge_point(Slic3r::Pointf->new(0,0)); # origin needs to be in the visible area - my ($bw, $bh) = @{$bb->size}; - my $bcenter = $bb->center; - - # calculate the scaling factor for fitting bed shape in canvas area - my $sfactor = min($cw/$bw, $ch/$bh); - my $shift = Slic3r::Pointf->new( - $ccenter->x - $bcenter->x * $sfactor, - $ccenter->y - $bcenter->y * $sfactor, #- - ); - $self->_scale_factor($sfactor); - $self->_shift(Slic3r::Pointf->new( - $shift->x + $cbb->x_min, - $shift->y - ($cbb->y_max-$self->GetSize->GetHeight), #++ - )); - - # draw bed fill - { - $dc->SetPen(Wx::Pen->new(Wx::Colour->new(0,0,0), 1, wxSOLID)); - $dc->SetBrush(Wx::Brush->new(Wx::Colour->new(255,255,255), wxSOLID)); - $dc->DrawPolygon([ map $self->to_pixels($_), @$bed_shape ], 0, 0); - } - - # draw grid - { - my $step = 10; # 1cm grid - my @polylines = (); - for (my $x = $bb->x_min - ($bb->x_min % $step) + $step; $x < $bb->x_max; $x += $step) { - push @polylines, Slic3r::Polyline->new_scale([$x, $bb->y_min], [$x, $bb->y_max]); - } - for (my $y = $bb->y_min - ($bb->y_min % $step) + $step; $y < $bb->y_max; $y += $step) { - push @polylines, Slic3r::Polyline->new_scale([$bb->x_min, $y], [$bb->x_max, $y]); - } - @polylines = @{intersection_pl(\@polylines, [$bed_polygon])}; - - $dc->SetPen(Wx::Pen->new(Wx::Colour->new(230,230,230), 1, wxSOLID)); - $dc->DrawLine(map @{$self->to_pixels([map unscale($_), @$_])}, @$_[0,-1]) for @polylines; - } - - # draw bed contour - { - $dc->SetPen(Wx::Pen->new(Wx::Colour->new(0,0,0), 1, wxSOLID)); - $dc->SetBrush(Wx::Brush->new(Wx::Colour->new(255,255,255), wxTRANSPARENT)); - $dc->DrawPolygon([ map $self->to_pixels($_), @$bed_shape ], 0, 0); - } - - my $origin_px = $self->to_pixels(Slic3r::Pointf->new(0,0)); - - # draw axes - { - my $axes_len = 50; - my $arrow_len = 6; - my $arrow_angle = deg2rad(45); - $dc->SetPen(Wx::Pen->new(Wx::Colour->new(255,0,0), 2, wxSOLID)); # red - my $x_end = Slic3r::Pointf->new($origin_px->[X] + $axes_len, $origin_px->[Y]); - $dc->DrawLine(@$origin_px, @$x_end); - foreach my $angle (-$arrow_angle, +$arrow_angle) { - my $end = $x_end->clone; - $end->translate(-$arrow_len, 0); - $end->rotate($angle, $x_end); - $dc->DrawLine(@$x_end, @$end); - } - - $dc->SetPen(Wx::Pen->new(Wx::Colour->new(0,255,0), 2, wxSOLID)); # green - my $y_end = Slic3r::Pointf->new($origin_px->[X], $origin_px->[Y] - $axes_len); - $dc->DrawLine(@$origin_px, @$y_end); - foreach my $angle (-$arrow_angle, +$arrow_angle) { - my $end = $y_end->clone; - $end->translate(0, +$arrow_len); - $end->rotate($angle, $y_end); - $dc->DrawLine(@$y_end, @$end); - } - } - - # draw origin - { - $dc->SetPen(Wx::Pen->new(Wx::Colour->new(0,0,0), 1, wxSOLID)); - $dc->SetBrush(Wx::Brush->new(Wx::Colour->new(0,0,0), wxSOLID)); - $dc->DrawCircle(@$origin_px, 3); - - $dc->SetTextForeground(Wx::Colour->new(0,0,0)); - $dc->SetFont(Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL)); - $dc->DrawText("(0,0)", $origin_px->[X] + 1, $origin_px->[Y] + 2); - } - - # draw current position - if (defined $self->pos) { - my $pos_px = $self->to_pixels($self->pos); - $dc->SetPen(Wx::Pen->new(Wx::Colour->new(200,0,0), 2, wxSOLID)); - $dc->SetBrush(Wx::Brush->new(Wx::Colour->new(200,0,0), wxTRANSPARENT)); - $dc->DrawCircle(@$pos_px, 5); - - $dc->DrawLine($pos_px->[X]-15, $pos_px->[Y], $pos_px->[X]+15, $pos_px->[Y]); - $dc->DrawLine($pos_px->[X], $pos_px->[Y]-15, $pos_px->[X], $pos_px->[Y]+15); - } - - $self->_painted(1); -} - -sub _mouse_event { - my ($self, $event) = @_; - - return if !$self->interactive; - return if !$self->_painted; - - my $pos = $event->GetPosition; - my $point = $self->to_units([ $pos->x, $pos->y ]); #]] - if ($event->LeftDown || $event->Dragging) { - $self->on_move->($point) if $self->on_move; - $self->Refresh; - } -} - -# convert G-code coordinates into pixels -sub to_pixels { - my ($self, $point) = @_; - - my $p = Slic3r::Pointf->new(@$point); - $p->scale($self->_scale_factor); - $p->translate(@{$self->_shift}); - return [$p->x, $self->GetSize->GetHeight - $p->y]; #]] -} - -# convert pixels into G-code coordinates -sub to_units { - my ($self, $point) = @_; - - my $p = Slic3r::Pointf->new( - $point->[X], - $self->GetSize->GetHeight - $point->[Y], - ); - $p->translate(@{$self->_shift->negative}); - $p->scale(1/$self->_scale_factor); - return $p; -} - -sub set_pos { - my ($self, $pos) = @_; - - $self->pos($pos); - $self->Refresh; -} - -1; diff --git a/lib/Slic3r/GUI/Controller.pm b/lib/Slic3r/GUI/Controller.pm deleted file mode 100644 index f7d90c7962..0000000000 --- a/lib/Slic3r/GUI/Controller.pm +++ /dev/null @@ -1,190 +0,0 @@ -# The "Controller" tab to control the printer using serial / USB. -# This feature is rarely used. Much more often, the firmware reads the G-codes from a SD card. -# May there be multiple subtabs per each printer connected? - -package Slic3r::GUI::Controller; -use strict; -use warnings; -use utf8; - -use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button :icon :dialog wxBORDER_NONE); -use Wx::Event qw(EVT_CLOSE EVT_LEFT_DOWN EVT_MENU); -use base qw(Wx::ScrolledWindow Class::Accessor); -use List::Util qw(first); - -__PACKAGE__->mk_accessors(qw(_selected_printer_preset)); - -our @ConfigOptions = qw(bed_shape serial_port serial_speed); - -sub new { - my ($class, $parent) = @_; - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [600,350]); - - $self->SetScrollbars(0, 1, 0, 1); - $self->{sizer} = my $sizer = Wx::BoxSizer->new(wxVERTICAL); - - # warning to show when there are no printers configured - { - $self->{text_no_printers} = Wx::StaticText->new($self, -1, - "No printers were configured for USB/serial control.", - wxDefaultPosition, wxDefaultSize); - $self->{sizer}->Add($self->{text_no_printers}, 0, wxTOP | wxLEFT, 30); - } - - # button for adding new printer panels - { - my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG), - wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); - $btn->SetToolTipString("Add printer…") - if $btn->can('SetToolTipString'); - - EVT_LEFT_DOWN($btn, sub { - my $menu = Wx::Menu->new; - my @panels = $self->print_panels; - # remove printers that already exist - # update configs of currently loaded print panels - foreach my $preset (@{wxTheApp->{preset_bundle}->printer}) { - my $preset_name = $preset->name; - next if ! $preset->config->serial_port || - defined first { defined $_ && $_->printer_name eq $preset_name } @panels; - my $myconfig = $preset->config->clone_only(\@ConfigOptions); - my $id = &Wx::NewId(); - $menu->Append($id, $preset_name); - EVT_MENU($menu, $id, sub { - $self->add_printer($preset_name, $myconfig); - }); - } - $self->PopupMenu($menu, $btn->GetPosition); - $menu->Destroy; - }); - $self->{sizer}->Add($btn, 0, wxTOP | wxLEFT, 10); - } - - $self->SetSizer($sizer); - $self->SetMinSize($self->GetSize); - #$sizer->SetSizeHints($self); - - EVT_CLOSE($self, sub { - my (undef, $event) = @_; - - if ($event->CanVeto) { - foreach my $panel ($self->print_panels) { - if ($panel->printing) { - my $confirm = Wx::MessageDialog->new( - $self, "Printer '" . $panel->printer_name . "' is printing.\n\nDo you want to stop printing?", - 'Unfinished Print', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION, - ); - if ($confirm->ShowModal == wxID_NO) { - $event->Veto; - return; - } - } - } - } - foreach my $panel ($self->print_panels) { - $panel->disconnect; - } - - $event->Skip; - }); - - $self->Layout; - - return $self; -} - -sub OnActivate { - my ($self) = @_; - - # get all available presets - my %presets = map { $_->name => $_->config->clone_only(\@ConfigOptions) } - grep { $_->config->serial_port } @{wxTheApp->{preset_bundle}->printer}; - - # decide which ones we want to keep - my %active = (); - - # keep the ones that are currently connected or have jobs in queue - $active{$_} = 1 for map $_->printer_name, - grep { $_->is_connected || @{$_->jobs} > 0 } - $self->print_panels; - - if (%presets) { - # if there are no active panels, use sensible defaults - if (!%active && keys %presets <= 2) { - # if only one or two presets exist, load them - $active{$_} = 1 for keys %presets; - } - if (!%active) { - # enable printers whose port is available - my %ports = map { $_ => 1 } Slic3r::GUI::scan_serial_ports; - $active{$_} = 1 - for grep exists $ports{$presets{$_}->serial_port}, keys %presets; - } - if (!%active && $self->_selected_printer_preset) { - # enable currently selected printer if it is configured - $active{$self->_selected_printer_preset} = 1 - if $presets{$self->_selected_printer_preset}; - } - } - - # apply changes - for my $panel ($self->print_panels) { - next if $active{$panel->printer_name}; - - $self->{sizer}->DetachWindow($panel); - $panel->Destroy; - } - $self->add_printer($_, $presets{$_}) for sort keys %active; - - # show/hide the warning about no printers - $self->{text_no_printers}->Show(!%presets); - - # show/hide the Add button - $self->{btn_add}->Show(keys %presets != keys %active); - - $self->Layout; - - # we need this in order to trigger the OnSize event of wxScrolledWindow which - # recalculates the virtual size - Wx::GetTopLevelParent($self)->SendSizeEvent; -} - -sub add_printer { - my ($self, $printer_name, $config) = @_; - - # check that printer doesn't exist already - foreach my $panel ($self->print_panels) { - if ($panel->printer_name eq $printer_name) { - return $panel; - } - } - - my $printer_panel = Slic3r::GUI::Controller::PrinterPanel->new($self, $printer_name, $config); - $self->{sizer}->Prepend($printer_panel, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10); - $self->Layout; - - return $printer_panel; -} - -sub print_panels { - my ($self) = @_; - return grep $_->isa('Slic3r::GUI::Controller::PrinterPanel'), - map $_->GetWindow, $self->{sizer}->GetChildren; -} - -# Called by Slic3r::GUI::Tab::Printer::_on_presets_changed -# when the presets are loaded or the user selects another preset. -sub update_presets { - my ($self, $presets) = @_; - # update configs of currently loaded print panels - my @presets = @$presets; - foreach my $panel ($self->print_panels) { - my $preset = $presets->find_preset($panel->printer_name, 0); - $panel->config($preset->config->clone_only(\@ConfigOptions)) - if defined $preset; - } - - $self->_selected_printer_preset($presets->get_selected_preset->name); -} - -1; diff --git a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm deleted file mode 100644 index de0565255b..0000000000 --- a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm +++ /dev/null @@ -1,190 +0,0 @@ -# A printer "Controller" -> "ManualControlDialog" subtab, opened per 3D printer connected? - -package Slic3r::GUI::Controller::ManualControlDialog; -use strict; -use warnings; -use utf8; - -use Wx qw(:dialog :id :misc :sizer :choicebook :button :bitmap - wxBORDER_NONE wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_CLOSE EVT_BUTTON); -use base qw(Wx::Dialog Class::Accessor); - -__PACKAGE__->mk_accessors(qw(sender config2 x_homed y_homed)); - -sub new { - my ($class, $parent, $config, $sender) = @_; - - my $self = $class->SUPER::new($parent, -1, "Manual Control", wxDefaultPosition, - [500,380], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - $self->sender($sender); - - $self->config2({ - xy_travel_speed => 130, - z_travel_speed => 10, - }); - - my $bed_sizer = Wx::FlexGridSizer->new(2, 3, 1, 1); - $bed_sizer->AddGrowableCol(1, 1); - $bed_sizer->AddGrowableRow(0, 1); - - my $move_button = sub { - my ($sizer, $label, $icon, $bold, $pos, $handler) = @_; - - my $btn = Wx::Button->new($self, -1, $label, wxDefaultPosition, wxDefaultSize, - wxBU_LEFT | wxBU_EXACTFIT); - $btn->SetFont($bold ? $Slic3r::GUI::small_bold_font : $Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("$icon.png"), wxBITMAP_TYPE_PNG)); - $btn->SetBitmapPosition($pos); - EVT_BUTTON($self, $btn, $handler); - $sizer->Add($btn, 1, wxEXPAND | wxALL, 0); - }; - - # Y buttons - { - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - for my $d (qw(+10 +1 +0.1)) { - $move_button->($sizer, $d, 'arrow_up', 0, wxLEFT, sub { $self->rel_move('Y', $d) }); - } - $move_button->($sizer, 'Y', 'house', 1, wxLEFT, sub { $self->home('Y') }); - for my $d (qw(-0.1 -1 -10)) { - $move_button->($sizer, $d, 'arrow_down', 0, wxLEFT, sub { $self->rel_move('Y', $d) }); - }; - $bed_sizer->Add($sizer, 1, wxEXPAND, 0); - } - - # Bed canvas - { - my $bed_shape = $config->bed_shape; - $self->{canvas} = my $canvas = Slic3r::GUI::2DBed->new($self, $bed_shape); - $canvas->interactive(1); - $canvas->on_move(sub { - my ($pos) = @_; - - if (!($self->x_homed && $self->y_homed)) { - Slic3r::GUI::show_error($self, "Please home both X and Y before moving."); - return ; - } - - # delete any pending commands to get a smoother movement - $self->sender->purge_queue(1); - $self->abs_xy_move($pos); - }); - $bed_sizer->Add($canvas, 0, wxEXPAND | wxRIGHT, 3); - } - - # Z buttons - { - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - for my $d (qw(+10 +1 +0.1)) { - $move_button->($sizer, $d, 'arrow_up', 0, wxLEFT, sub { $self->rel_move('Z', $d) }); - } - $move_button->($sizer, 'Z', 'house', 1, wxLEFT, sub { $self->home('Z') }); - for my $d (qw(-0.1 -1 -10)) { - $move_button->($sizer, $d, 'arrow_down', 0, wxLEFT, sub { $self->rel_move('Z', $d) }); - }; - $bed_sizer->Add($sizer, 1, wxEXPAND, 0); - } - - # XYZ home button - $move_button->($bed_sizer, 'XYZ', 'house', 1, wxTOP, sub { $self->home(undef) }); - - # X buttons - { - my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); - for my $d (qw(-10 -1 -0.1)) { - $move_button->($sizer, $d, 'arrow_left', 0, wxTOP, sub { $self->rel_move('X', $d) }); - } - $move_button->($sizer, 'X', 'house', 1, wxTOP, sub { $self->home('X') }); - for my $d (qw(+0.1 +1 +10)) { - $move_button->($sizer, $d, 'arrow_right', 0, wxTOP, sub { $self->rel_move('X', $d) }); - } - $bed_sizer->Add($sizer, 1, wxEXPAND, 0); - } - - my $optgroup = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Settings', - on_change => sub { - my ($opt_id, $value) = @_; - $self->config2->{$opt_id} = $value; - }, - ); - { - my $line = Slic3r::GUI::OptionsGroup::Line->new( - label => 'Speed (mm/s)', - ); - $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'xy_travel_speed', - type => 'f', - label => 'X/Y', - tooltip => '', - default => $self->config2->{xy_travel_speed}, - )); - $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'z_travel_speed', - type => 'f', - label => 'Z', - tooltip => '', - default => $self->config2->{z_travel_speed}, - )); - $optgroup->append_line($line); - } - - my $main_sizer = Wx::BoxSizer->new(wxVERTICAL); - $main_sizer->Add($bed_sizer, 1, wxEXPAND | wxALL, 10); - $main_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 10); - #$main_sizer->Add($self->CreateButtonSizer(wxCLOSE), 0, wxEXPAND); - #EVT_BUTTON($self, wxID_CLOSE, sub { $self->Close }); - - $self->SetSizer($main_sizer); - $self->SetMinSize($self->GetSize); - #$main_sizer->SetSizeHints($self); - $self->Layout; - - # needed to actually free memory - EVT_CLOSE($self, sub { - $self->EndModal(wxID_OK); - $self->Destroy; - }); - - return $self; -} - -sub abs_xy_move { - my ($self, $pos) = @_; - - $self->sender->send("G90", 1); # set absolute positioning - $self->sender->send(sprintf("G1 X%.1f Y%.1f F%d", @$pos, $self->config2->{xy_travel_speed}*60), 1); - $self->{canvas}->set_pos($pos); -} - -sub rel_move { - my ($self, $axis, $distance) = @_; - - my $speed = ($axis eq 'Z') ? $self->config2->{z_travel_speed} : $self->config2->{xy_travel_speed}; - $self->sender->send("G91", 1); # set relative positioning - $self->sender->send(sprintf("G1 %s%.1f F%d", $axis, $distance, $speed*60), 1); - $self->sender->send("G90", 1); # set absolute positioning - - if (my $pos = $self->{canvas}->pos) { - if ($axis eq 'X') { - $pos->translate($distance, 0); - } elsif ($axis eq 'Y') { - $pos->translate(0, $distance); - } - $self->{canvas}->set_pos($pos); - } -} - -sub home { - my ($self, $axis) = @_; - - $axis //= ''; - $self->sender->send(sprintf("G28 %s", $axis), 1); - $self->{canvas}->set_pos(undef); - $self->x_homed(1) if $axis eq 'X'; - $self->y_homed(1) if $axis eq 'Y'; -} - -1; diff --git a/lib/Slic3r/GUI/Controller/PrinterPanel.pm b/lib/Slic3r/GUI/Controller/PrinterPanel.pm deleted file mode 100644 index 794ea4e4ea..0000000000 --- a/lib/Slic3r/GUI/Controller/PrinterPanel.pm +++ /dev/null @@ -1,707 +0,0 @@ -package Slic3r::GUI::Controller::PrinterPanel; -use strict; -use warnings; -use utf8; - -use Wx qw(wxTheApp :panel :id :misc :sizer :button :bitmap :window :gauge :timer - :textctrl :font :systemsettings); -use Wx::Event qw(EVT_BUTTON EVT_MOUSEWHEEL EVT_TIMER EVT_SCROLLWIN); -use base qw(Wx::Panel Class::Accessor); - -__PACKAGE__->mk_accessors(qw(printer_name config sender jobs - printing status_timer temp_timer)); - -use constant CONNECTION_TIMEOUT => 3; # seconds -use constant STATUS_TIMER_INTERVAL => 1000; # milliseconds -use constant TEMP_TIMER_INTERVAL => 5000; # milliseconds - -sub new { - my ($class, $parent, $printer_name, $config) = @_; - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [500, 250]); - - $self->printer_name($printer_name || 'Printer'); - $self->config($config); - $self->jobs([]); - - # set up the timer that polls for updates - { - my $timer_id = &Wx::NewId(); - $self->status_timer(Wx::Timer->new($self, $timer_id)); - EVT_TIMER($self, $timer_id, sub { - my ($self, $event) = @_; - - if ($self->printing) { - my $queue_size = $self->sender->queue_size; - $self->{gauge}->SetValue($self->{gauge}->GetRange - $queue_size); - if ($queue_size == 0) { - $self->print_completed; - } - } - $self->{log_textctrl}->AppendText("$_\n") for @{$self->sender->purge_log}; - { - my $temp = $self->sender->getT; - if ($temp eq '') { - $self->{temp_panel}->Hide; - } else { - if (!$self->{temp_panel}->IsShown) { - $self->{temp_panel}->Show; - $self->Layout; - } - $self->{temp_text}->SetLabel($temp . "°C"); - - $temp = $self->sender->getB; - if ($temp eq '') { - $self->{bed_temp_text}->SetLabel('n.a.'); - } else { - $self->{bed_temp_text}->SetLabel($temp . "°C"); - } - } - } - }); - } - - # set up the timer that sends temperature requests - # (responses are handled by status_timer) - { - my $timer_id = &Wx::NewId(); - $self->temp_timer(Wx::Timer->new($self, $timer_id)); - EVT_TIMER($self, $timer_id, sub { - my ($self, $event) = @_; - $self->sender->send("M105", 1); # send it through priority queue - }); - } - - my $box = Wx::StaticBox->new($self, -1, ""); - my $sizer = Wx::StaticBoxSizer->new($box, wxHORIZONTAL); - my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); - - # printer name - { - my $text = Wx::StaticText->new($box, -1, $self->printer_name, wxDefaultPosition, [220,-1]); - my $font = $text->GetFont; - $font->SetPointSize(20); - $text->SetFont($font); - $left_sizer->Add($text, 0, wxEXPAND, 0); - } - - # connection info - { - my $conn_sizer = Wx::FlexGridSizer->new(2, 2, 1, 0); - $conn_sizer->SetFlexibleDirection(wxHORIZONTAL); - $conn_sizer->AddGrowableCol(1, 1); - $left_sizer->Add($conn_sizer, 0, wxEXPAND | wxTOP, 5); - { - my $text = Wx::StaticText->new($box, -1, "Port:", wxDefaultPosition, wxDefaultSize); - $text->SetFont($Slic3r::GUI::small_font); - $conn_sizer->Add($text, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); - } - my $serial_port_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - { - $self->{serial_port_combobox} = Wx::ComboBox->new($box, -1, $config->serial_port, wxDefaultPosition, wxDefaultSize, []); - $self->{serial_port_combobox}->SetFont($Slic3r::GUI::small_font); - $self->update_serial_ports; - $serial_port_sizer->Add($self->{serial_port_combobox}, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 1); - } - { - $self->{btn_rescan_serial} = my $btn = Wx::BitmapButton->new($box, -1, Wx::Bitmap->new(Slic3r::var("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG), - wxDefaultPosition, wxDefaultSize, &Wx::wxBORDER_NONE); - $btn->SetToolTipString("Rescan serial ports") - if $btn->can('SetToolTipString'); - $serial_port_sizer->Add($btn, 0, wxALIGN_CENTER_VERTICAL, 0); - EVT_BUTTON($self, $btn, sub { $self->update_serial_ports }); - } - $conn_sizer->Add($serial_port_sizer, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); - - { - my $text = Wx::StaticText->new($box, -1, "Speed:", wxDefaultPosition, wxDefaultSize); - $text->SetFont($Slic3r::GUI::small_font); - $conn_sizer->Add($text, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); - } - my $serial_speed_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - { - $self->{serial_speed_combobox} = Wx::ComboBox->new($box, -1, $config->serial_speed, wxDefaultPosition, wxDefaultSize, - ["115200", "250000"]); - $self->{serial_speed_combobox}->SetFont($Slic3r::GUI::small_font); - $serial_speed_sizer->Add($self->{serial_speed_combobox}, 0, wxALIGN_CENTER_VERTICAL, 0); - } - { - $self->{btn_disconnect} = my $btn = Wx::Button->new($box, -1, "Disconnect", wxDefaultPosition, wxDefaultSize); - $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG)); - $serial_speed_sizer->Add($btn, 0, wxLEFT, 5); - EVT_BUTTON($self, $btn, \&disconnect); - } - $conn_sizer->Add($serial_speed_sizer, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); - } - - # buttons - { - $self->{btn_connect} = my $btn = Wx::Button->new($box, -1, "Connect to printer", wxDefaultPosition, [-1, 40]); - my $font = $btn->GetFont; - $font->SetPointSize($font->GetPointSize + 2); - $btn->SetFont($font); - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("arrow_up.png"), wxBITMAP_TYPE_PNG)); - $left_sizer->Add($btn, 0, wxTOP, 15); - EVT_BUTTON($self, $btn, \&connect); - } - - # print progress bar - { - my $gauge = $self->{gauge} = Wx::Gauge->new($self, wxGA_HORIZONTAL, 100, wxDefaultPosition, wxDefaultSize); - $left_sizer->Add($self->{gauge}, 0, wxEXPAND | wxTOP, 15); - $gauge->Hide; - } - - # status - $self->{status_text} = Wx::StaticText->new($box, -1, "", wxDefaultPosition, [200,-1]); - $left_sizer->Add($self->{status_text}, 1, wxEXPAND | wxTOP, 15); - - # manual control - { - $self->{btn_manual_control} = my $btn = Wx::Button->new($box, -1, "Manual control", wxDefaultPosition, wxDefaultSize); - $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG)); - $btn->Hide; - $left_sizer->Add($btn, 0, wxTOP, 15); - EVT_BUTTON($self, $btn, sub { - my $dlg = Slic3r::GUI::Controller::ManualControlDialog->new - ($self, $self->config, $self->sender); - $dlg->ShowModal; - }); - } - - # temperature - { - my $temp_panel = $self->{temp_panel} = Wx::Panel->new($box, -1); - my $temp_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - - my $temp_font = Wx::Font->new($Slic3r::GUI::small_font); - $temp_font->SetWeight(wxFONTWEIGHT_BOLD); - { - my $text = Wx::StaticText->new($temp_panel, -1, "Temperature:", wxDefaultPosition, wxDefaultSize); - $text->SetFont($Slic3r::GUI::small_font); - $temp_sizer->Add($text, 0, wxALIGN_CENTER_VERTICAL); - - $self->{temp_text} = Wx::StaticText->new($temp_panel, -1, "", wxDefaultPosition, wxDefaultSize); - $self->{temp_text}->SetFont($temp_font); - $self->{temp_text}->SetForegroundColour(Wx::wxRED); - $temp_sizer->Add($self->{temp_text}, 1, wxALIGN_CENTER_VERTICAL); - } - { - my $text = Wx::StaticText->new($temp_panel, -1, "Bed:", wxDefaultPosition, wxDefaultSize); - $text->SetFont($Slic3r::GUI::small_font); - $temp_sizer->Add($text, 0, wxALIGN_CENTER_VERTICAL); - - $self->{bed_temp_text} = Wx::StaticText->new($temp_panel, -1, "", wxDefaultPosition, wxDefaultSize); - $self->{bed_temp_text}->SetFont($temp_font); - $self->{bed_temp_text}->SetForegroundColour(Wx::wxRED); - $temp_sizer->Add($self->{bed_temp_text}, 1, wxALIGN_CENTER_VERTICAL); - } - $temp_panel->SetSizer($temp_sizer); - $temp_panel->Hide; - $left_sizer->Add($temp_panel, 0, wxEXPAND | wxTOP | wxBOTTOM, 4); - } - - # print jobs panel - $self->{print_jobs_sizer} = my $print_jobs_sizer = Wx::BoxSizer->new(wxVERTICAL); - { - my $text = Wx::StaticText->new($box, -1, "Queue:", wxDefaultPosition, wxDefaultSize); - $text->SetFont($Slic3r::GUI::small_font); - $print_jobs_sizer->Add($text, 0, wxEXPAND, 0); - - $self->{jobs_panel} = Wx::ScrolledWindow->new($box, -1, wxDefaultPosition, wxDefaultSize, - wxVSCROLL | wxBORDER_NONE); - $self->{jobs_panel}->SetScrollbars(0, 1, 0, 1); - $self->{jobs_panel_sizer} = Wx::BoxSizer->new(wxVERTICAL); - $self->{jobs_panel}->SetSizer($self->{jobs_panel_sizer}); - $print_jobs_sizer->Add($self->{jobs_panel}, 1, wxEXPAND, 0); - - # TODO: fix this. We're trying to pass the scroll event to the parent but it - # doesn't work. - EVT_SCROLLWIN($self->{jobs_panel}, sub { - my ($panel, $event) = @_; - - my $controller = $self->GetParent; - my $new_event = Wx::ScrollWinEvent->new( - $event->GetEventType, - $event->GetPosition, - $event->GetOrientation, - ); - $controller->ProcessEvent($new_event); - }) if 0; - } - - my $log_sizer = Wx::BoxSizer->new(wxVERTICAL); - { - my $text = Wx::StaticText->new($box, -1, "Log:", wxDefaultPosition, wxDefaultSize); - $text->SetFont($Slic3r::GUI::small_font); - $log_sizer->Add($text, 0, wxEXPAND, 0); - - my $log = $self->{log_textctrl} = Wx::TextCtrl->new($box, -1, "", wxDefaultPosition, wxDefaultSize, - wxTE_MULTILINE | wxBORDER_NONE); - $log->SetBackgroundColour($box->GetBackgroundColour); - $log->SetFont($Slic3r::GUI::small_font); - $log->SetEditable(0); - $log_sizer->Add($self->{log_textctrl}, 1, wxEXPAND, 0); - } - - $sizer->Add($left_sizer, 0, wxEXPAND | wxALL, 0); - $sizer->Add($print_jobs_sizer, 2, wxEXPAND | wxALL, 0); - $sizer->Add($log_sizer, 1, wxEXPAND | wxLEFT, 15); - - $self->SetSizer($sizer); - $self->SetMinSize($self->GetSize); - - $self->_update_connection_controls; - - return $self; -} - -sub is_connected { - my ($self) = @_; - return $self->sender && $self->sender->is_connected; -} - -sub _update_connection_controls { - my ($self) = @_; - - $self->{btn_connect}->Show; - $self->{btn_disconnect}->Hide; - $self->{serial_port_combobox}->Enable; - $self->{serial_speed_combobox}->Enable; - $self->{btn_rescan_serial}->Enable; - $self->{btn_manual_control}->Hide; - $self->{btn_manual_control}->Disable; - - if ($self->is_connected) { - $self->{btn_connect}->Hide; - $self->{btn_manual_control}->Show; - if (!$self->printing || $self->printing->paused) { - $self->{btn_disconnect}->Show; - $self->{btn_manual_control}->Enable; - } - $self->{serial_port_combobox}->Disable; - $self->{serial_speed_combobox}->Disable; - $self->{btn_rescan_serial}->Disable; - } - - $self->Layout; -} - -sub set_status { - my ($self, $status) = @_; - $self->{status_text}->SetLabel($status); - $self->{status_text}->Wrap($self->{status_text}->GetSize->GetWidth); - $self->{status_text}->Refresh; - $self->Layout; -} - -sub connect { - my ($self) = @_; - - return if $self->is_connected; - - $self->set_status("Connecting..."); - $self->sender(Slic3r::GCode::Sender->new); - my $res = $self->sender->connect( - $self->{serial_port_combobox}->GetValue, - $self->{serial_speed_combobox}->GetValue, - ); - if (!$res) { - $self->set_status("Connection failed. Check serial port and speed."); - } else { - if ($self->sender->wait_connected) { - $self->set_status("Printer is online. You can now start printing from the queue on the right."); - $self->status_timer->Start(STATUS_TIMER_INTERVAL, wxTIMER_CONTINUOUS); - $self->temp_timer->Start(TEMP_TIMER_INTERVAL, wxTIMER_CONTINUOUS); - - # request temperature now, without waiting for the timer - $self->sender->send("M105", 1); - } else { - $self->set_status("Connection failed. Check serial port and speed."); - } - } - $self->_update_connection_controls; - $self->reload_jobs; -} - -sub disconnect { - my ($self) = @_; - - $self->status_timer->Stop; - $self->temp_timer->Stop; - return if !$self->is_connected; - - $self->printing->printing(0) if $self->printing; - $self->printing(undef); - $self->{gauge}->Hide; - $self->{temp_panel}->Hide; - $self->sender->disconnect; - $self->set_status(""); - $self->_update_connection_controls; - $self->reload_jobs; -} - -sub update_serial_ports { - my ($self) = @_; - - my $cb = $self->{serial_port_combobox}; - my $current = $cb->GetValue; - $cb->Clear; - $cb->Append($_) for Slic3r::GUI::scan_serial_ports; - $cb->SetValue($current); -} - -sub load_print_job { - my ($self, $gcode_file, $filament_stats) = @_; - - push @{$self->jobs}, my $job = Slic3r::GUI::Controller::PrinterPanel::PrintJob->new( - id => time() . $gcode_file . rand(1000), - gcode_file => $gcode_file, - filament_stats => $filament_stats, - ); - $self->reload_jobs; - return $job; -} - -sub delete_job { - my ($self, $job) = @_; - - $self->jobs([ grep $_->id ne $job->id, @{$self->jobs} ]); - $self->reload_jobs; -} - -sub print_job { - my ($self, $job) = @_; - - $self->printing($job); - $job->printing(1); - $self->reload_jobs; - - open my $fh, '<', $job->gcode_file; - my $line_count = 0; - while (my $row = <$fh>) { - $self->sender->send($row); - $line_count++; - } - close $fh; - - $self->_update_connection_controls; - $self->{gauge}->SetRange($line_count); - $self->{gauge}->SetValue(0); - $self->{gauge}->Enable; - $self->{gauge}->Show; - $self->Layout; - - $self->set_status('Printing...'); - $self->{log_textctrl}->AppendText(sprintf "=====\n"); - $self->{log_textctrl}->AppendText(sprintf "Printing %s\n", $job->name); - $self->{log_textctrl}->AppendText(sprintf "Print started at %s\n", $self->_timestamp); -} - -sub print_completed { - my ($self) = @_; - - my $job = $self->printing; - $self->printing(undef); - $job->printing(0); - $job->printed(1); - $self->_update_connection_controls; - $self->{gauge}->Hide; - $self->Layout; - - $self->set_status('Print completed.'); - $self->{log_textctrl}->AppendText(sprintf "Print completed at %s\n", $self->_timestamp); - - $self->reload_jobs; -} - -sub reload_jobs { - my ($self) = @_; - - # reorder jobs - @{$self->jobs} = sort { ($a->printed <=> $b->printed) || ($a->timestamp <=> $b->timestamp) } - @{$self->jobs}; - - # remove all panels - foreach my $child ($self->{jobs_panel_sizer}->GetChildren) { - my $window = $child->GetWindow; - $self->{jobs_panel_sizer}->Detach($window); - # now $child does not exist anymore - $window->Destroy; - } - - # re-add all panels - foreach my $job (@{$self->jobs}) { - my $panel = Slic3r::GUI::Controller::PrinterPanel::PrintJobPanel->new($self->{jobs_panel}, $job); - $self->{jobs_panel_sizer}->Add($panel, 0, wxEXPAND | wxBOTTOM, 5); - - $panel->on_delete_job(sub { - my ($job) = @_; - $self->delete_job($job); - }); - $panel->on_print_job(sub { - my ($job) = @_; - $self->print_job($job); - }); - $panel->on_pause_print(sub { - my ($job) = @_; - $self->sender->pause_queue; - $job->paused(1); - $self->reload_jobs; - $self->_update_connection_controls; - $self->{gauge}->Disable; - $self->set_status('Print is paused. Click on Resume to continue.'); - }); - $panel->on_abort_print(sub { - my ($job) = @_; - $self->sender->purge_queue; - $self->printing(undef); - $job->printing(0); - $job->paused(0); - $self->reload_jobs; - $self->_update_connection_controls; - $self->{gauge}->Disable; - $self->{gauge}->Hide; - $self->set_status('Print was aborted.'); - $self->{log_textctrl}->AppendText(sprintf "Print aborted at %s\n", $self->_timestamp); - }); - $panel->on_resume_print(sub { - my ($job) = @_; - $self->sender->resume_queue; - $job->paused(0); - $self->reload_jobs; - $self->_update_connection_controls; - $self->{gauge}->Enable; - $self->set_status('Printing...'); - }); - $panel->enable_print if $self->is_connected && !$self->printing; - - EVT_MOUSEWHEEL($panel, sub { - my (undef, $event) = @_; - Wx::PostEvent($self->{jobs_panel}, $event); - $event->Skip; - }); - } - - $self->{jobs_panel}->Layout; - $self->{print_jobs_sizer}->Layout; -} - -sub _timestamp { - my ($self) = @_; - - my @time = localtime(time); - return sprintf '%02d:%02d:%02d', @time[2,1,0]; -} - -package Slic3r::GUI::Controller::PrinterPanel::PrintJob; -use Moo; - -use File::Basename qw(basename); - -has 'id' => (is => 'ro', required => 1); -has 'timestamp' => (is => 'ro', default => sub { time }); -has 'gcode_file' => (is => 'ro', required => 1); -has 'filament_stats' => (is => 'rw'); -has 'printing' => (is => 'rw', default => sub { 0 }); -has 'paused' => (is => 'rw', default => sub { 0 }); -has 'printed' => (is => 'rw', default => sub { 0 }); - -sub name { - my ($self) = @_; - return basename($self->gcode_file); -} - -package Slic3r::GUI::Controller::PrinterPanel::PrintJobPanel; -use strict; -use warnings; -use utf8; - -use Wx qw(wxTheApp :panel :id :misc :sizer :button :bitmap :font :dialog :icon :timer - :colour :brush :pen); -use Wx::Event qw(EVT_BUTTON EVT_TIMER EVT_ERASE_BACKGROUND); -use base qw(Wx::Panel Class::Accessor); - -__PACKAGE__->mk_accessors(qw(job on_delete_job on_print_job on_pause_print on_resume_print - on_abort_print blink_timer)); - -sub new { - my ($class, $parent, $job) = @_; - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize); - - $self->job($job); - $self->SetBackgroundColour(wxWHITE); - - { - my $white_brush = Wx::Brush->new(wxWHITE, wxSOLID); - my $pen = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, wxSOLID); - EVT_ERASE_BACKGROUND($self, sub { - my ($self, $event) = @_; - my $dc = $event->GetDC; - my $size = $self->GetSize; - $dc->SetBrush($white_brush); - $dc->SetPen($pen); - $dc->DrawRoundedRectangle(0, 0, $size->GetWidth,$size->GetHeight, 6); - }); - } - - my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); - { - $self->{job_name_textctrl} = my $text = Wx::StaticText->new($self, -1, $job->name, wxDefaultPosition, wxDefaultSize); - my $font = $text->GetFont; - $font->SetWeight(wxFONTWEIGHT_BOLD); - $text->SetFont($font); - if ($job->printed) { - $text->SetForegroundColour($Slic3r::GUI::grey); - } - $left_sizer->Add($text, 0, wxEXPAND, 0); - } - { - my $filament_stats = join "\n", - map "$_ (" . sprintf("%.2f", $job->filament_stats->{$_}/1000) . "m)", - sort keys %{$job->filament_stats}; - my $text = Wx::StaticText->new($self, -1, $filament_stats, wxDefaultPosition, wxDefaultSize); - $text->SetFont($Slic3r::GUI::small_font); - if ($job->printed && !$job->printing) { - $text->SetForegroundColour($Slic3r::GUI::grey); - } - $left_sizer->Add($text, 0, wxEXPAND | wxTOP, 6); - } - - my $buttons_sizer = Wx::BoxSizer->new(wxVERTICAL); - my $button_style = Wx::wxBORDER_NONE | wxBU_EXACTFIT; - { - my $btn = $self->{btn_delete} = Wx::Button->new($self, -1, 'Delete', - wxDefaultPosition, wxDefaultSize, $button_style); - $btn->SetToolTipString("Delete this job from print queue") - if $btn->can('SetToolTipString'); - $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG)); - if ($job->printing) { - $btn->Hide; - } - $buttons_sizer->Add($btn, 0, wxBOTTOM, 2); - - EVT_BUTTON($self, $btn, sub { - my $res = Wx::MessageDialog->new($self, "Are you sure you want to delete this print job?", 'Delete Job', wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION)->ShowModal; - return unless $res == wxID_YES; - - wxTheApp->CallAfter(sub { - $self->on_delete_job->($job); - }); - }); - } - { - my $label = $job->printed ? 'Print Again' : 'Print This'; - my $btn = $self->{btn_print} = Wx::Button->new($self, -1, $label, wxDefaultPosition, wxDefaultSize, - $button_style); - $btn->SetFont($Slic3r::GUI::small_bold_font); - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_play.png"), wxBITMAP_TYPE_PNG)); - $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_play_blue.png"), wxBITMAP_TYPE_PNG)); - #$btn->SetBitmapPosition(wxRIGHT); - $btn->Hide; - $buttons_sizer->Add($btn, 0, wxBOTTOM, 2); - - EVT_BUTTON($self, $btn, sub { - wxTheApp->CallAfter(sub { - $self->on_print_job->($job); - }); - }); - } - { - my $btn = $self->{btn_pause} = Wx::Button->new($self, -1, "Pause", wxDefaultPosition, wxDefaultSize, - $button_style); - $btn->SetFont($Slic3r::GUI::small_font); - if (!$job->printing || $job->paused) { - $btn->Hide; - } - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_pause.png"), wxBITMAP_TYPE_PNG)); - $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_pause_blue.png"), wxBITMAP_TYPE_PNG)); - $buttons_sizer->Add($btn, 0, wxBOTTOM, 2); - - EVT_BUTTON($self, $btn, sub { - wxTheApp->CallAfter(sub { - $self->on_pause_print->($job); - }); - }); - } - { - my $btn = $self->{btn_resume} = Wx::Button->new($self, -1, "Resume", wxDefaultPosition, wxDefaultSize, - $button_style); - $btn->SetFont($Slic3r::GUI::small_font); - if (!$job->printing || !$job->paused) { - $btn->Hide; - } - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_play.png"), wxBITMAP_TYPE_PNG)); - $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_play_blue.png"), wxBITMAP_TYPE_PNG)); - $buttons_sizer->Add($btn, 0, wxBOTTOM, 2); - - EVT_BUTTON($self, $btn, sub { - wxTheApp->CallAfter(sub { - $self->on_resume_print->($job); - }); - }); - } - { - my $btn = $self->{btn_abort} = Wx::Button->new($self, -1, "Abort", wxDefaultPosition, wxDefaultSize, - $button_style); - $btn->SetFont($Slic3r::GUI::small_font); - if (!$job->printing) { - $btn->Hide; - } - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_stop.png"), wxBITMAP_TYPE_PNG)); - $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_stop_blue.png"), wxBITMAP_TYPE_PNG)); - $buttons_sizer->Add($btn, 0, wxBOTTOM, 2); - - EVT_BUTTON($self, $btn, sub { - wxTheApp->CallAfter(sub { - $self->on_abort_print->($job); - }); - }); - } - - my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $sizer->Add($left_sizer, 1, wxEXPAND | wxALL, 6); - $sizer->Add($buttons_sizer, 0, wxEXPAND | wxALL, 6); - $self->SetSizer($sizer); - - # set-up the timer that changes the job name color while printing - if ($self->job->printing && !$self->job->paused) { - my $timer_id = &Wx::NewId(); - $self->blink_timer(Wx::Timer->new($self, $timer_id)); - my $blink = 0; # closure - my $colour = Wx::Colour->new(0, 190, 0); - EVT_TIMER($self, $timer_id, sub { - my ($self, $event) = @_; - - $self->{job_name_textctrl}->SetForegroundColour($blink ? Wx::wxBLACK : $colour); - $blink = !$blink; - }); - $self->blink_timer->Start(1000, wxTIMER_CONTINUOUS); - } - - return $self; -} - -sub enable_print { - my ($self) = @_; - - if (!$self->job->printing) { - $self->{btn_print}->Show; - } - $self->Layout; -} - -sub Destroy { - my ($self) = @_; - - # There's a gap between the time Perl destroys the wxPanel object and - # the blink_timer member, so the wxTimer might still fire an event which - # isn't handled properly, causing a crash. So we ensure that blink_timer - # is stopped before we destroy the wxPanel. - $self->blink_timer->Stop if $self->blink_timer; - return $self->SUPER::Destroy; -} - -1; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 59c39dc8b8..e2a25a7ab3 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -150,9 +150,6 @@ sub _init_tabpanel { event_remove_object => $OBJECT_REMOVE_EVENT, event_update_scene => $UPDATE_SCENE_EVENT, ), L("Plater")); - if (!$self->{no_controller}) { - $panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), L("Controller")); - } } #TODO this is an example of a Slic3r XS interface call to add a new preset editor page to the main view. diff --git a/lib/Slic3r/GUI/OptionsGroup.pm b/lib/Slic3r/GUI/OptionsGroup.pm deleted file mode 100644 index 962e6ffc0d..0000000000 --- a/lib/Slic3r/GUI/OptionsGroup.pm +++ /dev/null @@ -1,498 +0,0 @@ -# A dialog group object. Used by the Tab, Preferences dialog, ManualControlDialog etc. - -package Slic3r::GUI::OptionsGroup; -use Moo; - -use List::Util qw(first); -use Wx qw(:combobox :font :misc :sizer :systemsettings :textctrl wxTheApp); -use Wx::Event qw(EVT_CHECKBOX EVT_COMBOBOX EVT_SPINCTRL EVT_TEXT EVT_KILL_FOCUS EVT_SLIDER); - -has 'parent' => (is => 'ro', required => 1); -has 'title' => (is => 'ro', required => 1); -has 'on_change' => (is => 'rw', default => sub { sub {} }); -has 'staticbox' => (is => 'ro', default => sub { 1 }); -has 'label_width' => (is => 'rw', default => sub { 180 }); -has 'extra_column' => (is => 'rw', default => sub { undef }); -has 'label_font' => (is => 'rw'); -has 'sidetext_font' => (is => 'rw', default => sub { Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }); -has 'sizer' => (is => 'rw'); -has '_disabled' => (is => 'rw', default => sub { 0 }); -has '_grid_sizer' => (is => 'rw'); -has '_options' => (is => 'ro', default => sub { {} }); -has '_fields' => (is => 'ro', default => sub { {} }); - -sub BUILD { - my $self = shift; - - if ($self->staticbox) { - my $box = Wx::StaticBox->new($self->parent, -1, $self->title); - $self->sizer(Wx::StaticBoxSizer->new($box, wxVERTICAL)); - } else { - $self->sizer(Wx::BoxSizer->new(wxVERTICAL)); - } - - my $num_columns = 1; - ++$num_columns if $self->label_width != 0; - ++$num_columns if $self->extra_column; - $self->_grid_sizer(Wx::FlexGridSizer->new(0, $num_columns, 0, 0)); - $self->_grid_sizer->SetFlexibleDirection(wxHORIZONTAL); - $self->_grid_sizer->AddGrowableCol($self->label_width != 0); - - # TODO: border size may be related to wxWidgets 2.8.x vs. 2.9.x instead of wxMAC specific - $self->sizer->Add($self->_grid_sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 5); -} - -# this method accepts a Slic3r::GUI::OptionsGroup::Line object -sub append_line { - my ($self, $line) = @_; - - if ($line->sizer || ($line->widget && $line->full_width)) { - # full-width widgets are appended *after* the grid sizer, so after all the non-full-width lines - my $sizer = $line->sizer // $line->widget->($self->parent); - $self->sizer->Add($sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15); - return; - } - - my $grid_sizer = $self->_grid_sizer; - - # if we have an extra column, build it - if ($self->extra_column) { - if (defined (my $item = $self->extra_column->($line))) { - $grid_sizer->Add($item, 0, wxALIGN_CENTER_VERTICAL, 0); - } else { - # if the callback provides no sizer for the extra cell, put a spacer - $grid_sizer->AddSpacer(1); - } - } - - # build label if we have it - my $label; - if ($self->label_width != 0) { - $label = Wx::StaticText->new($self->parent, -1, $line->label ? $line->label . ":" : "", wxDefaultPosition, [$self->label_width, -1]); - $label->SetFont($self->label_font) if $self->label_font; - $label->Wrap($self->label_width) ; # needed to avoid Linux/GTK bug - $grid_sizer->Add($label, 0, wxALIGN_CENTER_VERTICAL, 0); - $label->SetToolTipString($line->label_tooltip) if $line->label_tooltip; - } - - # if we have a widget, add it to the sizer - if ($line->widget) { - my $widget_sizer = $line->widget->($self->parent); - $grid_sizer->Add($widget_sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15); - return; - } - - # if we have a single option with no sidetext just add it directly to the grid sizer - my @options = @{$line->get_options}; - $self->_options->{$_->opt_id} = $_ for @options; - if (@options == 1 && !$options[0]->sidetext && !$options[0]->side_widget && !@{$line->get_extra_widgets}) { - my $option = $options[0]; - my $field = $self->_build_field($option); - $grid_sizer->Add($field, 0, ($option->full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0); - return; - } - - # if we're here, we have more than one option or a single option with sidetext - # so we need a horizontal sizer to arrange these things - my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $grid_sizer->Add($sizer, 0, 0, 0); - - foreach my $i (0..$#options) { - my $option = $options[$i]; - - # add label if any - if ($option->label) { - my $field_label = Wx::StaticText->new($self->parent, -1, $option->label . ":", wxDefaultPosition, wxDefaultSize); - $field_label->SetFont($self->sidetext_font); - $sizer->Add($field_label, 0, wxALIGN_CENTER_VERTICAL, 0); - } - - # add field - my $field = $self->_build_field($option); - $sizer->Add($field, 0, wxALIGN_CENTER_VERTICAL, 0); - - # add sidetext if any - if ($option->sidetext) { - my $sidetext = Wx::StaticText->new($self->parent, -1, $option->sidetext, wxDefaultPosition, wxDefaultSize); - $sidetext->SetFont($self->sidetext_font); - $sizer->Add($sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); - } - - # add side widget if any - if ($option->side_widget) { - $sizer->Add($option->side_widget->($self->parent), 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1); - } - - if ($option != $#options) { - $sizer->AddSpacer(4); - } - } - - # add extra sizers if any - foreach my $extra_widget (@{$line->get_extra_widgets}) { - $sizer->Add($extra_widget->($self->parent), 0, wxLEFT | wxALIGN_CENTER_VERTICAL , 4); - } -} - -sub create_single_option_line { - my ($self, $option) = @_; - - my $line = Slic3r::GUI::OptionsGroup::Line->new( - label => $option->label, - label_tooltip => $option->tooltip, - ); - $option->label(""); - $line->append_option($option); - - return $line; -} - -sub append_single_option_line { - my ($self, $option) = @_; - return $self->append_line($self->create_single_option_line($option)); -} - -sub _build_field { - my $self = shift; - my ($opt) = @_; - - my $opt_id = $opt->opt_id; - my $on_change = sub { - #! This function will be called from Field. - my ($opt_id, $value) = @_; - #! Call OptionGroup._on_change(...) - $self->_on_change($opt_id, $value) - unless $self->_disabled; - }; - my $on_kill_focus = sub { - my ($opt_id) = @_; - $self->_on_kill_focus($opt_id); - }; - - my $type = $opt->{gui_type} || $opt->{type}; - - my $field; - if ($type eq 'bool') { - $field = Slic3r::GUI::OptionsGroup::Field::Checkbox->new( - parent => $self->parent, - option => $opt, - ); - } elsif ($type eq 'i') { - $field = Slic3r::GUI::OptionsGroup::Field::SpinCtrl->new( - parent => $self->parent, - option => $opt, - ); - } elsif ($type eq 'color') { - $field = Slic3r::GUI::OptionsGroup::Field::ColourPicker->new( - parent => $self->parent, - option => $opt, - ); - } elsif ($type =~ /^(f|s|s@|percent)$/) { - $field = Slic3r::GUI::OptionsGroup::Field::TextCtrl->new( - parent => $self->parent, - option => $opt, - ); - } elsif ($type eq 'select' || $type eq 'select_open') { - $field = Slic3r::GUI::OptionsGroup::Field::Choice->new( - parent => $self->parent, - option => $opt, - ); - } elsif ($type eq 'f_enum_open' || $type eq 'i_enum_open' || $type eq 'i_enum_closed') { - $field = Slic3r::GUI::OptionsGroup::Field::NumericChoice->new( - parent => $self->parent, - option => $opt, - ); - } elsif ($type eq 'point') { - $field = Slic3r::GUI::OptionsGroup::Field::Point->new( - parent => $self->parent, - option => $opt, - ); - } elsif ($type eq 'slider') { - $field = Slic3r::GUI::OptionsGroup::Field::Slider->new( - parent => $self->parent, - option => $opt, - ); - } - return undef if !$field; - - #! setting up a function that will be triggered when the field changes - #! think of it as $field->on_change = ($on_change) - $field->on_change($on_change); - $field->on_kill_focus($on_kill_focus); - $self->_fields->{$opt_id} = $field; - - return $field->isa('Slic3r::GUI::OptionsGroup::Field::wxWindow') - ? $field->wxWindow - : $field->wxSizer; -} - -sub get_option { - my ($self, $opt_id) = @_; - return undef if !exists $self->_options->{$opt_id}; - return $self->_options->{$opt_id}; -} - -sub get_field { - my ($self, $opt_id) = @_; - return undef if !exists $self->_fields->{$opt_id}; - return $self->_fields->{$opt_id}; -} - -sub get_value { - my ($self, $opt_id) = @_; - - return if !exists $self->_fields->{$opt_id}; - return $self->_fields->{$opt_id}->get_value; -} - -sub set_value { - my ($self, $opt_id, $value) = @_; - - return if !exists $self->_fields->{$opt_id}; - $self->_fields->{$opt_id}->set_value($value); -} - -sub _on_change { - my ($self, $opt_id, $value) = @_; - $self->on_change->($opt_id, $value); -} - -sub enable { - my ($self) = @_; - - $_->enable for values %{$self->_fields}; -} - -sub disable { - my ($self) = @_; - - $_->disable for values %{$self->_fields}; -} - -sub _on_kill_focus { - my ($self, $opt_id) = @_; - # nothing -} - - -package Slic3r::GUI::OptionsGroup::Line; -use Moo; - -has 'label' => (is => 'rw', default => sub { "" }); -has 'full_width' => (is => 'rw', default => sub { 0 }); -has 'label_tooltip' => (is => 'rw', default => sub { "" }); -has 'sizer' => (is => 'rw'); -has 'widget' => (is => 'rw'); -has '_options' => (is => 'ro', default => sub { [] }); -# Extra UI components after the label and the edit widget of the option. -has '_extra_widgets' => (is => 'ro', default => sub { [] }); - -# this method accepts a Slic3r::GUI::OptionsGroup::Option object -sub append_option { - my ($self, $option) = @_; - push @{$self->_options}, $option; -} - -sub append_widget { - my ($self, $widget) = @_; - push @{$self->_extra_widgets}, $widget; -} - -sub get_options { - my ($self) = @_; - return [ @{$self->_options} ]; -} - -sub get_extra_widgets { - my ($self) = @_; - return [ @{$self->_extra_widgets} ]; -} - - -# Configuration of an option. -# This very much reflects the content of the C++ ConfigOptionDef class. -package Slic3r::GUI::OptionsGroup::Option; -use Moo; - -has 'opt_id' => (is => 'rw', required => 1); -has 'type' => (is => 'rw', required => 1); -has 'default' => (is => 'rw', required => 1); -has 'gui_type' => (is => 'rw', default => sub { undef }); -has 'gui_flags' => (is => 'rw', default => sub { "" }); -has 'label' => (is => 'rw', default => sub { "" }); -has 'sidetext' => (is => 'rw', default => sub { "" }); -has 'tooltip' => (is => 'rw', default => sub { "" }); -has 'multiline' => (is => 'rw', default => sub { 0 }); -has 'full_width' => (is => 'rw', default => sub { 0 }); -has 'width' => (is => 'rw', default => sub { undef }); -has 'height' => (is => 'rw', default => sub { undef }); -has 'min' => (is => 'rw', default => sub { undef }); -has 'max' => (is => 'rw', default => sub { undef }); -has 'labels' => (is => 'rw', default => sub { [] }); -has 'values' => (is => 'rw', default => sub { [] }); -has 'readonly' => (is => 'rw', default => sub { 0 }); -has 'side_widget' => (is => 'rw', default => sub { undef }); - - -package Slic3r::GUI::ConfigOptionsGroup; -use Moo; - -use List::Util qw(first); - -extends 'Slic3r::GUI::OptionsGroup'; -has 'config' => (is => 'ro', required => 1); -has 'full_labels' => (is => 'ro', default => sub { 0 }); -has '_opt_map' => (is => 'ro', default => sub { {} }); - -sub get_option { - my ($self, $opt_key, $opt_index) = @_; - - $opt_index //= -1; - - if (!$self->config->has($opt_key)) { - die "No $opt_key in ConfigOptionsGroup config"; - } - - my $opt_id = ($opt_index == -1 ? $opt_key : "${opt_key}#${opt_index}"); - $self->_opt_map->{$opt_id} = [ $opt_key, $opt_index ]; - - # Slic3r::Config::Options is a C++ Slic3r::PrintConfigDef exported as a Perl hash of hashes. - # The C++ counterpart is a constant singleton. - my $optdef = $Slic3r::Config::Options->{$opt_key}; # we should access this from $self->config - my $default_value = $self->_get_config_value($opt_key, $opt_index, $optdef->{gui_flags} =~ /\bserialized\b/); - - return Slic3r::GUI::OptionsGroup::Option->new( - opt_id => $opt_id, - type => $optdef->{type}, - default => $default_value, - gui_type => $optdef->{gui_type}, - gui_flags => $optdef->{gui_flags}, - label => ($self->full_labels && defined $optdef->{full_label}) ? $optdef->{full_label} : $optdef->{label}, - sidetext => $optdef->{sidetext}, - # calling serialize() ensures we get a stringified value - tooltip => $optdef->{tooltip} . " (default: " . $self->config->serialize($opt_key) . ")", - multiline => $optdef->{multiline}, - width => $optdef->{width}, - min => $optdef->{min}, - max => $optdef->{max}, - labels => $optdef->{labels}, - values => $optdef->{values}, - readonly => $optdef->{readonly}, - ); -} - -sub create_single_option_line { - my ($self, $opt_key, $opt_index) = @_; - - my $option; - if (ref($opt_key)) { - $option = $opt_key; - } else { - $option = $self->get_option($opt_key, $opt_index); - } - return $self->SUPER::create_single_option_line($option); -} - -sub append_single_option_line { - my ($self, $option, $opt_index) = @_; - return $self->append_line($self->create_single_option_line($option, $opt_index)); -} - -# Initialize UI components with the config values. -sub reload_config { - my ($self) = @_; - - foreach my $opt_id (keys %{ $self->_opt_map }) { - my ($opt_key, $opt_index) = @{ $self->_opt_map->{$opt_id} }; - my $option = $self->_options->{$opt_id}; - $self->set_value($opt_id, $self->_get_config_value($opt_key, $opt_index, $option->gui_flags =~ /\bserialized\b/)); - } -} - -sub get_fieldc { - my ($self, $opt_key, $opt_index) = @_; - - $opt_index //= -1; - my $opt_id = first { $self->_opt_map->{$_}[0] eq $opt_key && $self->_opt_map->{$_}[1] == $opt_index } - keys %{$self->_opt_map}; - return defined($opt_id) ? $self->get_field($opt_id) : undef; -} - -sub _get_config_value { - my ($self, $opt_key, $opt_index, $deserialize) = @_; - - if ($deserialize) { - # Want to edit a vector value (currently only multi-strings) in a single edit box. - # Aggregate the strings the old way. - # Currently used for the post_process config value only. - die "Can't deserialize option indexed value" if $opt_index != -1; - return join(';', @{$self->config->get($opt_key)}); - } else { - return $opt_index == -1 - ? $self->config->get($opt_key) - : $self->config->get_at($opt_key, $opt_index); - } -} - -sub _on_change { - my ($self, $opt_id, $value) = @_; - - if (exists $self->_opt_map->{$opt_id}) { - my ($opt_key, $opt_index) = @{ $self->_opt_map->{$opt_id} }; - my $option = $self->_options->{$opt_id}; - - # get value - my $field_value = $self->get_value($opt_id); - if ($option->gui_flags =~ /\bserialized\b/) { - die "Can't set serialized option indexed value" if $opt_index != -1; - # Split a string to multiple strings by a semi-colon. This is the old way of storing multi-string values. - # Currently used for the post_process config value only. - my @values = split /;/, $field_value; - $self->config->set($opt_key, \@values); - } else { - if ($opt_index == -1) { - $self->config->set($opt_key, $field_value); - } else { - my $value = $self->config->get($opt_key); - $value->[$opt_index] = $field_value; - $self->config->set($opt_key, $value); - } - } - } - - $self->SUPER::_on_change($opt_id, $value); -} - -sub _on_kill_focus { - my ($self, $opt_id) = @_; - - # when a field loses focus, reapply the config value to it - # (thus discarding any invalid input and reverting to the last - # accepted value) - $self->reload_config; -} - -# Static text shown among the options. -# Currently used for the filament cooling legend only. -package Slic3r::GUI::OptionsGroup::StaticText; -use Wx qw(:misc :systemsettings); -use base 'Wx::StaticText'; - -sub new { - my ($class, $parent) = @_; - - my $self = $class->SUPER::new($parent, -1, "", wxDefaultPosition, wxDefaultSize); - my $font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - $self->SetFont($font); - return $self; -} - -sub SetText { - my ($self, $value) = @_; - - $self->SetLabel($value); - $self->Wrap(400); - $self->GetParent->Layout; -} - -1; diff --git a/lib/Slic3r/GUI/OptionsGroup/Field.pm b/lib/Slic3r/GUI/OptionsGroup/Field.pm deleted file mode 100644 index 1a53daeb5f..0000000000 --- a/lib/Slic3r/GUI/OptionsGroup/Field.pm +++ /dev/null @@ -1,605 +0,0 @@ -# An input field class prototype. -package Slic3r::GUI::OptionsGroup::Field; -use Moo; - -# This is a base class for option fields. - -has 'parent' => (is => 'ro', required => 1); -# Slic3r::GUI::OptionsGroup::Option -has 'option' => (is => 'ro', required => 1); -# On change callback -has 'on_change' => (is => 'rw', default => sub { sub {} }); -has 'on_kill_focus' => (is => 'rw', default => sub { sub {} }); -# If set, the callback $self->on_change is not called. -# This is used to avoid recursive invocation of the field change/update by wxWidgets. -has 'disable_change_event' => (is => 'rw', default => sub { 0 }); - -# This method should not fire the on_change event -sub set_value { - my ($self, $value) = @_; - die "Method not implemented"; -} - -sub get_value { - my ($self) = @_; - die "Method not implemented"; -} - -sub set_tooltip { - my ($self, $tooltip) = @_; - - $self->SetToolTipString($tooltip) - if $tooltip && $self->can('SetToolTipString'); -} - -sub toggle { - my ($self, $enable) = @_; - $enable ? $self->enable : $self->disable; -} - -sub _on_change { - my ($self, $opt_id) = @_; - - $self->on_change->($opt_id, $self->get_value) - unless $self->disable_change_event; -} - -sub _on_kill_focus { - my ($self, $opt_id, $s, $event) = @_; - - # Without this, there will be nasty focus bugs on Windows. - # Also, docs for wxEvent::Skip() say "In general, it is recommended to skip all - # non-command events to allow the default handling to take place." - $event->Skip(1); - - $self->on_kill_focus->($opt_id); -} - - -package Slic3r::GUI::OptionsGroup::Field::wxWindow; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field'; - -has 'wxWindow' => (is => 'rw', trigger => 1); # wxWindow object - -sub _default_size { - my ($self) = @_; - - # default width on Windows is too large - return Wx::Size->new($self->option->width || 60, $self->option->height || -1); -} - -sub _trigger_wxWindow { - my ($self) = @_; - - $self->wxWindow->SetToolTipString($self->option->tooltip) - if $self->option->tooltip && $self->wxWindow->can('SetToolTipString'); -} - -sub set_value { - my ($self, $value) = @_; - - $self->disable_change_event(1); - $self->wxWindow->SetValue($value); - $self->disable_change_event(0); -} - -sub get_value { - my ($self) = @_; - return $self->wxWindow->GetValue; -} - -sub enable { - my ($self) = @_; - - $self->wxWindow->Enable; - $self->wxWindow->Refresh; -} - -sub disable { - my ($self) = @_; - - $self->wxWindow->Disable; - $self->wxWindow->Refresh; -} - - -package Slic3r::GUI::OptionsGroup::Field::Checkbox; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow'; - -use Wx qw(:misc); -use Wx::Event qw(EVT_CHECKBOX); - -sub BUILD { - my ($self) = @_; - - my $field = Wx::CheckBox->new($self->parent, -1, ""); - $self->wxWindow($field); - $field->SetValue($self->option->default); - $field->Disable if $self->option->readonly; - - EVT_CHECKBOX($self->parent, $field, sub { - $self->_on_change($self->option->opt_id); - }); -} - -sub get_value { - my ($self) = @_; - return $self->wxWindow->GetValue ? 1 : 0; -} - -package Slic3r::GUI::OptionsGroup::Field::SpinCtrl; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow'; - -use Wx qw(:misc); -use Wx::Event qw(EVT_SPINCTRL EVT_TEXT EVT_KILL_FOCUS); - -has 'tmp_value' => (is => 'rw'); - -sub BUILD { - my ($self) = @_; - - my $field = Wx::SpinCtrl->new($self->parent, -1, $self->option->default, wxDefaultPosition, $self->_default_size, - 0, $self->option->min || 0, $self->option->max || 2147483647, $self->option->default); - $self->wxWindow($field); - - EVT_SPINCTRL($self->parent, $field, sub { - $self->tmp_value(undef); - $self->_on_change($self->option->opt_id); - }); - EVT_TEXT($self->parent, $field, sub { - my ($s, $event) = @_; - - # On OSX/Cocoa, wxSpinCtrl::GetValue() doesn't return the new value - # when it was changed from the text control, so the on_change callback - # gets the old one, and on_kill_focus resets the control to the old value. - # As a workaround, we get the new value from $event->GetString and store - # here temporarily so that we can return it from $self->get_value - $self->tmp_value($event->GetString) if $event->GetString =~ /^\d+$/; - $self->_on_change($self->option->opt_id); - # We don't reset tmp_value here because _on_change might put callbacks - # in the CallAfter queue, and we want the tmp value to be available from - # them as well. - }); - EVT_KILL_FOCUS($field, sub { - $self->tmp_value(undef); - $self->_on_kill_focus($self->option->opt_id, @_); - }); -} - -sub get_value { - my ($self) = @_; - return $self->tmp_value // $self->wxWindow->GetValue; -} - - -package Slic3r::GUI::OptionsGroup::Field::TextCtrl; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow'; - -use Wx qw(:misc :textctrl); -use Wx::Event qw(EVT_TEXT EVT_KILL_FOCUS); - -sub BUILD { - my ($self) = @_; - - my $style = 0; - $style = wxTE_MULTILINE if $self->option->multiline; - my $field = Wx::TextCtrl->new($self->parent, -1, $self->option->default, wxDefaultPosition, - $self->_default_size, $style); - $self->wxWindow($field); - - # TODO: test loading a config that has empty string for multi-value options like 'wipe' - - EVT_TEXT($self->parent, $field, sub { - $self->_on_change($self->option->opt_id); - }); - EVT_KILL_FOCUS($field, sub { - $self->_on_kill_focus($self->option->opt_id, @_); - }); -} - -sub enable { - my ($self) = @_; - - $self->wxWindow->Enable; - $self->wxWindow->SetEditable(1); -} - -sub disable { - my ($self) = @_; - - $self->wxWindow->Disable; - $self->wxWindow->SetEditable(0); -} - - -package Slic3r::GUI::OptionsGroup::Field::Choice; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow'; - -use List::Util qw(first); -use Wx qw(:misc :combobox); -use Wx::Event qw(EVT_COMBOBOX EVT_TEXT); - -sub BUILD { - my ($self) = @_; - - my $style = 0; - $style |= wxCB_READONLY if defined $self->option->gui_type && $self->option->gui_type ne 'select_open'; - my $field = Wx::ComboBox->new($self->parent, -1, "", wxDefaultPosition, $self->_default_size, - $self->option->labels || $self->option->values || [], $style); - $self->wxWindow($field); - - $self->set_value($self->option->default); - - EVT_COMBOBOX($self->parent, $field, sub { - $self->_on_change($self->option->opt_id); - }); - EVT_TEXT($self->parent, $field, sub { - $self->_on_change($self->option->opt_id); - }); -} - -sub set_value { - my ($self, $value) = @_; - - $self->disable_change_event(1); - - my $idx; - if ($self->option->values) { - $idx = first { $self->option->values->[$_] eq $value } 0..$#{$self->option->values}; - # if value is not among indexes values we use SetValue() - } - - if (defined $idx) { - $self->wxWindow->SetSelection($idx); - } else { - $self->wxWindow->SetValue($value); - } - - $self->disable_change_event(0); -} - -sub set_values { - my ($self, $values) = @_; - - $self->disable_change_event(1); - - # it looks that Clear() also clears the text field in recent wxWidgets versions, - # but we want to preserve it - my $ww = $self->wxWindow; - my $value = $ww->GetValue; - $ww->Clear; - $ww->Append($_) for @$values; - $ww->SetValue($value); - - $self->disable_change_event(0); -} - -sub get_value { - my ($self) = @_; - - if ($self->option->values) { - my $idx = $self->wxWindow->GetSelection; - if ($idx != &Wx::wxNOT_FOUND) { - return $self->option->values->[$idx]; - } - } - return $self->wxWindow->GetValue; -} - -package Slic3r::GUI::OptionsGroup::Field::NumericChoice; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow'; - -use List::Util qw(first); -use Wx qw(wxTheApp :misc :combobox); -use Wx::Event qw(EVT_COMBOBOX EVT_TEXT); - -# if option has no 'values', indices are values -# if option has no 'labels', values are labels - -sub BUILD { - my ($self) = @_; - - my $field = Wx::ComboBox->new($self->parent, -1, $self->option->default, wxDefaultPosition, $self->_default_size, - $self->option->labels || $self->option->values); - $self->wxWindow($field); - - $self->set_value($self->option->default); - - EVT_COMBOBOX($self->parent, $field, sub { - my $disable_change_event = $self->disable_change_event; - $self->disable_change_event(1); - - my $idx = $field->GetSelection; # get index of selected value - my $label; - - if ($self->option->labels && $idx <= $#{$self->option->labels}) { - $label = $self->option->labels->[$idx]; - } elsif ($self->option->values && $idx <= $#{$self->option->values}) { - $label = $self->option->values->[$idx]; - } else { - $label = $idx; - } - - # The MSW implementation of wxComboBox will leave the field blank if we call - # SetValue() in the EVT_COMBOBOX event handler, so we postpone the call. - wxTheApp->CallAfter(sub { - my $dce = $self->disable_change_event; - $self->disable_change_event(1); - - # ChangeValue() is not exported in wxPerl - $field->SetValue($label); - - $self->disable_change_event($dce); - }); - - $self->disable_change_event($disable_change_event); - $self->_on_change($self->option->opt_id); - }); - EVT_TEXT($self->parent, $field, sub { - $self->_on_change($self->option->opt_id); - }); -} - -sub set_value { - my ($self, $value) = @_; - - $self->disable_change_event(1); - - my $field = $self->wxWindow; - if ($self->option->gui_flags =~ /\bshow_value\b/) { - $field->SetValue($value); - } else { - if ($self->option->values) { - # check whether we have a value index - my $value_idx = first { $self->option->values->[$_] eq $value } 0..$#{$self->option->values}; - if (defined $value_idx) { - $field->SetSelection($value_idx); - $self->disable_change_event(0); - return; - } - } elsif ($self->option->labels && $value <= $#{$self->option->labels}) { - # if we have no values, we expect value to be an index - $field->SetValue($self->option->labels->[$value]); - $self->disable_change_event(0); - return; - } - $field->SetValue($value); - } - - $self->disable_change_event(0); -} - -sub get_value { - my ($self) = @_; - - my $label = $self->wxWindow->GetValue; - if ($self->option->labels) { - my $value_idx = first { $self->option->labels->[$_] eq $label } 0..$#{$self->option->labels}; - if (defined $value_idx) { - if ($self->option->values) { - return $self->option->values->[$value_idx]; - } - return $value_idx; - } - } - return $label; -} - - -package Slic3r::GUI::OptionsGroup::Field::ColourPicker; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow'; - -use Wx qw(:misc :colour); -use Wx::Event qw(EVT_COLOURPICKER_CHANGED); - -sub BUILD { - my ($self) = @_; - - my $field = Wx::ColourPickerCtrl->new($self->parent, -1, - $self->_string_to_colour($self->option->default), wxDefaultPosition, - $self->_default_size); - $self->wxWindow($field); - - EVT_COLOURPICKER_CHANGED($self->parent, $field, sub { - $self->_on_change($self->option->opt_id); - }); -} - -sub set_value { - my ($self, $value) = @_; - - $self->disable_change_event(1); - $self->wxWindow->SetColour($self->_string_to_colour($value)); - $self->disable_change_event(0); -} - -sub get_value { - my ($self) = @_; - return $self->wxWindow->GetColour->GetAsString(wxC2S_HTML_SYNTAX); -} - -sub _string_to_colour { - my ($self, $string) = @_; - - $string =~ s/^#//; - # If the color is in an invalid format, set it to white. - $string = 'FFFFFF' if ($string !~ m/^[[:xdigit:]]{6}/); - return Wx::Colour->new(unpack 'C*', pack 'H*', $string); -} - - -package Slic3r::GUI::OptionsGroup::Field::wxSizer; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field'; - -has 'wxSizer' => (is => 'rw'); # wxSizer object - - -package Slic3r::GUI::OptionsGroup::Field::Point; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxSizer'; - -has 'x_textctrl' => (is => 'rw'); -has 'y_textctrl' => (is => 'rw'); - -use Slic3r::Geometry qw(X Y); -use Wx qw(:misc :sizer); -use Wx::Event qw(EVT_TEXT); - -sub BUILD { - my ($self) = @_; - - my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $self->wxSizer($sizer); - - my $field_size = Wx::Size->new(40, -1); - - $self->x_textctrl(Wx::TextCtrl->new($self->parent, -1, $self->option->default->[X], wxDefaultPosition, $field_size)); - $self->y_textctrl(Wx::TextCtrl->new($self->parent, -1, $self->option->default->[Y], wxDefaultPosition, $field_size)); - - my @items = ( - Wx::StaticText->new($self->parent, -1, "x:"), - $self->x_textctrl, - Wx::StaticText->new($self->parent, -1, " y:"), - $self->y_textctrl, - ); - $sizer->Add($_, 0, wxALIGN_CENTER_VERTICAL, 0) for @items; - - if ($self->option->tooltip) { - foreach my $item (@items) { - $item->SetToolTipString($self->option->tooltip) - if $item->can('SetToolTipString'); - } - } - - EVT_TEXT($self->parent, $_, sub { - $self->_on_change($self->option->opt_id); - }) for $self->x_textctrl, $self->y_textctrl; -} - -sub set_value { - my ($self, $value) = @_; - - $self->disable_change_event(1); - $self->x_textctrl->SetValue($value->[X]); - $self->y_textctrl->SetValue($value->[Y]); - $self->disable_change_event(0); -} - -sub get_value { - my ($self) = @_; - - return [ - $self->x_textctrl->GetValue, - $self->y_textctrl->GetValue, - ]; -} - -sub enable { - my ($self) = @_; - - $self->x_textctrl->Enable; - $self->y_textctrl->Enable; -} - -sub disable { - my ($self) = @_; - - $self->x_textctrl->Disable; - $self->y_textctrl->Disable; -} - - -package Slic3r::GUI::OptionsGroup::Field::Slider; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxSizer'; - -has 'scale' => (is => 'rw', default => sub { 10 }); -has 'slider' => (is => 'rw'); -has 'textctrl' => (is => 'rw'); - -use Wx qw(:misc :sizer); -use Wx::Event qw(EVT_SLIDER EVT_TEXT EVT_KILL_FOCUS); - -sub BUILD { - my ($self) = @_; - - my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $self->wxSizer($sizer); - - my $slider = Wx::Slider->new( - $self->parent, -1, - ($self->option->default // $self->option->min) * $self->scale, - ($self->option->min // 0) * $self->scale, - ($self->option->max // 100) * $self->scale, - wxDefaultPosition, - [ $self->option->width // -1, $self->option->height // -1 ], - ); - $self->slider($slider); - - my $textctrl = Wx::TextCtrl->new($self->parent, -1, $slider->GetValue/$self->scale, - wxDefaultPosition, [50,-1]); - $self->textctrl($textctrl); - - $sizer->Add($slider, 1, wxALIGN_CENTER_VERTICAL, 0); - $sizer->Add($textctrl, 0, wxALIGN_CENTER_VERTICAL, 0); - - EVT_SLIDER($self->parent, $slider, sub { - if (! $self->disable_change_event) { - # wxTextCtrl::SetLabel() does not work on Linux, use wxTextCtrl::SetValue() instead - $self->textctrl->SetValue($self->get_value); - $self->_on_change($self->option->opt_id); - } - }); - EVT_TEXT($self->parent, $textctrl, sub { - my $value = $textctrl->GetValue; - if ($value =~ /^-?\d+(\.\d*)?$/) { - $self->disable_change_event(1); - $self->slider->SetValue($value*$self->scale); - $self->disable_change_event(0); - $self->_on_change($self->option->opt_id); - } - }); - EVT_KILL_FOCUS($textctrl, sub { - $self->_on_kill_focus($self->option->opt_id, @_); - }); -} - -sub set_value { - my ($self, $value) = @_; - - $self->disable_change_event(1); - $self->slider->SetValue($value*$self->scale); - $self->textctrl->SetLabel($self->get_value); - $self->disable_change_event(0); -} - -sub get_value { - my ($self) = @_; - return $self->slider->GetValue/$self->scale; -} - -sub enable { - my ($self) = @_; - - $self->slider->Enable; - $self->textctrl->Enable; - $self->textctrl->SetEditable(1); -} - -sub disable { - my ($self) = @_; - - $self->slider->Disable; - $self->textctrl->Disable; - $self->textctrl->SetEditable(0); -} - -1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index de26578941..d3cca7e530 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -296,14 +296,6 @@ sub new { Slic3r::GUI::register_on_request_update_callback(sub { $self->schedule_background_process; }); -# # Initialize 2D preview canvas -# $self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config}); -# $self->{preview_notebook}->AddPage($self->{canvas}, L('2D')); -# $self->{canvas}->on_select_object($on_select_object); -# $self->{canvas}->on_double_click($on_double_click); -# $self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); }); -# $self->{canvas}->on_instances_moved($on_instances_moved); - # Initialize 3D toolpaths preview if ($Slic3r::GUI::have_OpenGL) { $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{gcode_preview_data}, $self->{config}); @@ -314,12 +306,6 @@ sub new { $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1; } - # Initialize toolpaths preview - if ($Slic3r::GUI::have_OpenGL) { - $self->{toolpaths2D} = Slic3r::GUI::Plater::2DToolpaths->new($self->{preview_notebook}, $self->{print}); - $self->{preview_notebook}->AddPage($self->{toolpaths2D}, L('Layers')); - } - EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub { my $preview = $self->{preview_notebook}->GetCurrentPage; if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D})) { @@ -994,7 +980,6 @@ sub remove { $self->stop_background_process; # Prevent toolpaths preview from rendering while we modify the Print object - $self->{toolpaths2D}->enabled(0) if $self->{toolpaths2D}; $self->{preview3D}->enabled(0) if $self->{preview3D}; # If no object index is supplied, remove the selected one. @@ -1020,7 +1005,6 @@ sub reset { $self->stop_background_process; # Prevent toolpaths preview from rendering while we modify the Print object - $self->{toolpaths2D}->enabled(0) if $self->{toolpaths2D}; $self->{preview3D}->enabled(0) if $self->{preview3D}; @{$self->{objects}} = (); @@ -1382,7 +1366,6 @@ sub async_apply_config { # 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}; # We also need to reload 3D scene because of the wipe tower preview box if ($self->{config}->wipe_tower) { @@ -1418,7 +1401,6 @@ sub start_background_process { sub stop_background_process { my ($self) = @_; $self->{background_slicing_process}->stop(); - $self->{toolpaths2D}->reload_print if $self->{canvas3D}; $self->{preview3D}->reload_print if $self->{preview3D}; } @@ -1525,7 +1507,6 @@ sub export_gcode { # 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}; # in case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth: @@ -1607,7 +1588,6 @@ sub on_process_completed { $self->object_list_changed; # refresh preview - $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; $self->{preview3D}->reload_print if $self->{preview3D}; } @@ -2043,31 +2023,31 @@ sub filament_color_box_lmouse_down } } -sub object_cut_dialog { - my ($self, $obj_idx) = @_; - - if (!defined $obj_idx) { - ($obj_idx, undef) = $self->selected_object; - } - - if (!$Slic3r::GUI::have_OpenGL) { - Slic3r::GUI::show_error($self, L("Please install the OpenGL modules to use this feature (see build instructions).")); - return; - } - - my $dlg = Slic3r::GUI::Plater::ObjectCutDialog->new($self, - object => $self->{objects}[$obj_idx], - model_object => $self->{model}->objects->[$obj_idx], - ); - return unless $dlg->ShowModal == wxID_OK; - - if (my @new_objects = $dlg->NewModelObjects) { - $self->remove($obj_idx); - $self->load_model_objects(grep defined($_), @new_objects); - $self->arrange; - Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas3D}) if $self->{canvas3D}; - } -} +#sub object_cut_dialog { +# my ($self, $obj_idx) = @_; +# +# if (!defined $obj_idx) { +# ($obj_idx, undef) = $self->selected_object; +# } +# +# if (!$Slic3r::GUI::have_OpenGL) { +# Slic3r::GUI::show_error($self, L("Please install the OpenGL modules to use this feature (see build instructions).")); +# return; +# } +# +# my $dlg = Slic3r::GUI::Plater::ObjectCutDialog->new($self, +# object => $self->{objects}[$obj_idx], +# model_object => $self->{model}->objects->[$obj_idx], +# ); +# return unless $dlg->ShowModal == wxID_OK; +# +# if (my @new_objects = $dlg->NewModelObjects) { +# $self->remove($obj_idx); +# $self->load_model_objects(grep defined($_), @new_objects); +# $self->arrange; +# Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas3D}) if $self->{canvas3D}; +# } +#} sub object_settings_dialog { my ($self, $obj_idx) = @_; diff --git a/lib/Slic3r/GUI/Plater/2D.pm b/lib/Slic3r/GUI/Plater/2D.pm deleted file mode 100644 index 88a05c2921..0000000000 --- a/lib/Slic3r/GUI/Plater/2D.pm +++ /dev/null @@ -1,372 +0,0 @@ -# 2D preview on the platter. -# 3D objects are visualized by their convex hulls. - -package Slic3r::GUI::Plater::2D; -use strict; -use warnings; -use utf8; - -use List::Util qw(min max first); -use Slic3r::Geometry qw(X Y scale unscale convex_hull); -use Slic3r::Geometry::Clipper qw(offset JT_ROUND intersection_pl); -use Wx qw(wxTheApp :misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_MOUSE_EVENTS EVT_PAINT EVT_ERASE_BACKGROUND EVT_SIZE); -use base 'Wx::Panel'; - -use Wx::Locale gettext => 'L'; - -sub new { - my $class = shift; - my ($parent, $size, $objects, $model, $config) = @_; - - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, $size, wxTAB_TRAVERSAL); - # This has only effect on MacOS. On Windows and Linux/GTK, the background is painted by $self->repaint(). - $self->SetBackgroundColour(Wx::wxWHITE); - - $self->{objects} = $objects; - $self->{model} = $model; - $self->{config} = $config; - $self->{on_select_object} = sub {}; - $self->{on_double_click} = sub {}; - $self->{on_right_click} = sub {}; - $self->{on_instances_moved} = sub {}; - - $self->{objects_brush} = Wx::Brush->new(Wx::Colour->new(210,210,210), wxSOLID); - $self->{selected_brush} = Wx::Brush->new(Wx::Colour->new(255,128,128), wxSOLID); - $self->{dragged_brush} = Wx::Brush->new(Wx::Colour->new(128,128,255), wxSOLID); - $self->{transparent_brush} = Wx::Brush->new(Wx::Colour->new(0,0,0), wxTRANSPARENT); - $self->{grid_pen} = Wx::Pen->new(Wx::Colour->new(230,230,230), 1, wxSOLID); - $self->{print_center_pen} = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, wxSOLID); - $self->{clearance_pen} = Wx::Pen->new(Wx::Colour->new(0,0,200), 1, wxSOLID); - $self->{skirt_pen} = Wx::Pen->new(Wx::Colour->new(150,150,150), 1, wxSOLID); - - $self->{user_drawn_background} = $^O ne 'darwin'; - - EVT_PAINT($self, \&repaint); - EVT_ERASE_BACKGROUND($self, sub {}) if $self->{user_drawn_background}; - EVT_MOUSE_EVENTS($self, \&mouse_event); - EVT_SIZE($self, sub { - $self->update_bed_size; - $self->Refresh; - }); - - return $self; -} - -sub on_select_object { - my ($self, $cb) = @_; - $self->{on_select_object} = $cb; -} - -sub on_double_click { - my ($self, $cb) = @_; - $self->{on_double_click} = $cb; -} - -sub on_right_click { - my ($self, $cb) = @_; - $self->{on_right_click} = $cb; -} - -sub on_instances_moved { - my ($self, $cb) = @_; - $self->{on_instances_moved} = $cb; -} - -sub repaint { - my ($self, $event) = @_; - - my $dc = Wx::AutoBufferedPaintDC->new($self); - my $size = $self->GetSize; - my @size = ($size->GetWidth, $size->GetHeight); - - if ($self->{user_drawn_background}) { - # On all systems the AutoBufferedPaintDC() achieves double buffering. - # On MacOS the background is erased, on Windows the background is not erased - # and on Linux/GTK the background is erased to gray color. - # Fill DC with the background on Windows & Linux/GTK. - my $brush_background = Wx::Brush->new(Wx::wxWHITE, wxSOLID); - $dc->SetPen(wxWHITE_PEN); - $dc->SetBrush($brush_background); - my $rect = $self->GetUpdateRegion()->GetBox(); - $dc->DrawRectangle($rect->GetLeft(), $rect->GetTop(), $rect->GetWidth(), $rect->GetHeight()); - } - - # draw grid - $dc->SetPen($self->{grid_pen}); - $dc->DrawLine(map @$_, @$_) for @{$self->{grid}}; - - # draw bed - { - $dc->SetPen($self->{print_center_pen}); - $dc->SetBrush($self->{transparent_brush}); - $dc->DrawPolygon($self->scaled_points_to_pixel($self->{bed_polygon}, 1), 0, 0); - } - - # draw print center - if (@{$self->{objects}} && wxTheApp->{app_config}->get("autocenter")) { - my $center = $self->unscaled_point_to_pixel($self->{print_center}); - $dc->SetPen($self->{print_center_pen}); - $dc->DrawLine($center->[X], 0, $center->[X], $size[Y]); - $dc->DrawLine(0, $center->[Y], $size[X], $center->[Y]); - $dc->SetTextForeground(Wx::Colour->new(0,0,0)); - $dc->SetFont(Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL)); - $dc->DrawLabel("X = " . sprintf('%.0f', $self->{print_center}->[X]), Wx::Rect->new(0, 0, $center->[X]*2, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_BOTTOM); - $dc->DrawRotatedText("Y = " . sprintf('%.0f', $self->{print_center}->[Y]), 0, $center->[Y]+15, 90); - } - - # draw frame - if (0) { - $dc->SetPen(wxBLACK_PEN); - $dc->SetBrush($self->{transparent_brush}); - $dc->DrawRectangle(0, 0, @size); - } - - # draw text if plate is empty - if (!@{$self->{objects}}) { - $dc->SetTextForeground(Wx::Colour->new(150,50,50)); - $dc->SetFont(Wx::Font->new(14, wxDEFAULT, wxNORMAL, wxNORMAL)); - $dc->DrawLabel( - join('-', +(localtime)[3,4]) eq '13-8' - ? L('What do you want to print today? ™') # Sept. 13, 2006. The first part ever printed by a RepRap to make another RepRap. - : L('Drag your objects here'), - Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL); - } - - # draw thumbnails - $dc->SetPen(wxBLACK_PEN); - $self->clean_instance_thumbnails; - for my $obj_idx (0 .. $#{$self->{objects}}) { - my $object = $self->{objects}[$obj_idx]; - my $model_object = $self->{model}->objects->[$obj_idx]; - next unless defined $object->thumbnail; - for my $instance_idx (0 .. $#{$model_object->instances}) { - my $instance = $model_object->instances->[$instance_idx]; - next if !defined $object->transformed_thumbnail; - - my $thumbnail = $object->transformed_thumbnail->clone; # in scaled model coordinates - $thumbnail->translate(map scale($_), @{$instance->offset}); - - $object->instance_thumbnails->[$instance_idx] = $thumbnail; - - if (defined $self->{drag_object} && $self->{drag_object}[0] == $obj_idx && $self->{drag_object}[1] == $instance_idx) { - $dc->SetBrush($self->{dragged_brush}); - } elsif ($object->selected) { - $dc->SetBrush($self->{selected_brush}); - } else { - $dc->SetBrush($self->{objects_brush}); - } - foreach my $expolygon (@$thumbnail) { - foreach my $points (@{$expolygon->pp}) { - $dc->DrawPolygon($self->scaled_points_to_pixel($points, 1), 0, 0); - } - } - - if (0) { - # draw bounding box for debugging purposes - my $bb = $model_object->instance_bounding_box($instance_idx); - $bb->scale($self->{scaling_factor}); - # no need to translate by instance offset because instance_bounding_box() does that - my $points = $bb->polygon->pp; - $dc->SetPen($self->{clearance_pen}); - $dc->SetBrush($self->{transparent_brush}); - $dc->DrawPolygon($self->_y($points), 0, 0); - } - - # if sequential printing is enabled and we have more than one object, draw clearance area - if ($self->{config}->complete_objects && (map @{$_->instances}, @{$self->{model}->objects}) > 1) { - my ($clearance) = @{offset([$thumbnail->convex_hull], (scale($self->{config}->extruder_clearance_radius) / 2), JT_ROUND, scale(0.1))}; - $dc->SetPen($self->{clearance_pen}); - $dc->SetBrush($self->{transparent_brush}); - $dc->DrawPolygon($self->scaled_points_to_pixel($clearance, 1), 0, 0); - } - } - } - - # draw skirt - if (@{$self->{objects}} && $self->{config}->skirts) { - my @points = map @{$_->contour}, map @$_, map @{$_->instance_thumbnails}, @{$self->{objects}}; - if (@points >= 3) { - my ($convex_hull) = @{offset([convex_hull(\@points)], scale max($self->{config}->brim_width + $self->{config}->skirt_distance), JT_ROUND, scale(0.1))}; - $dc->SetPen($self->{skirt_pen}); - $dc->SetBrush($self->{transparent_brush}); - $dc->DrawPolygon($self->scaled_points_to_pixel($convex_hull, 1), 0, 0); - } - } - - $event->Skip; -} - -sub mouse_event { - my ($self, $event) = @_; - my $pos = $event->GetPosition; - my $point = $self->point_to_model_units([ $pos->x, $pos->y ]); #]] - if ($event->ButtonDown) { - $self->{on_select_object}->(undef); - # traverse objects and instances in reverse order, so that if they're overlapping - # we get the one that gets drawn last, thus on top (as user expects that to move) - OBJECTS: for my $obj_idx (reverse 0 .. $#{$self->{objects}}) { - my $object = $self->{objects}->[$obj_idx]; - for my $instance_idx (reverse 0 .. $#{ $object->instance_thumbnails }) { - my $thumbnail = $object->instance_thumbnails->[$instance_idx]; - if (defined first { $_->contour->contains_point($point) } @$thumbnail) { - $self->{on_select_object}->($obj_idx); - - if ($event->LeftDown) { - # start dragging - my $instance = $self->{model}->objects->[$obj_idx]->instances->[$instance_idx]; - my $instance_origin = [ map scale($_), @{$instance->offset} ]; - $self->{drag_start_pos} = [ # displacement between the click and the instance origin in scaled model units - $point->x - $instance_origin->[X], - $point->y - $instance_origin->[Y], #- - ]; - $self->{drag_object} = [ $obj_idx, $instance_idx ]; - } elsif ($event->RightDown) { - $self->{on_right_click}->($pos->x, $pos->y); - } - - last OBJECTS; - } - } - } - $self->Refresh; - } elsif ($event->LeftUp) { - if ($self->{drag_object}) { - $self->{on_instances_moved}->(); - } - $self->{drag_start_pos} = undef; - $self->{drag_object} = undef; - $self->SetCursor(wxSTANDARD_CURSOR); - } elsif ($event->LeftDClick) { - $self->{on_double_click}->(); - } elsif ($event->Dragging) { - return if !$self->{drag_start_pos}; # concurrency problems - my ($obj_idx, $instance_idx) = @{ $self->{drag_object} }; - my $model_object = $self->{model}->objects->[$obj_idx]; - $model_object->instances->[$instance_idx]->set_offset( - Slic3r::Pointf->new( - unscale($point->[X] - $self->{drag_start_pos}[X]), - unscale($point->[Y] - $self->{drag_start_pos}[Y]), - )); - $self->Refresh; - } elsif ($event->Moving) { - my $cursor = wxSTANDARD_CURSOR; - if (defined first { $_->contour->contains_point($point) } map @$_, map @{$_->instance_thumbnails}, @{ $self->{objects} }) { - $cursor = Wx::Cursor->new(wxCURSOR_HAND); - } - $self->SetCursor($cursor); - } -} - -sub update_bed_size { - my ($self) = @_; - - # when the canvas is not rendered yet, its GetSize() method returns 0,0 - my $canvas_size = $self->GetSize; - my ($canvas_w, $canvas_h) = ($canvas_size->GetWidth, $canvas_size->GetHeight); - return if $canvas_w == 0; - - # get bed shape polygon - $self->{bed_polygon} = my $polygon = Slic3r::Polygon->new_scale(@{$self->{config}->bed_shape}); - my $bb = $polygon->bounding_box; - my $size = $bb->size; - - # calculate the scaling factor needed for constraining print bed area inside preview - # scaling_factor is expressed in pixel / mm - $self->{scaling_factor} = min($canvas_w / unscale($size->x), $canvas_h / unscale($size->y)); #) - - # calculate the displacement needed to center bed - $self->{bed_origin} = [ - $canvas_w/2 - (unscale($bb->x_max + $bb->x_min)/2 * $self->{scaling_factor}), - $canvas_h - ($canvas_h/2 - (unscale($bb->y_max + $bb->y_min)/2 * $self->{scaling_factor})), - ]; - - # calculate print center - my $center = $bb->center; - $self->{print_center} = [ unscale($center->x), unscale($center->y) ]; #)) - - # cache bed contours and grid - { - my $step = scale 10; # 1cm grid - my @polylines = (); - for (my $x = $bb->x_min - ($bb->x_min % $step) + $step; $x < $bb->x_max; $x += $step) { - push @polylines, Slic3r::Polyline->new([$x, $bb->y_min], [$x, $bb->y_max]); - } - for (my $y = $bb->y_min - ($bb->y_min % $step) + $step; $y < $bb->y_max; $y += $step) { - push @polylines, Slic3r::Polyline->new([$bb->x_min, $y], [$bb->x_max, $y]); - } - @polylines = @{intersection_pl(\@polylines, [$polygon])}; - $self->{grid} = [ map $self->scaled_points_to_pixel([ @$_[0,-1] ], 1), @polylines ]; - } -} - -sub clean_instance_thumbnails { - my ($self) = @_; - - foreach my $object (@{ $self->{objects} }) { - @{ $object->instance_thumbnails } = (); - } -} - -# convert a model coordinate into a pixel coordinate -sub unscaled_point_to_pixel { - my ($self, $point) = @_; - - my $canvas_height = $self->GetSize->GetHeight; - my $zero = $self->{bed_origin}; - return [ - $point->[X] * $self->{scaling_factor} + $zero->[X], - $canvas_height - $point->[Y] * $self->{scaling_factor} + ($zero->[Y] - $canvas_height), - ]; -} - -sub scaled_points_to_pixel { - my ($self, $points, $unscale) = @_; - - my $result = []; - foreach my $point (@$points) { - $point = [ map unscale($_), @$point ] if $unscale; - push @$result, $self->unscaled_point_to_pixel($point); - } - return $result; -} - -sub point_to_model_units { - my ($self, $point) = @_; - - my $zero = $self->{bed_origin}; - return Slic3r::Point->new( - scale ($point->[X] - $zero->[X]) / $self->{scaling_factor}, - scale ($zero->[Y] - $point->[Y]) / $self->{scaling_factor}, - ); -} - -sub reload_scene { - my ($self, $force) = @_; - - if (! $self->IsShown && ! $force) { - $self->{reload_delayed} = 1; - return; - } - - $self->{reload_delayed} = 0; - - foreach my $obj_idx (0..$#{$self->{model}->objects}) { - my $plater_object = $self->{objects}[$obj_idx]; - next if $plater_object->thumbnail; - # The thumbnail is not valid, update it with a convex hull of an object. - $plater_object->thumbnail(Slic3r::ExPolygon::Collection->new); - $plater_object->make_thumbnail($self->{model}, $obj_idx); - $plater_object->transform_thumbnail($self->{model}, $obj_idx); - } - - $self->Refresh; -} - -# Called by the Platter wxNotebook when this page is activated. -sub OnActivate { - my ($self) = @_; - $self->reload_scene(1) if ($self->{reload_delayed}); -} - -1; diff --git a/lib/Slic3r/GUI/Plater/2DToolpaths.pm b/lib/Slic3r/GUI/Plater/2DToolpaths.pm deleted file mode 100644 index bcd1aeed47..0000000000 --- a/lib/Slic3r/GUI/Plater/2DToolpaths.pm +++ /dev/null @@ -1,910 +0,0 @@ -# 2D preview of the tool paths of a single layer, using a thin line. -# OpenGL is used to render the paths. -# Vojtech also added a 2D simulation of under/over extrusion in a single layer. - -package Slic3r::GUI::Plater::2DToolpaths; -use strict; -use warnings; -use utf8; - -use Slic3r::Print::State ':steps'; -use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE wxWANTS_CHARS); -use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN); -use base qw(Wx::Panel Class::Accessor); - -__PACKAGE__->mk_accessors(qw(print enabled)); - -sub new { - my $class = shift; - my ($parent, $print) = @_; - - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS); - $self->SetBackgroundColour(wxWHITE); - - # init GUI elements - my $canvas = $self->{canvas} = Slic3r::GUI::Plater::2DToolpaths::Canvas->new($self, $print); - my $slider = $self->{slider} = Wx::Slider->new( - $self, -1, - 0, # default - 0, # min - # we set max to a bogus non-zero value because the MSW implementation of wxSlider - # will skip drawing the slider if max <= min: - 1, # max - wxDefaultPosition, - wxDefaultSize, - wxVERTICAL | wxSL_INVERSE, - ); - my $z_label = $self->{z_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, - [40,-1], wxALIGN_CENTRE_HORIZONTAL); - $z_label->SetFont($Slic3r::GUI::small_font); - - my $vsizer = Wx::BoxSizer->new(wxVERTICAL); - $vsizer->Add($slider, 1, wxALL | wxEXPAND | wxALIGN_CENTER, 3); - $vsizer->Add($z_label, 0, wxALL | wxEXPAND | wxALIGN_CENTER, 3); - - my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $sizer->Add($canvas, 1, wxALL | wxEXPAND, 0); - $sizer->Add($vsizer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5); - - EVT_SLIDER($self, $slider, sub { - $self->set_z($self->{layers_z}[$slider->GetValue]) - if $self->enabled; - }); - EVT_KEY_DOWN($canvas, sub { - my ($s, $event) = @_; - if ($event->HasModifiers) { - $event->Skip; - } else { - my $key = $event->GetKeyCode; - if ($key == ord('D') || $key == WXK_LEFT) { - # Keys: 'D' or WXK_LEFT - $slider->SetValue($slider->GetValue - 1); - $self->set_z($self->{layers_z}[$slider->GetValue]); - } elsif ($key == ord('U') || $key == WXK_RIGHT) { - # Keys: 'U' or WXK_RIGHT - $slider->SetValue($slider->GetValue + 1); - $self->set_z($self->{layers_z}[$slider->GetValue]); - } elsif ($key >= ord('1') && $key <= ord('3')) { - # Keys: '1' to '3' - $canvas->set_simulation_mode($key - ord('1')); - } else { - $event->Skip; - } - } - }); - - $self->SetSizer($sizer); - $self->SetMinSize($self->GetSize); - $sizer->SetSizeHints($self); - - # init print - $self->{print} = $print; - $self->reload_print; - - return $self; -} - -sub reload_print { - my ($self) = @_; - - # we require that there's at least one object and the posSlice step - # is performed on all of them (this ensures that _shifted_copies was - # populated and we know the number of layers) - if (!$self->print->object_step_done(STEP_SLICE)) { - $self->enabled(0); - $self->{slider}->Hide; - $self->{canvas}->Refresh; # clears canvas - return; - } - - $self->{canvas}->bb($self->print->total_bounding_box); - $self->{canvas}->_dirty(1); - - my %z = (); # z => 1 - foreach my $object (@{$self->{print}->objects}) { - foreach my $layer (@{$object->layers}, @{$object->support_layers}) { - $z{$layer->print_z} = 1; - } - } - $self->enabled(1); - $self->{layers_z} = [ sort { $a <=> $b } keys %z ]; - $self->{slider}->SetRange(0, scalar(@{$self->{layers_z}})-1); - if ((my $z_idx = $self->{slider}->GetValue) <= $#{$self->{layers_z}}) { - $self->set_z($self->{layers_z}[$z_idx]); - } else { - $self->{slider}->SetValue(0); - $self->set_z($self->{layers_z}[0]) if @{$self->{layers_z}}; - } - $self->{slider}->Show; - $self->Layout; -} - -sub set_z { - my ($self, $z) = @_; - - return if !$self->enabled; - $self->{z_label}->SetLabel(sprintf '%.2f', $z); - $self->{canvas}->set_z($z); -} - - -package Slic3r::GUI::Plater::2DToolpaths::Canvas; - -use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS); -use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); -use base qw(Wx::GLCanvas Class::Accessor); -use Wx::GLCanvas qw(:all); -use List::Util qw(min max first); -use Slic3r::Geometry qw(scale epsilon X Y); -use Slic3r::Print::State ':steps'; - -__PACKAGE__->mk_accessors(qw( - print z layers color init - bb - _camera_bb - _dirty - _zoom - _camera_target - _drag_start_xy - _texture_name - _texture_size - _extrusion_simulator - _simulation_mode -)); - -sub new { - my ($class, $parent, $print) = @_; - - my $self = (Wx::wxVERSION >= 3.000003) ? - # The wxWidgets 3.0.3-beta have a bug, they crash with NULL attribute list. - $class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "", - [WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24, 0]) : - $class->SUPER::new($parent); - # Immediatelly force creation of the OpenGL context to consume the static variable s_wglContextAttribs. - $self->GetContext(); - $self->print($print); - $self->_zoom(1); - - # 2D point in model space - $self->_camera_target(Slic3r::Pointf->new(0,0)); - - # Texture for the extrusion simulator. The texture will be allocated / reallocated on Resize. - $self->_texture_name(0); - $self->_texture_size(Slic3r::Point->new(0,0)); - $self->_extrusion_simulator(Slic3r::ExtrusionSimulator->new()); - $self->_simulation_mode(0); - - EVT_PAINT($self, sub { - my $dc = Wx::PaintDC->new($self); - $self->Render($dc); - }); - EVT_SIZE($self, sub { $self->_dirty(1) }); - EVT_IDLE($self, sub { - return unless $self->_dirty; - return if !$self->IsShownOnScreen; - $self->Resize; - $self->Refresh; - }); - EVT_MOUSEWHEEL($self, sub { - my ($self, $e) = @_; - - return if !$self->GetParent->enabled; - - my $old_zoom = $self->_zoom; - - # Calculate the zoom delta and apply it to the current zoom factor - my $zoom = -$e->GetWheelRotation() / $e->GetWheelDelta(); - $zoom = max(min($zoom, 4), -4); - $zoom /= 10; - $self->_zoom($self->_zoom / (1-$zoom)); - $self->_zoom(1.25) if $self->_zoom > 1.25; # prevent from zooming out too much - - { - # In order to zoom around the mouse point we need to translate - # the camera target. This math is almost there but not perfect yet... - my $camera_bb_size = $self->_camera_bb->size; - my $size = Slic3r::Pointf->new($self->GetSizeWH); - my $pos = Slic3r::Pointf->new($e->GetPositionXY); - - # calculate the zooming center in pixel coordinates relative to the viewport center - my $vec = Slic3r::Pointf->new($pos->x - $size->x/2, $pos->y - $size->y/2); #- - - # calculate where this point will end up after applying the new zoom - my $vec2 = $vec->clone; - $vec2->scale($old_zoom / $self->_zoom); - - # move the camera target by the difference of the two positions - $self->_camera_target->translate( - -($vec->x - $vec2->x) * $camera_bb_size->x / $size->x, - ($vec->y - $vec2->y) * $camera_bb_size->y / $size->y, #// - ); - } - - $self->_dirty(1); - }); - EVT_MOUSE_EVENTS($self, \&mouse_event); - - return $self; -} - -sub Destroy { - my ($self) = @_; - - # Deallocate the OpenGL resources. - my $context = $self->GetContext; - if ($context and $self->texture_id) { - $self->SetCurrent($context); - glDeleteTextures(1, ($self->texture_id)); - $self->SetCurrent(0); - $self->texture_id(0); - $self->texture_size(new Slic3r::Point(0, 0)); - } - return $self->SUPER::Destroy; -} - -sub mouse_event { - my ($self, $e) = @_; - - return if !$self->GetParent->enabled; - - my $pos = Slic3r::Pointf->new($e->GetPositionXY); - if ($e->Entering && (&Wx::wxMSW || $^O eq 'linux')) { - # wxMSW and Linux needs focus in order to catch key events - $self->SetFocus; - } elsif ($e->Dragging) { - if ($e->LeftIsDown || $e->MiddleIsDown || $e->RightIsDown) { - # if dragging, translate view - - if (defined $self->_drag_start_xy) { - my $move = $self->_drag_start_xy->vector_to($pos); # in pixels - - # get viewport and camera size in order to convert pixel to model units - my ($x, $y) = $self->GetSizeWH; - my $camera_bb_size = $self->_camera_bb->size; - - # compute translation in model units - $self->_camera_target->translate( - -$move->x * $camera_bb_size->x / $x, - $move->y * $camera_bb_size->y / $y, # /** - ); - - $self->_dirty(1); - } - $self->_drag_start_xy($pos); - } - } elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) { - $self->_drag_start_xy(undef); - } else { - $e->Skip(); - } -} - -sub set_z { - my ($self, $z) = @_; - - my $print = $self->print; - - # can we have interlaced layers? - my $interlaced = (defined first { $_->config->support_material } @{$print->objects}) - || (defined first { $_->config->infill_every_layers > 1 } @{$print->regions}); - - my $max_layer_height = $print->max_allowed_layer_height; - - my @layers = (); - foreach my $object (@{$print->objects}) { - foreach my $layer (@{$object->layers}, @{$object->support_layers}) { - if ($interlaced) { - push @layers, $layer - if $z > ($layer->print_z - $max_layer_height - epsilon) - && $z <= $layer->print_z + epsilon; - } else { - push @layers, $layer if abs($layer->print_z - $z) < epsilon; - } - } - } - - # reverse layers so that we draw the lowermost (i.e. current) on top - $self->z($z); - $self->layers([ reverse @layers ]); - $self->Refresh; -} - -sub set_simulation_mode -{ - my ($self, $mode) = @_; - $self->_simulation_mode($mode); - $self->_dirty(1); - $self->Refresh; -} - -sub Render { - my ($self, $dc) = @_; - - # prevent calling SetCurrent() when window is not shown yet - return unless $self->IsShownOnScreen; - return unless my $context = $self->GetContext; - $self->SetCurrent($context); - $self->InitGL; - - glClearColor(1, 1, 1, 0); - glClear(GL_COLOR_BUFFER_BIT); - - if (!$self->GetParent->enabled || !$self->layers) { - $self->SwapBuffers; - return; - } - - glDisable(GL_DEPTH_TEST); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - if ($self->_simulation_mode and $self->_texture_name and $self->_texture_size->x() > 0 and $self->_texture_size->y() > 0) { - $self->_simulate_extrusion(); - my ($x, $y) = $self->GetSizeWH; - glEnable(GL_TEXTURE_2D); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_REPLACE); - glBindTexture(GL_TEXTURE_2D, $self->_texture_name); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexImage2D_c(GL_TEXTURE_2D, - 0, # level (0 normal, heighr is form mip-mapping) - GL_RGBA, # internal format - $self->_texture_size->x(), $self->_texture_size->y(), - 0, # border - GL_RGBA, # format RGBA color data - GL_UNSIGNED_BYTE, # unsigned byte data - $self->_extrusion_simulator->image_ptr()); # ptr to texture data - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0, 1, 0, 1, 0, 1); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); - glVertex2f(0, 0); - glTexCoord2f($x/$self->_texture_size->x(), 0); - glVertex2f(1, 0); - glTexCoord2f($x/$self->_texture_size->x(), $y/$self->_texture_size->y()); - glVertex2f(1, 1); - glTexCoord2f(0, $y/$self->_texture_size->y()); - glVertex2f(0, 1); - glEnd(); - glPopMatrix(); - glBindTexture(GL_TEXTURE_2D, 0); - } - - # anti-alias - if (0) { - glEnable(GL_LINE_SMOOTH); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); - glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE); - } - - # Tesselator triangulates polygons with holes on the fly for the rendering purposes only. - my $tess; - if ($self->_simulation_mode() == 0 and !(&Wx::wxMSW && $OpenGL::VERSION < 0.6704)) { - # We can't use the GLU tesselator on MSW with older OpenGL versions - # because of an upstream bug: - # http://sourceforge.net/p/pogl/bugs/16/ - $tess = gluNewTess(); - gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_END, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_VERTEX, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_COMBINE, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_ERROR, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_EDGE_FLAG, 'DEFAULT'); - } - - foreach my $layer (@{$self->layers}) { - my $object = $layer->object; - - # only draw the slice for the current layer - next unless abs($layer->print_z - $self->z) < epsilon; - - # draw slice contour - glLineWidth(1); - foreach my $copy (@{ $object->_shifted_copies }) { - glPushMatrix(); - glTranslatef(@$copy, 0); - - foreach my $slice (@{$layer->slices}) { - glColor3f(0.95, 0.95, 0.95); - - if ($tess) { - gluTessBeginPolygon($tess); - foreach my $polygon (@$slice) { - gluTessBeginContour($tess); - gluTessVertex_p($tess, @$_, 0) for @$polygon; - gluTessEndContour($tess); - } - gluTessEndPolygon($tess); - } - - glColor3f(0.9, 0.9, 0.9); - foreach my $polygon (@$slice) { - foreach my $line (@{$polygon->lines}) { - glBegin(GL_LINES); - glVertex2f(@{$line->a}); - glVertex2f(@{$line->b}); - glEnd(); - } - } - } - glPopMatrix(); - } - } - - my $skirt_drawn = 0; - my $brim_drawn = 0; - foreach my $layer (@{$self->layers}) { - my $object = $layer->object; - my $print_z = $layer->print_z; - - # draw brim - if ($self->print->step_done(STEP_BRIM) && $layer->id == 0 && !$brim_drawn) { - $self->color([0, 0, 0]); - $self->_draw(undef, $print_z, $_) for @{$self->print->brim}; - $brim_drawn = 1; - } - if ($self->print->step_done(STEP_SKIRT) - && ($self->print->has_infinite_skirt() || $self->print->config->skirt_height > $layer->id) - && !$skirt_drawn) { - $self->color([0, 0, 0]); - $self->_draw(undef, $print_z, $_) for @{$self->print->skirt}; - $skirt_drawn = 1; - } - - foreach my $layerm (@{$layer->regions}) { - if ($object->step_done(STEP_PERIMETERS)) { - $self->color([0.7, 0, 0]); - $self->_draw($object, $print_z, $_) for map @$_, @{$layerm->perimeters}; - } - - if ($object->step_done(STEP_INFILL)) { - $self->color([0, 0, 0.7]); - $self->_draw($object, $print_z, $_) for map @$_, @{$layerm->fills}; - } - } - - if ($object->step_done(STEP_SUPPORTMATERIAL)) { - if ($layer->isa('Slic3r::Layer::Support')) { - $self->color([0, 0, 0]); - $self->_draw($object, $print_z, $_) for @{$layer->support_fills}; - } - } - } - - gluDeleteTess($tess) if $tess; - $self->SwapBuffers; -} - -sub _draw { - my ($self, $object, $print_z, $path) = @_; - - my @paths = ($path->isa('Slic3r::ExtrusionLoop') || $path->isa('Slic3r::ExtrusionMultiPath')) - ? @$path - : ($path); - - $self->_draw_path($object, $print_z, $_) for @paths; -} - -sub _draw_path { - my ($self, $object, $print_z, $path) = @_; - - return if $print_z - $path->height > $self->z - epsilon; - - if (abs($print_z - $self->z) < epsilon) { - glColor3f(@{$self->color}); - } else { - glColor3f(0.8, 0.8, 0.8); - } - - glLineWidth(1); - - if (defined $object) { - foreach my $copy (@{ $object->_shifted_copies }) { - glPushMatrix(); - glTranslatef(@$copy, 0); - foreach my $line (@{$path->polyline->lines}) { - glBegin(GL_LINES); - glVertex2f(@{$line->a}); - glVertex2f(@{$line->b}); - glEnd(); - } - glPopMatrix(); - } - } else { - foreach my $line (@{$path->polyline->lines}) { - glBegin(GL_LINES); - glVertex2f(@{$line->a}); - glVertex2f(@{$line->b}); - glEnd(); - } - } -} - -sub _simulate_extrusion { - my ($self) = @_; - $self->_extrusion_simulator->reset_accumulator(); - foreach my $layer (@{$self->layers}) { - if (abs($layer->print_z - $self->z) < epsilon) { - my $object = $layer->object; - my @shifts = (defined $object) ? @{$object->_shifted_copies} : (Slic3r::Point->new(0, 0)); - foreach my $layerm (@{$layer->regions}) { - my @extrusions = (); - if ($object->step_done(STEP_PERIMETERS)) { - push @extrusions, @$_ for @{$layerm->perimeters}; - } - if ($object->step_done(STEP_INFILL)) { - push @extrusions, @$_ for @{$layerm->fills}; - } - foreach my $extrusion_entity (@extrusions) { - my @paths = ($extrusion_entity->isa('Slic3r::ExtrusionLoop') || $extrusion_entity->isa('Slic3r::ExtrusionMultiPath')) - ? @$extrusion_entity - : ($extrusion_entity); - foreach my $path (@paths) { - print "width: ", $path->width, - " height: ", $path->height, - " mm3_per_mm: ", $path->mm3_per_mm, - " height2: ", $path->mm3_per_mm / $path->height, - "\n"; - $self->_extrusion_simulator->extrude_to_accumulator($path, $_, $self->_simulation_mode()) for @shifts; - } - } - } - } - } - $self->_extrusion_simulator->evaluate_accumulator($self->_simulation_mode()); -} - -sub InitGL { - my $self = shift; - - return if $self->init; - return unless $self->GetContext; - - my $texture_id = 0; - ($texture_id) = glGenTextures_p(1); - $self->_texture_name($texture_id); - $self->init(1); -} - -sub GetContext { - my ($self) = @_; - return $self->{context} ||= Wx::GLContext->new($self); -} - -sub SetCurrent { - my ($self, $context) = @_; - return $self->SUPER::SetCurrent($context); -} - -sub Resize { - my ($self) = @_; - - return unless $self->GetContext; - return unless $self->bb; - $self->_dirty(0); - - $self->SetCurrent($self->GetContext); - my ($x, $y) = $self->GetSizeWH; - - if ($self->_texture_size->x() < $x or $self->_texture_size->y() < $y) { - # Allocate a large enough OpenGL texture with power of 2 dimensions. - $self->_texture_size->set_x(1) if ($self->_texture_size->x() == 0); - $self->_texture_size->set_y(1) if ($self->_texture_size->y() == 0); - $self->_texture_size->set_x($self->_texture_size->x() * 2) while ($self->_texture_size->x() < $x); - $self->_texture_size->set_y($self->_texture_size->y() * 2) while ($self->_texture_size->y() < $y); - #print "screen size ", $x, "x", $y; - #print "texture size ", $self->_texture_size->x(), "x", $self->_texture_size->y(); - # Initialize an empty texture. - glBindTexture(GL_TEXTURE_2D, $self->_texture_name); - if (1) { - glTexImage2D_c(GL_TEXTURE_2D, - 0, # level (0 normal, heighr is form mip-mapping) - GL_RGBA, # internal format - $self->_texture_size->x(), $self->_texture_size->y(), - 0, # border - GL_RGBA, # format RGBA color data - GL_UNSIGNED_BYTE, # unsigned byte data - 0); # ptr to texture data - } - glBindTexture(GL_TEXTURE_2D, 0); - $self->_extrusion_simulator->set_image_size($self->_texture_size); - } - $self->_extrusion_simulator->set_viewport(Slic3r::Geometry::BoundingBox->new_from_points( - [Slic3r::Point->new(0, 0), Slic3r::Point->new($x, $y)])); - - glViewport(0, 0, $x, $y); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - my $bb = $self->bb->clone; - - # rescale in dependence of window aspect ratio - my $bb_size = $bb->size; - my $ratio_x = ($x != 0.0) ? $bb_size->x / $x : 1.0; - my $ratio_y = ($y != 0.0) ? $bb_size->y / $y : 1.0; - - if ($ratio_y < $ratio_x) { - if ($ratio_y != 0.0) { - my $new_size_y = $bb_size->y * $ratio_x / $ratio_y; - my $half_delta_size_y = 0.5 * ($new_size_y - $bb_size->y); - $bb->set_y_min($bb->y_min - $half_delta_size_y); - $bb->set_y_max($bb->y_max + $half_delta_size_y); - } - } elsif ($ratio_x < $ratio_y) { - if ($ratio_x != 0.0) { - my $new_size_x = $bb_size->x * $ratio_y / $ratio_x; - my $half_delta_size_x = 0.5 * ($new_size_x - $bb_size->x); - $bb->set_x_min($bb->x_min - $half_delta_size_x); - $bb->set_x_max($bb->x_max + $half_delta_size_x); - } - } - - # center bounding box around origin before scaling it - my $bb_center = $bb->center; - $bb->translate(@{$bb_center->negative}); - - # scale bounding box according to zoom factor - $bb->scale($self->_zoom); - - # reposition bounding box around original center - $bb->translate(@{$bb_center}); - - # translate camera - $bb->translate(@{$self->_camera_target}); - -# # keep camera_bb within total bb -# # (i.e. prevent user from panning outside the bounding box) -# { -# my @translate = (0,0); -# if ($bb->x_min < $self->bb->x_min) { -# $translate[X] += $self->bb->x_min - $bb->x_min; -# } -# if ($bb->y_min < $self->bb->y_min) { -# $translate[Y] += $self->bb->y_min - $bb->y_min; -# } -# if ($bb->x_max > $self->bb->x_max) { -# $translate[X] -= $bb->x_max - $self->bb->x_max; -# } -# if ($bb->y_max > $self->bb->y_max) { -# $translate[Y] -= $bb->y_max - $self->bb->y_max; -# } -# $self->_camera_target->translate(@translate); -# $bb->translate(@translate); -# } - - # save camera - $self->_camera_bb($bb); - - my ($x1, $y1, $x2, $y2) = ($bb->x_min, $bb->y_min, $bb->x_max, $bb->y_max); - if (($x2 - $x1)/($y2 - $y1) > $x/$y) { - # adjust Y - my $new_y = $y * ($x2 - $x1) / $x; - $y1 = ($y2 + $y1)/2 - $new_y/2; - $y2 = $y1 + $new_y; - } else { - my $new_x = $x * ($y2 - $y1) / $y; - $x1 = ($x2 + $x1)/2 - $new_x/2; - $x2 = $x1 + $new_x; - } - glOrtho($x1, $x2, $y1, $y2, 0, 1); - - # Set the adjusted bounding box at the extrusion simulator. - #print "Scene bbox ", $bb->x_min, ",", $bb->y_min, " ", $bb->x_max, ",", $bb->y_max, "\n"; - #print "Setting simulator bbox ", $x1, ",", $y1, " ", $x2, ",", $y2, "\n"; - $self->_extrusion_simulator->set_bounding_box( - Slic3r::Geometry::BoundingBox->new_from_points( - [Slic3r::Point->new($x1, $y1), Slic3r::Point->new($x2, $y2)])); - - glMatrixMode(GL_MODELVIEW); -} - -# Thick line drawing is not used anywhere. Probably not tested? -sub line { - my ( - $x1, $y1, $x2, $y2, # coordinates of the line - $w, # width/thickness of the line in pixel - $Cr, $Cg, $Cb, # RGB color components - $Br, $Bg, $Bb, # color of background when alphablend=false - # Br=alpha of color when alphablend=true - $alphablend, # use alpha blend or not - ) = @_; - - my $t; - my $R; - my $f = $w - int($w); - my $A; - - if ($alphablend) { - $A = $Br; - } else { - $A = 1; - } - - # determine parameters t,R - if ($w >= 0 && $w < 1) { - $t = 0.05; $R = 0.48 + 0.32 * $f; - if (!$alphablend) { - $Cr += 0.88 * (1-$f); - $Cg += 0.88 * (1-$f); - $Cb += 0.88 * (1-$f); - $Cr = 1.0 if ($Cr > 1.0); - $Cg = 1.0 if ($Cg > 1.0); - $Cb = 1.0 if ($Cb > 1.0); - } else { - $A *= $f; - } - } elsif ($w >= 1.0 && $w < 2.0) { - $t = 0.05 + $f*0.33; $R = 0.768 + 0.312*$f; - } elsif ($w >= 2.0 && $w < 3.0) { - $t = 0.38 + $f*0.58; $R = 1.08; - } elsif ($w >= 3.0 && $w < 4.0) { - $t = 0.96 + $f*0.48; $R = 1.08; - } elsif ($w >= 4.0 && $w < 5.0) { - $t= 1.44 + $f*0.46; $R = 1.08; - } elsif ($w >= 5.0 && $w < 6.0) { - $t= 1.9 + $f*0.6; $R = 1.08; - } elsif ($w >= 6.0) { - my $ff = $w - 6.0; - $t = 2.5 + $ff*0.50; $R = 1.08; - } - #printf( "w=%f, f=%f, C=%.4f\n", $w, $f, $C); - - # determine angle of the line to horizontal - my $tx = 0; my $ty = 0; # core thinkness of a line - my $Rx = 0; my $Ry = 0; # fading edge of a line - my $cx = 0; my $cy = 0; # cap of a line - my $ALW = 0.01; - my $dx = $x2 - $x1; - my $dy = $y2 - $y1; - if (abs($dx) < $ALW) { - # vertical - $tx = $t; $ty = 0; - $Rx = $R; $Ry = 0; - if ($w > 0.0 && $w < 1.0) { - $tx *= 8; - } elsif ($w == 1.0) { - $tx *= 10; - } - } elsif (abs($dy) < $ALW) { - #horizontal - $tx = 0; $ty = $t; - $Rx = 0; $Ry = $R; - if ($w > 0.0 && $w < 1.0) { - $ty *= 8; - } elsif ($w == 1.0) { - $ty *= 10; - } - } else { - if ($w < 3) { # approximate to make things even faster - my $m = $dy/$dx; - # and calculate tx,ty,Rx,Ry - if ($m > -0.4142 && $m <= 0.4142) { - # -22.5 < $angle <= 22.5, approximate to 0 (degree) - $tx = $t * 0.1; $ty = $t; - $Rx = $R * 0.6; $Ry = $R; - } elsif ($m > 0.4142 && $m <= 2.4142) { - # 22.5 < $angle <= 67.5, approximate to 45 (degree) - $tx = $t * -0.7071; $ty = $t * 0.7071; - $Rx = $R * -0.7071; $Ry = $R * 0.7071; - } elsif ($m > 2.4142 || $m <= -2.4142) { - # 67.5 < $angle <= 112.5, approximate to 90 (degree) - $tx = $t; $ty = $t*0.1; - $Rx = $R; $Ry = $R*0.6; - } elsif ($m > -2.4142 && $m < -0.4142) { - # 112.5 < angle < 157.5, approximate to 135 (degree) - $tx = $t * 0.7071; $ty = $t * 0.7071; - $Rx = $R * 0.7071; $Ry = $R * 0.7071; - } else { - # error in determining angle - printf("error in determining angle: m=%.4f\n", $m); - } - } else { # calculate to exact - $dx= $y1 - $y2; - $dy= $x2 - $x1; - my $L = sqrt($dx*$dx + $dy*$dy); - $dx /= $L; - $dy /= $L; - $cx = -0.6*$dy; $cy=0.6*$dx; - $tx = $t*$dx; $ty = $t*$dy; - $Rx = $R*$dx; $Ry = $R*$dy; - } - } - - # draw the line by triangle strip - glBegin(GL_TRIANGLE_STRIP); - if (!$alphablend) { - glColor3f($Br, $Bg, $Bb); - } else { - glColor4f($Cr, $Cg, $Cb, 0); - } - glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry); # fading edge - glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry); - - if (!$alphablend) { - glColor3f($Cr, $Cg, $Cb); - } else { - glColor4f($Cr, $Cg, $Cb, $A); - } - glVertex2f($x1 - $tx, $y1 - $ty); # core - glVertex2f($x2 - $tx, $y2 - $ty); - glVertex2f($x1 + $tx, $y1 + $ty); - glVertex2f($x2 + $tx, $y2 + $ty); - - if ((abs($dx) < $ALW || abs($dy) < $ALW) && $w <= 1.0) { - # printf("skipped one fading edge\n"); - } else { - if (!$alphablend) { - glColor3f($Br, $Bg, $Bb); - } else { - glColor4f($Cr, $Cg, $Cb, 0); - } - glVertex2f($x1 + $tx+ $Rx, $y1 + $ty + $Ry); # fading edge - glVertex2f($x2 + $tx+ $Rx, $y2 + $ty + $Ry); - } - glEnd(); - - # cap - if ($w < 3) { - # do not draw cap - } else { - # draw cap - glBegin(GL_TRIANGLE_STRIP); - if (!$alphablend) { - glColor3f($Br, $Bg, $Bb); - } else { - glColor4f($Cr, $Cg, $Cb, 0); - } - glVertex2f($x1 - $Rx + $cx, $y1 - $Ry + $cy); - glVertex2f($x1 + $Rx + $cx, $y1 + $Ry + $cy); - glColor3f($Cr, $Cg, $Cb); - glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry); - glVertex2f($x1 + $tx + $Rx, $y1 + $ty + $Ry); - glEnd(); - glBegin(GL_TRIANGLE_STRIP); - if (!$alphablend) { - glColor3f($Br, $Bg, $Bb); - } else { - glColor4f($Cr, $Cg, $Cb, 0); - } - glVertex2f($x2 - $Rx - $cx, $y2 - $Ry - $cy); - glVertex2f($x2 + $Rx - $cx, $y2 + $Ry - $cy); - glColor3f($Cr, $Cg, $Cb); - glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry); - glVertex2f($x2 + $tx + $Rx, $y2 + $ty + $Ry); - glEnd(); - } -} - - -package Slic3r::GUI::Plater::2DToolpaths::Dialog; - -use Wx qw(:dialog :id :misc :sizer); -use Wx::Event qw(EVT_CLOSE); -use base 'Wx::Dialog'; - -sub new { - my $class = shift; - my ($parent, $print) = @_; - my $self = $class->SUPER::new($parent, -1, "Toolpaths", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - $sizer->Add(Slic3r::GUI::Plater::2DToolpaths->new($self, $print), 1, wxEXPAND, 0); - $self->SetSizer($sizer); - $self->SetMinSize($self->GetSize); - - # needed to actually free memory - EVT_CLOSE($self, sub { - $self->EndModal(wxID_OK); - $self->Destroy; - }); - - return $self; -} - -1; diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm deleted file mode 100644 index 0685b41006..0000000000 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ /dev/null @@ -1,221 +0,0 @@ -# Generate an anonymous or "lambda" 3D object. This gets used with the Add Generic option in Settings. -# - -package Slic3r::GUI::Plater::LambdaObjectDialog; -use strict; -use warnings; -use utf8; - -use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL wxCB_READONLY wxTE_PROCESS_TAB); -use Wx::Event qw(EVT_CLOSE EVT_BUTTON EVT_COMBOBOX EVT_TEXT); -use Scalar::Util qw(looks_like_number); -use base 'Wx::Dialog'; - -sub new { - my $class = shift; - my ($parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, "Lambda Object", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - # Note whether the window was already closed, so a pending update is not executed. - $self->{already_closed} = 0; - $self->{object_parameters} = { - type => "box", - dim => [1, 1, 1], - cyl_r => 1, - cyl_h => 1, - sph_rho => 1.0, - slab_h => 1.0, - slab_z => 0.0, - }; - - $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); - my $button_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - my $button_ok = $self->CreateStdDialogButtonSizer(wxOK); - my $button_cancel = $self->CreateStdDialogButtonSizer(wxCANCEL); - $button_sizer->Add($button_ok); - $button_sizer->Add($button_cancel); - EVT_BUTTON($self, wxID_OK, sub { - # validate user input - return if !$self->CanClose; - - $self->EndModal(wxID_OK); - $self->Destroy; - }); - EVT_BUTTON($self, wxID_CANCEL, sub { - # validate user input - return if !$self->CanClose; - - $self->EndModal(wxID_CANCEL); - $self->Destroy; - }); - - my $optgroup_box; - $optgroup_box = $self->{optgroup_box} = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Add Cube...', - on_change => sub { - # Do validation - my ($opt_id) = @_; - if ($opt_id == 0 || $opt_id == 1 || $opt_id == 2) { - if (!looks_like_number($optgroup_box->get_value($opt_id))) { - return 0; - } - } - $self->{object_parameters}->{dim}[$opt_id] = $optgroup_box->get_value($opt_id); - }, - label_width => 100, - ); - my @options = ("box", "slab", "cylinder", "sphere"); - $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); - - $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 0, - label => 'L', - type => 'f', - default => '1', - )); - $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 1, - label => 'W', - type => 'f', - default => '1', - )); - $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 2, - label => 'H', - type => 'f', - default => '1', - )); - - my $optgroup_cylinder; - $optgroup_cylinder = $self->{optgroup_cylinder} = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Add Cylinder...', - on_change => sub { - # Do validation - my ($opt_id) = @_; - if ($opt_id eq 'cyl_r' || $opt_id eq 'cyl_h') { - if (!looks_like_number($optgroup_cylinder->get_value($opt_id))) { - return 0; - } - } - $self->{object_parameters}->{$opt_id} = $optgroup_cylinder->get_value($opt_id); - }, - label_width => 100, - ); - - $optgroup_cylinder->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => "cyl_r", - label => 'Radius', - type => 'f', - default => '1', - )); - $optgroup_cylinder->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => "cyl_h", - label => 'Height', - type => 'f', - default => '1', - )); - - my $optgroup_sphere; - $optgroup_sphere = $self->{optgroup_sphere} = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Add Sphere...', - on_change => sub { - # Do validation - my ($opt_id) = @_; - if ($opt_id eq 'sph_rho') { - if (!looks_like_number($optgroup_sphere->get_value($opt_id))) { - return 0; - } - } - $self->{object_parameters}->{$opt_id} = $optgroup_sphere->get_value($opt_id); - }, - label_width => 100, - ); - - $optgroup_sphere->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => "sph_rho", - label => 'Rho', - type => 'f', - default => '1', - )); - - my $optgroup_slab; - $optgroup_slab = $self->{optgroup_slab} = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Add Slab...', - on_change => sub { - # Do validation - my ($opt_id) = @_; - if ($opt_id eq 'slab_z' || $opt_id eq 'slab_h') { - if (!looks_like_number($optgroup_slab->get_value($opt_id))) { - return 0; - } - } - $self->{object_parameters}->{$opt_id} = $optgroup_slab->get_value($opt_id); - }, - label_width => 100, - ); - $optgroup_slab->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => "slab_h", - label => 'H', - type => 'f', - default => '1', - )); - $optgroup_slab->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => "slab_z", - label => 'Initial Z', - type => 'f', - default => '0', - )); - - - EVT_COMBOBOX($self, 1, sub{ - $self->{object_parameters}->{type} = $self->{type}->GetValue(); - $self->_update_ui; - }); - - - $self->{sizer}->Add($self->{type}, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->{sizer}->Add($optgroup_box->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->{sizer}->Add($optgroup_cylinder->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->{sizer}->Add($optgroup_sphere->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->{sizer}->Add($optgroup_slab->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->{sizer}->Add($button_sizer,0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->_update_ui; - - $self->SetSizer($self->{sizer}); - $self->{sizer}->Fit($self); - $self->{sizer}->SetSizeHints($self); - - - return $self; -} -sub CanClose { - return 1; -} -sub ObjectParameter { - my ($self) = @_; - return $self->{object_parameters}; -} - -sub _update_ui { - my ($self) = @_; - $self->{sizer}->Hide($self->{optgroup_cylinder}->sizer); - $self->{sizer}->Hide($self->{optgroup_slab}->sizer); - $self->{sizer}->Hide($self->{optgroup_box}->sizer); - $self->{sizer}->Hide($self->{optgroup_sphere}->sizer); - if ($self->{type}->GetValue eq "box") { - $self->{sizer}->Show($self->{optgroup_box}->sizer); - } elsif ($self->{type}->GetValue eq "cylinder") { - $self->{sizer}->Show($self->{optgroup_cylinder}->sizer); - } elsif ($self->{type}->GetValue eq "slab") { - $self->{sizer}->Show($self->{optgroup_slab}->sizer); - } elsif ($self->{type}->GetValue eq "sphere") { - $self->{sizer}->Show($self->{optgroup_sphere}->sizer); - } - $self->{sizer}->Fit($self); - $self->{sizer}->SetSizeHints($self); - -} -1; diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm deleted file mode 100644 index 26a6fdec32..0000000000 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ /dev/null @@ -1,284 +0,0 @@ -# Cut an object at a Z position, keep either the top or the bottom of the object. -# This dialog gets opened with the "Cut..." button above the platter. - -package Slic3r::GUI::Plater::ObjectCutDialog; -use strict; -use warnings; -use utf8; - -use Slic3r::Geometry qw(PI X); -use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_CLOSE EVT_BUTTON); -use List::Util qw(max); -use base 'Wx::Dialog'; - -sub new { - my ($class, $parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, $params{object}->name, wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - $self->{model_object} = $params{model_object}; - $self->{new_model_objects} = []; - # Mark whether the mesh cut is valid. - # If not, it needs to be recalculated by _update() on wxTheApp->CallAfter() or on exit of the dialog. - $self->{mesh_cut_valid} = 0; - # Note whether the window was already closed, so a pending update is not executed. - $self->{already_closed} = 0; - - # cut options - $self->{cut_options} = { - z => 0, - keep_upper => 1, - keep_lower => 1, - rotate_lower => 1, -# preview => 1, -# Disabled live preview by default as it is not stable and/or the calculation takes too long for interactive usage. - preview => 0, - }; - - my $optgroup; - $optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Cut', - on_change => sub { - my ($opt_id) = @_; - # There seems to be an issue with wxWidgets 3.0.2/3.0.3, where the slider - # genates tens of events for a single value change. - # Only trigger the recalculation if the value changes - # or a live preview was activated and the mesh cut is not valid yet. - if ($self->{cut_options}{$opt_id} != $optgroup->get_value($opt_id) || - ! $self->{mesh_cut_valid} && $self->_life_preview_active()) { - $self->{cut_options}{$opt_id} = $optgroup->get_value($opt_id); - $self->{mesh_cut_valid} = 0; - wxTheApp->CallAfter(sub { - $self->_update; - }); - } - }, - label_width => 120, - ); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'z', - type => 'slider', - label => 'Z', - default => $self->{cut_options}{z}, - min => 0, - max => $self->{model_object}->bounding_box->size->z, - full_width => 1, - )); - { - my $line = Slic3r::GUI::OptionsGroup::Line->new( - label => 'Keep', - ); - $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'keep_upper', - type => 'bool', - label => 'Upper part', - default => $self->{cut_options}{keep_upper}, - )); - $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'keep_lower', - type => 'bool', - label => 'Lower part', - default => $self->{cut_options}{keep_lower}, - )); - $optgroup->append_line($line); - } - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'rotate_lower', - label => 'Rotate lower part upwards', - type => 'bool', - tooltip => 'If enabled, the lower part will be rotated by 180° so that the flat cut surface lies on the print bed.', - default => $self->{cut_options}{rotate_lower}, - )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'preview', - label => 'Show preview', - type => 'bool', - tooltip => 'If enabled, object will be cut in real time.', - default => $self->{cut_options}{preview}, - )); - { - my $cut_button_sizer = Wx::BoxSizer->new(wxVERTICAL); - $self->{btn_cut} = Wx::Button->new($self, -1, "Perform cut", wxDefaultPosition, wxDefaultSize); - $cut_button_sizer->Add($self->{btn_cut}, 0, wxALIGN_RIGHT | wxALL, 10); - $optgroup->append_line(Slic3r::GUI::OptionsGroup::Line->new( - sizer => $cut_button_sizer, - )); - } - - # left pane with tree - my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); - $left_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - - # right pane with preview canvas - my $canvas; - if ($Slic3r::GUI::have_OpenGL) { - $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); - Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]); - Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); - Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size })); - $canvas->SetSize([500,500]); - $canvas->SetMinSize($canvas->GetSize); - Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->{config}); - Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($canvas, 1); - } - - $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); - $self->{sizer}->Add($left_sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10); - $self->{sizer}->Add($canvas, 1, wxEXPAND | wxALL, 0) if $canvas; - - $self->SetSizer($self->{sizer}); - $self->SetMinSize($self->GetSize); - $self->{sizer}->SetSizeHints($self); - - EVT_BUTTON($self, $self->{btn_cut}, sub { - # Recalculate the cut if the preview was not active. - $self->_perform_cut() unless $self->{mesh_cut_valid}; - - # Adjust position / orientation of the split object halves. - if ($self->{new_model_objects}{lower}) { - if ($self->{cut_options}{rotate_lower}) { - $self->{new_model_objects}{lower}->rotate(PI, Slic3r::Pointf3->new(1,0,0)); - $self->{new_model_objects}{lower}->center_around_origin; # align to Z = 0 - } - } - if ($self->{new_model_objects}{upper}) { - $self->{new_model_objects}{upper}->center_around_origin; # align to Z = 0 - } - - # Note that the window was already closed, so a pending update will not be executed. - $self->{already_closed} = 1; - $self->EndModal(wxID_OK); - $self->{canvas}->Destroy; - $self->Destroy(); - }); - - EVT_CLOSE($self, sub { - # Note that the window was already closed, so a pending update will not be executed. - $self->{already_closed} = 1; - $self->EndModal(wxID_CANCEL); - $self->{canvas}->Destroy; - $self->Destroy(); - }); - - $self->_update; - - return $self; -} - -# scale Z down to original size since we're using the transformed mesh for 3D preview -# and cut dialog but ModelObject::cut() needs Z without any instance transformation -sub _mesh_slice_z_pos -{ - my ($self) = @_; - return $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor; -} - -# Only perform live preview if just a single part of the object shall survive. -sub _life_preview_active -{ - my ($self) = @_; - return $self->{cut_options}{preview} && ($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower}); -} - -# Slice the mesh, keep the top / bottom part. -sub _perform_cut -{ - my ($self) = @_; - - # Early exit. If the cut is valid, don't recalculate it. - return if $self->{mesh_cut_valid}; - - my $z = $self->_mesh_slice_z_pos(); - - my ($new_model) = $self->{model_object}->cut($z); - my ($upper_object, $lower_object) = @{$new_model->objects}; - $self->{new_model} = $new_model; - $self->{new_model_objects} = {}; - if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) { - $self->{new_model_objects}{upper} = $upper_object; - } - if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) { - $self->{new_model_objects}{lower} = $lower_object; - } - - $self->{mesh_cut_valid} = 1; -} - -sub _update { - my ($self) = @_; - - # Don't update if the window was already closed. - # We are not sure whether the action planned by wxTheApp->CallAfter() may be triggered after the window is closed. - # Probably not, but better be safe than sorry, which is espetially true on multiple platforms. - return if $self->{already_closed}; - - # Only recalculate the cut, if the live cut preview is active. - my $life_preview_active = $self->_life_preview_active(); - $self->_perform_cut() if $life_preview_active; - - { - # scale Z down to original size since we're using the transformed mesh for 3D preview - # and cut dialog but ModelObject::cut() needs Z without any instance transformation - my $z = $self->_mesh_slice_z_pos(); - - - # update canvas - if ($self->{canvas}) { - # get volumes to render - my @objects = (); - if ($life_preview_active) { - push @objects, values %{$self->{new_model_objects}}; - } else { - push @objects, $self->{model_object}; - } - - my $z_cut = $z + $self->{model_object}->bounding_box->z_min; - - # get section contour - my @expolygons = (); - foreach my $volume (@{$self->{model_object}->volumes}) { - next if !$volume->mesh; - next if $volume->modifier; - my $expp = $volume->mesh->slice([ $z_cut ])->[0]; - push @expolygons, @$expp; - } - foreach my $expolygon (@expolygons) { - $self->{model_object}->instances->[0]->transform_polygon($_) - for @$expolygon; - $expolygon->translate(map Slic3r::Geometry::scale($_), @{ $self->{model_object}->instances->[0]->offset }); - } - - Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); - Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $_, 0, [0]) for @objects; - Slic3r::GUI::_3DScene::set_cutting_plane($self->{canvas}, $self->{cut_options}{z}, [@expolygons]); - Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); - Slic3r::GUI::_3DScene::render($self->{canvas}); - } - } - - # update controls - { - my $z = $self->{cut_options}{z}; - my $optgroup = $self->{optgroup}; - $optgroup->get_field('keep_upper')->toggle(my $have_upper = abs($z - $optgroup->get_option('z')->max) > 0.1); - $optgroup->get_field('keep_lower')->toggle(my $have_lower = $z > 0.1); - $optgroup->get_field('rotate_lower')->toggle($z > 0 && $self->{cut_options}{keep_lower}); -# Disabled live preview by default as it is not stable and/or the calculation takes too long for interactive usage. -# $optgroup->get_field('preview')->toggle($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower}); - - # update cut button - if (($self->{cut_options}{keep_upper} && $have_upper) - || ($self->{cut_options}{keep_lower} && $have_lower)) { - $self->{btn_cut}->Enable; - } else { - $self->{btn_cut}->Disable; - } - } -} - -sub NewModelObjects { - my ($self) = @_; - return values %{ $self->{new_model_objects} }; -} - -1; diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm deleted file mode 100644 index 783c1a9f5b..0000000000 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ /dev/null @@ -1,588 +0,0 @@ -# Configuration of mesh modifiers and their parameters. -# This panel is inserted into ObjectSettingsDialog. - -package Slic3r::GUI::Plater::ObjectPartsPanel; -use strict; -use warnings; -use utf8; - -use File::Basename qw(basename); -use Wx qw(:misc :sizer :treectrl :button :keycode wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxID_CANCEL wxMOD_CONTROL - wxTheApp); -use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_KEY_DOWN EVT_KEY_DOWN); -use List::Util qw(max); -use base 'Wx::Panel'; - -use constant ICON_OBJECT => 0; -use constant ICON_SOLIDMESH => 1; -use constant ICON_MODIFIERMESH => 2; - -sub new { - my ($class, $parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - - # C++ type Slic3r::ModelObject - my $object = $self->{model_object} = $params{model_object}; - - # Save state for sliders. - $self->{move_options} = { - x => 0, - y => 0, - z => 0, - }; - $self->{last_coords} = { - x => 0, - y => 0, - z => 0, - }; - - # create TreeCtrl - my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100], - wxTR_NO_BUTTONS | wxSUNKEN_BORDER | wxTR_HAS_VARIABLE_ROW_HEIGHT - | wxTR_SINGLE); - { - $self->{tree_icons} = Wx::ImageList->new(16, 16, 1); - $tree->AssignImageList($self->{tree_icons}); - $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("brick.png"), wxBITMAP_TYPE_PNG)); # ICON_OBJECT - $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG)); # ICON_SOLIDMESH - $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("plugin.png"), wxBITMAP_TYPE_PNG)); # ICON_MODIFIERMESH - - my $rootId = $tree->AddRoot("Object", ICON_OBJECT); - $tree->SetPlData($rootId, { type => 'object' }); - } - - # buttons - $self->{btn_load_part} = Wx::Button->new($self, -1, "Load part…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - $self->{btn_load_modifier} = Wx::Button->new($self, -1, "Load modifier…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - $self->{btn_load_lambda_modifier} = Wx::Button->new($self, -1, "Load generic…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - $self->{btn_delete} = Wx::Button->new($self, -1, "Delete part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - $self->{btn_split} = Wx::Button->new($self, -1, "Split part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - $self->{btn_move_up} = Wx::Button->new($self, -1, "", wxDefaultPosition, [40, -1], wxBU_LEFT); - $self->{btn_move_down} = Wx::Button->new($self, -1, "", wxDefaultPosition, [40, -1], wxBU_LEFT); - $self->{btn_load_part}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_load_modifier}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_load_lambda_modifier}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_delete}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_delete.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_split}->SetBitmap(Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_move_up}->SetBitmap(Wx::Bitmap->new(Slic3r::var("bullet_arrow_up.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_move_down}->SetBitmap(Wx::Bitmap->new(Slic3r::var("bullet_arrow_down.png"), wxBITMAP_TYPE_PNG)); - - # buttons sizer - my $buttons_sizer = Wx::GridSizer->new(2, 3); - $buttons_sizer->Add($self->{btn_load_part}, 0, wxEXPAND | wxBOTTOM | wxRIGHT, 5); - $buttons_sizer->Add($self->{btn_load_modifier}, 0, wxEXPAND | wxBOTTOM | wxRIGHT, 5); - $buttons_sizer->Add($self->{btn_load_lambda_modifier}, 0, wxEXPAND | wxBOTTOM, 5); - $buttons_sizer->Add($self->{btn_delete}, 0, wxEXPAND | wxRIGHT, 5); - $buttons_sizer->Add($self->{btn_split}, 0, wxEXPAND | wxRIGHT, 5); - { - my $up_down_sizer = Wx::GridSizer->new(1, 2); - $up_down_sizer->Add($self->{btn_move_up}, 0, wxEXPAND | wxRIGHT, 5); - $up_down_sizer->Add($self->{btn_move_down}, 0, wxEXPAND, 5); - $buttons_sizer->Add($up_down_sizer, 0, wxEXPAND, 5); - } - $self->{btn_load_part}->SetFont($Slic3r::GUI::small_font); - $self->{btn_load_modifier}->SetFont($Slic3r::GUI::small_font); - $self->{btn_load_lambda_modifier}->SetFont($Slic3r::GUI::small_font); - $self->{btn_delete}->SetFont($Slic3r::GUI::small_font); - $self->{btn_split}->SetFont($Slic3r::GUI::small_font); - $self->{btn_move_up}->SetFont($Slic3r::GUI::small_font); - $self->{btn_move_down}->SetFont($Slic3r::GUI::small_font); - - # part settings panel - $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; $self->_update_canvas; }); - my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL); - $settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0); - - my $optgroup_movers; - $optgroup_movers = $self->{optgroup_movers} = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Move', - on_change => sub { - my ($opt_id) = @_; - # There seems to be an issue with wxWidgets 3.0.2/3.0.3, where the slider - # genates tens of events for a single value change. - # Only trigger the recalculation if the value changes - # or a live preview was activated and the mesh cut is not valid yet. - if ($self->{move_options}{$opt_id} != $optgroup_movers->get_value($opt_id)) { - $self->{move_options}{$opt_id} = $optgroup_movers->get_value($opt_id); - wxTheApp->CallAfter(sub { - $self->_update; - }); - } - }, - label_width => 20, - ); - $optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'x', - type => 'slider', - label => 'X', - default => 0, - min => -($self->{model_object}->bounding_box->size->x)*4, - max => $self->{model_object}->bounding_box->size->x*4, - full_width => 1, - )); - $optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'y', - type => 'slider', - label => 'Y', - default => 0, - min => -($self->{model_object}->bounding_box->size->y)*4, - max => $self->{model_object}->bounding_box->size->y*4, - full_width => 1, - )); - $optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'z', - type => 'slider', - label => 'Z', - default => 0, - min => -($self->{model_object}->bounding_box->size->z)*4, - max => $self->{model_object}->bounding_box->size->z*4, - full_width => 1, - )); - - # left pane with tree - my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); - $left_sizer->Add($tree, 3, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10); - $left_sizer->Add($buttons_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10); - $left_sizer->Add($settings_sizer, 5, wxEXPAND | wxALL, 0); - $left_sizer->Add($optgroup_movers->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - - # right pane with preview canvas - my $canvas; - if ($Slic3r::GUI::have_OpenGL) { - $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); - Slic3r::GUI::_3DScene::enable_picking($canvas, 1); - Slic3r::GUI::_3DScene::set_select_by($canvas, 'volume'); - Slic3r::GUI::_3DScene::register_on_select_object_callback($canvas, sub { - my ($volume_idx) = @_; - $self->reload_tree($volume_idx); - }); - Slic3r::GUI::_3DScene::load_model_object($canvas, $self->{model_object}, 0, [0]); - Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); - Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size })); - $canvas->SetSize([500,700]); - Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->GetParent->GetParent->{config}); - Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($canvas); - Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($canvas, 1); - } - - $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); - $self->{sizer}->Add($left_sizer, 0, wxEXPAND | wxALL, 0); - $self->{sizer}->Add($canvas, 1, wxEXPAND | wxALL, 0) if $canvas; - - $self->SetSizer($self->{sizer}); - $self->{sizer}->SetSizeHints($self); - - # attach events - EVT_TREE_ITEM_COLLAPSING($self, $tree, sub { - my ($self, $event) = @_; - $event->Veto; - }); - EVT_TREE_SEL_CHANGED($self, $tree, sub { - my ($self, $event) = @_; - return if $self->{disable_tree_sel_changed_event}; - $self->selection_changed; - }); - EVT_TREE_KEY_DOWN($self, $tree, \&on_tree_key_down); - EVT_BUTTON($self, $self->{btn_load_part}, sub { $self->on_btn_load(0) }); - EVT_BUTTON($self, $self->{btn_load_modifier}, sub { $self->on_btn_load(1) }); - EVT_BUTTON($self, $self->{btn_load_lambda_modifier}, sub { $self->on_btn_lambda(1) }); - EVT_BUTTON($self, $self->{btn_delete}, \&on_btn_delete); - EVT_BUTTON($self, $self->{btn_split}, \&on_btn_split); - EVT_BUTTON($self, $self->{btn_move_up}, \&on_btn_move_up); - EVT_BUTTON($self, $self->{btn_move_down}, \&on_btn_move_down); - EVT_KEY_DOWN($canvas, sub { - my ($canvas, $event) = @_; - if ($event->GetKeyCode == WXK_DELETE) { - $canvas->GetParent->on_btn_delete; - } else { - $event->Skip; - } - }); - - $self->reload_tree; - - return $self; -} - -sub reload_tree { - my ($self, $selected_volume_idx) = @_; - - $selected_volume_idx //= -1; - my $object = $self->{model_object}; - my $tree = $self->{tree}; - my $rootId = $tree->GetRootItem; - - # despite wxWidgets states that DeleteChildren "will not generate any events unlike Delete() method", - # the MSW implementation of DeleteChildren actually calls Delete() for each item, so - # EVT_TREE_SEL_CHANGED is being called, with bad effects (the event handler is called; this - # subroutine is never continued; an invisible EndModal is called on the dialog causing Plater - # to continue its logic and rescheduling the background process etc. GH #2774) - $self->{disable_tree_sel_changed_event} = 1; - $tree->DeleteChildren($rootId); - $self->{disable_tree_sel_changed_event} = 0; - - my $selectedId = $rootId; - foreach my $volume_id (0..$#{$object->volumes}) { - my $volume = $object->volumes->[$volume_id]; - - my $icon = $volume->modifier ? ICON_MODIFIERMESH : ICON_SOLIDMESH; - my $itemId = $tree->AppendItem($rootId, $volume->name || $volume_id, $icon); - if ($volume_id == $selected_volume_idx) { - $selectedId = $itemId; - } - $tree->SetPlData($itemId, { - type => 'volume', - volume_id => $volume_id, - }); - } - $tree->ExpandAll; - - Slic3r::GUI->CallAfter(sub { - $self->{tree}->SelectItem($selectedId); - - # SelectItem() should trigger EVT_TREE_SEL_CHANGED as per wxWidgets docs, - # but in fact it doesn't if the given item is already selected (this happens - # on first load) - $self->selection_changed; - }); -} - -sub get_selection { - my ($self) = @_; - - my $nodeId = $self->{tree}->GetSelection; - if ($nodeId->IsOk) { - return $self->{tree}->GetPlData($nodeId); - } - return undef; -} - -sub selection_changed { - my ($self) = @_; - - # deselect all meshes - if ($self->{canvas}) { - Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas}); - } - - # disable things as if nothing is selected - $self->{'btn_' . $_}->Disable for (qw(delete move_up move_down split)); - $self->{settings_panel}->disable; - $self->{settings_panel}->set_config(undef); - - # reset move sliders - $self->{optgroup_movers}->set_value("x", 0); - $self->{optgroup_movers}->set_value("y", 0); - $self->{optgroup_movers}->set_value("z", 0); - $self->{move_options} = { - x => 0, - y => 0, - z => 0, - }; - $self->{last_coords} = { - x => 0, - y => 0, - z => 0, - }; - - if (my $itemData = $self->get_selection) { - my ($config, @opt_keys); - if ($itemData->{type} eq 'volume') { - # select volume in 3D preview - if ($self->{canvas}) { - Slic3r::GUI::_3DScene::select_volume($self->{canvas}, $itemData->{volume_id}); - } - $self->{btn_delete}->Enable; - $self->{btn_split}->Enable; - $self->{btn_move_up}->Enable if $itemData->{volume_id} > 0; - $self->{btn_move_down}->Enable if $itemData->{volume_id} + 1 < $self->{model_object}->volumes_count; - - # attach volume config to settings panel - my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ]; - - if ($volume->modifier) { - $self->{optgroup_movers}->enable; - } else { - $self->{optgroup_movers}->disable; - } - $config = $volume->config; - $self->{staticbox}->SetLabel('Part Settings'); - - # get default values - @opt_keys = @{Slic3r::Config::PrintRegion->new->get_keys}; - } elsif ($itemData->{type} eq 'object') { - # select nothing in 3D preview - - # attach object config to settings panel - $self->{optgroup_movers}->disable; - $self->{staticbox}->SetLabel('Object Settings'); - @opt_keys = (map @{$_->get_keys}, Slic3r::Config::PrintObject->new, Slic3r::Config::PrintRegion->new); - $config = $self->{model_object}->config; - } - # get default values - my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys); - - # decide which settings will be shown by default - if ($itemData->{type} eq 'object') { - $config->set_ifndef('wipe_into_objects', 0); - $config->set_ifndef('wipe_into_infill', 0); - } - - # append default extruder - push @opt_keys, 'extruder'; - $default_config->set('extruder', 0); - $config->set_ifndef('extruder', 0); - $self->{settings_panel}->set_default_config($default_config); - $self->{settings_panel}->set_config($config); - $self->{settings_panel}->set_opt_keys(\@opt_keys); - - # disable minus icon to remove the settings - if ($itemData->{type} eq 'object') { - $self->{settings_panel}->set_fixed_options([qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)]); - } else { - $self->{settings_panel}->set_fixed_options([qw(extruder)]); - } - - $self->{settings_panel}->enable; - } - - Slic3r::GUI::_3DScene::render($self->{canvas}) if $self->{canvas}; -} - -sub on_btn_load { - my ($self, $is_modifier) = @_; - - my @input_files = wxTheApp->open_model($self); - foreach my $input_file (@input_files) { - my $model = eval { Slic3r::Model->read_from_file($input_file) }; - if ($@) { - Slic3r::GUI::show_error($self, $@); - next; - } - - foreach my $object (@{$model->objects}) { - foreach my $volume (@{$object->volumes}) { - my $new_volume = $self->{model_object}->add_volume($volume); - $new_volume->set_modifier($is_modifier); - $new_volume->set_name(basename($input_file)); - - # apply the same translation we applied to the object - $new_volume->mesh->translate(@{$self->{model_object}->origin_translation}); - - # set a default extruder value, since user can't add it manually - $new_volume->config->set_ifndef('extruder', 0); - - $self->{parts_changed} = 1; - } - } - } - - $self->{model_object}->center_around_origin if $self->{parts_changed}; - $self->_parts_changed; -} - -sub on_btn_lambda { - my ($self, $is_modifier) = @_; - - my $dlg = Slic3r::GUI::Plater::LambdaObjectDialog->new($self); - if ($dlg->ShowModal() == wxID_CANCEL) { - return; - } - my $params = $dlg->ObjectParameter; - my $type = "".$params->{"type"}; - my $name = "lambda-".$params->{"type"}; - my $mesh; - - if ($type eq "box") { - $mesh = Slic3r::TriangleMesh::cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); - } elsif ($type eq "cylinder") { - $mesh = Slic3r::TriangleMesh::cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); - } elsif ($type eq "sphere") { - $mesh = Slic3r::TriangleMesh::sphere($params->{"sph_rho"}); - } elsif ($type eq "slab") { - $mesh = Slic3r::TriangleMesh::cube($self->{model_object}->bounding_box->size->x*1.5, $self->{model_object}->bounding_box->size->y*1.5, $params->{"slab_h"}); - # box sets the base coordinate at 0,0, move to center of plate and move it up to initial_z - $mesh->translate(-$self->{model_object}->bounding_box->size->x*1.5/2.0, -$self->{model_object}->bounding_box->size->y*1.5/2.0, $params->{"slab_z"}); - } else { - return; - } - $mesh->repair; - my $new_volume = $self->{model_object}->add_volume(mesh => $mesh); - $new_volume->set_modifier($is_modifier); - $new_volume->set_name($name); - - # set a default extruder value, since user can't add it manually - $new_volume->config->set_ifndef('extruder', 0); - - $self->{parts_changed} = 1; - $self->_parts_changed; -} - -sub on_tree_key_down { - my ($self, $event) = @_; - my $keycode = $event->GetKeyCode; - # Wx >= 0.9911 - if (defined(&Wx::TreeEvent::GetKeyEvent)) { - if ($event->GetKeyEvent->GetModifiers & wxMOD_CONTROL) { - if ($keycode == WXK_UP) { - $event->Skip; - $self->on_btn_move_up; - } elsif ($keycode == WXK_DOWN) { - $event->Skip; - $self->on_btn_move_down; - } - } elsif ($keycode == WXK_DELETE) { - $self->on_btn_delete; - } - } -} - -sub on_btn_move_up { - my ($self) = @_; - my $itemData = $self->get_selection; - if ($itemData && $itemData->{type} eq 'volume') { - my $volume_id = $itemData->{volume_id}; - if ($self->{model_object}->move_volume_up($volume_id)) { - Slic3r::GUI::_3DScene::move_volume_up($self->{canvas}, $volume_id); - $self->{parts_changed} = 1; - $self->reload_tree($volume_id - 1); - } - } -} - -sub on_btn_move_down { - my ($self) = @_; - my $itemData = $self->get_selection; - if ($itemData && $itemData->{type} eq 'volume') { - my $volume_id = $itemData->{volume_id}; - if ($self->{model_object}->move_volume_down($volume_id)) { - Slic3r::GUI::_3DScene::move_volume_down($self->{canvas}, $volume_id); - $self->{parts_changed} = 1; - $self->reload_tree($volume_id + 1); - } - } -} - -sub on_btn_delete { - my ($self) = @_; - - my $itemData = $self->get_selection; - if ($itemData && $itemData->{type} eq 'volume') { - my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}]; - - # if user is deleting the last solid part, throw error - if (!$volume->modifier && scalar(grep !$_->modifier, @{$self->{model_object}->volumes}) == 1) { - Slic3r::GUI::show_error($self, "You can't delete the last solid part from this object."); - return; - } - - $self->{model_object}->delete_volume($itemData->{volume_id}); - $self->{parts_changed} = 1; - } - - $self->{model_object}->center_around_origin if $self->{parts_changed}; - $self->_parts_changed; -} - -sub on_btn_split { - my ($self) = @_; - - my $itemData = $self->get_selection; - if ($itemData && $itemData->{type} eq 'volume') { - my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}]; - my $nozzle_dmrs = $self->GetParent->GetParent->GetParent->{config}->get('nozzle_diameter'); - $self->{parts_changed} = 1 if $volume->split(scalar(@$nozzle_dmrs)) > 1; - } - - $self->_parts_changed; -} - -sub _parts_changed { - my ($self) = @_; - - $self->reload_tree; - if ($self->{canvas}) { - Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); - Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]); - Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas}); - Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); - Slic3r::GUI::_3DScene::render($self->{canvas}); - } -} - -sub CanClose { - my $self = shift; - - return 1; # skip validation for now - - # validate options before allowing user to dismiss the dialog - # the validate method only works on full configs so we have - # to merge our settings with the default ones - my $config = $self->GetParent->GetParent->GetParent->GetParent->GetParent->config->clone; - eval { - $config->apply($self->model_object->config); - $config->validate; - }; - return ! Slic3r::GUI::catch_error($self); -} - -sub Destroy { - my ($self) = @_; - $self->{canvas}->Destroy if ($self->{canvas}); -} - -sub PartsChanged { - my ($self) = @_; - return $self->{parts_changed}; -} - -sub PartSettingsChanged { - my ($self) = @_; - return $self->{part_settings_changed}; -} - -sub _update_canvas { - my ($self) = @_; - - if ($self->{canvas}) { - Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); - Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]); - - # restore selection, if any - if (my $itemData = $self->get_selection) { - if ($itemData->{type} eq 'volume') { - Slic3r::GUI::_3DScene::select_volume($self->{canvas}, $itemData->{volume_id}); - } - } - - Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); - Slic3r::GUI::_3DScene::render($self->{canvas}); - } -} - -sub _update { - my ($self) = @_; - my ($m_x, $m_y, $m_z) = ($self->{move_options}{x}, $self->{move_options}{y}, $self->{move_options}{z}); - my ($l_x, $l_y, $l_z) = ($self->{last_coords}{x}, $self->{last_coords}{y}, $self->{last_coords}{z}); - - my $itemData = $self->get_selection; - if ($itemData && $itemData->{type} eq 'volume') { - my $d = Slic3r::Pointf3->new($m_x - $l_x, $m_y - $l_y, $m_z - $l_z); - my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}]; - $volume->mesh->translate(@{$d}); - $self->{last_coords}{x} = $m_x; - $self->{last_coords}{y} = $m_y; - $self->{last_coords}{z} = $m_z; - } - - $self->{parts_changed} = 1; - my @objects = (); - push @objects, $self->{model_object}; - Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); - Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $_, 0, [0]) for @objects; - Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); - Slic3r::GUI::_3DScene::render($self->{canvas}); -} - -1; diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm deleted file mode 100644 index 3befba708d..0000000000 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ /dev/null @@ -1,224 +0,0 @@ -# This dialog opens up when double clicked on an object line in the list at the right side of the platter. -# One may load additional STLs and additional modifier STLs, -# one may change the properties of the print per each modifier mesh or a Z-span. - -package Slic3r::GUI::Plater::ObjectSettingsDialog; -use strict; -use warnings; -use utf8; - -use Wx qw(:dialog :id :misc :sizer :systemsettings :notebook wxTAB_TRAVERSAL wxTheApp); -use Wx::Event qw(EVT_BUTTON); -use base 'Wx::Dialog'; - -# Called with -# %params{object} of a Perl type Slic3r::GUI::Plater::Object -# %params{model_object} of a C++ type Slic3r::ModelObject -sub new { - my ($class, $parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, "Settings for " . $params{object}->name, wxDefaultPosition, [700,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - $self->{$_} = $params{$_} for keys %params; - - $self->{tabpanel} = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL); - $self->{tabpanel}->AddPage($self->{parts} = Slic3r::GUI::Plater::ObjectPartsPanel->new($self->{tabpanel}, model_object => $params{model_object}), "Parts"); - $self->{tabpanel}->AddPage($self->{layers} = Slic3r::GUI::Plater::ObjectDialog::LayersTab->new($self->{tabpanel}), "Layers"); - - my $buttons = $self->CreateStdDialogButtonSizer(wxOK); - EVT_BUTTON($self, wxID_OK, sub { - # validate user input - return if !$self->{parts}->CanClose; - return if !$self->{layers}->CanClose; - - # notify tabs - $self->{layers}->Closing; - - # save window size - wxTheApp->save_window_pos($self, "object_settings"); - - $self->EndModal(wxID_OK); - $self->{parts}->Destroy; - $self->Destroy; - }); - - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - $sizer->Add($self->{tabpanel}, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10); - $sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - - $self->SetSizer($sizer); - $self->SetMinSize($self->GetSize); - - $self->Layout; - - wxTheApp->restore_window_pos($self, "object_settings"); - - return $self; -} - -sub PartsChanged { - my ($self) = @_; - return $self->{parts}->PartsChanged; -} - -sub PartSettingsChanged { - my ($self) = @_; - return $self->{parts}->PartSettingsChanged || $self->{layers}->LayersChanged; -} - -package Slic3r::GUI::Plater::ObjectDialog::BaseTab; -use base 'Wx::Panel'; - -sub model_object { - my ($self) = @_; - # $self->GetParent->GetParent is of type Slic3r::GUI::Plater::ObjectSettingsDialog - return $self->GetParent->GetParent->{model_object}; -} - -package Slic3r::GUI::Plater::ObjectDialog::LayersTab; -use Wx qw(:dialog :id :misc :sizer :systemsettings); -use Wx::Grid; -use Wx::Event qw(EVT_GRID_CELL_CHANGED); -use base 'Slic3r::GUI::Plater::ObjectDialog::BaseTab'; - -sub new { - my $class = shift; - my ($parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize); - - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - - { - my $label = Wx::StaticText->new($self, -1, "You can use this section to override the default layer height for parts of this object.", - wxDefaultPosition, [-1, 40]); - $label->SetFont(Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); - $sizer->Add($label, 0, wxEXPAND | wxALL, 10); - } - - my $grid = $self->{grid} = Wx::Grid->new($self, -1, wxDefaultPosition, wxDefaultSize); - $sizer->Add($grid, 1, wxEXPAND | wxALL, 10); - $grid->CreateGrid(0, 3); - $grid->DisableDragRowSize; - $grid->HideRowLabels; - $grid->SetColLabelValue(0, "Min Z (mm)"); - $grid->SetColLabelValue(1, "Max Z (mm)"); - $grid->SetColLabelValue(2, "Layer height (mm)"); - $grid->SetColSize($_, 135) for 0..2; - $grid->SetDefaultCellAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE); - - # load data - foreach my $range (@{ $self->model_object->layer_height_ranges }) { - $grid->AppendRows(1); - my $i = $grid->GetNumberRows-1; - $grid->SetCellValue($i, $_, $range->[$_]) for 0..2; - } - $grid->AppendRows(1); # append one empty row - - EVT_GRID_CELL_CHANGED($grid, sub { - my ($grid, $event) = @_; - - # remove any non-numeric character - my $value = $grid->GetCellValue($event->GetRow, $event->GetCol); - $value =~ s/,/./g; - $value =~ s/[^0-9.]//g; - $grid->SetCellValue($event->GetRow, $event->GetCol, ($event->GetCol == 2) ? $self->_clamp_layer_height($value) : $value); - - # if there's no empty row, let's append one - for my $i (0 .. $grid->GetNumberRows) { - if ($i == $grid->GetNumberRows) { - # if we're here then we found no empty row - $grid->AppendRows(1); - last; - } - if (!grep $grid->GetCellValue($i, $_), 0..2) { - # exit loop if this row is empty - last; - } - } - - $self->{layers_changed} = 1; - }); - - $self->SetSizer($sizer); - $sizer->SetSizeHints($self); - - return $self; -} - -sub _clamp_layer_height -{ - my ($self, $value) = @_; - # $self->GetParent->GetParent is of type Slic3r::GUI::Plater::ObjectSettingsDialog - my $config = $self->GetParent->GetParent->{config}; - if ($value =~ /^[0-9,.E]+$/) { - # Looks like a number. Validate the layer height. - my $nozzle_dmrs = $config->get('nozzle_diameter'); - my $min_layer_heights = $config->get('min_layer_height'); - my $max_layer_heights = $config->get('max_layer_height'); - my $min_layer_height = 1000.; - my $max_layer_height = 0.; - my $max_nozzle_dmr = 0.; - for (my $i = 0; $i < int(@{$nozzle_dmrs}); $i += 1) { - $min_layer_height = $min_layer_heights->[$i] if ($min_layer_heights->[$i] < $min_layer_height); - $max_layer_height = $max_layer_heights->[$i] if ($max_layer_heights->[$i] > $max_layer_height); - $max_nozzle_dmr = $nozzle_dmrs ->[$i] if ($nozzle_dmrs ->[$i] > $max_nozzle_dmr ); - } - $min_layer_height = 0.005 if ($min_layer_height < 0.005); - $max_layer_height = $max_nozzle_dmr * 0.75 if ($max_layer_height == 0.); - $max_layer_height = $max_nozzle_dmr if ($max_layer_height > $max_nozzle_dmr); - return ($value < $min_layer_height) ? $min_layer_height : - ($value > $max_layer_height) ? $max_layer_height : $value; - } else { - # If an invalid numeric value has been entered, use the default layer height. - return $config->get('layer_height'); - } -} - -sub CanClose { - my $self = shift; - - # validate ranges before allowing user to dismiss the dialog - - foreach my $range ($self->_get_ranges) { - my ($min, $max, $height) = @$range; - if ($max <= $min) { - Slic3r::GUI::show_error($self, "Invalid Z range $min-$max."); - return 0; - } - if ($min < 0 || $max < 0) { - Slic3r::GUI::show_error($self, "Invalid Z range $min-$max."); - return 0; - } - if ($height < 0) { - Slic3r::GUI::show_error($self, "Invalid layer height $height."); - return 0; - } - # TODO: check for overlapping ranges - } - - return 1; -} - -sub Closing { - my $self = shift; - - # save ranges into the plater object - $self->model_object->set_layer_height_ranges([ $self->_get_ranges ]); -} - -sub _get_ranges { - my $self = shift; - - my @ranges = (); - for my $i (0 .. $self->{grid}->GetNumberRows-1) { - my ($min, $max, $height) = map $self->{grid}->GetCellValue($i, $_), 0..2; - next if $min eq '' || $max eq '' || $height eq ''; - push @ranges, [ $min, $max, $height ]; - } - return sort { $a->[0] <=> $b->[0] } @ranges; -} - -sub LayersChanged { - my ($self) = @_; - return $self->{layers_changed}; -} - -1; diff --git a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm deleted file mode 100644 index ea4ce71329..0000000000 --- a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm +++ /dev/null @@ -1,182 +0,0 @@ -# Included in ObjectSettingsDialog -> ObjectPartsPanel. -# Maintains, displays, adds and removes overrides of slicing parameters for an object and its modifier mesh. - -package Slic3r::GUI::Plater::OverrideSettingsPanel; -use strict; -use warnings; -use utf8; - -use List::Util qw(first); -use Wx qw(:misc :sizer :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG - wxTheApp); -use Wx::Event qw(EVT_BUTTON EVT_LEFT_DOWN EVT_MENU); -use base 'Wx::ScrolledWindow'; - -use constant ICON_MATERIAL => 0; -use constant ICON_SOLIDMESH => 1; -use constant ICON_MODIFIERMESH => 2; - -my %icons = ( - 'Advanced' => 'wand.png', - 'Extruders' => 'funnel.png', - 'Extrusion Width' => 'funnel.png', - 'Infill' => 'infill.png', - 'Layers and Perimeters' => 'layers.png', - 'Skirt and brim' => 'box.png', - 'Speed' => 'time.png', - 'Speed > Acceleration' => 'time.png', - 'Support material' => 'building.png', -); - -sub new { - my ($class, $parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - # C++ class Slic3r::DynamicPrintConfig, initially empty. - $self->{default_config} = Slic3r::Config->new; - $self->{config} = Slic3r::Config->new; - # On change callback. - $self->{on_change} = $params{on_change}; - $self->{fixed_options} = {}; - - $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); - - $self->{options_sizer} = Wx::BoxSizer->new(wxVERTICAL); - $self->{sizer}->Add($self->{options_sizer}, 0, wxEXPAND | wxALL, 0); - - # option selector - { - # create the button - my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG), - wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE); - EVT_LEFT_DOWN($btn, sub { - my $menu = Wx::Menu->new; - # create category submenus - my %categories = (); # category => submenu - foreach my $opt_key (@{$self->{options}}) { - if (my $cat = $Slic3r::Config::Options->{$opt_key}{category}) { - $categories{$cat} //= Wx::Menu->new; - } - } - # append submenus to main menu - my @categories = ('Layers and Perimeters', 'Infill', 'Support material', 'Speed', 'Extruders', 'Extrusion Width', 'Advanced'); - #foreach my $cat (sort keys %categories) { - foreach my $cat (@categories) { - wxTheApp->append_submenu($menu, $cat, "", $categories{$cat}, undef, $icons{$cat}); - } - # append options to submenus - foreach my $opt_key (@{$self->{options}}) { - my $cat = $Slic3r::Config::Options->{$opt_key}{category} or next; - my $cb = sub { - $self->{config}->set($opt_key, $self->{default_config}->get($opt_key)); - $self->update_optgroup; - $self->{on_change}->($opt_key) if $self->{on_change}; - }; - wxTheApp->append_menu_item($categories{$cat}, $self->{option_labels}{$opt_key}, - $Slic3r::Config::Options->{$opt_key}{tooltip}, $cb); - } - $self->PopupMenu($menu, $btn->GetPosition); - $menu->Destroy; - }); - - my $h_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $h_sizer->Add($btn, 0, wxALL, 0); - $self->{sizer}->Add($h_sizer, 0, wxEXPAND | wxBOTTOM, 10); - } - - $self->SetSizer($self->{sizer}); - $self->SetScrollbars(0, 1, 0, 1); - - $self->set_opt_keys($params{opt_keys}) if $params{opt_keys}; - $self->update_optgroup; - - return $self; -} - -sub set_default_config { - my ($self, $config) = @_; - $self->{default_config} = $config; -} - -sub set_config { - my ($self, $config) = @_; - $self->{config} = $config; - $self->update_optgroup; -} - -sub set_opt_keys { - my ($self, $opt_keys) = @_; - # sort options by category+label - $self->{option_labels} = { map { $_ => $Slic3r::Config::Options->{$_}{full_label} // $Slic3r::Config::Options->{$_}{label} } @$opt_keys }; - $self->{options} = [ sort { $self->{option_labels}{$a} cmp $self->{option_labels}{$b} } @$opt_keys ]; -} - -sub set_fixed_options { - my ($self, $opt_keys) = @_; - $self->{fixed_options} = { map {$_ => 1} @$opt_keys }; - $self->update_optgroup; -} - -sub update_optgroup { - my $self = shift; - - $self->{options_sizer}->Clear(1); - return if !defined $self->{config}; - - my %categories = (); - foreach my $opt_key (@{$self->{config}->get_keys}) { - my $category = $Slic3r::Config::Options->{$opt_key}{category}; - $categories{$category} ||= []; - push @{$categories{$category}}, $opt_key; - } - foreach my $category (sort keys %categories) { - my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new( - parent => $self, - title => $category, - config => $self->{config}, - full_labels => 1, - label_font => $Slic3r::GUI::small_font, - sidetext_font => $Slic3r::GUI::small_font, - label_width => 150, - on_change => sub { $self->{on_change}->() if $self->{on_change} }, - extra_column => sub { - my ($line) = @_; - - my $opt_key = $line->get_options->[0]->opt_id; # we assume that we have one option per line - - # disallow deleting fixed options - return undef if $self->{fixed_options}{$opt_key}; - - my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG), - wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE); - EVT_BUTTON($self, $btn, sub { - $self->{config}->erase($opt_key); - $self->{on_change}->() if $self->{on_change}; - wxTheApp->CallAfter(sub { $self->update_optgroup }); - }); - return $btn; - }, - ); - foreach my $opt_key (sort @{$categories{$category}}) { - $optgroup->append_single_option_line($opt_key); - } - $self->{options_sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM, 0); - } - $self->GetParent->Layout; # we need this for showing scrollbars -} - -# work around a wxMAC bug causing controls not being disabled when calling Disable() on a Window -sub enable { - my ($self) = @_; - - $self->{btn_add}->Enable; - $self->Enable; -} - -sub disable { - my ($self) = @_; - - $self->{btn_add}->Disable; - $self->Disable; -} - -1; diff --git a/lib/Slic3r/GUI/SystemInfo.pm b/lib/Slic3r/GUI/SystemInfo.pm deleted file mode 100644 index 717ff12e5e..0000000000 --- a/lib/Slic3r/GUI/SystemInfo.pm +++ /dev/null @@ -1,70 +0,0 @@ -package Slic3r::GUI::SystemInfo; -use strict; -use warnings; -use utf8; - -use Wx qw(:font :html :misc :dialog :sizer :systemsettings :frame :id wxTheClipboard); -use Wx::Event qw(EVT_HTML_LINK_CLICKED EVT_LEFT_DOWN EVT_BUTTON); -use Wx::Html; -use base 'Wx::Dialog'; - -sub new { - my ($class, %params) = @_; - my $self = $class->SUPER::new($params{parent}, -1, 'Slic3r Prusa Edition - System Information', wxDefaultPosition, [600, 340], - wxDEFAULT_DIALOG_STYLE | wxMAXIMIZE_BOX | wxRESIZE_BORDER); - $self->{text_info} = $params{text_info}; - - $self->SetBackgroundColour(Wx::wxWHITE); - my $vsizer = Wx::BoxSizer->new(wxVERTICAL); - $self->SetSizer($vsizer); - - # text - my $text = - '' . - '' . - ($params{slic3r_info} // '') . - ($params{copyright_info} // '') . - ($params{system_info} // '') . - ($params{opengl_info} // '') . - '' . - ''; - my $html = $self->{html} = Wx::HtmlWindow->new($self, -1, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO); - my $font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - my $size = &Wx::wxMSW ? 8 : 10; - $html->SetFonts($font->GetFaceName, $font->GetFaceName, [$size * 1.5, $size * 1.4, $size * 1.3, $size, $size, $size, $size]); - $html->SetBorders(10); - $html->SetPage($text); - $vsizer->Add($html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 0); - EVT_HTML_LINK_CLICKED($self, $html, \&link_clicked); - - my $buttons = $self->CreateStdDialogButtonSizer(wxOK); - my $btn_copy_to_clipboard = Wx::Button->new($self, -1, "Copy to Clipboard", wxDefaultPosition, wxDefaultSize); - $buttons->Insert(0, $btn_copy_to_clipboard, 0, wxLEFT, 5); - EVT_BUTTON($self, $btn_copy_to_clipboard, \©_to_clipboard); - $self->SetEscapeId(wxID_CLOSE); - EVT_BUTTON($self, wxID_CLOSE, sub { - $self->EndModal(wxID_CLOSE); - $self->Close; - }); -# $vsizer->Add($buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3); - $vsizer->Add($buttons, 0, wxEXPAND | wxALL, 5); - - return $self; -} - -sub link_clicked { - my ($self, $event) = @_; - - Wx::LaunchDefaultBrowser($event->GetLinkInfo->GetHref); - $event->Skip(0); -} - -sub copy_to_clipboard { - my ($self, $event) = @_; - my $data = $self->{text_info}; - wxTheClipboard->Open; - wxTheClipboard->SetData(Wx::TextDataObject->new($data)); - wxTheClipboard->Close; -} - -1; diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 180d0d028d..286a73e2de 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -122,6 +122,7 @@ sub line_intersection { : undef; } +# Used by test cases. sub collinear { my ($line1, $line2, $require_overlapping) = @_; my $intersection = _line_intersection(map @$_, @$line1, @$line2); @@ -226,6 +227,7 @@ sub bounding_box { return @bb[X1,Y1,X2,Y2]; } +# used by ExPolygon::size sub size_2D { my @bounding_box = bounding_box(@_); return ( @@ -234,6 +236,7 @@ sub size_2D { ); } +# Used by sub collinear, which is used by test cases. # bounding_box_intersect($d, @a, @b) # Return true if the given bounding boxes @a and @b intersect # in $d dimensions. Used by sub collinear. @@ -252,6 +255,7 @@ sub bounding_box_intersect { return 1; } +# Used by test cases. # this assumes a CCW rotation from $p2 to $p3 around $p1 sub angle3points { my ($p1, $p2, $p3) = @_; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 38aa318e6e..85c3ed757b 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -24,7 +24,7 @@ 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->set_status(95, "Running post-processing scripts"); $self->config->setenv; for my $script (@{$self->config->post_process}) { # Ignore empty post processing script lines. @@ -57,13 +57,13 @@ sub export_png { for(my $oi = 0; $oi < $objnum; $oi++) { $sobjects[$oi]->slice; - $self->status_cb->(($oi + 1)*100/$objnum - 1, "Slicing..."); + $self->set_status(($oi + 1)*100/$objnum - 1, "Slicing..."); } my $fh = $params{output_file}; - $self->status_cb->(90, "Exporting zipped archive..."); + $self->set_status(90, "Exporting zipped archive..."); $self->print_to_png($fh); - $self->status_cb->(100, "Done."); + $self->set_status(100, "Done."); } # Export SVG slices for the offline SLA printing. @@ -77,7 +77,7 @@ sub export_svg { for(my $oi = 0; $oi < $objnum; $oi++) { $sobjects[$oi]->slice; - $self->status_cb->(($oi + 1)*100/$objnum - 1, "Slicing..."); + $self->set_status(($oi + 1)*100/$objnum - 1, "Slicing..."); } my $fh = $params{output_fh}; diff --git a/lib/Slic3r/SVG.pm b/lib/Slic3r/SVG.pm deleted file mode 100644 index 1aa7dd27d5..0000000000 --- a/lib/Slic3r/SVG.pm +++ /dev/null @@ -1,142 +0,0 @@ -package Slic3r::SVG; -use strict; -use warnings; - -use SVG; - -use constant X => 0; -use constant Y => 1; - -our $filltype = 'evenodd'; - -sub factor { - return &Slic3r::SCALING_FACTOR * 10; -} - -sub svg { - my $svg = SVG->new(width => 200 * 10, height => 200 * 10); - my $marker_end = $svg->marker( - id => "endArrow", - viewBox => "0 0 10 10", - refX => "1", - refY => "5", - markerUnits => "strokeWidth", - orient => "auto", - markerWidth => "10", - markerHeight => "8", - ); - $marker_end->polyline( - points => "0,0 10,5 0,10 1,5", - fill => "darkblue", - ); - - return $svg; -} - -sub output { - my ($filename, @things) = @_; - - my $svg = svg(); - my $arrows = 1; - - while (my $type = shift @things) { - my $value = shift @things; - - if ($type eq 'no_arrows') { - $arrows = 0; - } elsif ($type =~ /^(?:(.+?)_)?expolygons$/) { - my $colour = $1; - $value = [ map $_->pp, @$value ]; - - my $g = $svg->group( - style => { - 'stroke-width' => 0, - 'stroke' => $colour || 'black', - 'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')), - 'fill-type' => $filltype, - }, - ); - foreach my $expolygon (@$value) { - my $points = join ' ', map "M $_ z", map join(" ", reverse map $_->[0]*factor() . " " . $_->[1]*factor(), @$_), @$expolygon; - $g->path( - d => $points, - ); - } - } elsif ($type =~ /^(?:(.+?)_)?(polygon|polyline)s$/) { - my ($colour, $method) = ($1, $2); - $value = [ map $_->pp, @$value ]; - - my $g = $svg->group( - style => { - 'stroke-width' => ($method eq 'polyline') ? 1 : 0, - 'stroke' => $colour || 'black', - 'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')), - }, - ); - foreach my $polygon (@$value) { - my $path = $svg->get_path( - 'x' => [ map($_->[X] * factor(), @$polygon) ], - 'y' => [ map($_->[Y] * factor(), @$polygon) ], - -type => 'polygon', - ); - $g->$method( - %$path, - 'marker-end' => !$arrows ? "" : "url(#endArrow)", - ); - } - } elsif ($type =~ /^(?:(.+?)_)?points$/) { - my $colour = $1 // 'black'; - my $r = $colour eq 'black' ? 1 : 3; - $value = [ map $_->pp, @$value ]; - - my $g = $svg->group( - style => { - 'stroke-width' => 2, - 'stroke' => $colour, - 'fill' => $colour, - }, - ); - foreach my $point (@$value) { - $g->circle( - cx => $point->[X] * factor(), - cy => $point->[Y] * factor(), - r => $r, - ); - } - } elsif ($type =~ /^(?:(.+?)_)?lines$/) { - my $colour = $1; - $value = [ map $_->pp, @$value ]; - - my $g = $svg->group( - style => { - 'stroke-width' => 2, - }, - ); - foreach my $line (@$value) { - $g->line( - x1 => $line->[0][X] * factor(), - y1 => $line->[0][Y] * factor(), - x2 => $line->[1][X] * factor(), - y2 => $line->[1][Y] * factor(), - style => { - 'stroke' => $colour || 'black', - }, - 'marker-end' => !$arrows ? "" : "url(#endArrow)", - ); - } - } - } - - write_svg($svg, $filename); -} - -sub write_svg { - my ($svg, $filename) = @_; - - Slic3r::open(\my $fh, '>', $filename); - print $fh $svg->xmlify; - close $fh; - printf "SVG written to %s\n", $filename; -} - -1; diff --git a/utils/view-mesh.pl b/utils/view-mesh.pl deleted file mode 100644 index 03153ab3f5..0000000000 --- a/utils/view-mesh.pl +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/perl -# This script displays 3D preview of a mesh - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Getopt::Long qw(:config no_auto_abbrev); -use Slic3r; -use Slic3r::GUI; -use Slic3r::GUI::3DScene; -$|++; - -my %opt = (); -{ - my %options = ( - 'help' => sub { usage() }, - 'cut=f' => \$opt{cut}, - 'enable-moving' => \$opt{enable_moving}, - ); - GetOptions(%options) or usage(1); - $ARGV[0] or usage(1); -} - -{ - my $model = Slic3r::Model->read_from_file($ARGV[0]); - $_->center_around_origin for @{$model->objects}; # and align to Z = 0 - - my $app = Slic3r::ViewMesh->new; - $app->{canvas}->enable_picking(1); - $app->{canvas}->enable_moving($opt{enable_moving}); - $app->{canvas}->load_object($model, 0); - $app->{canvas}->set_auto_bed_shape; - $app->{canvas}->zoom_to_volumes; - $app->{canvas}->SetCuttingPlane($opt{cut}) if defined $opt{cut}; - $app->MainLoop; -} - - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: view-mesh.pl [ OPTIONS ] file.stl - - --help Output this usage screen and exit - --cut Z Display the cutting plane at the given Z - -EOF - exit ($exit_code || 0); -} - -package Slic3r::ViewMesh; -use Wx qw(:sizer); -use base qw(Wx::App); - -sub OnInit { - my $self = shift; - - my $frame = Wx::Frame->new(undef, -1, 'Mesh Viewer', [-1, -1], [500, 400]); - my $panel = Wx::Panel->new($frame, -1); - - $self->{canvas} = Slic3r::GUI::3DScene->new($panel); - - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - $sizer->Add($self->{canvas}, 1, wxEXPAND, 0); - $panel->SetSizer($sizer); - $sizer->SetSizeHints($panel); - - $frame->Show(1); -} - -__END__ diff --git a/utils/view-toolpaths.pl b/utils/view-toolpaths.pl deleted file mode 100755 index e064885ca3..0000000000 --- a/utils/view-toolpaths.pl +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/perl -# This script displays 3D preview of a mesh - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Getopt::Long qw(:config no_auto_abbrev); -use Slic3r; -use Slic3r::GUI; -use Slic3r::GUI::3DScene; -$|++; - -my %opt = (); -{ - my %options = ( - 'help' => sub { usage() }, - 'load=s' => \$opt{load}, - '3D' => \$opt{d3}, - 'duplicate=i' => \$opt{duplicate}, - ); - GetOptions(%options) or usage(1); - $ARGV[0] or usage(1); -} - -{ - # load model - my $model = Slic3r::Model->read_from_file($ARGV[0]); - - # load config - my $config = Slic3r::Config::new_from_defaults; - if ($opt{load}) { - $config->apply(Slic3r::Config::load($opt{load})); - } - - # init print - my $sprint = Slic3r::Print::Simple->new; - $sprint->duplicate($opt{duplicate} // 1); - $sprint->apply_config($config); - $sprint->set_model($model); - $sprint->process; - - # visualize toolpaths - $Slic3r::ViewToolpaths::print = $sprint->_print; - $Slic3r::ViewToolpaths::d3 = $opt{d3}; - my $app = Slic3r::ViewToolpaths->new; - $app->MainLoop; -} - - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: view-toolpaths.pl [ OPTIONS ] file.stl - - --help Output this usage screen and exit - --load CONFIG Loads the supplied config file - -EOF - exit ($exit_code || 0); -} - - -package Slic3r::ViewToolpaths; -use Wx qw(:sizer); -use base qw(Wx::App Class::Accessor); - -our $print; -our $d3; - -sub OnInit { - my $self = shift; - - my $frame = Wx::Frame->new(undef, -1, 'Toolpaths', [-1, -1], [500, 500]); - my $panel = Wx::Panel->new($frame, -1); - - my $canvas; - if ($d3) { - $canvas = Slic3r::GUI::3DScene->new($panel); - $canvas->set_bed_shape($print->config->bed_shape); - $canvas->load_print_toolpaths($print); - - foreach my $object (@{$print->objects}) { - #$canvas->load_print_object_slices($object); - $canvas->load_print_object_toolpaths($object); - #$canvas->load_object($object->model_object); - } - $canvas->zoom_to_volumes; - } else { - $canvas = Slic3r::GUI::Plater::2DToolpaths->new($panel, $print); - } - - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - $sizer->Add($canvas, 1, wxEXPAND, 0); - $panel->SetSizer($sizer); - - $frame->Show(1); -} - -__END__ diff --git a/utils/wireframe.pl b/utils/wireframe.pl deleted file mode 100644 index 5399f31aca..0000000000 --- a/utils/wireframe.pl +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/perl -# This script exports experimental G-code for wireframe printing -# (inspired by the brilliant WirePrint concept) - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Getopt::Long qw(:config no_auto_abbrev); -use Slic3r; -use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale unscale X Y PI); - -my %opt = ( - step_height => 5, - nozzle_angle => 30, - nozzle_width => 10, - first_layer_height => 0.3, -); -{ - my %options = ( - 'help' => sub { usage() }, - 'output|o=s' => \$opt{output_file}, - 'step-height|h=f' => \$opt{step_height}, - 'nozzle-angle|a=f' => \$opt{nozzle_angle}, - 'nozzle-width|w=f' => \$opt{nozzle_width}, - 'first-layer-height=f' => \$opt{first_layer_height}, - ); - GetOptions(%options) or usage(1); - $opt{output_file} or usage(1); - $ARGV[0] or usage(1); -} - -{ - # load model - my $model = Slic3r::Model->read_from_file($ARGV[0]); - $model->center_instances_around_point(Slic3r::Pointf->new(100,100)); - my $mesh = $model->mesh; - $mesh->translate(0, 0, -$mesh->bounding_box->z_min); - - # get slices - my @z = (); - my $z_max = $mesh->bounding_box->z_max; - for (my $z = $opt{first_layer_height}; $z <= $z_max; $z += $opt{step_height}) { - push @z, $z; - } - my @slices = @{$mesh->slice(\@z)}; - - my $flow = Slic3r::Flow->new( - width => 0.35, - height => 0.35, - nozzle_diameter => 0.35, - bridge => 1, - ); - - my $config = Slic3r::Config::Print->new; - $config->set('gcode_comments', 1); - - open my $fh, '>', $opt{output_file}; - my $gcodegen = Slic3r::GCode->new( - enable_loop_clipping => 0, # better bonding - ); - $gcodegen->apply_print_config($config); - $gcodegen->set_extruders([0]); - print $fh $gcodegen->set_extruder(0); - print $fh $gcodegen->writer->preamble; - - my $e = $gcodegen->writer->extruder->e_per_mm3 * $flow->mm3_per_mm; - - foreach my $layer_id (0..$#z) { - my $z = $z[$layer_id]; - - foreach my $island (@{$slices[$layer_id]}) { - foreach my $polygon (@$island) { - if ($layer_id > 0) { - # find the lower polygon that we want to connect to this one - my $lower = $slices[$layer_id-1]->[0]->contour; # 't was easy, wasn't it? - my $lower_z = $z[$layer_id-1]; - - { - my @points = (); - - # keep all points with strong angles - { - my @pp = @$polygon; - foreach my $i (0..$#pp) { - push @points, $pp[$i-1] if abs($pp[$i-1]->ccw_angle($pp[$i-2], $pp[$i]) - PI) > PI/3; - } - } - - $polygon = Slic3r::Polygon->new(@points); - } - #$polygon = Slic3r::Polygon->new(@{$polygon->split_at_first_point->equally_spaced_points(scale $opt{nozzle_width})}); - - # find vertical lines - my @vertical = (); - foreach my $point (@{$polygon}) { - push @vertical, Slic3r::Line->new($point->projection_onto_polygon($lower), $point); - } - - next if !@vertical; - - my @points = (); - foreach my $line (@vertical) { - push @points, Slic3r::Pointf3->new( - unscale($line->a->x), - unscale($line->a->y), #)) - $lower_z, - ); - push @points, Slic3r::Pointf3->new( - unscale($line->b->x), - unscale($line->b->y), #)) - $z, - ); - } - - # reappend first point as destination of the last diagonal segment - push @points, Slic3r::Pointf3->new( - unscale($vertical[0]->a->x), - unscale($vertical[0]->a->y), #)) - $lower_z, - ); - - # move to the position of the first vertical line - print $fh $gcodegen->writer->travel_to_xyz(shift @points); - - # extrude segments - foreach my $point (@points) { - print $fh $gcodegen->writer->extrude_to_xyz($point, $e * $gcodegen->writer->get_position->distance_to($point)); - } - } - } - - print $fh $gcodegen->writer->travel_to_z($z); - foreach my $polygon (@$island) { - #my $polyline = $polygon->split_at_vertex(Slic3r::Point->new_scale(@{$gcodegen->writer->get_position}[0,1])); - my $polyline = $polygon->split_at_first_point; - print $fh $gcodegen->writer->travel_to_xy(Slic3r::Pointf->new_unscale(@{ $polyline->first_point }), "move to first contour point"); - - foreach my $line (@{$polyline->lines}) { - my $point = Slic3r::Pointf->new_unscale(@{ $line->b }); - print $fh $gcodegen->writer->extrude_to_xy($point, $e * unscale($line->length)); - } - } - } - } - - close $fh; -} - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: wireframe.pl [ OPTIONS ] file.stl - - --help Output this usage screen and exit - --output, -o Write to the specified file - --step-height, -h Use the specified step height - --nozzle-angle, -a Max nozzle angle - --nozzle-width, -w External nozzle diameter - -EOF - exit ($exit_code || 0); -} - -__END__ diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index a677d9e5aa..651513c622 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -28,11 +28,13 @@ template class PrintState; void Print::clear_objects() { tbb::mutex::scoped_lock lock(m_mutex); - for (int i = int(m_objects.size())-1; i >= 0; --i) - this->delete_object(i); + for (PrintObject *object : m_objects) + delete object; + m_objects.clear(); for (PrintRegion *region : m_regions) delete region; m_regions.clear(); + this->invalidate_all_steps(); } void Print::delete_object(size_t idx) @@ -47,21 +49,28 @@ void Print::delete_object(size_t idx) void Print::reload_object(size_t /* idx */) { - tbb::mutex::scoped_lock lock(m_mutex); - /* TODO: this method should check whether the per-object config and per-material configs - have changed in such a way that regions need to be rearranged or we can just apply - the diff and invalidate something. Same logic as apply_config() - For now we just re-add all objects since we haven't implemented this incremental logic yet. - This should also check whether object volumes (parts) have changed. */ - - // collect all current model objects - ModelObjectPtrs model_objects; - model_objects.reserve(m_objects.size()); - for (PrintObject *object : m_objects) - model_objects.push_back(object->model_object()); - // remove our print objects - this->clear_objects(); - // re-add model objects + ModelObjectPtrs model_objects; + { + tbb::mutex::scoped_lock lock(m_mutex); + /* TODO: this method should check whether the per-object config and per-material configs + have changed in such a way that regions need to be rearranged or we can just apply + the diff and invalidate something. Same logic as apply_config() + For now we just re-add all objects since we haven't implemented this incremental logic yet. + This should also check whether object volumes (parts) have changed. */ + // collect all current model objects + model_objects.reserve(m_objects.size()); + for (PrintObject *object : m_objects) + model_objects.push_back(object->model_object()); + // remove our print objects + for (PrintObject *object : m_objects) + delete object; + m_objects.clear(); + for (PrintRegion *region : m_regions) + delete region; + m_regions.clear(); + this->invalidate_all_steps(); + } + // re-add model objects for (ModelObject *mo : model_objects) this->add_model_object(mo); } diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index bda8d2f2f1..3212d6538b 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -170,6 +170,7 @@ _constant() void set_callback_event(int evt) %code%{ %}; void set_status_silent(); + void set_status(int percent, const char *message); void process() %code%{ try { From 2c9dc4dbbf86a9c6e0b85ac1c29233ff040a2299 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 14 Sep 2018 10:59:50 +0200 Subject: [PATCH 12/36] Finished porting of the status bar to C++. --- lib/Slic3r/GUI.pm | 1 - lib/Slic3r/GUI/MainFrame.pm | 1 + lib/Slic3r/GUI/Plater.pm | 2 +- lib/Slic3r/GUI/ProgressStatusBar.pm | 18 ------------------ xs/src/slic3r/GUI/ProgressStatusBar.cpp | 1 + xs/src/slic3r/GUI/ProgressStatusBar.hpp | 3 +++ xs/xsp/ProgressStatusBar.xsp | 9 ++++----- 7 files changed, 10 insertions(+), 25 deletions(-) delete mode 100644 lib/Slic3r/GUI/ProgressStatusBar.pm diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index d25f1a8aff..8da2871105 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -10,7 +10,6 @@ use Slic3r::GUI::MainFrame; use Slic3r::GUI::Plater; use Slic3r::GUI::Plater::3D; use Slic3r::GUI::Plater::3DPreview; -use Slic3r::GUI::ProgressStatusBar; use Wx::Locale gettext => 'L'; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index e2a25a7ab3..092311e759 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -114,6 +114,7 @@ sub new { # Save the slic3r.ini. Usually the ini file is saved from "on idle" callback, # but in rare cases it may not have been called yet. wxTheApp->{app_config}->save; + $self->{statusbar}->ResetCancelCallback(); $self->{plater}->{print} = undef if($self->{plater}); Slic3r::GUI::_3DScene::remove_all_canvases(); Slic3r::GUI::deregister_on_request_update_callback(); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d3cca7e530..3bb9e59dcb 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1534,7 +1534,7 @@ sub on_process_completed { # At this point of time the thread should be either finished or canceled, # so the following call just confirms, that the produced data were consumed. $self->{background_slicing_process}->stop; - $self->statusbar->SetCancelCallback(undef); + $self->statusbar->ResetCancelCallback(); $self->statusbar->StopBusy; $self->statusbar->SetStatusText(""); diff --git a/lib/Slic3r/GUI/ProgressStatusBar.pm b/lib/Slic3r/GUI/ProgressStatusBar.pm deleted file mode 100644 index edc0c0ce33..0000000000 --- a/lib/Slic3r/GUI/ProgressStatusBar.pm +++ /dev/null @@ -1,18 +0,0 @@ -# Status bar at the bottom of the main screen. -# Now it just implements cancel cb on perl side, every other functionality is -# in C++ - -package Slic3r::GUI::ProgressStatusBar; -use strict; -use warnings; - -our $cancel_cb; - -sub SetCancelCallback { - my $self = shift; - my ($cb) = @_; - $cancel_cb = $cb; - $cb ? $self->ShowCancelButton : $self->HideCancelButton; -} - -1; diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.cpp b/xs/src/slic3r/GUI/ProgressStatusBar.cpp index 3c2bdee6a0..67fcd1dcc6 100644 --- a/xs/src/slic3r/GUI/ProgressStatusBar.cpp +++ b/xs/src/slic3r/GUI/ProgressStatusBar.cpp @@ -54,6 +54,7 @@ ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id): cancelbutton_->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) { if(cancel_cb_) cancel_cb_(); + m_perl_cancel_callback.call(); cancelbutton_->Hide(); }); } diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.hpp b/xs/src/slic3r/GUI/ProgressStatusBar.hpp index a117cacfbf..91c180ba6b 100644 --- a/xs/src/slic3r/GUI/ProgressStatusBar.hpp +++ b/xs/src/slic3r/GUI/ProgressStatusBar.hpp @@ -4,6 +4,8 @@ #include #include +#include "../../libslic3r/Utils.hpp" + class wxTimer; class wxGauge; class wxButton; @@ -50,6 +52,7 @@ public: void show_cancel_button(); void hide_cancel_button(); + PerlCallback m_perl_cancel_callback; private: bool busy_ = false; CancelFn cancel_cb_; diff --git a/xs/xsp/ProgressStatusBar.xsp b/xs/xsp/ProgressStatusBar.xsp index 6f41b2d2b3..c089cfd7c1 100644 --- a/xs/xsp/ProgressStatusBar.xsp +++ b/xs/xsp/ProgressStatusBar.xsp @@ -39,10 +39,9 @@ void SetStatusText(const char *txt) %code%{ THIS->set_status_text(txt); %}; - void ShowCancelButton() - %code%{ THIS->show_cancel_button(); %}; - - void HideCancelButton() - %code%{ THIS->hide_cancel_button(); %}; + void SetCancelCallback(SV* callback) + %code%{ THIS->m_perl_cancel_callback.register_callback(callback); THIS->show_cancel_button();%}; + void ResetCancelCallback() + %code%{ THIS->m_perl_cancel_callback.deregister_callback(); THIS->hide_cancel_button(); %}; }; \ No newline at end of file From bb7f504296c99fdc35197dedda85d38e6ef387f0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 14 Sep 2018 11:16:54 +0200 Subject: [PATCH 13/36] Removed "Slice to SVG" and "Quick Slice" --- README.md | 1 - lib/Slic3r/GUI.pm | 1 - lib/Slic3r/GUI/MainFrame.pm | 149 +----------------------------------- lib/Slic3r/GUI/Plater.pm | 52 +------------ lib/Slic3r/Print.pm | 102 ------------------------ lib/Slic3r/Print/Simple.pm | 6 -- slic3r.pl | 6 +- t/svg.t | 37 --------- 8 files changed, 3 insertions(+), 351 deletions(-) delete mode 100644 t/svg.t diff --git a/README.md b/README.md index e3947b9f5a..f6c0677c48 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,6 @@ The author of the Silk icon set is Mark James. and [input_filename] (default: [input_filename_base].gcode) --post-process Generated G-code will be processed with the supplied script; call this more than once to process through multiple scripts. - --export-svg Export a SVG file containing slices instead of G-code. --export-png Export zipped PNG files containing slices instead of G-code. -m, --merge If multiple files are supplied, they will be composed into a single print rather than processed individually. diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 8da2871105..01143aeeee 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -28,7 +28,6 @@ use constant FILE_WILDCARDS => { prusa => 'Prusa Control files (*.prusa)|*.prusa;*.PRUSA', ini => 'INI files *.ini|*.ini;*.INI', gcode => 'G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC', - svg => 'SVG files *.svg|*.svg;*.SVG', }; use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf threemf prusa)}; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 092311e759..554beba684 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -305,31 +305,8 @@ sub _init_menubar { $self->export_configbundle; }, undef, 'lorry_go.png'); $fileMenu->AppendSeparator(); - my $repeat; - $self->_append_menu_item($fileMenu, L("Q&uick Slice…\tCtrl+U"), L('Slice a file into a G-code'), sub { - wxTheApp->CallAfter(sub { - $self->quick_slice; - $repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file); - }); - }, undef, 'cog_go.png'); - $self->_append_menu_item($fileMenu, L("Quick Slice and Save &As…\tCtrl+Alt+U"), L('Slice a file into a G-code, save as'), sub { - wxTheApp->CallAfter(sub { - $self->quick_slice(save_as => 1); - $repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file); - }); - }, undef, 'cog_go.png'); - $repeat = $self->_append_menu_item($fileMenu, L("&Repeat Last Quick Slice\tCtrl+Shift+U"), L('Repeat last quick slice'), sub { - wxTheApp->CallAfter(sub { - $self->quick_slice(reslice => 1); - }); - }, undef, 'cog_go.png'); - $repeat->Enable(0); - $fileMenu->AppendSeparator(); - $self->_append_menu_item($fileMenu, L("Slice to SV&G…\tCtrl+G"), L('Slice file to a multi-layer SVG'), sub { - $self->quick_slice(save_as => 1, export_svg => 1); - }, undef, 'shape_handles.png'); $self->_append_menu_item($fileMenu, L("Slice to PNG…"), L('Slice file to a set of PNG files'), sub { - $self->slice_to_png; #$self->quick_slice(save_as => 0, export_png => 1); + $self->slice_to_png; }, undef, 'shape_handles.png'); $self->{menu_item_reslice_now} = $self->_append_menu_item( $fileMenu, L("(&Re)Slice Now\tCtrl+S"), L('Start new slicing process'), @@ -483,130 +460,6 @@ sub slice_to_png { $appController->print_ctl()->slice_to_png(); } -# To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG". -sub quick_slice { - my ($self, %params) = @_; - - my $progress_dialog; - eval { - # validate configuration - my $config = wxTheApp->{preset_bundle}->full_config(); - $config->validate; - - # select input file - my $input_file; - if (!$params{reslice}) { - my $dialog = Wx::FileDialog->new($self, L('Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):'), - wxTheApp->{app_config}->get_last_dir, "", - &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if ($dialog->ShowModal != wxID_OK) { - $dialog->Destroy; - return; - } - $input_file = $dialog->GetPaths; - $dialog->Destroy; - $qs_last_input_file = $input_file unless $params{export_svg}; - } else { - if (!defined $qs_last_input_file) { - Wx::MessageDialog->new($self, L("No previously sliced file."), - L('Error'), wxICON_ERROR | wxOK)->ShowModal(); - return; - } - if (! -e $qs_last_input_file) { - Wx::MessageDialog->new($self, L("Previously sliced file (").$qs_last_input_file.L(") not found."), - L('File Not Found'), wxICON_ERROR | wxOK)->ShowModal(); - return; - } - $input_file = $qs_last_input_file; - } - my $input_file_basename = basename($input_file); - wxTheApp->{app_config}->update_skein_dir(dirname($input_file)); - - my $print_center; - { - my $bed_shape = Slic3r::Polygon->new_scale(@{$config->bed_shape}); - $print_center = Slic3r::Pointf->new_unscale(@{$bed_shape->bounding_box->center}); - } - - 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); - - $sprint->apply_config($config); - $sprint->set_model($model); - - # Copy the names of active presets into the placeholder parser. - wxTheApp->{preset_bundle}->export_selections_pp($sprint->placeholder_parser); - - # select output file - my $output_file; - if ($params{reslice}) { - $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, L('Save ') . ($params{export_svg} ? L('SVG') : L('G-code')) . L(' file as:'), - wxTheApp->{app_config}->get_last_output_dir(dirname($output_file)), - basename($output_file), $params{export_svg} ? &Slic3r::GUI::FILE_WILDCARDS->{svg} : &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if ($dlg->ShowModal != wxID_OK) { - $dlg->Destroy; - return; - } - $output_file = $dlg->GetPath; - $qs_last_output_file = $output_file unless $params{export_svg}; - wxTheApp->{app_config}->update_last_output_dir(dirname($output_file)); - $dlg->Destroy; - } elsif($params{export_png}) { - $output_file = $sprint->output_filepath; - $output_file =~ s/\.[gG][cC][oO][dD][eE]$/.zip/; - # my $dlg = Wx::DirDialog->new($self, L('Choose output directory')); - my $dlg = Wx::FileDialog->new($self, L('Save zip file as:'), - wxTheApp->{app_config}->get_last_output_dir(dirname($output_file)), - basename($output_file), '*.zip', wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if ($dlg->ShowModal != wxID_OK) { - $dlg->Destroy; - return; - } - $output_file = $dlg->GetPath; - $dlg->Destroy; - } - - # show processbar dialog - $progress_dialog = Wx::ProgressDialog->new(L('Slicing…'), L("Processing ").$input_file_basename."…", - 100, $self, 4); - $progress_dialog->Pulse; - - { - my @warnings = (); - local $SIG{__WARN__} = sub { push @warnings, $_[0] }; - - $sprint->output_file($output_file); - if ($params{export_svg}) { - $sprint->export_svg; - } - elsif($params{export_png}) { - $sprint->export_png; - } - else { - $sprint->export_gcode; - } - Slic3r::GUI::warning_catcher($self)->($_) for @warnings; - } - $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 { $self->{progress_dialog}->Destroy if $self->{progress_dialog} }); -} - sub reslice_now { my ($self) = @_; $self->{plater}->reslice if $self->{plater}; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 3bb9e59dcb..d552641123 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -87,9 +87,6 @@ sub new { $self->item_changed_selection($obj_idx) if (defined($obj_idx)); } }; - my $on_double_click = sub { - $self->object_settings_dialog if $self->selected_object; - }; my $on_right_click = sub { my ($canvas, $click_pos_x, $click_pos_y) = @_; @@ -243,7 +240,7 @@ sub new { $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config}); $self->{preview_notebook}->AddPage($self->{canvas3D}, L('3D')); Slic3r::GUI::_3DScene::register_on_select_object_callback($self->{canvas3D}, $on_select_object); - Slic3r::GUI::_3DScene::register_on_double_click_callback($self->{canvas3D}, $on_double_click); +# Slic3r::GUI::_3DScene::register_on_double_click_callback($self->{canvas3D}, $on_double_click); Slic3r::GUI::_3DScene::register_on_right_click_callback($self->{canvas3D}, sub { $on_right_click->($self->{canvas3D}, @_); }); Slic3r::GUI::_3DScene::register_on_arrange_callback($self->{canvas3D}, sub { $self->arrange }); Slic3r::GUI::_3DScene::register_on_rotate_object_left_callback($self->{canvas3D}, sub { $self->rotate(-45, Z, 'relative') }); @@ -1988,14 +1985,6 @@ sub collect_selections { return $selections; } -# doesn't used now -sub list_item_activated { - my ($self, $event, $obj_idx) = @_; - - $obj_idx //= $event->GetIndex; - $self->object_settings_dialog($obj_idx); -} - # Called when clicked on the filament preset combo box. # When clicked on the icon, show the color picker. sub filament_color_box_lmouse_down @@ -2049,45 +2038,6 @@ sub filament_color_box_lmouse_down # } #} -sub object_settings_dialog { - my ($self, $obj_idx) = @_; - ($obj_idx, undef) = $self->selected_object if !defined $obj_idx; - my $model_object = $self->{model}->objects->[$obj_idx]; - - # validate config before opening the settings dialog because - # that dialog can't be closed if validation fails, but user - # can't fix any error which is outside that dialog - eval { wxTheApp->{preset_bundle}->full_config->validate; }; - return if Slic3r::GUI::catch_error($_[0]); - - my $dlg = Slic3r::GUI::Plater::ObjectSettingsDialog->new($self, - object => $self->{objects}[$obj_idx], - model_object => $model_object, - config => wxTheApp->{preset_bundle}->full_config, - ); - $self->stop_background_process; - $dlg->ShowModal; - -# # update thumbnail since parts may have changed -# if ($dlg->PartsChanged) { -# # recenter and re-align to Z = 0 -# $model_object->center_around_origin; -# $self->reset_thumbnail($obj_idx); -# } - - # update print - if ($dlg->PartsChanged || $dlg->PartSettingsChanged) { - $self->{print}->reload_object($obj_idx); - $self->schedule_background_process; -# $self->{canvas}->reload_scene if $self->{canvas}; - my $selections = $self->collect_selections; - Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); - Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0); - } else { - $self->resume_background_process; - } -} - sub changed_object_settings { my ($self, $obj_idx, $parts_changed, $part_settings_changed) = @_; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 85c3ed757b..79bc41ca3e 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -66,106 +66,4 @@ sub export_png { $self->set_status(100, "Done."); } -# 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 = @_; - - my @sobjects = @{$self->objects}; - my $objnum = scalar @sobjects; - for(my $oi = 0; $oi < $objnum; $oi++) - { - $sobjects[$oi]->slice; - $self->set_status(($oi + 1)*100/$objnum - 1, "Slicing..."); - } - - 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"; - print "Exporting to $output_file..." unless $params{quiet}; - } - - my $print_bb = $self->bounding_box; - my $print_size = $print_bb->size; - print $fh sprintf <<"EOF", unscale($print_size->[X]), unscale($print_size->[Y]); - - - - -EOF - - my $print_polygon = sub { - my ($polygon, $type) = @_; - printf $fh qq{ \n}, - $type, (join ' ', map { join ',', map unscale $_, @$_ } @$polygon), - ($type eq 'contour' ? 'white' : 'black'); - }; - - my @layers = sort { $a->print_z <=> $b->print_z } - map { @{$_->layers}, @{$_->support_layers} } - @{$self->objects}; - - my $layer_id = -1; - my @previous_layer_slices = (); - for my $layer (@layers) { - $layer_id++; - if ($layer->slice_z == -1) { - printf $fh qq{ \n}, $layer_id; - } else { - printf $fh qq{ \n}, $layer_id, unscale($layer->slice_z); - } - - my @current_layer_slices = (); - # sort slices so that the outermost ones come first - my @slices = sort { $a->contour->contains_point($b->contour->first_point) ? 0 : 1 } @{$layer->slices}; - foreach my $copy (@{$layer->object->_shifted_copies}) { - foreach my $slice (@slices) { - my $expolygon = $slice->clone; - $expolygon->translate(@$copy); - $expolygon->translate(-$print_bb->x_min, -$print_bb->y_min); - $print_polygon->($expolygon->contour, 'contour'); - $print_polygon->($_, 'hole') for @{$expolygon->holes}; - push @current_layer_slices, $expolygon; - } - } - # generate support material - if ($self->has_support_material && $layer->id > 0) { - my (@supported_slices, @unsupported_slices) = (); - foreach my $expolygon (@current_layer_slices) { - my $intersection = intersection_ex( - [ map @$_, @previous_layer_slices ], - [ @$expolygon ], - ); - @$intersection - ? push @supported_slices, $expolygon - : push @unsupported_slices, $expolygon; - } - my @supported_points = map @$_, @$_, @supported_slices; - foreach my $expolygon (@unsupported_slices) { - # look for the nearest point to this island among all - # supported points - my $contour = $expolygon->contour; - my $support_point = $contour->first_point->nearest_point(\@supported_points) - or next; - my $anchor_point = $support_point->nearest_point([ @$contour ]); - printf $fh qq{ \n}, - map @$_, $support_point, $anchor_point; - } - } - print $fh qq{ \n}; - @previous_layer_slices = @current_layer_slices; - } - - print $fh "\n"; - close $fh; - print "Done.\n" unless $params{quiet}; -} - 1; diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm index 3b18646d9b..b5b749f12b 100644 --- a/lib/Slic3r/Print/Simple.pm +++ b/lib/Slic3r/Print/Simple.pm @@ -91,12 +91,6 @@ sub export_gcode { $self->_print->export_gcode($self->output_file // ''); } -sub export_svg { - my ($self) = @_; - $self->_print->validate; - $self->_print->export_svg(output_file => $self->output_file); -} - sub export_png { my ($self) = @_; diff --git a/slic3r.pl b/slic3r.pl index 7be053cc80..4bf687cf53 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -42,7 +42,6 @@ my %cli_options = (); 'no-plater' => \$opt{no_plater}, 'gui-mode=s' => \$opt{obsolete_ignore_this_option_gui_mode}, 'datadir=s' => \$opt{datadir}, - 'export-svg' => \$opt{export_svg}, 'export-png' => \$opt{export_png}, 'merge|m' => \$opt{merge}, 'repair' => \$opt{repair}, @@ -212,9 +211,7 @@ if (@ARGV) { # slicing from command line # Do the apply_config once again to validate the layer height profiles at all the newly added PrintObjects. $sprint->apply_config($config); - if ($opt{export_svg}) { - $sprint->export_svg; - } elsif ($opt{export_png}) { + if ($opt{export_png}) { $sprint->export_png; } else { my $t0 = [gettimeofday]; @@ -280,7 +277,6 @@ Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ... and [input_filename] (default: $config->{output_filename_format}) --post-process Generated G-code will be processed with the supplied script; call this more than once to process through multiple scripts. - --export-svg Export a SVG file containing slices instead of G-code. --export-png Export zipped PNG files containing slices instead of G-code. -m, --merge If multiple files are supplied, they will be composed into a single print rather than processed individually. diff --git a/t/svg.t b/t/svg.t deleted file mode 100644 index 47d24ceb87..0000000000 --- a/t/svg.t +++ /dev/null @@ -1,37 +0,0 @@ -use Test::More tests => 2; -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Slic3r; -use Slic3r::Test; -use IO::Scalar; - -{ - my $print = Slic3r::Test::init_print('20mm_cube'); - eval { - my $fh = IO::Scalar->new(\my $gcode); - $print->print->export_svg(output_fh => $fh, quiet => 1); - $fh->close; - }; - die $@ if $@; - ok !$@, 'successful SVG export'; -} - -{ - my $print = Slic3r::Test::init_print('two_hollow_squares'); - eval { - my $fh = IO::Scalar->new(\my $gcode); - $print->print->export_svg(output_fh => $fh, quiet => 1); - $fh->close; - }; - die $@ if $@; - ok !$@, 'successful SVG export of object with two islands'; -} - -__END__ From d934b63424ab76ddcd8a87f272838df611dfcd0c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 17 Sep 2018 12:01:02 +0200 Subject: [PATCH 14/36] Removed Print.pm, ported execution of post processing scripts into C++ (WIP, waits for update of boost::system module on our build server) Removed other mention of the "Controller". --- lib/Slic3r.pm | 1 - lib/Slic3r/GUI.pm | 5 -- lib/Slic3r/GUI/MainFrame.pm | 7 +- lib/Slic3r/Print.pm | 69 ------------------- slic3r.pl | 2 - xs/CMakeLists.txt | 2 + xs/src/libslic3r/GCode.cpp | 12 ++-- xs/src/libslic3r/GCode/PostProcessor.cpp | 55 +++++++++++++++ xs/src/libslic3r/GCode/PostProcessor.hpp | 15 ++++ xs/src/libslic3r/Print.cpp | 10 ++- xs/src/libslic3r/Print.hpp | 2 +- .../slic3r/GUI/BackgroundSlicingProcess.cpp | 10 ++- xs/src/slic3r/GUI/GUI.cpp | 10 +-- xs/src/slic3r/GUI/GUI.hpp | 2 +- xs/src/slic3r/GUI/Preferences.cpp | 11 +-- xs/src/slic3r/GUI/Tab.cpp | 4 ++ xs/src/slic3r/GUI/Tab.hpp | 19 +++-- xs/xsp/GUI.xsp | 4 +- xs/xsp/Print.xsp | 10 ++- 19 files changed, 124 insertions(+), 126 deletions(-) delete mode 100644 lib/Slic3r/Print.pm create mode 100644 xs/src/libslic3r/GCode/PostProcessor.cpp create mode 100644 xs/src/libslic3r/GCode/PostProcessor.hpp diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 020edd70f8..44100db8cf 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -49,7 +49,6 @@ use Slic3r::Model; use Slic3r::Point; use Slic3r::Polygon; use Slic3r::Polyline; -use Slic3r::Print; use Slic3r::Print::Object; use Slic3r::Print::Simple; use Slic3r::Surface; diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 01143aeeee..216be34416 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -33,7 +33,6 @@ use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf # Datadir provided on the command line. our $datadir; -# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. our $no_plater; our @cb; @@ -103,8 +102,6 @@ sub OnInit { print STDERR "Creating main frame...\n"; 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, lang_ch_event => $LANGUAGE_CHANGE_EVENT, preferences_event => $PREFERENCES_EVENT, @@ -170,8 +167,6 @@ sub recreate_GUI{ my ($self) = @_; my $topwindow = $self->GetTopWindow(); $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, lang_ch_event => $LANGUAGE_CHANGE_EVENT, preferences_event => $PREFERENCES_EVENT, diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 554beba684..3f023c06f7 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -57,8 +57,6 @@ sub new { } # store input params - # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. - $self->{no_controller} = $params{no_controller}; $self->{no_plater} = $params{no_plater}; $self->{loaded} = 0; $self->{lang_ch_event} = $params{lang_ch_event}; @@ -199,8 +197,6 @@ sub _init_tabpanel { ? 'load_current_preset' : 'update_tab_ui'; $self->{options_tabs}{$tab_name_other}->$update_action; } - # Update the controller printers. - $self->{controller}->update_presets($presets) if $self->{controller}; } $self->{plater}->on_config_change($tab->get_config); } @@ -239,8 +235,7 @@ sub _init_tabpanel { $self->{plater}->update(); }); - - Slic3r::GUI::create_preset_tabs($self->{no_controller}, $VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT); + Slic3r::GUI::create_preset_tabs($VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT); $self->{options_tabs} = {}; for my $tab_name (qw(print filament sla_material printer)) { $self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name"); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm deleted file mode 100644 index 79bc41ca3e..0000000000 --- a/lib/Slic3r/Print.pm +++ /dev/null @@ -1,69 +0,0 @@ -# The slicing work horse. -# Extends C++ class Slic3r::Print -package Slic3r::Print; -use strict; -use warnings; - -use File::Basename qw(basename fileparse); -use File::Spec; -use List::Util qw(min max first sum); -use Slic3r::ExtrusionLoop ':roles'; -use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Flow ':roles'; -use Slic3r::Geometry qw(X Y unscale); -use Slic3r::Geometry::Clipper qw(diff_ex union_ex intersection_ex intersection offset - union JT_ROUND JT_SQUARE); -use Slic3r::Print::State ':steps'; - -sub size { - my $self = shift; - return $self->bounding_box->size; -} - -sub run_post_process_scripts { - my ($self, $output_file) = @_; - # run post-processing scripts - if (@{$self->config->post_process}) { -# $self->set_status(95, "Running post-processing scripts"); - $self->config->setenv; - for my $script (@{$self->config->post_process}) { - # Ignore empty post processing script lines. - next if $script =~ /^\s*$/; - Slic3r::debugf " '%s' '%s'\n", $script, $output_file; - # -x doesn't return true on Windows except for .exe files - if (($^O eq 'MSWin32') ? !(-e $script) : !(-x $script)) { - die "The configured post-processing script is not executable: check permissions. ($script)\n"; - } - if ($^O eq 'MSWin32' && $script =~ /\.[pP][lL]/) { - # The current process (^X) may be slic3r.exe or slic3r-console.exe. - # Replace it with the current perl interpreter. - my($filename, $directories, $suffix) = fileparse($^X); - $filename =~ s/^slic3r.*$/perl5\.24\.0\.exe/; - my $interpreter = $directories . $filename; - system($interpreter, $script, $output_file); - } else { - system($script, $output_file); - } - } - } -} - -sub export_png { - my $self = shift; - my %params = @_; - - my @sobjects = @{$self->objects}; - my $objnum = scalar @sobjects; - for(my $oi = 0; $oi < $objnum; $oi++) - { - $sobjects[$oi]->slice; - $self->set_status(($oi + 1)*100/$objnum - 1, "Slicing..."); - } - - my $fh = $params{output_file}; - $self->set_status(90, "Exporting zipped archive..."); - $self->print_to_png($fh); - $self->set_status(100, "Done."); -} - -1; diff --git a/slic3r.pl b/slic3r.pl index 4bf687cf53..38c10a9007 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -38,7 +38,6 @@ my %cli_options = (); 'load=s@' => \$opt{load}, 'autosave=s' => \$opt{autosave}, 'ignore-nonexistent-config' => \$opt{ignore_nonexistent_config}, - 'no-controller' => \$opt{no_controller}, 'no-plater' => \$opt{no_plater}, 'gui-mode=s' => \$opt{obsolete_ignore_this_option_gui_mode}, 'datadir=s' => \$opt{datadir}, @@ -107,7 +106,6 @@ if ((!@ARGV || $opt{gui}) && !$opt{no_gui} && !$opt{save} && eval "require Slic3 { no warnings 'once'; $Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // ''); - $Slic3r::GUI::no_controller = $opt{no_controller}; $Slic3r::GUI::no_plater = $opt{no_plater}; $Slic3r::GUI::autosave = $opt{autosave}; } diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 7e35a27b15..a442530dbb 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -105,6 +105,8 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/GCode/Analyzer.hpp ${LIBDIR}/libslic3r/GCode/CoolingBuffer.cpp ${LIBDIR}/libslic3r/GCode/CoolingBuffer.hpp + ${LIBDIR}/libslic3r/GCode/PostProcessor.cpp + ${LIBDIR}/libslic3r/GCode/PostProcessor.hpp ${LIBDIR}/libslic3r/GCode/PressureEqualizer.cpp ${LIBDIR}/libslic3r/GCode/PressureEqualizer.hpp ${LIBDIR}/libslic3r/GCode/PreviewData.cpp diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 91c9d6b0fa..ac09db4bd4 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -418,14 +419,9 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ { PROFILE_CLEAR(); - if (print->is_step_done(psGCodeExport)) { - // Does the file exist? If so, we hope that it is still valid. - FILE *f = boost::nowide::fopen(path, "r"); - if (f != nullptr) { - ::fclose(f); - return; - } - } + // Does the file exist? If so, we hope that it is still valid. + if (print->is_step_done(psGCodeExport) && boost::filesystem::exists(boost::filesystem::path(path))) + return; print->set_started(psGCodeExport); diff --git a/xs/src/libslic3r/GCode/PostProcessor.cpp b/xs/src/libslic3r/GCode/PostProcessor.cpp new file mode 100644 index 0000000000..f3b28f3985 --- /dev/null +++ b/xs/src/libslic3r/GCode/PostProcessor.cpp @@ -0,0 +1,55 @@ +#include "PostProcessor.hpp" + +namespace Slic3r { + +#ifdef WIN32 + +//FIXME Ignore until we include boost::process +void run_post_process_scripts(const std::string &path, const PrintConfig &config) +{ +} + +#else + +#include + +void run_post_process_scripts(const std::string &path, const PrintConfig &config) +{ + if (config.post_process.values.empty()) + return; + config.setenv_(); + for (std::string script: config.post_process.values) { + // Ignore empty post processing script lines. + boost::trim(script); + if (script.empty()) + continue; + BOOST_LOG_TRIVIAL(info) << "Executing script " << script << " on file " << path; + if (! boost::filesystem::exists(boost::filesystem::path(path))) + throw std::runtime_exception(std::string("The configured post-processing script does not exist: ") + path); +#ifndef WIN32 + file_status fs = boost::filesystem::status(path); + //FIXME test if executible by the effective UID / GID. + // throw std::runtime_exception(std::string("The configured post-processing script is not executable: check permissions. ") + path)); +#endif + int result = 0; +#ifdef WIN32 + if (boost::iends_with(file, ".gcode")) { + // The current process may be slic3r.exe or slic3r-console.exe. + // Find the path of the process: + wchar_t wpath_exe[_MAX_PATH + 1]; + ::GetModuleFileNameW(nullptr, wpath_exe, _MAX_PATH); + boost::filesystem::path path_exe(wpath_exe); + // Replace it with the current perl interpreter. + result = boost::process::system((path_exe.parent_path() / "perl5.24.0.exe").string(), script, output_file); + } else +#else + result = boost::process::system(script, output_file); +#endif + if (result < 0) + BOOST_LOG_TRIVIAL(error) << "Script " << script << " on file " << path << " failed. Negative error code returned."; + } +} + +#endif + +} // namespace Slic3r diff --git a/xs/src/libslic3r/GCode/PostProcessor.hpp b/xs/src/libslic3r/GCode/PostProcessor.hpp new file mode 100644 index 0000000000..ce47374cb3 --- /dev/null +++ b/xs/src/libslic3r/GCode/PostProcessor.hpp @@ -0,0 +1,15 @@ +#ifndef slic3r_GCode_PostProcessor_hpp_ +#define slic3r_GCode_PostProcessor_hpp_ + +#include + +#include "../libslic3r.h" +#include "../PrintConfig.hpp" + +namespace Slic3r { + +extern void run_post_process_scripts(const std::string &path, const PrintConfig &config); + +} // namespace Slic3r + +#endif /* slic3r_GCode_PostProcessor_hpp_ */ diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 651513c622..03034cfdc3 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -1331,8 +1331,15 @@ std::string Print::output_filepath(const std::string &path) const return path; } -void Print::print_to_png(const std::string &dirpath) +void Print::export_png(const std::string &dirpath) { + size_t idx = 0; + for (PrintObject *obj : m_objects) { + obj->slice(); + this->set_status(int(floor(idx * 100. / m_objects.size() + 0.5)), "Slicing..."); + ++ idx; + } + this->set_status(90, "Exporting zipped archive..."); print_to(*this, dirpath, float(m_config.bed_size_x.value), @@ -1341,6 +1348,7 @@ void Print::print_to_png(const std::string &dirpath) int(m_config.pixel_height.value), float(m_config.exp_time.value), float(m_config.exp_time_first.value)); + this->set_status(100, "Done."); } // Returns extruder this eec should be printed with, according to PrintRegion config diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 106640c4d9..8999f27d78 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -368,7 +368,7 @@ public: void process(); void export_gcode(const std::string &path_template, GCodePreviewData *preview_data); // SLA export, temporary. - void print_to_png(const std::string &dirpath); + void export_png(const std::string &dirpath); // methods for handling state bool is_step_done(PrintStep step) const { return m_state.is_done(step); } diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 2f88b25cab..338db3010b 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -7,6 +7,7 @@ // Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx. #include "../../libslic3r/Print.hpp" +#include "../../libslic3r/GCode/PostProcessor.hpp" //#undef NDEBUG #include @@ -59,9 +60,12 @@ void BackgroundSlicingProcess::thread_proc() if (! m_print->canceled()) { wxQueueEvent(GUI::g_wxPlater, new wxCommandEvent(m_event_sliced_id)); m_print->export_gcode(m_temp_output_path, m_gcode_preview_data); - if (! m_print->canceled() && ! m_output_path.empty() && - copy_file(m_temp_output_path, m_output_path) != 0) - throw std::runtime_error("Copying of the temporary G-code to the output G-code failed"); + if (! m_print->canceled() && ! m_output_path.empty()) { + if (copy_file(m_temp_output_path, m_output_path) != 0) + throw std::runtime_error("Copying of the temporary G-code to the output G-code failed"); + m_print->set_status(95, "Running post-processing scripts"); + run_post_process_scripts(m_output_path, m_print->config()); + } } } catch (CanceledException &ex) { // Canceled, this is all right. diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index e5ab22e8eb..7ce858aec1 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -611,13 +611,13 @@ void open_preferences_dialog(int event_preferences) dlg->ShowModal(); } -void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed) +void create_preset_tabs(int event_value_change, int event_presets_changed) { update_label_colours_from_appconfig(); - add_created_tab(new TabPrint (g_wxTabPanel, no_controller), event_value_change, event_presets_changed); - add_created_tab(new TabFilament (g_wxTabPanel, no_controller), event_value_change, event_presets_changed); - add_created_tab(new TabSLAMaterial (g_wxTabPanel, no_controller), event_value_change, event_presets_changed); - add_created_tab(new TabPrinter (g_wxTabPanel, no_controller), event_value_change, event_presets_changed); + add_created_tab(new TabPrint (g_wxTabPanel), event_value_change, event_presets_changed); + add_created_tab(new TabFilament (g_wxTabPanel), event_value_change, event_presets_changed); + add_created_tab(new TabSLAMaterial (g_wxTabPanel), event_value_change, event_presets_changed); + add_created_tab(new TabPrinter (g_wxTabPanel), event_value_change, event_presets_changed); } std::vector preset_tabs = { diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 7105b8b4e4..05ed580a35 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -165,7 +165,7 @@ extern void config_wizard(int run_reason); extern void open_preferences_dialog(int event_preferences); // Create a new preset tab (print, filament and printer), -void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed); +void create_preset_tabs(int event_value_change, int event_presets_changed); TabIface* get_preset_tab_iface(char *name); // add it at the end of the tab panel. diff --git a/xs/src/slic3r/GUI/Preferences.cpp b/xs/src/slic3r/GUI/Preferences.cpp index 2500afa136..89a8ead925 100644 --- a/xs/src/slic3r/GUI/Preferences.cpp +++ b/xs/src/slic3r/GUI/Preferences.cpp @@ -71,14 +71,6 @@ void PreferencesDialog::build() option = Option (def, "preset_update"); m_optgroup->append_single_option_line(option); - def.label = L("Disable USB/serial connection"); - def.type = coBool; - def.tooltip = L("Disable communication with the printer over a serial / USB cable. " - "This simplifies the user interface in case the printer is never attached to the computer."); - def.default_value = new ConfigOptionBool{ app_config->get("no_controller")[0] == '1' }; // 1; - option = Option (def,"no_controller"); - m_optgroup->append_single_option_line(option); - def.label = L("Suppress \" - default - \" presets"); def.type = coBool; def.tooltip = L("Suppress \" - default - \" presets in the Print / Filament / Printer " @@ -118,8 +110,7 @@ void PreferencesDialog::build() void PreferencesDialog::accept() { - if (m_values.find("no_controller") != m_values.end()|| - m_values.find("no_defaults") != m_values.end()|| + if (m_values.find("no_defaults") != m_values.end()|| m_values.find("use_legacy_opengl")!= m_values.end()) { warning_catcher(this, _(L("You need to restart Slic3r to make the changes effective."))); } diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index 5daf2784dc..83b834219d 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -1528,6 +1528,7 @@ void TabPrinter::build_fff() }; +#if 0 if (!m_no_controller) { optgroup = page->new_optgroup(_(L("USB/Serial connection"))); @@ -1572,6 +1573,7 @@ void TabPrinter::build_fff() line.append_widget(serial_test); optgroup->append_line(line); } +#endif optgroup = page->new_optgroup(_(L("Printer Host upload"))); @@ -1745,8 +1747,10 @@ void TabPrinter::build_fff() build_extruder_pages(); +#if 0 if (!m_no_controller) update_serial_ports(); +#endif } void TabPrinter::build_sla() diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp index a1214465ea..e4e37d4eb8 100644 --- a/xs/src/slic3r/GUI/Tab.hpp +++ b/xs/src/slic3r/GUI/Tab.hpp @@ -170,7 +170,6 @@ protected: std::vector m_pages; bool m_disable_tree_sel_changed_event; bool m_show_incompatible_presets; - bool m_no_controller; std::vector m_reload_dependent_tabs = {}; enum OptStatus { osSystemValue = 1, osInitValue = 2 }; @@ -199,8 +198,8 @@ public: public: Tab() {} - Tab(wxNotebook* parent, const wxString& title, const char* name, bool no_controller) : - m_parent(parent), m_title(title), m_name(name), m_no_controller(no_controller) { + Tab(wxNotebook* parent, const wxString& title, const char* name) : + m_parent(parent), m_title(title), m_name(name) { Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL, name); get_tabs_list().push_back(this); } @@ -282,8 +281,8 @@ class TabPrint : public Tab { public: TabPrint() {} - TabPrint(wxNotebook* parent, bool no_controller) : - Tab(parent, _(L("Print Settings")), "print", no_controller) {} + TabPrint(wxNotebook* parent) : + Tab(parent, _(L("Print Settings")), "print") {} ~TabPrint(){} ogStaticText* m_recommended_thin_wall_thickness_description_line; @@ -302,8 +301,8 @@ class TabFilament : public Tab ogStaticText* m_cooling_description_line; public: TabFilament() {} - TabFilament(wxNotebook* parent, bool no_controller) : - Tab(parent, _(L("Filament Settings")), "filament", no_controller) {} + TabFilament(wxNotebook* parent) : + Tab(parent, _(L("Filament Settings")), "filament") {} ~TabFilament(){} void build() override; @@ -335,7 +334,7 @@ public: PrinterTechnology m_printer_technology = ptFFF; TabPrinter() {} - TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Printer Settings")), "printer", no_controller) {} + TabPrinter(wxNotebook* parent) : Tab(parent, _(L("Printer Settings")), "printer") {} ~TabPrinter(){} void build() override; @@ -357,8 +356,8 @@ class TabSLAMaterial : public Tab { public: TabSLAMaterial() {} - TabSLAMaterial(wxNotebook* parent, bool no_controller) : - Tab(parent, _(L("SLA Material Settings")), "sla_material", no_controller) {} + TabSLAMaterial(wxNotebook* parent) : + Tab(parent, _(L("SLA Material Settings")), "sla_material") {} ~TabSLAMaterial(){} void build() override; diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index 3aee4035e8..6700a47657 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -50,8 +50,8 @@ void set_plater(SV *ui) void add_menus(SV *ui, int event_preferences_changed, int event_language_change) %code%{ Slic3r::GUI::add_menus((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_preferences_changed, event_language_change); %}; -void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed) - %code%{ Slic3r::GUI::create_preset_tabs(no_controller, event_value_change, event_presets_changed); %}; +void create_preset_tabs(int event_value_change, int event_presets_changed) + %code%{ Slic3r::GUI::create_preset_tabs(event_value_change, event_presets_changed); %}; void show_error_id(int id, std::string msg) %code%{ Slic3r::GUI::show_error_id(id, msg); %}; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 3212d6538b..f29cb52fc4 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -151,8 +151,6 @@ _constant() } %}; - void print_to_png(std::string dirpath); - void add_model_object(ModelObject* model_object, int idx = -1); bool apply_config(DynamicPrintConfig* config) %code%{ RETVAL = THIS->apply_config(*config); %}; @@ -166,6 +164,7 @@ _constant() %}; Clone bounding_box(); Clone total_bounding_box(); + Clone size() %code%{ RETVAL = THIS->bounding_box().size(); %}; void set_callback_event(int evt) %code%{ %}; @@ -196,4 +195,11 @@ _constant() } %}; + void export_png(char *path) %code%{ + try { + THIS->export_png(path); + } catch (std::exception& e) { + croak(e.what()); + } + %}; }; From fe3b92870f217a3356094a0a6ad60b25ccf18996 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 17 Sep 2018 15:12:13 +0200 Subject: [PATCH 15/36] Merged with dev --- lib/Slic3r/GUI.pm | 24 - lib/Slic3r/GUI/MainFrame.pm | 12 +- lib/Slic3r/GUI/Plater.pm | 257 ++++--- lib/Slic3r/GUI/Plater/3DPreview.pm | 354 ++++----- resources/icons/support_blocker.png | Bin 0 -> 656 bytes resources/icons/support_enforcer.png | Bin 0 -> 509 bytes xs/CMakeLists.txt | 3 +- xs/src/libnest2d/libnest2d/libnest2d.hpp | 8 + .../libnest2d/selections/djd_heuristic.hpp | 14 +- .../libnest2d/libnest2d/selections/filler.hpp | 2 +- .../libnest2d/selections/firstfit.hpp | 11 +- .../selections/selection_boilerplate.hpp | 8 +- xs/src/libslic3r/EdgeGrid.cpp | 6 +- xs/src/libslic3r/EdgeGrid.hpp | 2 +- xs/src/libslic3r/ExPolygonCollection.cpp | 9 +- xs/src/libslic3r/ExtrusionEntity.hpp | 40 +- .../libslic3r/ExtrusionEntityCollection.hpp | 16 +- xs/src/libslic3r/Fill/FillHoneycomb.cpp | 4 +- xs/src/libslic3r/Flow.cpp | 9 +- xs/src/libslic3r/Format/3mf.cpp | 20 +- xs/src/libslic3r/Format/AMF.cpp | 64 +- xs/src/libslic3r/Format/PRUS.cpp | 18 +- xs/src/libslic3r/GCode.cpp | 11 +- xs/src/libslic3r/GCode.hpp | 1 + xs/src/libslic3r/GCode/WipeTower.hpp | 6 + xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 47 +- xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 10 +- xs/src/libslic3r/LayerRegion.cpp | 3 +- xs/src/libslic3r/Model.cpp | 103 ++- xs/src/libslic3r/Model.hpp | 69 +- xs/src/libslic3r/ModelArrange.hpp | 83 ++- xs/src/libslic3r/MultiPoint.hpp | 4 +- xs/src/libslic3r/Point.hpp | 1 + xs/src/libslic3r/Polygon.hpp | 6 + xs/src/libslic3r/Polyline.cpp | 14 +- xs/src/libslic3r/Polyline.hpp | 2 + xs/src/libslic3r/Print.cpp | 13 +- xs/src/libslic3r/Print.hpp | 18 +- xs/src/libslic3r/PrintConfig.cpp | 31 +- xs/src/libslic3r/PrintConfig.hpp | 11 + xs/src/libslic3r/PrintObject.cpp | 97 ++- xs/src/libslic3r/PrintRegion.cpp | 5 + xs/src/libslic3r/Slicing.cpp | 6 +- xs/src/libslic3r/SupportMaterial.cpp | 671 ++++++++++++------ xs/src/libslic3r/SupportMaterial.hpp | 16 + xs/src/libslic3r/SurfaceCollection.hpp | 5 + xs/src/libslic3r/Technologies.hpp | 12 + xs/src/libslic3r/TriangleMesh.cpp | 472 ++++++++---- xs/src/libslic3r/TriangleMesh.hpp | 37 +- xs/src/libslic3r/Utils.hpp | 1 + xs/src/libslic3r/libslic3r.h | 2 + xs/src/libslic3r/utils.cpp | 10 + xs/src/slic3r/AppController.cpp | 382 +--------- xs/src/slic3r/AppController.hpp | 72 +- xs/src/slic3r/AppControllerWx.cpp | 66 +- xs/src/slic3r/GUI/3DScene.cpp | 28 +- xs/src/slic3r/GUI/3DScene.hpp | 1 + xs/src/slic3r/GUI/AppConfig.cpp | 8 + xs/src/slic3r/GUI/AppConfig.hpp | 8 + xs/src/slic3r/GUI/ConfigWizard.cpp | 16 +- xs/src/slic3r/GUI/FirmwareDialog.cpp | 25 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 16 + xs/src/slic3r/GUI/GLCanvas3D.hpp | 3 +- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 6 + xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 1 + xs/src/slic3r/GUI/GLGizmo.cpp | 261 ++++--- xs/src/slic3r/GUI/GLGizmo.hpp | 23 +- xs/src/slic3r/GUI/GUI.cpp | 169 +++-- xs/src/slic3r/GUI/GUI.hpp | 30 +- xs/src/slic3r/GUI/GUI_ObjectParts.cpp | 655 ++++++++++++----- xs/src/slic3r/GUI/GUI_ObjectParts.hpp | 30 +- xs/src/slic3r/GUI/LambdaObjectDialog.cpp | 55 +- xs/src/slic3r/GUI/LambdaObjectDialog.hpp | 11 +- xs/src/slic3r/GUI/OptionsGroup.cpp | 2 +- xs/src/slic3r/GUI/OptionsGroup.hpp | 6 +- xs/src/slic3r/GUI/Preset.cpp | 2 +- xs/src/slic3r/GUI/ProgressIndicator.hpp | 10 +- xs/src/slic3r/GUI/ProgressStatusBar.cpp | 2 +- xs/src/slic3r/GUI/ProgressStatusBar.hpp | 3 +- xs/src/slic3r/GUI/Tab.cpp | 5 +- xs/src/slic3r/GUI/wxExtensions.cpp | 261 ++++++- xs/src/slic3r/GUI/wxExtensions.hpp | 136 +++- xs/src/slic3r/Utils/Serial.cpp | 7 +- xs/xsp/GUI.xsp | 58 +- xs/xsp/GUI_3DScene.xsp | 9 + xs/xsp/Layer.xsp | 2 - xs/xsp/Model.xsp | 27 +- xs/xsp/Print.xsp | 6 + xs/xsp/ProgressStatusBar.xsp | 3 +- xs/xsp/XS.xsp | 5 + 90 files changed, 3310 insertions(+), 1748 deletions(-) create mode 100644 resources/icons/support_blocker.png create mode 100644 resources/icons/support_enforcer.png create mode 100644 xs/src/libslic3r/Technologies.hpp diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 216be34416..31f614ba92 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -334,28 +334,4 @@ sub set_menu_item_icon { } } -sub save_window_pos { - my ($self, $window, $name) = @_; - - $self->{app_config}->set("${name}_pos", join ',', $window->GetScreenPositionXY); - $self->{app_config}->set("${name}_size", join ',', $window->GetSizeWH); - $self->{app_config}->set("${name}_maximized", $window->IsMaximized); - $self->{app_config}->save; -} - -sub restore_window_pos { - my ($self, $window, $name) = @_; - if ($self->{app_config}->has("${name}_pos")) { - my $size = [ split ',', $self->{app_config}->get("${name}_size"), 2 ]; - $window->SetSize($size); - - my $display = Wx::Display->new->GetClientArea(); - my $pos = [ split ',', $self->{app_config}->get("${name}_pos"), 2 ]; - if (($pos->[0] + $size->[0]/2) < $display->GetRight && ($pos->[1] + $size->[1]/2) < $display->GetBottom) { - $window->Move($pos); - } - $window->Maximize(1) if $self->{app_config}->get("${name}_maximized"); - } -} - 1; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 3f023c06f7..c1975cd5d3 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -95,7 +95,7 @@ sub new { $self->Fit; $self->SetMinSize([760, 490]); $self->SetSize($self->GetMinSize); - wxTheApp->restore_window_pos($self, "main_frame"); + Slic3r::GUI::restore_window_size($self, "main_frame"); $self->Show; $self->Layout; } @@ -108,7 +108,7 @@ sub new { return; } # save window size - wxTheApp->save_window_pos($self, "main_frame"); + Slic3r::GUI::save_window_size($self, "main_frame"); # Save the slic3r.ini. Usually the ini file is saved from "on idle" callback, # but in rare cases it may not have been called yet. wxTheApp->{app_config}->save; @@ -207,10 +207,12 @@ sub _init_tabpanel { EVT_COMMAND($self, -1, $OBJECT_SELECTION_CHANGED_EVENT, sub { my ($self, $event) = @_; my $obj_idx = $event->GetId; - my $child = $event->GetInt == 1 ? 1 : undef; +# my $child = $event->GetInt == 1 ? 1 : undef; +# $self->{plater}->select_object($obj_idx < 0 ? undef: $obj_idx, $child); +# $self->{plater}->item_changed_selection($obj_idx); - $self->{plater}->select_object($obj_idx < 0 ? undef: $obj_idx, $child); - $self->{plater}->item_changed_selection($obj_idx); + my $vol_idx = $event->GetInt; + $self->{plater}->select_object_from_cpp($obj_idx < 0 ? undef: $obj_idx, $vol_idx<0 ? -1 : $vol_idx); }); # The following event is emited by the C++ GUI implementation on object settings change. diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d552641123..5a8bf3f8a9 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -104,10 +104,11 @@ sub new { # callback to enable/disable action buttons my $enable_action_buttons = sub { my ($enable) = @_; - $self->{btn_export_gcode}->Enable($enable); - $self->{btn_reslice}->Enable($enable); - $self->{btn_print}->Enable($enable); - $self->{btn_send_gcode}->Enable($enable); + Slic3r::GUI::enable_action_buttons($enable); +# $self->{btn_export_gcode}->Enable($enable); +# $self->{btn_reslice}->Enable($enable); +# $self->{btn_print}->Enable($enable); +# $self->{btn_send_gcode}->Enable($enable); }; # callback to react to gizmo scale @@ -230,7 +231,14 @@ sub new { my ($obj_idx, $object) = $self->selected_object; if (defined $obj_idx) { my $vol_idx = Slic3r::GUI::_3DScene::get_first_volume_id($self->{canvas3D}, $obj_idx); - Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $vol_idx) if ($vol_idx != -1); + #Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $vol_idx) if ($vol_idx != -1); + my $inst_cnt = $self->{model}->objects->[$obj_idx]->instances_count; + for (0..$inst_cnt-1){ + Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $_ + $vol_idx) if ($vol_idx != -1); + } + + my $volume_idx = Slic3r::GUI::_3DScene::get_in_object_volume_id($self->{canvas3D}, $vol_idx); + Slic3r::GUI::select_current_volume($obj_idx, $volume_idx) if ($volume_idx != -1); } } }; @@ -368,26 +376,35 @@ sub new { # } ### Panel for right column -# $self->{right_panel} = Wx::Panel->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - $self->{right_panel} = Wx::ScrolledWindow->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - $self->{right_panel}->SetScrollbars(0, 1, 1, 1); + $self->{right_panel} = Wx::Panel->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); +# $self->{right_panel} = Wx::ScrolledWindow->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); +# $self->{right_panel}->SetScrollbars(0, 1, 1, 1); + + ### Scrolled Window for panel without "Export G-code" and "Slice now" buttons + my $scrolled_window_sizer = $self->{scrolled_window_sizer} = Wx::BoxSizer->new(wxVERTICAL); + $scrolled_window_sizer->SetMinSize([320, -1]); + my $scrolled_window_panel = $self->{scrolled_window_panel} = Wx::ScrolledWindow->new($self->{right_panel}, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + $scrolled_window_panel->SetSizer($scrolled_window_sizer); + $scrolled_window_panel->SetScrollbars(0, 1, 1, 1); # right pane buttons - $self->{btn_export_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Export G-code…"), wxDefaultPosition, [-1, 30], wxNO_BORDER);#, wxBU_LEFT); - $self->{btn_reslice} = Wx::Button->new($self->{right_panel}, -1, L("Slice now"), wxDefaultPosition, [-1, 30], wxBU_LEFT); - $self->{btn_print} = Wx::Button->new($self->{right_panel}, -1, L("Print…"), wxDefaultPosition, [-1, 30], wxBU_LEFT); - $self->{btn_send_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Send to printer"), wxDefaultPosition, [-1, 30], wxBU_LEFT); - $self->{btn_export_stl} = Wx::Button->new($self->{right_panel}, -1, L("Export STL…"), wxDefaultPosition, [-1, 30], wxBU_LEFT); + $self->{btn_export_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Export G-code…"), wxDefaultPosition, [-1, 30],);# wxNO_BORDER);#, wxBU_LEFT); + $self->{btn_reslice} = Wx::Button->new($self->{right_panel}, -1, L("Slice now"), wxDefaultPosition, [-1, 30]);#, wxNO_BORDER);#, wxBU_LEFT); +# $self->{btn_print} = Wx::Button->new($self->{right_panel}, -1, L("Print…"), wxDefaultPosition, [-1, 30], wxBU_LEFT); +# $self->{btn_send_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Send to printer"), wxDefaultPosition, [-1, 30], wxBU_LEFT); + $self->{btn_print} = Wx::Button->new($scrolled_window_panel, -1, L("Print…"), wxDefaultPosition, [-1, 30], wxBU_LEFT); + $self->{btn_send_gcode} = Wx::Button->new($scrolled_window_panel, -1, L("Send to printer"), wxDefaultPosition, [-1, 30], wxBU_LEFT); + #$self->{btn_export_stl} = Wx::Button->new($self->{right_panel}, -1, L("Export STL…"), wxDefaultPosition, [-1, 30], wxBU_LEFT); #$self->{btn_export_gcode}->SetFont($Slic3r::GUI::small_font); #$self->{btn_export_stl}->SetFont($Slic3r::GUI::small_font); $self->{btn_print}->Hide; $self->{btn_send_gcode}->Hide; # export_gcode cog_go.png +#! reslice reslice.png my %icons = qw( print arrow_up.png send_gcode arrow_up.png - reslice reslice.png export_stl brick_go.png ); for (grep $self->{"btn_$_"}, keys %icons) { @@ -494,9 +511,11 @@ sub new { # $self->{preset_choosers}{$group}[$idx] $self->{preset_choosers} = {}; for my $group (qw(print filament sla_material printer)) { - my $text = Wx::StaticText->new($self->{right_panel}, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); +# my $text = Wx::StaticText->new($self->{right_panel}, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); + my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); $text->SetFont($Slic3r::GUI::small_font); - my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY); +# my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY); + my $choice = Wx::BitmapComboBox->new($scrolled_window_panel, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY); if ($group eq 'filament') { EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down(0, @_); } ); } @@ -515,26 +534,13 @@ sub new { } my $frequently_changed_parameters_sizer = $self->{frequently_changed_parameters_sizer} = Wx::BoxSizer->new(wxVERTICAL); - Slic3r::GUI::add_frequently_changed_parameters($self->{right_panel}, $frequently_changed_parameters_sizer, $presets); - - my $expert_mode_part_sizer = Wx::BoxSizer->new(wxVERTICAL); - Slic3r::GUI::add_expert_mode_part( $self->{right_panel}, $expert_mode_part_sizer, - $self->{model}, - $self->{event_object_selection_changed}, - $self->{event_object_settings_changed}, - $self->{event_remove_object}, - $self->{event_update_scene}); -# if ($expert_mode_part_sizer->IsShown(2)==1) -# { -# $expert_mode_part_sizer->Layout; -# $expert_mode_part_sizer->Show(2, 0); # ? Why doesn't work -# $self->{right_panel}->Layout; -# } +#! Slic3r::GUI::add_frequently_changed_parameters($self->{right_panel}, $frequently_changed_parameters_sizer, $presets); + Slic3r::GUI::add_frequently_changed_parameters($self->{scrolled_window_panel}, $frequently_changed_parameters_sizer, $presets); my $object_info_sizer; { -# my $box = Wx::StaticBox->new($scrolled_window_panel, -1, L("Info")); - my $box = Wx::StaticBox->new($self->{right_panel}, -1, L("Info")); + my $box = Wx::StaticBox->new($scrolled_window_panel, -1, L("Info")); +# my $box = Wx::StaticBox->new($self->{right_panel}, -1, L("Info")); $box->SetFont($Slic3r::GUI::small_bold_font); $object_info_sizer = Wx::StaticBoxSizer->new($box, wxVERTICAL); $object_info_sizer->SetMinSize([300,-1]); @@ -554,25 +560,26 @@ sub new { ); while (my $field = shift @info) { my $label = shift @info; -# my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - my $text = Wx::StaticText->new($self->{right_panel}, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); +# my $text = Wx::StaticText->new($self->{right_panel}, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); $text->SetFont($Slic3r::GUI::small_font); #!$grid_sizer->Add($text, 0); -# $self->{"object_info_$field"} = Wx::StaticText->new($scrolled_window_panel, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - $self->{"object_info_$field"} = Wx::StaticText->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + $self->{"object_info_$field"} = Wx::StaticText->new($scrolled_window_panel, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); +# $self->{"object_info_$field"} = Wx::StaticText->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); $self->{"object_info_$field"}->SetFont($Slic3r::GUI::small_font); if ($field eq 'manifold') { -# $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($scrolled_window_panel, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG)); - $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($self->{right_panel}, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG)); + $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($scrolled_window_panel, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG)); +# $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($self->{right_panel}, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG)); #$self->{object_info_manifold_warning_icon}->Hide; $self->{"object_info_manifold_warning_icon_show"} = sub { if ($self->{object_info_manifold_warning_icon}->IsShown() != $_[0]) { + # this fuction show/hide info_manifold_warning_icon on the c++ side now Slic3r::GUI::set_show_manifold_warning_icon($_[0]); - my $mode = wxTheApp->{app_config}->get("view_mode"); - return if ($mode eq "" || $mode eq "simple"); - $self->{object_info_manifold_warning_icon}->Show($_[0]); - $self->Layout + #my $mode = wxTheApp->{app_config}->get("view_mode"); + #return if ($mode eq "" || $mode eq "simple"); + #$self->{object_info_manifold_warning_icon}->Show($_[0]); + #$self->Layout } }; $self->{"object_info_manifold_warning_icon_show"}->(0); @@ -590,18 +597,19 @@ sub new { } } - my $print_info_sizer = $self->{print_info_sizer} = Wx::StaticBoxSizer->new( -# Wx::StaticBox->new($scrolled_window_panel, -1, L("Sliced Info")), wxVERTICAL); - Wx::StaticBox->new($self->{right_panel}, -1, L("Sliced Info")), wxVERTICAL); + my $print_info_box = Wx::StaticBox->new($scrolled_window_panel, -1, L("Sliced Info")); + $print_info_box->SetFont($Slic3r::GUI::small_bold_font); + my $print_info_sizer = $self->{print_info_sizer} = Wx::StaticBoxSizer->new($print_info_box, wxVERTICAL); +# Wx::StaticBox->new($self->{right_panel}, -1, L("Sliced Info")), wxVERTICAL); $print_info_sizer->SetMinSize([300,-1]); my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL); $self->{buttons_sizer} = $buttons_sizer; $buttons_sizer->AddStretchSpacer(1); - $buttons_sizer->Add($self->{btn_export_stl}, 0, wxALIGN_RIGHT, 0); - $buttons_sizer->Add($self->{btn_reslice}, 0, wxALIGN_RIGHT, 0); - $buttons_sizer->Add($self->{btn_print}, 0, wxALIGN_RIGHT, 0); - $buttons_sizer->Add($self->{btn_send_gcode}, 0, wxALIGN_RIGHT, 0); +# $buttons_sizer->Add($self->{btn_export_stl}, 0, wxALIGN_RIGHT, 0); +#! $buttons_sizer->Add($self->{btn_reslice}, 0, wxALIGN_RIGHT, 0); + $buttons_sizer->Add($self->{btn_print}, 0, wxALIGN_RIGHT | wxBOTTOM | wxTOP, 5); + $buttons_sizer->Add($self->{btn_send_gcode}, 0, wxALIGN_RIGHT | wxBOTTOM | wxTOP, 5); # $scrolled_window_sizer->Add($self->{list}, 1, wxEXPAND, 5); # $scrolled_window_sizer->Add($object_info_sizer, 0, wxEXPAND, 0); @@ -611,24 +619,39 @@ sub new { ### Sizer for info boxes my $info_sizer = $self->{info_sizer} = Wx::BoxSizer->new(wxVERTICAL); $info_sizer->SetMinSize([318, -1]); - $info_sizer->Add($object_info_sizer, 0, wxEXPAND | wxBOTTOM, 5); - $info_sizer->Add($print_info_sizer, 0, wxEXPAND | wxBOTTOM, 5); + $info_sizer->Add($object_info_sizer, 0, wxEXPAND | wxTOP, 20); + $info_sizer->Add($print_info_sizer, 0, wxEXPAND | wxTOP, 20); + + $scrolled_window_sizer->Add($presets, 0, wxEXPAND | wxLEFT, 2) if defined $presets; + $scrolled_window_sizer->Add($frequently_changed_parameters_sizer, 1, wxEXPAND | wxLEFT, 0) if defined $frequently_changed_parameters_sizer; + $scrolled_window_sizer->Add($buttons_sizer, 0, wxEXPAND, 0); + $scrolled_window_sizer->Add($info_sizer, 0, wxEXPAND | wxLEFT, 20); + # Show the box initially, let it be shown after the slicing is finished. + $self->print_info_box_show(0); + + ### Sizer for "Export G-code" & "Slice now" buttons + my $btns_sizer = Wx::BoxSizer->new(wxVERTICAL); + $btns_sizer->SetMinSize([318, -1]); + $btns_sizer->Add($self->{btn_reslice}, 0, wxEXPAND, 0); + $btns_sizer->Add($self->{btn_export_gcode}, 0, wxEXPAND | wxTOP, 5); my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); $self->{right_panel}->SetSizer($right_sizer); $right_sizer->SetMinSize([320, -1]); - $right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets; - $right_sizer->Add($frequently_changed_parameters_sizer, 1, wxEXPAND | wxTOP, 0) if defined $frequently_changed_parameters_sizer; - $right_sizer->Add($expert_mode_part_sizer, 0, wxEXPAND | wxTOP, 10) if defined $expert_mode_part_sizer; - $right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM | wxTOP, 10); - $right_sizer->Add($info_sizer, 0, wxEXPAND | wxLEFT, 20); +#! $right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets; +#! $right_sizer->Add($frequently_changed_parameters_sizer, 1, wxEXPAND | wxTOP, 0) if defined $frequently_changed_parameters_sizer; +#! $right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM | wxTOP, 10); +#! $right_sizer->Add($info_sizer, 0, wxEXPAND | wxLEFT, 20); # Show the box initially, let it be shown after the slicing is finished. - $self->print_info_box_show(0); - $right_sizer->Add($self->{btn_export_gcode}, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 20); +#! $self->print_info_box_show(0); + $right_sizer->Add($scrolled_window_panel, 1, wxEXPAND | wxTOP, 5); +# $right_sizer->Add($self->{btn_reslice}, 0, wxEXPAND | wxLEFT | wxTOP, 20); +# $right_sizer->Add($self->{btn_export_gcode}, 0, wxEXPAND | wxLEFT | wxTOP, 20); + $right_sizer->Add($btns_sizer, 0, wxEXPAND | wxLEFT | wxTOP, 20); my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); $hsizer->Add($self->{preview_notebook}, 1, wxEXPAND | wxTOP, 1); - $hsizer->Add($self->{right_panel}, 0, wxEXPAND | wxLEFT | wxRIGHT, 3); + $hsizer->Add($self->{right_panel}, 0, wxEXPAND | wxLEFT | wxRIGHT, 0);#3); my $sizer = Wx::BoxSizer->new(wxVERTICAL); # $sizer->Add($self->{htoolbar}, 0, wxEXPAND, 0) if $self->{htoolbar}; @@ -639,16 +662,21 @@ sub new { $self->SetSizer($sizer); # Send sizers/buttons to C++ - Slic3r::GUI::set_objects_from_perl( $self->{right_panel}, - $frequently_changed_parameters_sizer, - $expert_mode_part_sizer, - $info_sizer, - $self->{btn_export_gcode}, - $self->{btn_export_stl}, - $self->{btn_reslice}, - $self->{btn_print}, - $self->{btn_send_gcode}, - $self->{object_info_manifold_warning_icon} ); + Slic3r::GUI::set_objects_from_perl( $self->{scrolled_window_panel}, + $frequently_changed_parameters_sizer, + $info_sizer, + $self->{btn_export_gcode}, + # $self->{btn_export_stl}, + $self->{btn_reslice}, + $self->{btn_print}, + $self->{btn_send_gcode}, + $self->{object_info_manifold_warning_icon} ); + + Slic3r::GUI::set_model_events_from_perl( $self->{model}, + $self->{event_object_selection_changed}, + $self->{event_object_settings_changed}, + $self->{event_remove_object}, + $self->{event_update_scene}); } # Last correct selected item for each preset @@ -1594,14 +1622,16 @@ sub print_info_box_show { # my $scrolled_window_panel = $self->{scrolled_window_panel}; # my $scrolled_window_sizer = $self->{scrolled_window_sizer}; # return if (!$show && ($scrolled_window_sizer->IsShown(2) == $show)); - my $panel = $self->{right_panel}; + my $panel = $self->{scrolled_window_panel};#$self->{right_panel}; my $sizer = $self->{info_sizer}; - return if (!$sizer || !$show && ($sizer->IsShown(1) == $show)); +# return if (!$sizer || !$show && ($sizer->IsShown(1) == $show)); + return if (!$sizer); Slic3r::GUI::set_show_print_info($show); - return if (wxTheApp->{app_config}->get("view_mode") eq "simple"); +# return if (wxTheApp->{app_config}->get("view_mode") eq "simple"); - if ($show) { +# if ($show) + { my $print_info_sizer = $self->{print_info_sizer}; $print_info_sizer->Clear(1); my $grid_sizer = Wx::FlexGridSizer->new(2, 2, 5, 5); @@ -1609,20 +1639,37 @@ sub print_info_box_show { $grid_sizer->AddGrowableCol(1, 1); $grid_sizer->AddGrowableCol(3, 1); $print_info_sizer->Add($grid_sizer, 0, wxEXPAND); + my $is_wipe_tower = $self->{print}->total_wipe_tower_filament > 0; my @info = ( L("Used Filament (m)") - => sprintf("%.2f" , $self->{print}->total_used_filament / 1000), + => $is_wipe_tower ? + sprintf("%.2f (%.2f %s + %.2f %s)" , $self->{print}->total_used_filament / 1000, + ($self->{print}->total_used_filament - $self->{print}->total_wipe_tower_filament) / 1000, + L("objects"), + $self->{print}->total_wipe_tower_filament / 1000, + L("wipe tower")) : + sprintf("%.2f" , $self->{print}->total_used_filament / 1000), + L("Used Filament (mm³)") => sprintf("%.2f" , $self->{print}->total_extruded_volume), L("Used Filament (g)"), => sprintf("%.2f" , $self->{print}->total_weight), L("Cost"), - => sprintf("%.2f" , $self->{print}->total_cost), + => $is_wipe_tower ? + sprintf("%.2f (%.2f %s + %.2f %s)" , $self->{print}->total_cost, + ($self->{print}->total_cost - $self->{print}->total_wipe_tower_cost), + L("objects"), + $self->{print}->total_wipe_tower_cost, + L("wipe tower")) : + sprintf("%.2f" , $self->{print}->total_cost), L("Estimated printing time (normal mode)") => $self->{print}->estimated_normal_print_time, L("Estimated printing time (silent mode)") => $self->{print}->estimated_silent_print_time ); + # if there is a wipe tower, insert number of toolchanges info into the array: + splice (@info, 8, 0, L("Number of tool changes") => sprintf("%.d", $self->{print}->wipe_tower_number_of_toolchanges)) if ($is_wipe_tower); + while ( my $label = shift @info) { my $value = shift @info; next if $value eq "N/A"; @@ -1639,7 +1686,7 @@ sub print_info_box_show { # $scrolled_window_sizer->Show(2, $show); # $scrolled_window_panel->Layout; - $sizer->Show(1, $show); + $sizer->Show(1, $show && wxTheApp->{app_config}->get("view_mode") ne "simple"); $self->Layout; $panel->Refresh; @@ -1823,6 +1870,7 @@ sub _get_export_file { # (i.e. when an object is added/removed/moved/rotated/scaled) sub update { my ($self, $force_autocenter) = @_; + $self->Freeze; if (wxTheApp->{app_config}->get("autocenter") || $force_autocenter) { $self->{model}->center_instances_around_point($self->bed_centerf); } @@ -1836,6 +1884,7 @@ sub update { $self->{preview3D}->reset_gcode_preview_data if $self->{preview3D}; $self->{preview3D}->reload_print if $self->{preview3D}; $self->schedule_background_process; + $self->Thaw; } # When a printer technology is changed, the UI needs to be updated to show/hide needed preset combo boxes. @@ -1868,7 +1917,8 @@ sub on_extruders_change { my @presets = $choices->[0]->GetStrings; # initialize new choice - my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [@presets], wxCB_READONLY); +# my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [@presets], wxCB_READONLY); + my $choice = Wx::BitmapComboBox->new($self->{scrolled_window_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [@presets], wxCB_READONLY); my $extruder_idx = scalar @$choices; EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down($extruder_idx, @_); } ); push @$choices, $choice; @@ -1895,6 +1945,7 @@ sub on_extruders_change { $choices->[-1]->Destroy; pop @$choices; } + $self->{right_panel}->Layout; $self->Layout; } @@ -2221,15 +2272,59 @@ sub select_object { if (defined $obj_idx) { $self->{objects}->[$obj_idx]->selected(1); # Select current object in the list on c++ side, if item isn't child - if (!defined $child){ - Slic3r::GUI::select_current_object($obj_idx);} +# if (!defined $child){ +# Slic3r::GUI::select_current_object($obj_idx);} # all selections in the object list is on c++ side } else { # Unselect all objects in the list on c++ side - Slic3r::GUI::unselect_objects(); +# Slic3r::GUI::unselect_objects(); # all selections in the object list is on c++ side } $self->selection_changed(1); } +sub select_object_from_cpp { + my ($self, $obj_idx, $vol_idx) = @_; + + # remove current selection + foreach my $o (0..$#{$self->{objects}}) { + $self->{objects}->[$o]->selected(0); + } + + my $curr = Slic3r::GUI::_3DScene::get_select_by($self->{canvas3D}); + + if (defined $obj_idx) { + if ($vol_idx == -1){ + if ($curr eq 'object') { + $self->{objects}->[$obj_idx]->selected(1); + } + elsif ($curr eq 'volume') { + Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'object'); + } + + my $selections = $self->collect_selections; + Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); + } + else { + if ($curr eq 'object') { + Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'volume'); + } + + my $selections = []; + Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D}); + Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); + my $volume_idx = Slic3r::GUI::_3DScene::get_first_volume_id($self->{canvas3D}, $obj_idx); + + my $inst_cnt = $self->{model}->objects->[$obj_idx]->instances_count; + for (0..$inst_cnt-1){ + Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $vol_idx*$inst_cnt + $_ + $volume_idx) if ($volume_idx != -1); + } + } + } + + $self->selection_changed(1); +} + sub selected_object { my ($self) = @_; my $obj_idx = first { $self->{objects}[$_]->selected } 0..$#{ $self->{objects} }; diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index 09c2f0b8cf..06d1a798b5 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -5,12 +5,12 @@ use utf8; use Slic3r::Print::State ':steps'; use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE wxCB_READONLY); -use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX EVT_CHOICE EVT_CHECKLISTBOX); +use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX EVT_CHOICE EVT_CHECKLISTBOX EVT_SIZE); use base qw(Wx::Panel Class::Accessor); use Wx::Locale gettext => 'L'; -__PACKAGE__->mk_accessors(qw(print gcode_preview_data enabled _loaded canvas slider_low slider_high single_layer)); +__PACKAGE__->mk_accessors(qw(print gcode_preview_data enabled _loaded canvas slider_low slider_high single_layer double_slider_sizer)); sub new { my $class = shift; @@ -27,47 +27,47 @@ sub new { Slic3r::GUI::_3DScene::enable_shader($canvas, 1); Slic3r::GUI::_3DScene::set_config($canvas, $config); $self->canvas($canvas); - my $slider_low = Wx::Slider->new( - $self, -1, - 0, # default - 0, # min +# my $slider_low = Wx::Slider->new( +# $self, -1, +# 0, # default +# 0, # min # we set max to a bogus non-zero value because the MSW implementation of wxSlider # will skip drawing the slider if max <= min: - 1, # max - wxDefaultPosition, - wxDefaultSize, - wxVERTICAL | wxSL_INVERSE, - ); - $self->slider_low($slider_low); - my $slider_high = Wx::Slider->new( - $self, -1, - 0, # default - 0, # min +# 1, # max +# wxDefaultPosition, +# wxDefaultSize, +# wxVERTICAL | wxSL_INVERSE, +# ); +# $self->slider_low($slider_low); +# my $slider_high = Wx::Slider->new( +# $self, -1, +# 0, # default +# 0, # min # we set max to a bogus non-zero value because the MSW implementation of wxSlider # will skip drawing the slider if max <= min: - 1, # max - wxDefaultPosition, - wxDefaultSize, - wxVERTICAL | wxSL_INVERSE, - ); - $self->slider_high($slider_high); +# 1, # max +# wxDefaultPosition, +# wxDefaultSize, +# wxVERTICAL | wxSL_INVERSE, +# ); +# $self->slider_high($slider_high); - my $z_label_low = $self->{z_label_low} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, - [40,-1], wxALIGN_CENTRE_HORIZONTAL); - $z_label_low->SetFont($Slic3r::GUI::small_font); - my $z_label_high = $self->{z_label_high} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, - [40,-1], wxALIGN_CENTRE_HORIZONTAL); - $z_label_high->SetFont($Slic3r::GUI::small_font); +# my $z_label_low = $self->{z_label_low} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, +# [40,-1], wxALIGN_CENTRE_HORIZONTAL); +# $z_label_low->SetFont($Slic3r::GUI::small_font); +# my $z_label_high = $self->{z_label_high} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, +# [40,-1], wxALIGN_CENTRE_HORIZONTAL); +# $z_label_high->SetFont($Slic3r::GUI::small_font); - my $z_label_low_idx = $self->{z_label_low_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, - [40,-1], wxALIGN_CENTRE_HORIZONTAL); - $z_label_low_idx->SetFont($Slic3r::GUI::small_font); - my $z_label_high_idx = $self->{z_label_high_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, - [40,-1], wxALIGN_CENTRE_HORIZONTAL); - $z_label_high_idx->SetFont($Slic3r::GUI::small_font); +# my $z_label_low_idx = $self->{z_label_low_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, +# [40,-1], wxALIGN_CENTRE_HORIZONTAL); +# $z_label_low_idx->SetFont($Slic3r::GUI::small_font); +# my $z_label_high_idx = $self->{z_label_high_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, +# [40,-1], wxALIGN_CENTRE_HORIZONTAL); +# $z_label_high_idx->SetFont($Slic3r::GUI::small_font); - $self->single_layer(0); - my $checkbox_singlelayer = $self->{checkbox_singlelayer} = Wx::CheckBox->new($self, -1, L("1 Layer")); +# $self->single_layer(0); +# my $checkbox_singlelayer = $self->{checkbox_singlelayer} = Wx::CheckBox->new($self, -1, L("1 Layer")); my $label_view_type = $self->{label_view_type} = Wx::StaticText->new($self, -1, L("View")); @@ -102,26 +102,31 @@ sub new { .L("Wipe tower")."|" .L("Custom"); Slic3r::GUI::create_combochecklist($combochecklist_features, $feature_text, $feature_items, 1); + + my $double_slider_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + Slic3r::GUI::create_double_slider($self, $double_slider_sizer, $self->canvas); + $self->double_slider_sizer($double_slider_sizer); my $checkbox_travel = $self->{checkbox_travel} = Wx::CheckBox->new($self, -1, L("Travel")); my $checkbox_retractions = $self->{checkbox_retractions} = Wx::CheckBox->new($self, -1, L("Retractions")); my $checkbox_unretractions = $self->{checkbox_unretractions} = Wx::CheckBox->new($self, -1, L("Unretractions")); my $checkbox_shells = $self->{checkbox_shells} = Wx::CheckBox->new($self, -1, L("Shells")); - my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); - my $vsizer = Wx::BoxSizer->new(wxVERTICAL); - my $vsizer_outer = Wx::BoxSizer->new(wxVERTICAL); - $vsizer->Add($slider_low, 3, wxALIGN_CENTER_HORIZONTAL, 0); - $vsizer->Add($z_label_low_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0); - $vsizer->Add($z_label_low, 0, wxALIGN_CENTER_HORIZONTAL, 0); - $hsizer->Add($vsizer, 0, wxEXPAND, 0); - $vsizer = Wx::BoxSizer->new(wxVERTICAL); - $vsizer->Add($slider_high, 3, wxALIGN_CENTER_HORIZONTAL, 0); - $vsizer->Add($z_label_high_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0); - $vsizer->Add($z_label_high, 0, 0, 0); - $hsizer->Add($vsizer, 0, wxEXPAND, 0); - $vsizer_outer->Add($hsizer, 3, wxALIGN_CENTER_HORIZONTAL, 0); - $vsizer_outer->Add($checkbox_singlelayer, 0, wxTOP | wxALIGN_CENTER_HORIZONTAL, 5); +# my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); +# my $vsizer = Wx::BoxSizer->new(wxVERTICAL); +# my $vsizer_outer = Wx::BoxSizer->new(wxVERTICAL); +# $vsizer->Add($slider_low, 3, wxALIGN_CENTER_HORIZONTAL, 0); +# $vsizer->Add($z_label_low_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0); +# $vsizer->Add($z_label_low, 0, wxALIGN_CENTER_HORIZONTAL, 0); +# $hsizer->Add($vsizer, 0, wxEXPAND, 0); +# $vsizer = Wx::BoxSizer->new(wxVERTICAL); +# $vsizer->Add($slider_high, 3, wxALIGN_CENTER_HORIZONTAL, 0); +# $vsizer->Add($z_label_high_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0); +# $vsizer->Add($z_label_high, 0, 0, 0); +# $hsizer->Add($vsizer, 0, wxEXPAND, 0); +# $vsizer_outer->Add($hsizer, 3, wxALIGN_CENTER_HORIZONTAL, 0); +# $vsizer_outer->Add($double_slider_sizer, 3, wxEXPAND, 0); +# $vsizer_outer->Add($checkbox_singlelayer, 0, wxTOP | wxALIGN_CENTER_HORIZONTAL, 5); my $bottom_sizer = Wx::BoxSizer->new(wxHORIZONTAL); $bottom_sizer->Add($label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); @@ -140,81 +145,83 @@ sub new { my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); $sizer->Add($canvas, 1, wxALL | wxEXPAND, 0); - $sizer->Add($vsizer_outer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5); +# $sizer->Add($vsizer_outer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5); + $sizer->Add($double_slider_sizer, 0, wxEXPAND, 0);#wxTOP | wxBOTTOM | wxEXPAND, 5); my $main_sizer = Wx::BoxSizer->new(wxVERTICAL); $main_sizer->Add($sizer, 1, wxALL | wxEXPAND, 0); $main_sizer->Add($bottom_sizer, 0, wxALL | wxEXPAND, 0); - EVT_SLIDER($self, $slider_low, sub { - $slider_high->SetValue($slider_low->GetValue) if $self->single_layer; - $self->set_z_idx_low ($slider_low ->GetValue) - }); - EVT_SLIDER($self, $slider_high, sub { - $slider_low->SetValue($slider_high->GetValue) if $self->single_layer; - $self->set_z_idx_high($slider_high->GetValue) - }); - EVT_KEY_DOWN($canvas, sub { - my ($s, $event) = @_; - my $key = $event->GetKeyCode; - if ($event->HasModifiers) { - $event->Skip; - } else { - if ($key == ord('U')) { - $slider_high->SetValue($slider_high->GetValue + 1); - $slider_low->SetValue($slider_high->GetValue) if ($event->ShiftDown()); - $self->set_z_idx_high($slider_high->GetValue); - } elsif ($key == ord('D')) { - $slider_high->SetValue($slider_high->GetValue - 1); - $slider_low->SetValue($slider_high->GetValue) if ($event->ShiftDown()); - $self->set_z_idx_high($slider_high->GetValue); - } elsif ($key == ord('S')) { - $checkbox_singlelayer->SetValue(! $checkbox_singlelayer->GetValue()); - $self->single_layer($checkbox_singlelayer->GetValue()); - if ($self->single_layer) { - $slider_low->SetValue($slider_high->GetValue); - $self->set_z_idx_high($slider_high->GetValue); - } - } else { - $event->Skip; - } - } - }); - EVT_KEY_DOWN($slider_low, sub { - my ($s, $event) = @_; - my $key = $event->GetKeyCode; - if ($event->HasModifiers) { - $event->Skip; - } else { - if ($key == WXK_LEFT) { - } elsif ($key == WXK_RIGHT) { - $slider_high->SetFocus; - } else { - $event->Skip; - } - } - }); - EVT_KEY_DOWN($slider_high, sub { - my ($s, $event) = @_; - my $key = $event->GetKeyCode; - if ($event->HasModifiers) { - $event->Skip; - } else { - if ($key == WXK_LEFT) { - $slider_low->SetFocus; - } elsif ($key == WXK_RIGHT) { - } else { - $event->Skip; - } - } - }); - EVT_CHECKBOX($self, $checkbox_singlelayer, sub { - $self->single_layer($checkbox_singlelayer->GetValue()); - if ($self->single_layer) { - $slider_low->SetValue($slider_high->GetValue); - $self->set_z_idx_high($slider_high->GetValue); - } - }); +# EVT_SLIDER($self, $slider_low, sub { +# $slider_high->SetValue($slider_low->GetValue) if $self->single_layer; +# $self->set_z_idx_low ($slider_low ->GetValue) +# }); +# EVT_SLIDER($self, $slider_high, sub { +# $slider_low->SetValue($slider_high->GetValue) if $self->single_layer; +# $self->set_z_idx_high($slider_high->GetValue) +# }); +# EVT_KEY_DOWN($canvas, sub { +# my ($s, $event) = @_; +# Slic3r::GUI::update_double_slider_from_canvas($event); +# my $key = $event->GetKeyCode; +# if ($event->HasModifiers) { +# $event->Skip; +# } else { +# if ($key == ord('U')) { +# $slider_high->SetValue($slider_high->GetValue + 1); +# $slider_low->SetValue($slider_high->GetValue) if ($event->ShiftDown()); +# $self->set_z_idx_high($slider_high->GetValue); +# } elsif ($key == ord('D')) { +# $slider_high->SetValue($slider_high->GetValue - 1); +# $slider_low->SetValue($slider_high->GetValue) if ($event->ShiftDown()); +# $self->set_z_idx_high($slider_high->GetValue); +# } elsif ($key == ord('S')) { +# $checkbox_singlelayer->SetValue(! $checkbox_singlelayer->GetValue()); +# $self->single_layer($checkbox_singlelayer->GetValue()); +# if ($self->single_layer) { +# $slider_low->SetValue($slider_high->GetValue); +# $self->set_z_idx_high($slider_high->GetValue); +# } +# } else { +# $event->Skip; +# } +# } +# }); +# EVT_KEY_DOWN($slider_low, sub { +# my ($s, $event) = @_; +# my $key = $event->GetKeyCode; +# if ($event->HasModifiers) { +# $event->Skip; +# } else { +# if ($key == WXK_LEFT) { +# } elsif ($key == WXK_RIGHT) { +# $slider_high->SetFocus; +# } else { +# $event->Skip; +# } +# } +# }); +# EVT_KEY_DOWN($slider_high, sub { +# my ($s, $event) = @_; +# my $key = $event->GetKeyCode; +# if ($event->HasModifiers) { +# $event->Skip; +# } else { +# if ($key == WXK_LEFT) { +# $slider_low->SetFocus; +# } elsif ($key == WXK_RIGHT) { +# } else { +# $event->Skip; +# } +# } +# }); +# EVT_CHECKBOX($self, $checkbox_singlelayer, sub { +# $self->single_layer($checkbox_singlelayer->GetValue()); +# if ($self->single_layer) { +# $slider_low->SetValue($slider_high->GetValue); +# $self->set_z_idx_high($slider_high->GetValue); +# } +# }); EVT_CHOICE($self, $choice_view_type, sub { my $selection = $choice_view_type->GetCurrentSelection(); $self->{preferred_color_mode} = ($selection == $self->{tool_idx}) ? 'tool' : 'feature'; @@ -243,6 +250,12 @@ sub new { $self->gcode_preview_data->set_shells_visible($checkbox_shells->IsChecked()); $self->refresh_print; }); + + EVT_SIZE($self, sub { + my ($s, $event) = @_; + $event->Skip; + $self->Refresh; + }); $self->SetSizer($main_sizer); $self->SetMinSize($self->GetSize); @@ -392,62 +405,69 @@ sub load_print { sub reset_sliders { my ($self) = @_; $self->enabled(0); - $self->set_z_range(0,0); - $self->slider_low->Hide; - $self->slider_high->Hide; - $self->{z_label_low}->SetLabel(""); - $self->{z_label_high}->SetLabel(""); - $self->{z_label_low_idx}->SetLabel(""); - $self->{z_label_high_idx}->SetLabel(""); +# $self->set_z_range(0,0); +# $self->slider_low->Hide; +# $self->slider_high->Hide; +# $self->{z_label_low}->SetLabel(""); +# $self->{z_label_high}->SetLabel(""); +# $self->{z_label_low_idx}->SetLabel(""); +# $self->{z_label_high_idx}->SetLabel(""); + + Slic3r::GUI::reset_double_slider(); + $self->double_slider_sizer->Hide(0); } sub update_sliders { my ($self, $n_layers) = @_; - my $z_idx_low = $self->slider_low->GetValue; - my $z_idx_high = $self->slider_high->GetValue; +# my $z_idx_low = $self->slider_low->GetValue; +# my $z_idx_high = $self->slider_high->GetValue; $self->enabled(1); - $self->slider_low->SetRange(0, $n_layers - 1); - $self->slider_high->SetRange(0, $n_layers - 1); +# $self->slider_low->SetRange(0, $n_layers - 1); +# $self->slider_high->SetRange(0, $n_layers - 1); - if ($self->{force_sliders_full_range}) { - $z_idx_low = 0; - $z_idx_high = $n_layers - 1; - } elsif ($z_idx_high < $n_layers && ($self->single_layer || $z_idx_high != 0)) { - # search new indices for nearest z (size of $self->{layers_z} may change in dependence of what is shown) - if (defined($self->{z_low})) { - for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) { - if ($self->{layers_z}[$i] <= $self->{z_low}) { - $z_idx_low = $i; - last; - } - } - } - if (defined($self->{z_high})) { - for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) { - if ($self->{layers_z}[$i] <= $self->{z_high}) { - $z_idx_high = $i; - last; - } - } - } - } elsif ($z_idx_high >= $n_layers) { - # Out of range. Disable 'single layer' view. - $self->single_layer(0); - $self->{checkbox_singlelayer}->SetValue(0); - $z_idx_low = 0; - $z_idx_high = $n_layers - 1; - } else { - $z_idx_low = 0; - $z_idx_high = $n_layers - 1; - } +# if ($self->{force_sliders_full_range}) { +# $z_idx_low = 0; +# $z_idx_high = $n_layers - 1; +# } elsif ($z_idx_high < $n_layers && ($self->single_layer || $z_idx_high != 0)) { +# # search new indices for nearest z (size of $self->{layers_z} may change in dependence of what is shown) +# if (defined($self->{z_low})) { +# for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) { +# if ($self->{layers_z}[$i] <= $self->{z_low}) { +# $z_idx_low = $i; +# last; +# } +# } +# } +# if (defined($self->{z_high})) { +# for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) { +# if ($self->{layers_z}[$i] <= $self->{z_high}) { +# $z_idx_high = $i; +# last; +# } +# } +# } +# } elsif ($z_idx_high >= $n_layers) { +# # Out of range. Disable 'single layer' view. +# $self->single_layer(0); +# $self->{checkbox_singlelayer}->SetValue(0); +# $z_idx_low = 0; +# $z_idx_high = $n_layers - 1; +# } else { +# $z_idx_low = 0; +# $z_idx_high = $n_layers - 1; +# } - $self->slider_low->SetValue($z_idx_low); - $self->slider_high->SetValue($z_idx_high); - $self->slider_low->Show; - $self->slider_high->Show; - $self->set_z_range($self->{layers_z}[$z_idx_low], $self->{layers_z}[$z_idx_high]); +# $self->slider_low->SetValue($z_idx_low); +# $self->slider_high->SetValue($z_idx_high); +# $self->slider_low->Show; +# $self->slider_high->Show; +# $self->set_z_range($self->{layers_z}[$z_idx_low], $self->{layers_z}[$z_idx_high]); + + Slic3r::GUI::update_double_slider($self->{force_sliders_full_range}); + $self->double_slider_sizer->Show(0); + $self->Layout; } diff --git a/resources/icons/support_blocker.png b/resources/icons/support_blocker.png new file mode 100644 index 0000000000000000000000000000000000000000..8a66c73f4f6aff7721a84c2ce768d78cf0aac128 GIT binary patch literal 656 zcmV;B0&o3^P)3tMfkRIh^O5_aHwQOgY^tr#t)fIa>v|l+!Iu&j8?5 zD&5$3|LEPgI4eX@x30IEwdTq6#7Z>)zN`NQ%Vfy# z7XVTy!ZBoaS1;y8HLt0ybQA(_#j70v1iMWx;22}K!q*zRhPDmIWh#2wInrDgO~xe3 zE?yc?r*pE~B})ME0;1i`$y%dKS$aNfe-bh3jGk9Xbvh@zEzSUdQd&?d!ktaiyDDYY z*7N@0$wxaMN93of$+KL*F~&MVZIn{2vV4nD?>YLAFG!_Rj@k9T;}uy@LTG=>YXyLA z$DP{^_Yw<Ej`Df#>9`*nR_mu-4n!x_N*QLMSDKC>SrNDU|}B-U*!2uup$1 qFjxG()tt0=-#>3KFScK2jQs;LJU(a{EOJ%=00007xp;PyQ4hxbfI(F{TE$onlr!JkMLv%4_b<9)@c8Cc-L1UY_VnnU< z`##UxVG_-m(tCS(`Ml4|hc|L}{7-f7>ZdK_qE2ahPlbTD@p(WA~Wj%jHP<%{<(*p7aN%A&0KzR z$;{07vM?L{5qEp;dcHqj`m#((PIJv%c)Fh@Gty}!opjL&V6u4aMCuep2j=GA6mP!m z25_rbFk?EIrW8ETCD9}RgQ=6{wUz2-ZEJgr;9*|TwNwNkH=%pT3QjPGH|f>9dFj2l&{SD#&Z0pQ}J^8f;aksW~9 z`|^U3X4d5`{U8P4-O@rj+4naP?epGIw8!)}S~4KOas1W>00000NkvXXu0mjf^^oOe literal 0 HcmV?d00001 diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index a442530dbb..878ce4dfea 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -179,6 +179,7 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/SurfaceCollection.hpp ${LIBDIR}/libslic3r/SVG.cpp ${LIBDIR}/libslic3r/SVG.hpp + ${LIBDIR}/libslic3r/Technologies.hpp ${LIBDIR}/libslic3r/TriangleMesh.cpp ${LIBDIR}/libslic3r/TriangleMesh.hpp ${LIBDIR}/libslic3r/SLABasePool.hpp @@ -568,7 +569,7 @@ if (WIN32) endif () # SLIC3R_MSVC_PDB -if (MSVC AND SLIC3R_MSVC_PDB AND ${CMAKE_BUILD_TYPE} STREQUAL "Release") +if (MSVC AND SLIC3R_MSVC_PDB AND "${CMAKE_BUILD_TYPE}" STREQUAL "Release") set_target_properties(XS PROPERTIES COMPILE_FLAGS "/Zi" LINK_FLAGS "/DEBUG /OPT:REF /OPT:ICF" diff --git a/xs/src/libnest2d/libnest2d/libnest2d.hpp b/xs/src/libnest2d/libnest2d/libnest2d.hpp index 4d1e62f991..8841d1b735 100644 --- a/xs/src/libnest2d/libnest2d/libnest2d.hpp +++ b/xs/src/libnest2d/libnest2d/libnest2d.hpp @@ -640,6 +640,7 @@ public: // The progress function will be called with the number of placed items using ProgressFunction = std::function; +using StopCondition = std::function; /** * A wrapper interface (trait) class for any selections strategy provider. @@ -674,6 +675,8 @@ public: */ void progressIndicator(ProgressFunction fn) { impl_.progressIndicator(fn); } + void stopCondition(StopCondition cond) { impl_.stopCondition(cond); } + /** * \brief A method to start the calculation on the input sequence. * @@ -864,6 +867,11 @@ public: selector_.progressIndicator(func); return *this; } + /// Set a predicate to tell when to abort nesting. + inline Nester& stopCondition(StopCondition fn) { + selector_.stopCondition(fn); return *this; + } + inline PackGroup lastResult() { PackGroup ret; for(size_t i = 0; i < selector_.binCount(); i++) { diff --git a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp index ee93d05928..39761f5572 100644 --- a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp +++ b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp @@ -551,7 +551,7 @@ public: // Safety test: try to pack each item into an empty bin. If it fails // then it should be removed from the not_packed list { auto it = store_.begin(); - while (it != store_.end()) { + while (it != store_.end() && !this->stopcond_()) { Placer p(bin); p.configure(pconfig); if(!p.pack(*it, rem(it, store_))) { it = store_.erase(it); @@ -592,9 +592,11 @@ public: bool do_pairs = config_.try_pairs; bool do_triplets = config_.try_triplets; + StopCondition stopcond = this->stopcond_; // The DJD heuristic algorithm itself: auto packjob = [INITIAL_FILL_AREA, bin_area, w, do_triplets, do_pairs, + stopcond, &tryOneByOne, &tryGroupsOfTwo, &tryGroupsOfThree, @@ -606,12 +608,12 @@ public: double waste = .0; bool lasttry = false; - while(!not_packed.empty()) { + while(!not_packed.empty() && !stopcond()) { {// Fill the bin up to INITIAL_FILL_PROPORTION of its capacity auto it = not_packed.begin(); - while(it != not_packed.end() && + while(it != not_packed.end() && !stopcond() && filled_area < INITIAL_FILL_AREA) { if(placer.pack(*it, rem(it, not_packed))) { @@ -623,14 +625,14 @@ public: } } - // try pieses one by one + // try pieces one by one while(tryOneByOne(placer, not_packed, waste, free_area, filled_area)) { waste = 0; lasttry = false; makeProgress(placer, idx, 1); } - // try groups of 2 pieses + // try groups of 2 pieces while(do_pairs && tryGroupsOfTwo(placer, not_packed, waste, free_area, filled_area)) { @@ -638,7 +640,7 @@ public: makeProgress(placer, idx, 2); } - // try groups of 3 pieses + // try groups of 3 pieces while(do_triplets && tryGroupsOfThree(placer, not_packed, waste, free_area, filled_area)) { diff --git a/xs/src/libnest2d/libnest2d/selections/filler.hpp b/xs/src/libnest2d/libnest2d/selections/filler.hpp index 0da7220a19..5f95a6eff8 100644 --- a/xs/src/libnest2d/libnest2d/selections/filler.hpp +++ b/xs/src/libnest2d/libnest2d/selections/filler.hpp @@ -60,7 +60,7 @@ public: placer.configure(pconfig); auto it = store_.begin(); - while(it != store_.end()) { + while(it != store_.end() && !this->stopcond_()) { if(!placer.pack(*it, {std::next(it), store_.end()})) { if(packed_bins_.back().empty()) ++it; placer.clearItems(); diff --git a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp index bca7497db8..6bb9c60e45 100644 --- a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp +++ b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp @@ -56,10 +56,12 @@ public: this->progress_(static_cast(--total)); }; + auto& cancelled = this->stopcond_; + // Safety test: try to pack each item into an empty bin. If it fails // then it should be removed from the list { auto it = store_.begin(); - while (it != store_.end()) { + while (it != store_.end() && !cancelled()) { Placer p(bin); p.configure(pconfig); if(!p.pack(*it)) { it = store_.erase(it); @@ -67,13 +69,14 @@ public: } } + auto it = store_.begin(); - while(it != store_.end()) { + while(it != store_.end() && !cancelled()) { bool was_packed = false; size_t j = 0; - while(!was_packed) { - for(; j < placers.size() && !was_packed; j++) { + while(!was_packed && !cancelled()) { + for(; j < placers.size() && !was_packed && !cancelled(); j++) { if((was_packed = placers[j].pack(*it, rem(it, store_) ))) makeProgress(placers[j], j); } diff --git a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp b/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp index 05bbae658e..cfb98a9c8e 100644 --- a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp +++ b/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp @@ -2,6 +2,7 @@ #define SELECTION_BOILERPLATE_HPP #include "../libnest2d.hpp" +#include namespace libnest2d { namespace selections { @@ -25,14 +26,15 @@ public: return packed_bins_[binIndex]; } - inline void progressIndicator(ProgressFunction fn) { - progress_ = fn; - } + inline void progressIndicator(ProgressFunction fn) { progress_ = fn; } + + inline void stopCondition(StopCondition cond) { stopcond_ = cond; } protected: PackGroup packed_bins_; ProgressFunction progress_ = [](unsigned){}; + StopCondition stopcond_ = [](){ return false; }; }; } diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp index 50f424e6d0..2b2893c805 100644 --- a/xs/src/libslic3r/EdgeGrid.cpp +++ b/xs/src/libslic3r/EdgeGrid.cpp @@ -1225,8 +1225,10 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo return true; } -Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const +Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) const { + assert(std::abs(2 * offset) < m_resolution); + typedef std::unordered_multimap EndPointMapType; // 0) Prepare a binary grid. size_t cell_rows = m_rows + 2; @@ -1237,7 +1239,7 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const cell_inside[r * cell_cols + c] = cell_inside_or_crossing(r - 1, c - 1); // Fill in empty cells, which have a left / right neighbor filled. // Fill in empty cells, which have the top / bottom neighbor filled. - { + if (fill_holes) { std::vector cell_inside2(cell_inside); for (int r = 1; r + 1 < int(cell_rows); ++ r) { for (int c = 1; c + 1 < int(cell_cols); ++ c) { diff --git a/xs/src/libslic3r/EdgeGrid.hpp b/xs/src/libslic3r/EdgeGrid.hpp index 3eb741865c..ab1aa4ed0a 100644 --- a/xs/src/libslic3r/EdgeGrid.hpp +++ b/xs/src/libslic3r/EdgeGrid.hpp @@ -58,7 +58,7 @@ public: const size_t cols() const { return m_cols; } // For supports: Contours enclosing the rasterized edges. - Polygons contours_simplified(coord_t offset) const; + Polygons contours_simplified(coord_t offset, bool fill_holes) const; protected: struct Cell { diff --git a/xs/src/libslic3r/ExPolygonCollection.cpp b/xs/src/libslic3r/ExPolygonCollection.cpp index e52498ecba..6933544b6f 100644 --- a/xs/src/libslic3r/ExPolygonCollection.cpp +++ b/xs/src/libslic3r/ExPolygonCollection.cpp @@ -61,12 +61,11 @@ ExPolygonCollection::rotate(double angle, const Point ¢er) } template -bool -ExPolygonCollection::contains(const T &item) const +bool ExPolygonCollection::contains(const T &item) const { - for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) { - if (it->contains(item)) return true; - } + for (const ExPolygon &poly : this->expolygons) + if (poly.contains(item)) + return true; return false; } template bool ExPolygonCollection::contains(const Point &item) const; diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index 2bd03600c4..cce3020f88 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -91,6 +91,8 @@ public: // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. virtual double min_mm3_per_mm() const = 0; virtual Polyline as_polyline() const = 0; + virtual void collect_polylines(Polylines &dst) const = 0; + virtual Polylines as_polylines() const { Polylines dst; this->collect_polylines(dst); return dst; } virtual double length() const = 0; virtual double total_volume() const = 0; }; @@ -123,8 +125,11 @@ public: ExtrusionPath* clone() const { return new ExtrusionPath (*this); } void reverse() { this->polyline.reverse(); } - Point first_point() const { return this->polyline.points.front(); } - Point last_point() const { return this->polyline.points.back(); } + Point first_point() const override { return this->polyline.points.front(); } + Point last_point() const override { return this->polyline.points.back(); } + size_t size() const { return this->polyline.size(); } + bool empty() const { return this->polyline.empty(); } + bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); } // Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection. // Currently not used. void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const; @@ -133,8 +138,8 @@ public: void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const; void clip_end(double distance); void simplify(double tolerance); - virtual double length() const; - virtual ExtrusionRole role() const { return m_role; } + double length() const override; + ExtrusionRole role() const override { return m_role; } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const; @@ -149,7 +154,8 @@ public: // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. double min_mm3_per_mm() const { return this->mm3_per_mm; } Polyline as_polyline() const { return this->polyline; } - virtual double total_volume() const { return mm3_per_mm * unscale(length()); } + void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); } + double total_volume() const override { return mm3_per_mm * unscale(length()); } private: void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const; @@ -178,10 +184,10 @@ public: bool can_reverse() const { return true; } ExtrusionMultiPath* clone() const { return new ExtrusionMultiPath(*this); } void reverse(); - Point first_point() const { return this->paths.front().polyline.points.front(); } - Point last_point() const { return this->paths.back().polyline.points.back(); } - virtual double length() const; - virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); } + Point first_point() const override { return this->paths.front().polyline.points.front(); } + Point last_point() const override { return this->paths.back().polyline.points.back(); } + double length() const override; + ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const; @@ -196,7 +202,8 @@ public: // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. double min_mm3_per_mm() const; Polyline as_polyline() const; - virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; } + void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); } + double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; } }; // Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging. @@ -218,18 +225,18 @@ public: bool make_clockwise(); bool make_counter_clockwise(); void reverse(); - Point first_point() const { return this->paths.front().polyline.points.front(); } - Point last_point() const { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); } + Point first_point() const override { return this->paths.front().polyline.points.front(); } + Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); } Polygon polygon() const; - virtual double length() const; + double length() const override; bool split_at_vertex(const Point &point); void split_at(const Point &point, bool prefer_non_overhang); void clip_end(double distance, ExtrusionPaths* paths) const; // Test, whether the point is extruded by a bridging flow. // This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead. bool has_overhang_point(const Point &point) const; - virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); } - ExtrusionLoopRole loop_role() const { return m_loop_role; } + ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); } + ExtrusionLoopRole loop_role() const { return m_loop_role; } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const; @@ -244,7 +251,8 @@ public: // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. double min_mm3_per_mm() const; Polyline as_polyline() const { return this->polygon().split_at_first_point(); } - virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; } + void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); } + double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; } private: ExtrusionLoopRole m_loop_role; diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp index 3b34145f8a..ee0d3d5cd4 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp @@ -24,7 +24,7 @@ public: explicit operator ExtrusionPaths() const; bool is_collection() const { return true; }; - virtual ExtrusionRole role() const { + ExtrusionRole role() const override { ExtrusionRole out = erNone; for (const ExtrusionEntity *ee : entities) { ExtrusionRole er = ee->role(); @@ -71,11 +71,11 @@ public: Point last_point() const { return this->entities.back()->last_point(); } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. - virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const; + void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. // Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill. - virtual void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const; + void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override; Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const { Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; } Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const @@ -84,14 +84,20 @@ public: void flatten(ExtrusionEntityCollection* retval) const; ExtrusionEntityCollection flatten() const; double min_mm3_per_mm() const; - virtual double total_volume() const {double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; } + double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; } // Following methods shall never be called on an ExtrusionEntityCollection. Polyline as_polyline() const { CONFESS("Calling as_polyline() on a ExtrusionEntityCollection"); return Polyline(); }; - virtual double length() const { + + void collect_polylines(Polylines &dst) const override { + for (ExtrusionEntity* extrusion_entity : this->entities) + extrusion_entity->collect_polylines(dst); + } + + double length() const override { CONFESS("Calling length() on a ExtrusionEntityCollection"); return 0.; } diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.cpp b/xs/src/libslic3r/Fill/FillHoneycomb.cpp index 6f26167a25..cbfe926f2f 100644 --- a/xs/src/libslic3r/Fill/FillHoneycomb.cpp +++ b/xs/src/libslic3r/Fill/FillHoneycomb.cpp @@ -86,8 +86,8 @@ void FillHoneycomb::_fill_surface_single( Polylines paths; { Polylines p; - for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it) - p.push_back((Polyline)(*it)); + for (Polygon &poly : polygons) + p.emplace_back(poly.points); paths = intersection_pl(p, to_polygons(expolygon)); } diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp index c33de8b6e7..9ad482dafb 100644 --- a/xs/src/libslic3r/Flow.cpp +++ b/xs/src/libslic3r/Flow.cpp @@ -115,7 +115,8 @@ Flow support_material_flow(const PrintObject *object, float layer_height) // if object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)), (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value), - false); + // bridge_flow_ratio + 0.f); } Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) @@ -127,7 +128,8 @@ Flow support_material_1st_layer_flow(const PrintObject *object, float layer_heig (width.value > 0) ? width : object->config().extrusion_width, float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)), (layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value)), - false); + // bridge_flow_ratio + 0.f); } Flow support_material_interface_flow(const PrintObject *object, float layer_height) @@ -139,7 +141,8 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig // if object->config().support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_interface_extruder-1)), (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value), - false); + // bridge_flow_ratio + 0.f); } } diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp index 464cbb8e67..43c99f19fd 100644 --- a/xs/src/libslic3r/Format/3mf.cpp +++ b/xs/src/libslic3r/Format/3mf.cpp @@ -71,6 +71,7 @@ const char* VOLUME_TYPE = "volume"; const char* NAME_KEY = "name"; const char* MODIFIER_KEY = "modifier"; +const char* VOLUME_TYPE_KEY = "volume_type"; const unsigned int VALID_OBJECT_TYPES_COUNT = 1; const char* VALID_OBJECT_TYPES[] = @@ -1252,9 +1253,13 @@ namespace Slic3r { // we extract from the given matrix only the values currently used // translation +#if ENABLE_MODELINSTANCE_3D_OFFSET + Vec3d offset(transform(0, 3), transform(1, 3), transform(2, 3)); +#else double offset_x = transform(0, 3); double offset_y = transform(1, 3); double offset_z = transform(2, 3); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET // scale double sx = ::sqrt(sqr(transform(0, 0)) + sqr(transform(1, 0)) + sqr(transform(2, 0))); @@ -1287,8 +1292,12 @@ namespace Slic3r { double angle_z = (rotation.axis() == Vec3d::UnitZ()) ? rotation.angle() : -rotation.angle(); +#if ENABLE_MODELINSTANCE_3D_OFFSET + instance.set_offset(offset); +#else instance.offset(0) = offset_x; instance.offset(1) = offset_y; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET instance.scaling_factor = sx; instance.rotation = angle_z; } @@ -1434,7 +1443,9 @@ namespace Slic3r { if (metadata.key == NAME_KEY) volume->name = metadata.value; else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1")) - volume->modifier = true; + volume->set_type(ModelVolume::PARAMETER_MODIFIER); + else if (metadata.key == VOLUME_TYPE_KEY) + volume->set_type(ModelVolume::type_from_string(metadata.value)); else volume->config.set_deserialize(metadata.key, metadata.value); } @@ -1949,9 +1960,12 @@ namespace Slic3r { if (!volume->name.empty()) stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n"; - // stores volume's modifier field - if (volume->modifier) + // stores volume's modifier field (legacy, to support old slicers) + if (volume->is_modifier()) stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n"; + // stores volume's type (overrides the modifier field above) + stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " << + VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n"; // stores volume's config data for (const std::string& key : volume->config.keys()) diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp index 81617ad729..803d9ee54f 100644 --- a/xs/src/libslic3r/Format/AMF.cpp +++ b/xs/src/libslic3r/Format/AMF.cpp @@ -29,7 +29,12 @@ // VERSION NUMBERS // 0 : .amf, .amf.xml and .zip.amf files saved by older slic3r. No version definition in them. // 1 : Introduction of amf versioning. No other change in data saved into amf files. +#if ENABLE_MODELINSTANCE_3D_OFFSET +// 2 : Added z component of offset. +const unsigned int VERSION_AMF = 2; +#else const unsigned int VERSION_AMF = 1; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version"; const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config"; @@ -119,19 +124,31 @@ struct AMFParserContext NODE_TYPE_INSTANCE, // amf/constellation/instance NODE_TYPE_DELTAX, // amf/constellation/instance/deltax NODE_TYPE_DELTAY, // amf/constellation/instance/deltay +#if ENABLE_MODELINSTANCE_3D_OFFSET + NODE_TYPE_DELTAZ, // amf/constellation/instance/deltaz +#endif // ENABLE_MODELINSTANCE_3D_OFFSET NODE_TYPE_RZ, // amf/constellation/instance/rz NODE_TYPE_SCALE, // amf/constellation/instance/scale NODE_TYPE_METADATA, // anywhere under amf/*/metadata }; struct Instance { +#if ENABLE_MODELINSTANCE_3D_OFFSET + Instance() : deltax_set(false), deltay_set(false), deltaz_set(false), rz_set(false), scale_set(false) {} +#else Instance() : deltax_set(false), deltay_set(false), rz_set(false), scale_set(false) {} +#endif // ENABLE_MODELINSTANCE_3D_OFFSET // Shift in the X axis. float deltax; bool deltax_set; // Shift in the Y axis. float deltay; bool deltay_set; +#if ENABLE_MODELINSTANCE_3D_OFFSET + // Shift in the Z axis. + float deltaz; + bool deltaz_set; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET // Rotation around the Z axis. float rz; bool rz_set; @@ -254,6 +271,10 @@ void AMFParserContext::startElement(const char *name, const char **atts) node_type_new = NODE_TYPE_DELTAX; else if (strcmp(name, "deltay") == 0) node_type_new = NODE_TYPE_DELTAY; +#if ENABLE_MODELINSTANCE_3D_OFFSET + else if (strcmp(name, "deltaz") == 0) + node_type_new = NODE_TYPE_DELTAZ; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET else if (strcmp(name, "rz") == 0) node_type_new = NODE_TYPE_RZ; else if (strcmp(name, "scale") == 0) @@ -314,7 +335,15 @@ void AMFParserContext::characters(const XML_Char *s, int len) { switch (m_path.size()) { case 4: +#if ENABLE_MODELINSTANCE_3D_OFFSET + if (m_path.back() == NODE_TYPE_DELTAX || + m_path.back() == NODE_TYPE_DELTAY || + m_path.back() == NODE_TYPE_DELTAZ || + m_path.back() == NODE_TYPE_RZ || + m_path.back() == NODE_TYPE_SCALE) +#else if (m_path.back() == NODE_TYPE_DELTAX || m_path.back() == NODE_TYPE_DELTAY || m_path.back() == NODE_TYPE_RZ || m_path.back() == NODE_TYPE_SCALE) +#endif // ENABLE_MODELINSTANCE_3D_OFFSET m_value[0].append(s, len); break; case 6: @@ -354,6 +383,14 @@ void AMFParserContext::endElement(const char * /* name */) m_instance->deltay_set = true; m_value[0].clear(); break; +#if ENABLE_MODELINSTANCE_3D_OFFSET + case NODE_TYPE_DELTAZ: + assert(m_instance); + m_instance->deltaz = float(atof(m_value[0].c_str())); + m_instance->deltaz_set = true; + m_value[0].clear(); + break; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET case NODE_TYPE_RZ: assert(m_instance); m_instance->rz = float(atof(m_value[0].c_str())); @@ -458,9 +495,14 @@ void AMFParserContext::endElement(const char * /* name */) p = end + 1; } m_object->layer_height_profile_valid = true; - } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume && strcmp(opt_key, "modifier") == 0) { - // Is this volume a modifier volume? - m_volume->modifier = atoi(m_value[1].c_str()) == 1; + } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) { + if (strcmp(opt_key, "modifier") == 0) { + // Is this volume a modifier volume? + // "modifier" flag comes first in the XML file, so it may be later overwritten by the "type" flag. + m_volume->set_type((atoi(m_value[1].c_str()) == 1) ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); + } else if (strcmp(opt_key, "volume_type") == 0) { + m_volume->set_type(ModelVolume::type_from_string(m_value[1])); + } } } else if (m_path.size() == 3) { if (m_path[1] == NODE_TYPE_MATERIAL) { @@ -498,8 +540,12 @@ void AMFParserContext::endDocument() for (const Instance &instance : object.second.instances) if (instance.deltax_set && instance.deltay_set) { ModelInstance *mi = m_model.objects[object.second.idx]->add_instance(); +#if ENABLE_MODELINSTANCE_3D_OFFSET + mi->set_offset(Vec3d((double)instance.deltax, (double)instance.deltay, (double)instance.deltaz)); +#else mi->offset(0) = instance.deltax; mi->offset(1) = instance.deltay; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET mi->rotation = instance.rz_set ? instance.rz : 0.f; mi->scaling_factor = instance.scale_set ? instance.scale : 1.f; } @@ -781,8 +827,9 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c stream << " " << volume->config.serialize(key) << "\n"; if (!volume->name.empty()) stream << " " << xml_escape(volume->name) << "\n"; - if (volume->modifier) + if (volume->is_modifier()) stream << " 1\n"; + stream << " " << ModelVolume::type_to_string(volume->type()) << "\n"; for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) { stream << " \n"; for (int j = 0; j < 3; ++j) @@ -800,12 +847,21 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c " \n" " %lf\n" " %lf\n" +#if ENABLE_MODELINSTANCE_3D_OFFSET + " %lf\n" +#endif // ENABLE_MODELINSTANCE_3D_OFFSET " %lf\n" " %lf\n" " \n", object_id, +#if ENABLE_MODELINSTANCE_3D_OFFSET + instance->get_offset(X), + instance->get_offset(Y), + instance->get_offset(Z), +#else instance->offset(0), instance->offset(1), +#endif // ENABLE_MODELINSTANCE_3D_OFFSET instance->rotation, instance->scaling_factor); //FIXME missing instance->scaling_factor diff --git a/xs/src/libslic3r/Format/PRUS.cpp b/xs/src/libslic3r/Format/PRUS.cpp index 3cf3fc075d..45eb56c631 100644 --- a/xs/src/libslic3r/Format/PRUS.cpp +++ b/xs/src/libslic3r/Format/PRUS.cpp @@ -166,7 +166,11 @@ bool load_prus(const char *path, Model *model) float trafo[3][4] = { 0 }; double instance_rotation = 0.; double instance_scaling_factor = 1.f; - Vec2d instance_offset(0., 0.); +#if ENABLE_MODELINSTANCE_3D_OFFSET + Vec3d instance_offset = Vec3d::Zero(); +#else + Vec2d instance_offset(0., 0.); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET bool trafo_set = false; unsigned int group_id = (unsigned int)-1; unsigned int extruder_id = (unsigned int)-1; @@ -207,8 +211,12 @@ bool load_prus(const char *path, Model *model) for (size_t c = 0; c < 3; ++ c) trafo[r][c] += mat_trafo(r, c); } +#if ENABLE_MODELINSTANCE_3D_OFFSET + instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2])); +#else instance_offset(0) = position[0] - zero[0]; instance_offset(1) = position[1] - zero[1]; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET trafo[2][3] = position[2] / instance_scaling_factor; trafo_set = true; } @@ -360,8 +368,12 @@ bool load_prus(const char *path, Model *model) ModelInstance *instance = model_object->add_instance(); instance->rotation = instance_rotation; instance->scaling_factor = instance_scaling_factor; - instance->offset = instance_offset; - ++ num_models; +#if ENABLE_MODELINSTANCE_3D_OFFSET + instance->set_offset(instance_offset); +#else + instance->offset = instance_offset; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET + ++num_models; if (group_id != (size_t)-1) group_to_model_object[group_id] = model_object; } else { diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index ac09db4bd4..3c6e6de530 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -277,7 +277,6 @@ std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gco } - std::string WipeTowerIntegration::prime(GCode &gcodegen) { assert(m_layer_idx == 0); @@ -1000,8 +999,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms(); print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A"; for (const Extruder &extruder : m_writer.extruders()) { - double used_filament = extruder.used_filament(); - double extruded_volume = extruder.extruded_volume(); + double used_filament = extruder.used_filament() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] : 0.f); + double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter double filament_weight = extruded_volume * extruder.filament_density() * 0.001; double filament_cost = filament_weight * extruder.filament_cost() * 0.001; print.m_print_statistics.filament_stats.insert(std::pair(extruder.id(), (float)used_filament)); @@ -1014,8 +1013,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _write_format(file, "; filament cost = %.1lf\n", filament_cost); } } - print.m_print_statistics.total_used_filament = print.m_print_statistics.total_used_filament + used_filament; - print.m_print_statistics.total_extruded_volume = print.m_print_statistics.total_extruded_volume + extruded_volume; + print.m_print_statistics.total_used_filament += used_filament; + print.m_print_statistics.total_extruded_volume += extruded_volume; + print.m_print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; + print.m_print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; } _write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost); _write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str()); diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 34183012a1..45f17a68ae 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -98,6 +98,7 @@ public: 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); + std::vector used_filament_length() const; private: WipeTowerIntegration& operator=(const WipeTowerIntegration&); diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp index c35a0feb3c..5cbbc1ca90 100644 --- a/xs/src/libslic3r/GCode/WipeTower.hpp +++ b/xs/src/libslic3r/GCode/WipeTower.hpp @@ -154,6 +154,12 @@ public: // the wipe tower has been completely covered by the tool change extrusions, // or the rest of the tower has been filled by a sparse infill with the finish_layer() method. virtual bool layer_finished() const = 0; + + // Returns used filament length per extruder: + virtual std::vector get_used_filament() const = 0; + + // Returns total number of toolchanges: + virtual int get_number_of_toolchanges() const = 0; }; }; // namespace Slic3r diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index 2c96cce7b6..54bdfdfd66 100644 --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -111,9 +111,10 @@ public: const WipeTower::xy start_pos_rotated() const { return m_start_pos; } const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); } float elapsed_time() const { return m_elapsed_time; } + float get_and_reset_used_filament_length() { float temp = m_used_filament_length; m_used_filament_length = 0.f; return temp; } // Extrude with an explicitely provided amount of extrusion. - Writer& extrude_explicit(float x, float y, float e, float f = 0.f) + Writer& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false) { if (x == m_current_pos.x && y == m_current_pos.y && e == 0.f && (f == 0.f || f == m_current_feedrate)) // Neither extrusion nor a travel move. @@ -122,6 +123,8 @@ public: float dx = x - m_current_pos.x; float dy = y - m_current_pos.y; double len = sqrt(dx*dx+dy*dy); + if (record_length) + m_used_filament_length += e; // Now do the "internal rotation" with respect to the wipe tower center @@ -162,8 +165,8 @@ public: return *this; } - Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f) - { return extrude_explicit(dest.x, dest.y, e, f); } + Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f, bool record_length = false) + { return extrude_explicit(dest.x, dest.y, e, f, record_length); } // Travel to a new XY position. f=0 means use the current value. Writer& travel(float x, float y, float f = 0.f) @@ -177,7 +180,7 @@ public: { float dx = x - m_current_pos.x; float dy = y - m_current_pos.y; - return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f); + return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true); } Writer& extrude(const WipeTower::xy &dest, const float f = 0.f) @@ -259,8 +262,8 @@ public: // extrude quickly amount e to x2 with feed f. Writer& ram(float x1, float x2, float dy, float e0, float e, float f) { - extrude_explicit(x1, m_current_pos.y + dy, e0, f); - extrude_explicit(x2, m_current_pos.y, e); + extrude_explicit(x1, m_current_pos.y + dy, e0, f, true); + extrude_explicit(x2, m_current_pos.y, e, 0.f, true); return *this; } @@ -404,6 +407,7 @@ private: float m_last_fan_speed = 0.f; int current_temp = -1; const float m_default_analyzer_line_width; + float m_used_filament_length = 0.f; std::string set_format_X(float x) { @@ -525,6 +529,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( ++ m_num_tool_changes; } + m_old_temperature = -1; // If the priming is turned off in config, the temperature changing commands will not actually appear + // in the output gcode - we should not remember emitting them (we will output them twice in the worst case) + // Reset the extruder current to a normal value. writer.set_extruder_trimpot(550) .feedrate(6000) @@ -537,6 +544,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( // so that tool_change() will know to extrude the wipe tower brim: m_print_brim = true; + // Ask our writer about how much material was consumed: + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + ToolChangeResult result; result.priming = true; result.print_z = this->m_z_pos; @@ -606,10 +616,10 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo toolchange_Load(writer, cleaning_box); writer.travel(writer.x(),writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area. + ++ m_num_tool_changes; } else toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature); - ++ m_num_tool_changes; m_depth_traversed += wipe_area; if (last_change_in_layer) {// draw perimeter line @@ -632,6 +642,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo ";------------------\n" "\n\n"); + // Ask our writer about how much material was consumed: + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + ToolChangeResult result; result.priming = false; result.print_z = this->m_z_pos; @@ -683,6 +696,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo m_print_brim = false; // Mark the brim as extruded + // Ask our writer about how much material was consumed: + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + ToolChangeResult result; result.priming = false; result.print_z = this->m_z_pos; @@ -804,8 +820,9 @@ void WipeTowerPrusaMM::toolchange_Unload( .load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed) .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/ .resume_preview(); - - if (new_temperature != 0 && new_temperature != m_old_temperature ) { // Set the extruder temperature, but don't wait. + if (new_temperature != 0 && (new_temperature != m_old_temperature || m_is_first_layer) ) { // Set the extruder temperature, but don't wait. + // If the required temperature is the same as last time, don't emit the M104 again (if user adjusted the value, it would be reset) + // However, always change temperatures on the first layer (this is to avoid issues with priming lines turned off). writer.set_extruder_temp(new_temperature, false); m_old_temperature = new_temperature; } @@ -849,6 +866,9 @@ void WipeTowerPrusaMM::toolchange_Change( const unsigned int new_tool, material_type new_material) { + // Ask the writer about how much of the old filament we consumed: + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + // Speed override for the material. Go slow for flex and soluble materials. int speed_override; switch (new_material) { @@ -911,7 +931,6 @@ void WipeTowerPrusaMM::toolchange_Wipe( const float& xl = cleaning_box.ld.x; const float& xr = cleaning_box.rd.x; - // Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least // the ordered volume, even if it means violating the box. This can later be removed and simply // wipe until the end of the assigned area. @@ -926,7 +945,6 @@ void WipeTowerPrusaMM::toolchange_Wipe( m_left_to_right = !m_left_to_right; } - // now the wiping itself: for (int i = 0; true; ++i) { if (i!=0) { @@ -935,7 +953,7 @@ void WipeTowerPrusaMM::toolchange_Wipe( else if (wipe_speed < 2210.f) wipe_speed = 4200.f; else wipe_speed = std::min(4800.f, wipe_speed + 50.f); } - + float traversed_x = writer.x(); if (m_left_to_right) writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff); @@ -1050,6 +1068,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() m_depth_traversed = m_wipe_tower_depth-m_perimeter_width; + // Ask our writer about how much material was consumed: + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + ToolChangeResult result; result.priming = false; result.print_z = this->m_z_pos; @@ -1166,6 +1187,8 @@ void WipeTowerPrusaMM::generate(std::vector layer_result; for (auto layer : m_plan) diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp index 305dbc40a8..06625d189f 100644 --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp @@ -46,7 +46,7 @@ public: WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction, float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging, const std::vector>& wiping_matrix, unsigned int initial_tool) : - m_wipe_tower_pos(x, y), + m_wipe_tower_pos(x, y), m_wipe_tower_width(width), m_wipe_tower_rotation_angle(rotation_angle), m_y_shift(0.f), @@ -94,6 +94,8 @@ public: m_filpar[idx].ramming_step_multiplicator /= 100; while (stream >> speed) m_filpar[idx].ramming_speed.push_back(speed); + + m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later } @@ -172,6 +174,9 @@ public: return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed); } + virtual std::vector get_used_filament() const override { return m_used_filament_length; } + virtual int get_number_of_toolchanges() const override { return m_num_tool_changes; } + private: WipeTowerPrusaMM(); @@ -331,6 +336,9 @@ private: std::vector m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...)) std::vector::iterator m_layer_info = m_plan.end(); + // Stores information about used filament length per extruder: + std::vector m_used_filament_length; + // Returns gcode for wipe tower brim // sideOnly -- set to false -- experimental, draw brim on sides of wipe tower diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index 65184c9d12..e0f97703cf 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -15,8 +15,7 @@ namespace Slic3r { -Flow -LayerRegion::flow(FlowRole role, bool bridge, double width) const +Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const { return m_region->flow( role, diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index dc21ab4ae0..8e8396e7a9 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -242,10 +242,19 @@ void Model::center_instances_around_point(const Vec2d &point) for (size_t i = 0; i < o->instances.size(); ++ i) bb.merge(o->instance_bounding_box(i, false)); +#if ENABLE_MODELINSTANCE_3D_OFFSET + Vec2d shift2 = point - to_2d(bb.center()); + Vec3d shift3 = Vec3d(shift2(0), shift2(1), 0.0); +#else Vec2d shift = point - 0.5 * to_2d(bb.size()) - to_2d(bb.min); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET for (ModelObject *o : this->objects) { for (ModelInstance *i : o->instances) +#if ENABLE_MODELINSTANCE_3D_OFFSET + i->set_offset(i->get_offset() + shift3); +#else i->offset += shift; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET o->invalidate_bounding_box(); } } @@ -311,8 +320,13 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) size_t idx = 0; for (ModelObject *o : this->objects) { for (ModelInstance *i : o->instances) { +#if ENABLE_MODELINSTANCE_3D_OFFSET + Vec2d offset_xy = positions[idx] - instance_centers[idx]; + i->set_offset(Vec3d(offset_xy(0), offset_xy(1), i->get_offset(Z))); +#else i->offset = positions[idx] - instance_centers[idx]; - ++ idx; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET + ++idx; } o->invalidate_bounding_box(); } @@ -336,7 +350,11 @@ void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb) for (const ModelInstance *i : instances) { for (const Vec2d &pos : positions) { ModelInstance *instance = o->add_instance(*i); +#if ENABLE_MODELINSTANCE_3D_OFFSET + instance->set_offset(instance->get_offset() + Vec3d(pos(0), pos(1), 0.0)); +#else instance->offset += pos; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET } } o->invalidate_bounding_box(); @@ -366,13 +384,21 @@ void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist) ModelObject* object = this->objects.front(); object->clear_instances(); +#if ENABLE_MODELINSTANCE_3D_OFFSET + Vec3d ext_size = object->bounding_box().size() + dist * Vec3d::Ones(); +#else Vec3d size = object->bounding_box().size(); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET for (size_t x_copy = 1; x_copy <= x; ++x_copy) { for (size_t y_copy = 1; y_copy <= y; ++y_copy) { ModelInstance* instance = object->add_instance(); - instance->offset(0) = (size(0) + dist) * (x_copy-1); - instance->offset(1) = (size(1) + dist) * (y_copy-1); +#if ENABLE_MODELINSTANCE_3D_OFFSET + instance->set_offset(Vec3d(ext_size(0) * (double)(x_copy - 1), ext_size(1) * (double)(y_copy - 1), 0.0)); +#else + instance->offset(0) = (size(0) + dist) * (x_copy - 1); + instance->offset(1) = (size(1) + dist) * (y_copy - 1); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET } } } @@ -601,7 +627,8 @@ const BoundingBoxf3& ModelObject::bounding_box() const if (! m_bounding_box_valid) { BoundingBoxf3 raw_bbox; for (const ModelVolume *v : this->volumes) - if (! v->modifier) + if (v->is_model_part()) + // mesh.bounding_box() returns a cached value. raw_bbox.merge(v->mesh.bounding_box()); BoundingBoxf3 bb; for (const ModelInstance *i : this->instances) @@ -632,7 +659,7 @@ TriangleMesh ModelObject::raw_mesh() const { TriangleMesh mesh; for (const ModelVolume *v : this->volumes) - if (! v->modifier) + if (v->is_model_part()) mesh.merge(v->mesh); return mesh; } @@ -643,7 +670,7 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const { BoundingBoxf3 bb; for (const ModelVolume *v : this->volumes) - if (! v->modifier) { + if (v->is_model_part()) { if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances"); bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true)); } @@ -655,7 +682,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_ { BoundingBoxf3 bb; for (ModelVolume *v : this->volumes) - if (! v->modifier) + if (v->is_model_part()) bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate)); return bb; } @@ -666,7 +693,7 @@ void ModelObject::center_around_origin() // center this object around the origin BoundingBoxf3 bb; for (ModelVolume *v : this->volumes) - if (! v->modifier) + if (v->is_model_part()) bb.merge(v->mesh.bounding_box()); // Shift is the vector from the center of the bottom face of the bounding box to the origin @@ -676,12 +703,21 @@ void ModelObject::center_around_origin() this->translate(shift); this->origin_translation += shift; +#if ENABLE_MODELINSTANCE_3D_OFFSET + // set z to zero, translation in z has already been done within the mesh + shift(2) = 0.0; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET + if (!this->instances.empty()) { for (ModelInstance *i : this->instances) { // apply rotation and scaling to vector as well before translating instance, // in order to leave final position unaltered +#if ENABLE_MODELINSTANCE_3D_OFFSET + i->set_offset(i->get_offset() + i->transform_vector(-shift, true)); +#else Vec3d i_shift = i->world_matrix(true) * shift; i->offset -= to_2d(i_shift); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET } this->invalidate_bounding_box(); } @@ -763,7 +799,7 @@ size_t ModelObject::facets_count() const { size_t num = 0; for (const ModelVolume *v : this->volumes) - if (! v->modifier) + if (v->is_model_part()) num += v->mesh.stl.stats.number_of_facets; return num; } @@ -771,7 +807,7 @@ size_t ModelObject::facets_count() const bool ModelObject::needed_repair() const { for (const ModelVolume *v : this->volumes) - if (! v->modifier && v->mesh.needed_repair()) + if (v->is_model_part() && v->mesh.needed_repair()) return true; return false; } @@ -787,7 +823,7 @@ void ModelObject::cut(coordf_t z, Model* model) const lower->input_file = ""; for (ModelVolume *volume : this->volumes) { - if (volume->modifier) { + if (! volume->is_model_part()) { // don't cut modifiers upper->add_volume(*volume); lower->add_volume(*volume); @@ -839,7 +875,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects) ModelVolume* new_volume = new_object->add_volume(*mesh); new_volume->name = volume->name; new_volume->config = volume->config; - new_volume->modifier = volume->modifier; + new_volume->set_type(volume->type()); new_volume->material_id(volume->material_id()); new_objects->push_back(new_object); @@ -854,7 +890,7 @@ void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_ { for (const ModelVolume* vol : this->volumes) { - if (!vol->modifier) + if (vol->is_model_part()) { for (ModelInstance* inst : this->instances) { @@ -951,6 +987,38 @@ const TriangleMesh& ModelVolume::get_convex_hull() const return m_convex_hull; } +ModelVolume::Type ModelVolume::type_from_string(const std::string &s) +{ + // Legacy support + if (s == "1") + return PARAMETER_MODIFIER; + // New type (supporting the support enforcers & blockers) + if (s == "ModelPart") + return MODEL_PART; + if (s == "ParameterModifier") + return PARAMETER_MODIFIER; + if (s == "SupportEnforcer") + return SUPPORT_ENFORCER; + if (s == "SupportBlocker") + return SUPPORT_BLOCKER; + assert(s == "0"); + // Default value if invalud type string received. + return MODEL_PART; +} + +std::string ModelVolume::type_to_string(const Type t) +{ + switch (t) { + case MODEL_PART: return "ModelPart"; + case PARAMETER_MODIFIER: return "ParameterModifier"; + case SUPPORT_ENFORCER: return "SupportEnforcer"; + case SUPPORT_BLOCKER: return "SupportBlocker"; + default: + assert(false); + return "ModelPart"; + } +} + // Split this volume, append the result to the object owning this volume. // Return the number of volumes created from this one. // This is useful to assign different materials to different volumes of an object. @@ -1005,8 +1073,13 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes } // Translate the bounding box. if (! dont_translate) { +#if ENABLE_MODELINSTANCE_3D_OFFSET + bbox.min += this->m_offset; + bbox.max += this->m_offset; +#else Eigen::Map(bbox.min.data()) += this->offset; Eigen::Map(bbox.max.data()) += this->offset; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET } } return bbox; @@ -1033,7 +1106,11 @@ Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, b Transform3d m = Transform3d::Identity(); if (!dont_translate) +#if ENABLE_MODELINSTANCE_3D_OFFSET + m.translate(m_offset); +#else m.translate(Vec3d(offset(0), offset(1), 0.0)); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET if (!dont_rotate) m.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ())); diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 8ca1d8ea94..4c2356429a 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -166,15 +166,27 @@ public: // Configuration parameters specific to an object model geometry or a modifier volume, // overriding the global Slic3r settings and the ModelObject settings. DynamicPrintConfig config; - // Is it an object to be printed, or a modifier volume? - bool modifier; - + + enum Type { + MODEL_TYPE_INVALID = -1, + MODEL_PART = 0, + PARAMETER_MODIFIER, + SUPPORT_ENFORCER, + SUPPORT_BLOCKER, + }; + // A parent object owning this modifier volume. - ModelObject* get_object() const { return this->object; }; + ModelObject* get_object() const { return this->object; }; + Type type() const { return m_type; } + void set_type(const Type t) { m_type = t; } + bool is_model_part() const { return m_type == MODEL_PART; } + bool is_modifier() const { return m_type == PARAMETER_MODIFIER; } + bool is_support_enforcer() const { return m_type == SUPPORT_ENFORCER; } + bool is_support_blocker() const { return m_type == SUPPORT_BLOCKER; } t_model_material_id material_id() const { return this->_material_id; } - void material_id(t_model_material_id material_id); - ModelMaterial* material() const; - void set_material(t_model_material_id material_id, const ModelMaterial &material); + void material_id(t_model_material_id material_id); + ModelMaterial* material() const; + void set_material(t_model_material_id material_id, const ModelMaterial &material); // Split this volume, append the result to the object owning this volume. // Return the number of volumes created from this one. // This is useful to assign different materials to different volumes of an object. @@ -185,24 +197,30 @@ public: void calculate_convex_hull(); const TriangleMesh& get_convex_hull() const; + // Helpers for loading / storing into AMF / 3MF files. + static Type type_from_string(const std::string &s); + static std::string type_to_string(const Type t); + private: // Parent object owning this ModelVolume. - ModelObject* object; - t_model_material_id _material_id; + ModelObject* object; + // Is it an object to be printed, or a modifier volume? + Type m_type; + t_model_material_id _material_id; - ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object) + ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object) { if (mesh.stl.stats.number_of_facets > 1) calculate_convex_hull(); } - ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), modifier(false), object(object) {} + ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {} ModelVolume(ModelObject *object, const ModelVolume &other) : - name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), modifier(other.modifier), object(object) + name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object) { this->material_id(other.material_id()); } ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : - name(other.name), mesh(std::move(mesh)), config(other.config), modifier(other.modifier), object(object) + name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object) { this->material_id(other.material_id()); if (mesh.stl.stats.number_of_facets > 1) @@ -225,15 +243,32 @@ public: friend class ModelObject; +#if ENABLE_MODELINSTANCE_3D_OFFSET +private: + Vec3d m_offset; // in unscaled coordinates + +public: +#endif // ENABLE_MODELINSTANCE_3D_OFFSET + double rotation; // Rotation around the Z axis, in radians around mesh center point double scaling_factor; +#if !ENABLE_MODELINSTANCE_3D_OFFSET Vec2d offset; // in unscaled coordinates - +#endif // !ENABLE_MODELINSTANCE_3D_OFFSET + // flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state()) EPrintVolumeState print_volume_state; ModelObject* get_object() const { return this->object; } +#if ENABLE_MODELINSTANCE_3D_OFFSET + const Vec3d& get_offset() const { return m_offset; } + double get_offset(Axis axis) const { return m_offset(axis); } + + void set_offset(const Vec3d& offset) { m_offset = offset; } + void set_offset(Axis axis, double offset) { m_offset(axis) = offset; } +#endif // ENABLE_MODELINSTANCE_3D_OFFSET + // To be called on an external mesh void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; // Calculate a bounding box of a transformed mesh. To be called on an external mesh. @@ -253,9 +288,15 @@ private: // Parent object, owning this instance. ModelObject* object; +#if ENABLE_MODELINSTANCE_3D_OFFSET + ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), m_offset(Vec3d::Zero()), object(object), print_volume_state(PVS_Inside) {} + ModelInstance(ModelObject *object, const ModelInstance &other) : + rotation(other.rotation), scaling_factor(other.scaling_factor), m_offset(other.m_offset), object(object), print_volume_state(PVS_Inside) {} +#else ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), offset(Vec2d::Zero()), object(object), print_volume_state(PVS_Inside) {} ModelInstance(ModelObject *object, const ModelInstance &other) : rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object), print_volume_state(PVS_Inside) {} +#endif // ENABLE_MODELINSTANCE_3D_OFFSET }; diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index 36bd8ed979..372689c39d 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -130,6 +130,7 @@ objfunc(const PointImpl& bincenter, double norm, // A norming factor for physical dimensions // a spatial index to quickly get neighbors of the candidate item const SpatIndex& spatindex, + const SpatIndex& smalls_spatindex, const ItemGroup& remaining ) { @@ -161,7 +162,7 @@ objfunc(const PointImpl& bincenter, // Will hold the resulting score double score = 0; - if(isBig(item.area())) { + if(isBig(item.area()) || spatindex.empty()) { // This branch is for the bigger items.. auto minc = ibb.minCorner(); // bottom left corner @@ -183,6 +184,8 @@ objfunc(const PointImpl& bincenter, // The smalles distance from the arranged pile center: auto dist = *(std::min_element(dists.begin(), dists.end())) / norm; + auto bindist = pl::distance(ibb.center(), bincenter) / norm; + dist = 0.8*dist + 0.2*bindist; // Density is the pack density: how big is the arranged pile double density = 0; @@ -207,14 +210,20 @@ objfunc(const PointImpl& bincenter, // candidate to be aligned with only one item. auto alignment_score = 1.0; - density = (fullbb.width()*fullbb.height()) / (norm*norm); + density = std::sqrt((fullbb.width() / norm )* + (fullbb.height() / norm)); auto querybb = item.boundingBox(); // Query the spatial index for the neighbors std::vector result; result.reserve(spatindex.size()); - spatindex.query(bgi::intersects(querybb), - std::back_inserter(result)); + if(isBig(item.area())) { + spatindex.query(bgi::intersects(querybb), + std::back_inserter(result)); + } else { + smalls_spatindex.query(bgi::intersects(querybb), + std::back_inserter(result)); + } for(auto& e : result) { // now get the score for the best alignment auto idx = e.second; @@ -235,12 +244,8 @@ objfunc(const PointImpl& bincenter, if(result.empty()) score = 0.5 * dist + 0.5 * density; else - score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score; + score = 0.40 * dist + 0.40 * density + 0.2 * alignment_score; } - } else if( !isBig(item.area()) && spatindex.empty()) { - auto bindist = pl::distance(ibb.center(), bincenter) / norm; - // Bindist is surprisingly enough... - score = bindist; } else { // Here there are the small items that should be placed around the // already processed bigger items. @@ -291,6 +296,7 @@ protected: PConfig pconf_; // Placement configuration double bin_area_; SpatIndex rtree_; + SpatIndex smallsrtree_; double norm_; Pile merged_pile_; Box pilebb_; @@ -299,7 +305,8 @@ protected: public: _ArrBase(const TBin& bin, Distance dist, - std::function progressind): + std::function progressind, + std::function stopcond): pck_(bin, dist), bin_area_(sl::area(bin)), norm_(std::sqrt(sl::area(bin))) { @@ -317,6 +324,7 @@ public: pilebb_ = sl::boundingBox(merged_pile); rtree_.clear(); + smallsrtree_.clear(); // We will treat big items (compared to the print bed) differently auto isBig = [this](double a) { @@ -326,10 +334,12 @@ public: for(unsigned idx = 0; idx < items.size(); ++idx) { Item& itm = items[idx]; if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx}); + smallsrtree_.insert({itm.boundingBox(), idx}); } }; pck_.progressIndicator(progressind); + pck_.stopCondition(stopcond); } template inline IndexedPackGroup operator()(Args&&...args) { @@ -343,8 +353,9 @@ class AutoArranger: public _ArrBase { public: AutoArranger(const Box& bin, Distance dist, - std::function progressind): - _ArrBase(bin, dist, progressind) + std::function progressind, + std::function stopcond): + _ArrBase(bin, dist, progressind, stopcond) { pconf_.object_function = [this, bin] (const Item &item) { @@ -357,6 +368,7 @@ public: bin_area_, norm_, rtree_, + smallsrtree_, remaining_); double score = std::get<0>(result); @@ -380,8 +392,9 @@ class AutoArranger: public _ArrBase { public: AutoArranger(const lnCircle& bin, Distance dist, - std::function progressind): - _ArrBase(bin, dist, progressind) { + std::function progressind, + std::function stopcond): + _ArrBase(bin, dist, progressind, stopcond) { pconf_.object_function = [this, &bin] (const Item &item) { @@ -393,6 +406,7 @@ public: bin_area_, norm_, rtree_, + smallsrtree_, remaining_); double score = std::get<0>(result); @@ -421,8 +435,9 @@ template<> class AutoArranger: public _ArrBase { public: AutoArranger(const PolygonImpl& bin, Distance dist, - std::function progressind): - _ArrBase(bin, dist, progressind) + std::function progressind, + std::function stopcond): + _ArrBase(bin, dist, progressind, stopcond) { pconf_.object_function = [this, &bin] (const Item &item) { @@ -435,6 +450,7 @@ public: bin_area_, norm_, rtree_, + smallsrtree_, remaining_); double score = std::get<0>(result); @@ -449,8 +465,9 @@ template<> // Specialization with no bin class AutoArranger: public _ArrBase { public: - AutoArranger(Distance dist, std::function progressind): - _ArrBase(Box(0, 0), dist, progressind) + AutoArranger(Distance dist, std::function progressind, + std::function stopcond): + _ArrBase(Box(0, 0), dist, progressind, stopcond) { this->pconf_.object_function = [this] (const Item &item) { @@ -462,6 +479,7 @@ public: 0, norm_, rtree_, + smallsrtree_, remaining_); return std::get<0>(result); }; @@ -511,8 +529,13 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { if(item.vertexCount() > 3) { item.rotation(objinst->rotation); item.translation( { +#if ENABLE_MODELINSTANCE_3D_OFFSET + ClipperLib::cInt(objinst->get_offset(X) / SCALING_FACTOR), + ClipperLib::cInt(objinst->get_offset(Y) / SCALING_FACTOR) +#else ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR), ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR) +#endif // ENABLE_MODELINSTANCE_3D_OFFSET }); ret.emplace_back(objinst, item); } @@ -649,11 +672,19 @@ void applyResult( // appropriately auto off = item.translation(); Radians rot = item.rotation(); +#if ENABLE_MODELINSTANCE_3D_OFFSET + Vec3d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR, 0.0); +#else Vec2d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET // write the tranformation data into the model instance inst_ptr->rotation = rot; +#if ENABLE_MODELINSTANCE_3D_OFFSET + inst_ptr->set_offset(foff); +#else inst_ptr->offset = foff; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET } } @@ -680,12 +711,16 @@ void applyResult( * remaining items which do not fit onto the print area next to the print * bed or leave them untouched (let the user arrange them by hand or remove * them). + * \param progressind Progress indicator callback called when an object gets + * packed. The unsigned argument is the number of items remaining to pack. + * \param stopcondition A predicate returning true if abort is needed. */ bool arrange(Model &model, coordf_t min_obj_distance, const Slic3r::Polyline& bed, BedShapeHint bedhint, bool first_bin_only, - std::function progressind) + std::function progressind, + std::function stopcondition) { using ArrangeResult = _IndexedPackGroup; @@ -710,6 +745,8 @@ bool arrange(Model &model, coordf_t min_obj_distance, BoundingBox bbb(bed); + auto& cfn = stopcondition; + auto binbb = Box({ static_cast(bbb.min(0)), static_cast(bbb.min(1)) @@ -723,7 +760,7 @@ bool arrange(Model &model, coordf_t min_obj_distance, case BedShapeType::BOX: { // Create the arranger for the box shaped bed - AutoArranger arrange(binbb, min_obj_distance, progressind); + AutoArranger arrange(binbb, min_obj_distance, progressind, cfn); // Arrange and return the items with their respective indices within the // input sequence. @@ -735,7 +772,7 @@ bool arrange(Model &model, coordf_t min_obj_distance, auto c = bedhint.shape.circ; auto cc = lnCircle(c); - AutoArranger arrange(cc, min_obj_distance, progressind); + AutoArranger arrange(cc, min_obj_distance, progressind, cfn); result = arrange(shapes.begin(), shapes.end()); break; } @@ -747,7 +784,7 @@ bool arrange(Model &model, coordf_t min_obj_distance, auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); P irrbed = sl::create(std::move(ctour)); - AutoArranger

arrange(irrbed, min_obj_distance, progressind); + AutoArranger

arrange(irrbed, min_obj_distance, progressind, cfn); // Arrange and return the items with their respective indices within the // input sequence. @@ -756,7 +793,7 @@ bool arrange(Model &model, coordf_t min_obj_distance, } }; - if(result.empty()) return false; + if(result.empty() || stopcondition()) return false; if(first_bin_only) { applyResult(result.front(), 0, shapemap); diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp index 1fef4083b7..03b89df51d 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/xs/src/libslic3r/MultiPoint.hpp @@ -35,8 +35,10 @@ public: Point first_point() const; virtual Point last_point() const = 0; virtual Lines lines() const = 0; + size_t size() const { return points.size(); } + bool empty() const { return points.empty(); } double length() const; - bool is_valid() const { return this->points.size() >= 2; } + bool is_valid() const { return this->points.size() >= 2; } int find_point(const Point &point) const; bool has_boundary_point(const Point &point) const; diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index c1aff561f2..6d9d82d25a 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -22,6 +22,7 @@ typedef Point Vector; // Vector types with a fixed point coordinate base type. typedef Eigen::Matrix Vec2crd; typedef Eigen::Matrix Vec3crd; +typedef Eigen::Matrix Vec3i; typedef Eigen::Matrix Vec2i64; typedef Eigen::Matrix Vec3i64; diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp index 54909352ce..63162d9539 100644 --- a/xs/src/libslic3r/Polygon.hpp +++ b/xs/src/libslic3r/Polygon.hpp @@ -103,6 +103,12 @@ inline void polygons_rotate(Polygons &polys, double angle) p.rotate(cos_angle, sin_angle); } +inline void polygons_reverse(Polygons &polys) +{ + for (Polygon &p : polys) + p.reverse(); +} + inline Points to_points(const Polygon &poly) { return poly.points; diff --git a/xs/src/libslic3r/Polyline.cpp b/xs/src/libslic3r/Polyline.cpp index b2e50bca3f..e0cd5221c0 100644 --- a/xs/src/libslic3r/Polyline.cpp +++ b/xs/src/libslic3r/Polyline.cpp @@ -184,15 +184,13 @@ void Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const bool Polyline::is_straight() const { - /* Check that each segment's direction is equal to the line connecting - first point and last point. (Checking each line against the previous - one would cause the error to accumulate.) */ + // Check that each segment's direction is equal to the line connecting + // first point and last point. (Checking each line against the previous + // one would cause the error to accumulate.) double dir = Line(this->first_point(), this->last_point()).direction(); - - Lines lines = this->lines(); - for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { - if (!line->parallel_to(dir)) return false; - } + for (const auto &line: this->lines()) + if (! line.parallel_to(dir)) + return false; return true; } diff --git a/xs/src/libslic3r/Polyline.hpp b/xs/src/libslic3r/Polyline.hpp index 0c934e0748..925b88acae 100644 --- a/xs/src/libslic3r/Polyline.hpp +++ b/xs/src/libslic3r/Polyline.hpp @@ -21,6 +21,8 @@ public: Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {} Polyline(std::initializer_list list) : MultiPoint(list) {} explicit Polyline(const Point &p1, const Point &p2) { points.reserve(2); points.emplace_back(p1); points.emplace_back(p2); } + explicit Polyline(const Points &points) : MultiPoint(points) {} + explicit Polyline(Points &&points) : MultiPoint(std::move(points)) {} Polyline& operator=(const Polyline &other) { points = other.points; return *this; } Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; } static Polyline new_scale(const std::vector &points) { diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 03034cfdc3..85634d1bb1 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -392,9 +392,12 @@ void Print::add_model_object(ModelObject* model_object, int idx) //FIXME lock mutex! this->invalidate_all_steps(); - for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) { + size_t volume_id = 0; + for (const ModelVolume *volume : model_object->volumes) { + if (! volume->is_model_part() && ! volume->is_modifier()) + continue; // Get the config applied to this volume. - PrintRegionConfig config = this->_region_config_from_model_volume(*model_object->volumes[volume_id]); + PrintRegionConfig config = this->_region_config_from_model_volume(*volume); // Find an existing print region with the same config. size_t region_id = size_t(-1); for (size_t i = 0; i < m_regions.size(); ++ i) @@ -409,6 +412,7 @@ void Print::add_model_object(ModelObject* model_object, int idx) } // Assign volume to a region. object->add_region_volume(region_id, volume_id); + ++ volume_id; } // Apply config to print object. @@ -893,7 +897,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) { ModelVolume *volume = model_object->volumes[volume_id]; //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned. - if (! volume->material_id().empty() && ! volume->config.has("extruder")) + if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder")) volume->config.opt("extruder", true)->value = int(volume_id + 1); } } @@ -1295,6 +1299,9 @@ void Print::_make_wipe_tower() } m_wipe_tower_data.final_purge = Slic3r::make_unique( wipe_tower.tool_change((unsigned int)-1, false)); + + m_wipe_tower_data.used_filament = wipe_tower.get_used_filament(); + m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges(); } std::string Print::output_filename() const diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 8999f27d78..19da875ab7 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -135,7 +135,10 @@ public: const Print* print() const { return m_print; } const PrintRegionConfig& config() const { return m_config; } Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const; + // Average diameter of nozzles participating on extruding this region. coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const; + // Average diameter of nozzles participating on extruding this region. + coordf_t bridging_height_avg(const PrintConfig &print_config) const; // Methods modifying the PrintRegion's state: public: @@ -252,6 +255,10 @@ public: // Called when slicing to SVG (see Print.pm sub export_svg), and used by perimeters.t void slice(); + // Helpers to slice support enforcer / blocker meshes by the support generator. + std::vector slice_support_enforcers() const; + std::vector slice_support_blockers() const; + private: void make_perimeters(); void prepare_infill(); @@ -297,6 +304,7 @@ private: void set_started(PrintObjectStep step); void set_done(PrintObjectStep step); std::vector _slice_region(size_t region_id, const std::vector &z, bool modifier); + std::vector _slice_volumes(const std::vector &z, const std::vector &volumes) const; }; struct WipeTowerData @@ -309,6 +317,8 @@ struct WipeTowerData std::unique_ptr priming; std::vector> tool_changes; std::unique_ptr final_purge; + std::vector used_filament; + int number_of_toolchanges; // Depth of the wipe tower to pass to GLCanvas3D for exact bounding box: float depth; @@ -318,6 +328,8 @@ struct WipeTowerData priming.reset(nullptr); tool_changes.clear(); final_purge.reset(nullptr); + used_filament.clear(); + number_of_toolchanges = -1; depth = 0.f; } }; @@ -331,6 +343,8 @@ struct PrintStatistics double total_extruded_volume; double total_cost; double total_weight; + double total_wipe_tower_cost; + double total_wipe_tower_filament; std::map filament_stats; void clear() { @@ -340,6 +354,8 @@ struct PrintStatistics total_extruded_volume = 0.; total_cost = 0.; total_weight = 0.; + total_wipe_tower_cost = 0.; + total_wipe_tower_filament = 0.; filament_stats.clear(); } }; @@ -463,7 +479,7 @@ private: // If the background processing stop was requested, throw CanceledException. // To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly. - void throw_if_canceled() { if (m_canceled) throw CanceledException(); } + void throw_if_canceled() const { if (m_canceled) throw CanceledException(); } void _make_skirt(); void _make_brim(); diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index a3e84a3562..ed02f6d439 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -161,7 +161,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Speed for printing bridges."); def->sidetext = L("mm/s"); def->cli = "bridge-speed=f"; - def->aliases.push_back("bridge_feed_rate"); + def->aliases = { "bridge_feed_rate" }; def->min = 0; def->default_value = new ConfigOptionFloat(60); @@ -274,7 +274,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Distance used for the auto-arrange feature of the plater."); def->sidetext = L("mm"); def->cli = "duplicate-distance=f"; - def->aliases.push_back("multiply_distance"); + def->aliases = { "multiply_distance" }; def->min = 0; def->default_value = new ConfigOptionFloat(6); @@ -335,7 +335,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back(L("Archimedean Chords")); def->enum_labels.push_back(L("Octagram Spiral")); // solid_fill_pattern is an obsolete equivalent to external_fill_pattern. - def->aliases.push_back("solid_fill_pattern"); + def->aliases = { "solid_fill_pattern" }; def->default_value = new ConfigOptionEnum(ipRectilinear); def = this->add("external_perimeter_extrusion_width", coFloatOrPercent); @@ -923,8 +923,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Speed for printing the internal fill. Set to zero for auto."); def->sidetext = L("mm/s"); def->cli = "infill-speed=f"; - def->aliases.push_back("print_feed_rate"); - def->aliases.push_back("infill_feed_rate"); + def->aliases = { "print_feed_rate", "infill_feed_rate" }; def->min = 0; def->default_value = new ConfigOptionFloat(80); @@ -1272,7 +1271,7 @@ void PrintConfigDef::init_fff_params() def->category = L("Extruders"); def->tooltip = L("The extruder to use when printing perimeters and brim. First extruder is 1."); def->cli = "perimeter-extruder=i"; - def->aliases.push_back("perimeters_extruder"); + def->aliases = { "perimeters_extruder" }; def->min = 1; def->default_value = new ConfigOptionInt(1); @@ -1285,7 +1284,7 @@ void PrintConfigDef::init_fff_params() "If expressed as percentage (for example 200%) it will be computed over layer height."); def->sidetext = L("mm or % (leave 0 for default)"); def->cli = "perimeter-extrusion-width=s"; - def->aliases.push_back("perimeters_extrusion_width"); + def->aliases = { "perimeters_extrusion_width" }; def->default_value = new ConfigOptionFloatOrPercent(0, false); def = this->add("perimeter_speed", coFloat); @@ -1294,7 +1293,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Speed for perimeters (contours, aka vertical shells). Set to zero for auto."); def->sidetext = L("mm/s"); def->cli = "perimeter-speed=f"; - def->aliases.push_back("perimeter_feed_rate"); + def->aliases = { "perimeter_feed_rate" }; def->min = 0; def->default_value = new ConfigOptionFloat(60); @@ -1307,7 +1306,7 @@ void PrintConfigDef::init_fff_params() "if the Extra Perimeters option is enabled."); def->sidetext = L("(minimum)"); def->cli = "perimeters=i"; - def->aliases.push_back("perimeter_offsets"); + def->aliases = { "perimeter_offsets" }; def->min = 0; def->default_value = new ConfigOptionInt(3); @@ -1635,7 +1634,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s or %"); def->cli = "solid-infill-speed=s"; def->ratio_over = "infill_speed"; - def->aliases.push_back("solid_infill_feed_rate"); + def->aliases = { "solid_infill_feed_rate" }; def->min = 0; def->default_value = new ConfigOptionFloatOrPercent(20, false); @@ -1717,6 +1716,14 @@ void PrintConfigDef::init_fff_params() def->cli = "support-material!"; def->default_value = new ConfigOptionBool(false); + def = this->add("support_material_auto", coBool); + def->label = L("Auto generated supports"); + def->category = L("Support material"); + def->tooltip = L("If checked, supports will be generated automatically based on the overhang threshold value."\ + " If unchecked, supports will be generated inside the \"Support Enforcer\" volumes only."); + def->cli = "support-material-auto!"; + def->default_value = new ConfigOptionBool(true); + def = this->add("support_material_xy_spacing", coFloatOrPercent); def->label = L("XY separation between an object and its support"); def->category = L("Support material"); @@ -1755,7 +1762,7 @@ void PrintConfigDef::init_fff_params() "for the first object layer."); def->sidetext = L("mm"); def->cli = "support-material-contact-distance=f"; - def->min = 0; +// def->min = 0; def->enum_values.push_back("0"); def->enum_values.push_back("0.2"); def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str()); @@ -1981,7 +1988,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Speed for travel moves (jumps between distant extrusion points)."); def->sidetext = L("mm/s"); def->cli = "travel-speed=f"; - def->aliases.push_back("travel_feed_rate"); + def->aliases = { "travel_feed_rate" }; def->min = 1; def->default_value = new ConfigOptionFloat(130); diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 5bc99a51a3..bc3b0ef490 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -345,6 +345,7 @@ public: ConfigOptionFloatOrPercent extrusion_width; ConfigOptionFloatOrPercent first_layer_height; ConfigOptionBool infill_only_where_needed; + // Force the generation of solid shells between adjacent materials/volumes. ConfigOptionBool interface_shells; ConfigOptionFloat layer_height; ConfigOptionInt raft_layers; @@ -352,6 +353,9 @@ public: // ConfigOptionFloat seam_preferred_direction; // ConfigOptionFloat seam_preferred_direction_jitter; ConfigOptionBool support_material; + // Automatic supports (generated based on support_material_threshold). + ConfigOptionBool support_material_auto; + // Direction of the support pattern (in XY plane). ConfigOptionFloat support_material_angle; ConfigOptionBool support_material_buildplate_only; ConfigOptionFloat support_material_contact_distance; @@ -361,12 +365,15 @@ public: ConfigOptionBool support_material_interface_contact_loops; ConfigOptionInt support_material_interface_extruder; ConfigOptionInt support_material_interface_layers; + // Spacing between interface lines (the hatching distance). Set zero to get a solid interface. ConfigOptionFloat support_material_interface_spacing; ConfigOptionFloatOrPercent support_material_interface_speed; ConfigOptionEnum support_material_pattern; + // Spacing between support material lines (the hatching distance). ConfigOptionFloat support_material_spacing; ConfigOptionFloat support_material_speed; ConfigOptionBool support_material_synchronize_layers; + // Overhang angle threshold. ConfigOptionInt support_material_threshold; ConfigOptionBool support_material_with_sheath; ConfigOptionFloatOrPercent support_material_xy_spacing; @@ -389,6 +396,7 @@ protected: // OPT_PTR(seam_preferred_direction); // OPT_PTR(seam_preferred_direction_jitter); OPT_PTR(support_material); + OPT_PTR(support_material_auto); OPT_PTR(support_material_angle); OPT_PTR(support_material_buildplate_only); OPT_PTR(support_material_contact_distance); @@ -436,10 +444,12 @@ public: ConfigOptionInt infill_every_layers; ConfigOptionFloatOrPercent infill_overlap; ConfigOptionFloat infill_speed; + // Detect bridging perimeters ConfigOptionBool overhangs; ConfigOptionInt perimeter_extruder; ConfigOptionFloatOrPercent perimeter_extrusion_width; ConfigOptionFloat perimeter_speed; + // Total number of perimeters. ConfigOptionInt perimeters; ConfigOptionFloatOrPercent small_perimeter_speed; ConfigOptionFloat solid_infill_below_area; @@ -447,6 +457,7 @@ public: ConfigOptionFloatOrPercent solid_infill_extrusion_width; ConfigOptionInt solid_infill_every_layers; ConfigOptionFloatOrPercent solid_infill_speed; + // Detect thin walls. ConfigOptionBool thin_walls; ConfigOptionFloatOrPercent top_infill_extrusion_width; ConfigOptionInt top_solid_layers; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 7882c58a89..ef2364dc41 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -112,8 +112,18 @@ bool PrintObject::reload_model_instances() Points copies; copies.reserve(m_model_object->instances.size()); for (const ModelInstance *mi : m_model_object->instances) + { +#if ENABLE_MODELINSTANCE_3D_OFFSET + if (mi->is_printable()) + { + const Vec3d& offset = mi->get_offset(); + copies.emplace_back(Point::new_scale(offset(0), offset(1))); + } +#else if (mi->is_printable()) copies.emplace_back(Point::new_scale(mi->offset(0), mi->offset(1))); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET + } return this->set_copies(copies); } @@ -490,6 +500,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector PrintObject::_slice_region(size_t region_id, const std::vector &z, bool modifier) { - std::vector layers; + std::vector volumes; if (region_id < this->region_volumes.size()) { - std::vector &volumes = this->region_volumes[region_id]; - if (! volumes.empty()) { - // Compose mesh. - //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. - TriangleMesh mesh; - for (int volume_id : volumes) { - ModelVolume *volume = this->model_object()->volumes[volume_id]; - if (volume->modifier == modifier) - mesh.merge(volume->mesh); - m_print->throw_if_canceled(); - } - if (mesh.stl.stats.number_of_facets > 0) { - // transform mesh - // we ignore the per-instance transformations currently and only - // consider the first one - this->model_object()->instances.front()->transform_mesh(&mesh, true); - // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift - mesh.translate(- unscale(m_copies_shift(0)), - unscale(m_copies_shift(1)), - float(this->model_object()->bounding_box().min(2))); - // perform actual slicing - TriangleMeshSlicer mslicer; - Print *print = this->print(); - auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); - mslicer.init(&mesh, callback); - mslicer.slice(z, &layers, callback); - m_print->throw_if_canceled(); - } + for (int volume_id : this->region_volumes[region_id]) { + const ModelVolume *volume = this->model_object()->volumes[volume_id]; + if (modifier ? volume->is_modifier() : volume->is_model_part()) + volumes.emplace_back(volume); + } + } + return this->_slice_volumes(z, volumes); +} + +std::vector PrintObject::slice_support_enforcers() const +{ + std::vector volumes; + for (const ModelVolume *volume : this->model_object()->volumes) + if (volume->is_support_enforcer()) + volumes.emplace_back(volume); + std::vector zs; + zs.reserve(this->layers().size()); + for (const Layer *l : this->layers()) + zs.emplace_back(l->slice_z); + return this->_slice_volumes(zs, volumes); +} + +std::vector PrintObject::slice_support_blockers() const +{ + std::vector volumes; + for (const ModelVolume *volume : this->model_object()->volumes) + if (volume->is_support_blocker()) + volumes.emplace_back(volume); + std::vector zs; + zs.reserve(this->layers().size()); + for (const Layer *l : this->layers()) + zs.emplace_back(l->slice_z); + return this->_slice_volumes(zs, volumes); +} + +std::vector PrintObject::_slice_volumes(const std::vector &z, const std::vector &volumes) const +{ + std::vector layers; + if (! volumes.empty()) { + // Compose mesh. + //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. + TriangleMesh mesh; + for (const ModelVolume *v : volumes) + mesh.merge(v->mesh); + if (mesh.stl.stats.number_of_facets > 0) { + // transform mesh + // we ignore the per-instance transformations currently and only + // consider the first one + this->model_object()->instances.front()->transform_mesh(&mesh, true); + // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift + mesh.translate(- unscale(m_copies_shift(0)), - unscale(m_copies_shift(1)), - float(this->model_object()->bounding_box().min(2))); + // perform actual slicing + TriangleMeshSlicer mslicer; + const Print *print = this->print(); + auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); + mslicer.init(&mesh, callback); + mslicer.slice(z, &layers, callback); + m_print->throw_if_canceled(); } } return layers; diff --git a/xs/src/libslic3r/PrintRegion.cpp b/xs/src/libslic3r/PrintRegion.cpp index cdc8c532ae..c112548b40 100644 --- a/xs/src/libslic3r/PrintRegion.cpp +++ b/xs/src/libslic3r/PrintRegion.cpp @@ -56,4 +56,9 @@ coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const print_config.nozzle_diameter.get_at(m_config.solid_infill_extruder.value - 1)) / 3.; } +coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const +{ + return this->nozzle_dmr_avg(print_config) * sqrt(m_config.bridge_flow_ratio.value); +} + } diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp index 1bc38502b5..b3e3145493 100644 --- a/xs/src/libslic3r/Slicing.cpp +++ b/xs/src/libslic3r/Slicing.cpp @@ -224,9 +224,9 @@ std::vector layer_height_profile_adaptive( // 1) Initialize the SlicingAdaptive class with the object meshes. SlicingAdaptive as; as.set_slicing_parameters(slicing_params); - for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it) - if (! (*it)->modifier) - as.add_mesh(&(*it)->mesh); + for (const ModelVolume *volume : volumes) + if (volume->is_model_part()) + as.add_mesh(&volume->mesh); as.prepare(); // 2) Generate layers using the algorithm of @platsch diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index a6b6c1bb88..2bcf597e6c 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -248,10 +248,10 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) #ifdef SLIC3R_DEBUG static int iRun = 0; iRun ++; - for (MyLayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it) + for (const MyLayer *layer : top_contacts) Slic3r::SVG::export_expolygons( - debug_out_path("support-top-contacts-%d-%lf.svg", iRun, (*it)->print_z), - union_ex((*it)->polygons, false)); + debug_out_path("support-top-contacts-%d-%lf.svg", iRun, layer->print_z), + union_ex(layer->polygons, false)); #endif /* SLIC3R_DEBUG */ BOOST_LOG_TRIVIAL(info) << "Support generator - Creating bottom contacts"; @@ -282,7 +282,17 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) MyLayersPtr intermediate_layers = this->raft_and_intermediate_support_layers( object, bottom_contacts, top_contacts, layer_storage); - this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy); +// this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy); + this->trim_support_layers_by_object(object, top_contacts, + m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, + m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy); + +#ifdef SLIC3R_DEBUG + for (const MyLayer *layer : top_contacts) + Slic3r::SVG::export_expolygons( + debug_out_path("support-top-contacts-trimmed-by-object-%d-%lf.svg", iRun, layer->print_z), + union_ex(layer->polygons, false)); +#endif BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers"; @@ -420,29 +430,17 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t { // 1) Count the new polygons first. size_t n_polygons_new = 0; - for (const LayerRegion* pregion : layer.regions()) { - const LayerRegion ®ion = *pregion; - const SurfaceCollection &slices = region.slices; - for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) { - const Surface &surface = *it; + for (const LayerRegion *region : layer.regions()) + for (const Surface &surface : region->slices.surfaces) if (surface.surface_type == surface_type) n_polygons_new += surface.expolygon.holes.size() + 1; - } - } - // 2) Collect the new polygons. Polygons out; out.reserve(n_polygons_new); - for (const LayerRegion *pregion : layer.regions()) { - const LayerRegion ®ion = *pregion; - const SurfaceCollection &slices = region.slices; - for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) { - const Surface &surface = *it; + for (const LayerRegion *region : layer.regions()) + for (const Surface &surface : region->slices.surfaces) if (surface.surface_type == surface_type) polygons_append(out, surface.expolygon); - } - } - return out; } @@ -452,8 +450,8 @@ Polygons collect_slices_outer(const Layer &layer) { Polygons out; out.reserve(out.size() + layer.slices.expolygons.size()); - for (ExPolygons::const_iterator it = layer.slices.expolygons.begin(); it != layer.slices.expolygons.end(); ++ it) - out.push_back(it->contour); + for (const ExPolygon &expoly : layer.slices.expolygons) + out.emplace_back(expoly.contour); return out; } @@ -461,8 +459,11 @@ class SupportGridPattern { public: SupportGridPattern( + // Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy) const Polygons &support_polygons, - const Polygons &trimming_polygons, + // Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons. + const Polygons &trimming_polygons, + // Grid spacing, given by "support_material_spacing" + m_support_material_flow.spacing() coordf_t support_spacing, coordf_t support_angle) : m_support_polygons(&support_polygons), m_trimming_polygons(&trimming_polygons), @@ -485,7 +486,8 @@ public: m_grid.set_bbox(bbox); m_grid.create(*m_support_polygons, grid_resolution); m_grid.calculate_sdf(); - // Extract a bounding contour from the grid, trim by the object. + // Sample a single point per input support polygon, keep it as a reference to maintain corresponding + // polygons if ever these polygons get split into parts by the trimming polygons. m_island_samples = island_samples(*m_support_polygons); } @@ -493,22 +495,22 @@ public: // and trim the extracted polygons by trimming_polygons. // Trimming by the trimming_polygons may split the extracted polygons into pieces. // Remove all the pieces, which do not contain any of the island_samples. - Polygons extract_support(const coord_t offset_in_grid) + Polygons extract_support(const coord_t offset_in_grid, bool fill_holes) { // Generate islands, so each island may be tested for overlap with m_island_samples. + assert(std::abs(2 * offset_in_grid) < m_grid.resolution()); ExPolygons islands = diff_ex( - m_grid.contours_simplified(offset_in_grid), + m_grid.contours_simplified(offset_in_grid, fill_holes), *m_trimming_polygons, false); // Extract polygons, which contain some of the m_island_samples. Polygons out; - std::vector> samples_inside; - for (ExPolygon &island : islands) { BoundingBox bbox = get_extents(island.contour); + // Samples are sorted lexicographically. auto it_lower = std::lower_bound(m_island_samples.begin(), m_island_samples.end(), Point(bbox.min - Point(1, 1))); auto it_upper = std::upper_bound(m_island_samples.begin(), m_island_samples.end(), Point(bbox.max + Point(1, 1))); - samples_inside.clear(); + std::vector> samples_inside; for (auto it = it_lower; it != it_upper; ++ it) if (bbox.contains(*it)) samples_inside.push_back(std::make_pair(*it, false)); @@ -569,8 +571,10 @@ public: private: SupportGridPattern& operator=(const SupportGridPattern &rhs); +#if 0 // Get some internal point of an expolygon, to be used as a representative // sample to test, whether this island is inside another island. + //FIXME this was quick, but not sufficiently robust. static Point island_sample(const ExPolygon &expoly) { // Find the lowest point lexicographically. @@ -591,7 +595,10 @@ private: double coef = 20. / sqrt(l2); return Point(p2(0) + coef * v(0), p2(1) + coef * v(1)); } +#endif + // Sample one internal point per expolygon. + // FIXME this is quite an overkill to calculate a complete offset just to get a single point, but at least it is robust. static Points island_samples(const ExPolygons &expolygons) { Points pts; @@ -629,9 +636,164 @@ private: coordf_t m_support_spacing; Slic3r::EdgeGrid::Grid m_grid; + // Internal sample points of supporting expolygons. These internal points are used to pick regions corresponding + // to the initial supporting regions, after these regions werre grown and possibly split to many by the trimming polygons. Points m_island_samples; }; +namespace SupportMaterialInternal { + static inline bool has_bridging_perimeters(const ExtrusionLoop &loop) + { + for (const ExtrusionPath &ep : loop.paths) + if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty()) + return ep.size() >= (ep.is_closed() ? 3 : 2); + return false; + } + static bool has_bridging_perimeters(const ExtrusionEntityCollection &perimeters) + { + for (const ExtrusionEntity *ee : perimeters.entities) { + if (ee->is_collection()) { + for (const ExtrusionEntity *ee2 : static_cast(ee)->entities) { + assert(! ee2->is_collection()); + if (ee2->is_loop()) + if (has_bridging_perimeters(*static_cast(ee2))) + return true; + } + } else if (ee->is_loop() && has_bridging_perimeters(*static_cast(ee))) + return true; + } + return false; + } + static bool has_bridging_fills(const ExtrusionEntityCollection &fills) + { + for (const ExtrusionEntity *ee : fills.entities) { + assert(ee->is_collection()); + for (const ExtrusionEntity *ee2 : static_cast(ee)->entities) { + assert(! ee2->is_collection()); + assert(! ee2->is_loop()); + if (ee2->role() == erBridgeInfill) + return true; + } + } + return false; + } + static bool has_bridging_extrusions(const Layer &layer) + { + for (const LayerRegion *region : layer.regions()) { + if (SupportMaterialInternal::has_bridging_perimeters(region->perimeters)) + return true; + if (region->fill_surfaces.has(stBottomBridge) && has_bridging_fills(region->fills)) + return true; + } + return false; + } + + static inline void collect_bridging_perimeter_areas(const ExtrusionLoop &loop, const float expansion_scaled, Polygons &out) + { + assert(expansion_scaled >= 0.f); + for (const ExtrusionPath &ep : loop.paths) + if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty()) { + float exp = 0.5f * scale_(ep.width) + expansion_scaled; + if (ep.is_closed()) { + if (ep.size() >= 3) { + // This is a complete loop. + // Add the outer contour first. + Polygon poly; + poly.points = ep.polyline.points; + poly.points.pop_back(); + if (poly.area() < 0) + poly.reverse(); + polygons_append(out, offset(poly, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS)); + Polygons holes = offset(poly, - exp, SUPPORT_SURFACES_OFFSET_PARAMETERS); + polygons_reverse(holes); + polygons_append(out, holes); + } + } else if (ep.size() >= 2) { + // Offset the polyline. + polygons_append(out, offset(ep.polyline, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS)); + } + } + } + static void collect_bridging_perimeter_areas(const ExtrusionEntityCollection &perimeters, const float expansion_scaled, Polygons &out) + { + for (const ExtrusionEntity *ee : perimeters.entities) { + if (ee->is_collection()) { + for (const ExtrusionEntity *ee2 : static_cast(ee)->entities) { + assert(! ee2->is_collection()); + if (ee2->is_loop()) + collect_bridging_perimeter_areas(*static_cast(ee2), expansion_scaled, out); + } + } else if (ee->is_loop()) + collect_bridging_perimeter_areas(*static_cast(ee), expansion_scaled, out); + } + } + + static void remove_bridges_from_contacts( + const PrintConfig &print_config, + const Layer &lower_layer, + const Polygons &lower_layer_polygons, + LayerRegion *layerm, + float fw, + Polygons &contact_polygons) + { + // compute the area of bridging perimeters + Polygons bridges; + { + // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported. + Polygons lower_grown_slices = offset(lower_layer_polygons, + //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width. + 0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder-1))), + SUPPORT_SURFACES_OFFSET_PARAMETERS); + // Collect perimeters of this layer. + //FIXME split_at_first_point() could split a bridge mid-way + #if 0 + Polylines overhang_perimeters = layerm->perimeters.as_polylines(); + // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() + for (Polyline &polyline : overhang_perimeters) + polyline.points[0].x += 1; + // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters. + overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices); + #else + Polylines overhang_perimeters = diff_pl(layerm->perimeters.as_polylines(), lower_grown_slices); + #endif + + // only consider straight overhangs + // only consider overhangs having endpoints inside layer's slices + // convert bridging polylines into polygons by inflating them with their thickness + // since we're dealing with bridges, we can't assume width is larger than spacing, + // so we take the largest value and also apply safety offset to be ensure no gaps + // are left in between + Flow bridge_flow = layerm->flow(frPerimeter, true); + float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing())); + for (Polyline &polyline : overhang_perimeters) + if (polyline.is_straight()) { + // This is a bridge + polyline.extend_start(fw); + polyline.extend_end(fw); + // Is the straight perimeter segment supported at both sides? + if (lower_layer.slices.contains(polyline.first_point()) && lower_layer.slices.contains(polyline.last_point())) + // Offset a polyline into a thick line. + polygons_append(bridges, offset(polyline, 0.5f * w + 10.f)); + } + bridges = union_(bridges); + } + // remove the entire bridges and only support the unsupported edges + //FIXME the brided regions are already collected as layerm->bridged. Use it? + for (const Surface &surface : layerm->fill_surfaces.surfaces) + if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1) + polygons_append(bridges, surface.expolygon); + //FIXME add the gap filled areas. Extrude the gaps with a bridge flow? + contact_polygons = diff(contact_polygons, bridges, true); + // Add the bridge anchors into the region. + //FIXME add supports at regular intervals to support long bridges! + polygons_append(contact_polygons, + intersection( + // Offset unsupported edges into polygons. + offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS), + bridges)); + } +} + // Generate top contact layers supporting overhangs. // For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined. // If supports over bed surface only are requested, don't generate contact layers over an object. @@ -643,9 +805,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ ++ iRun; #endif /* SLIC3R_DEBUG */ + // Slice support enforcers / support blockers. + std::vector enforcers = object.slice_support_enforcers(); + std::vector blockers = object.slice_support_blockers(); + // Output layers, sorted by top Z. MyLayersPtr contact_out; + const bool support_auto = m_object_config->support_material_auto.value; // If user specified a custom angle threshold, convert it to radians. // Zero means automatic overhang detection. const double threshold_rad = (m_object_config->support_material_threshold.value > 0) ? @@ -680,10 +847,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // Note that layer_id < layer->id when raft_layers > 0 as the layer->id incorporates the raft layers. // So layer_id == 0 means first object layer and layer->id == 0 means first print layer if there are no explicit raft layers. size_t num_layers = this->has_support() ? object.layer_count() : 1; - contact_out.assign(num_layers, nullptr); + // For each overhang layer, two supporting layers may be generated: One for the overhangs extruded with a bridging flow, + // and the other for the overhangs extruded with a normal flow. + contact_out.assign(num_layers * 2, nullptr); tbb::spin_mutex layer_storage_mutex; tbb::parallel_for(tbb::blocked_range(this->has_raft() ? 0 : 1, num_layers), - [this, &object, &buildplate_covered, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out](const tbb::blocked_range& range) { + [this, &object, &buildplate_covered, &enforcers, &blockers, support_auto, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out] + (const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { const Layer &layer = *object.layers()[layer_id]; @@ -694,6 +864,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ Polygons contact_polygons; Polygons slices_margin_cached; float slices_margin_cached_offset = -1.; + Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices.expolygons); + // Offset of the lower layer, to trim the support polygons with to calculate dense supports. + float no_interface_offset = 0.f; if (layer_id == 0) { // This is the first object layer, so the object is being printed on a raft and // we're here just to get the object footprint for the raft. @@ -708,6 +881,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // Extrusion width accounts for the roundings of the extrudates. // It is the maximum widh of the extrudate. float fw = float(layerm->flow(frExternalPerimeter).scaled_width()); + no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw); float lower_layer_offset = (layer_id < m_object_config->support_material_enforce_layers.value) ? // Enforce a full possible support, ignore the overhang angle. @@ -720,7 +894,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // Overhang polygons for this layer and region. Polygons diff_polygons; Polygons layerm_polygons = to_polygons(layerm->slices); - Polygons lower_layer_polygons = to_polygons(lower_layer.slices.expolygons); if (lower_layer_offset == 0.f) { // Support everything. diff_polygons = diff(layerm_polygons, lower_layer_polygons); @@ -730,28 +903,61 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]); } } else { - // Get the regions needing a suport, collapse very tiny spots. - //FIXME cache the lower layer offset if this layer has multiple regions. - diff_polygons = offset2( - diff(layerm_polygons, - offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)), - -0.1f*fw, +0.1f*fw); - if (! buildplate_covered.empty()) { - // Don't support overhangs above the top surfaces. - // This step is done before the contact surface is calculated by growing the overhang region. - diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]); + if (support_auto) { + // Get the regions needing a suport, collapse very tiny spots. + //FIXME cache the lower layer offset if this layer has multiple regions. + #if 1 + diff_polygons = offset2( + diff(layerm_polygons, + offset2(lower_layer_polygons, - 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)), + //FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to + // no support at all for not so steep overhangs. + - 0.1f * fw, 0.1f * fw); + #else + diff_polygons = + diff(layerm_polygons, + offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); + #endif + if (! buildplate_covered.empty()) { + // Don't support overhangs above the top surfaces. + // This step is done before the contact surface is calculated by growing the overhang region. + diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]); + } + if (! diff_polygons.empty()) { + // Offset the support regions back to a full overhang, restrict them to the full overhang. + // This is done to increase size of the supporting columns below, as they are calculated by + // propagating these contact surfaces downwards. + diff_polygons = diff( + intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), + lower_layer_polygons); + } } - if (diff_polygons.empty()) - continue; - // Offset the support regions back to a full overhang, restrict them to the full overhang. - diff_polygons = diff( - intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), - lower_layer_polygons); + if (! enforcers.empty()) { + // Apply the "support enforcers". + //FIXME add the "enforcers" to the sparse support regions only. + const ExPolygons &enforcer = enforcers[layer_id - 1]; + if (! enforcer.empty()) { + // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes. + Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(enforcer)), + offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)); + if (! new_contacts.empty()) { + if (diff_polygons.empty()) + diff_polygons = std::move(new_contacts); + else + diff_polygons = union_(diff_polygons, new_contacts); + } + } + } + } + // Apply the "support blockers". + if (! diff_polygons.empty() && ! blockers.empty() && ! blockers[layer_id].empty()) { + // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes. + diff_polygons = diff(diff_polygons, to_polygons(blockers[layer_id])); } if (diff_polygons.empty()) continue; - #ifdef SLIC3R_DEBUG + #ifdef SLIC3R_DEBUG { ::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg", iRun, layer_id, @@ -762,73 +968,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ } #endif /* SLIC3R_DEBUG */ - if (m_object_config->dont_support_bridges) { - // compute the area of bridging perimeters - // Note: this is duplicate code from GCode.pm, we need to refactor - if (true) { - Polygons bridged_perimeters; - { - Flow bridge_flow = layerm->flow(frPerimeter, true); - coordf_t nozzle_diameter = m_print_config->nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder-1); - Polygons lower_grown_slices = offset(lower_layer_polygons, 0.5f*float(scale_(nozzle_diameter)), SUPPORT_SURFACES_OFFSET_PARAMETERS); - - // Collect perimeters of this layer. - // TODO: split_at_first_point() could split a bridge mid-way - Polylines overhang_perimeters; - for (ExtrusionEntity* extrusion_entity : layerm->perimeters.entities) { - const ExtrusionEntityCollection *island = dynamic_cast(extrusion_entity); - assert(island != NULL); - for (size_t i = 0; i < island->entities.size(); ++ i) { - ExtrusionEntity *entity = island->entities[i]; - ExtrusionLoop *loop = dynamic_cast(entity); - overhang_perimeters.push_back(loop ? - loop->as_polyline() : - dynamic_cast(entity)->polyline); - } - } - - // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() - for (Polyline &polyline : overhang_perimeters) - polyline.points[0](0) += 1; - // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters. - overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices); - - // only consider straight overhangs - // only consider overhangs having endpoints inside layer's slices - // convert bridging polylines into polygons by inflating them with their thickness - // since we're dealing with bridges, we can't assume width is larger than spacing, - // so we take the largest value and also apply safety offset to be ensure no gaps - // are left in between - float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing())); - for (Polyline &polyline : overhang_perimeters) - if (polyline.is_straight()) { - // This is a bridge - polyline.extend_start(fw); - polyline.extend_end(fw); - // Is the straight perimeter segment supported at both sides? - if (layer.slices.contains(polyline.first_point()) && layer.slices.contains(polyline.last_point())) - // Offset a polyline into a thick line. - polygons_append(bridged_perimeters, offset(polyline, 0.5f * w + 10.f)); - } - bridged_perimeters = union_(bridged_perimeters); - } - // remove the entire bridges and only support the unsupported edges - Polygons bridges; - for (const Surface &surface : layerm->fill_surfaces.surfaces) - if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1) - polygons_append(bridges, surface.expolygon); - diff_polygons = diff(diff_polygons, bridges, true); - polygons_append(bridges, bridged_perimeters); - polygons_append(diff_polygons, - intersection( - // Offset unsupported edges into polygons. - offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS), - bridges)); - } else { - // just remove bridged areas - diff_polygons = diff(diff_polygons, layerm->bridged, true); - } - } // if (m_objconfig->dont_support_bridges) + if (this->m_object_config->dont_support_bridges) + SupportMaterialInternal::remove_bridges_from_contacts( + *m_print_config, lower_layer, lower_layer_polygons, layerm, fw, diff_polygons); if (diff_polygons.empty()) continue; @@ -842,7 +984,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ union_ex(diff_polygons, false)); #endif /* SLIC3R_DEBUG */ - if (this->has_contact_loops()) + //FIXME the overhang_polygons are used to construct the support towers as well. + //if (this->has_contact_loops()) + // Store the exact contour of the overhang for the contact loops. polygons_append(overhang_polygons, diff_polygons); // Let's define the required contact area by using a max gap of half the upper @@ -851,12 +995,15 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // on the other side of the object (if it's very thin). { //FIMXE 1) Make the offset configurable, 2) Make the Z span configurable. + //FIXME one should trim with the layer span colliding with the support layer, this layer + // may be lower than lower_layer, so the support area needed may need to be actually bigger! + // For the same reason, the non-bridging support area may be smaller than the bridging support area! float slices_margin_offset = std::min(lower_layer_offset, float(scale_(m_gap_xy))); if (slices_margin_cached_offset != slices_margin_offset) { slices_margin_cached_offset = slices_margin_offset; slices_margin_cached = (slices_margin_offset == 0.f) ? - to_polygons(lower_layer.slices.expolygons) : - offset(lower_layer.slices.expolygons, slices_margin_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS); + lower_layer_polygons : + offset2(to_polygons(lower_layer.slices.expolygons), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS); if (! buildplate_covered.empty()) { // Trim the inflated contact surfaces by the top surfaces as well. polygons_append(slices_margin_cached, buildplate_covered[layer_id]); @@ -879,58 +1026,72 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ } // for each layer.region } // end of Generate overhang/contact_polygons for non-raft layers. - // now apply the contact areas to the layer were they need to be made + // Now apply the contact areas to the layer where they need to be made. if (! contact_polygons.empty()) { - // get the average nozzle diameter used on this layer MyLayer &new_layer = layer_allocate(layer_storage, layer_storage_mutex, sltTopContact); new_layer.idx_object_layer_above = layer_id; - if (m_slicing_params.soluble_interface) { + MyLayer *bridging_layer = nullptr; + if (layer_id == 0) { + // This is a raft contact layer sitting directly on the print bed. + assert(this->has_raft()); + new_layer.print_z = m_slicing_params.raft_contact_top_z; + new_layer.bottom_z = m_slicing_params.raft_interface_top_z; + new_layer.height = m_slicing_params.contact_raft_layer_height; + } else if (m_slicing_params.soluble_interface) { // Align the contact surface height with a layer immediately below the supported layer. - new_layer.print_z = layer.print_z - layer.height; - if (layer_id == 0) { - // This is a raft contact layer sitting directly on the print bed. - new_layer.height = m_slicing_params.contact_raft_layer_height; - new_layer.bottom_z = m_slicing_params.raft_interface_top_z; - } else { - // Interface layer will be synchronized with the object. - assert(layer_id > 0); - new_layer.height = object.layers()[layer_id - 1]->height; - new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers()[layer_id - 2]->print_z; - } + // Interface layer will be synchronized with the object. + new_layer.print_z = layer.print_z - layer.height; + new_layer.height = object.layers()[layer_id - 1]->height; + new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers()[layer_id - 2]->print_z; } else { - // Contact layer will be printed with a normal flow, but - // it will support layers printed with a bridging flow. - //FIXME Probably printing with the bridge flow? How about the unsupported perimeters? Are they printed with the bridging flow? - // In the future we may switch to a normal extrusion flow for the supported bridges. - // Get the average nozzle diameter used on this layer. - coordf_t nozzle_dmr = 0.; - for (const LayerRegion *region : layer.regions()) - nozzle_dmr += region->region()->nozzle_dmr_avg(*m_print_config); - nozzle_dmr /= coordf_t(layer.regions().size()); - new_layer.print_z = layer.print_z - nozzle_dmr - m_object_config->support_material_contact_distance; + new_layer.print_z = layer.print_z - layer.height - m_object_config->support_material_contact_distance; new_layer.bottom_z = new_layer.print_z; new_layer.height = 0.; - if (layer_id == 0) { - // This is a raft contact layer sitting directly on the print bed. - assert(this->has_raft()); - new_layer.bottom_z = m_slicing_params.raft_interface_top_z; - new_layer.height = m_slicing_params.contact_raft_layer_height; + // Ignore this contact area if it's too low. + // Don't want to print a layer below the first layer height as it may not stick well. + //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact + // and it may actually make sense to do it with a thinner layer than the first layer height. + if (new_layer.print_z < m_slicing_params.first_print_layer_height - EPSILON) { + // This contact layer is below the first layer height, therefore not printable. Don't support this surface. + continue; + } else if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) { + // Align the layer with the 1st layer height. + new_layer.print_z = m_slicing_params.first_print_layer_height; + new_layer.bottom_z = 0; + new_layer.height = m_slicing_params.first_print_layer_height; } else { - // Ignore this contact area if it's too low. - // Don't want to print a layer below the first layer height as it may not stick well. - //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact - // and it may actually make sense to do it with a thinner layer than the first layer height. - if (new_layer.print_z < m_slicing_params.first_print_layer_height - EPSILON) { - // This contact layer is below the first layer height, therefore not printable. Don't support this surface. - continue; - } else if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) { - // Align the layer with the 1st layer height. - new_layer.print_z = m_slicing_params.first_print_layer_height; - new_layer.bottom_z = 0; - new_layer.height = m_slicing_params.first_print_layer_height; - } else { - // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and - // its height will be set adaptively later on. + // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and + // its height will be set adaptively later on. + } + + // Contact layer will be printed with a normal flow, but + // it will support layers printed with a bridging flow. + if (SupportMaterialInternal::has_bridging_extrusions(layer)) { + coordf_t bridging_height = 0.; + for (const LayerRegion *region : layer.regions()) + bridging_height += region->region()->bridging_height_avg(*m_print_config); + bridging_height /= coordf_t(layer.regions().size()); + coordf_t bridging_print_z = layer.print_z - bridging_height - m_object_config->support_material_contact_distance; + if (bridging_print_z >= m_slicing_params.first_print_layer_height - EPSILON) { + // Not below the first layer height means this layer is printable. + if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) { + // Align the layer with the 1st layer height. + bridging_print_z = m_slicing_params.first_print_layer_height; + } + if (bridging_print_z < new_layer.print_z - EPSILON) { + // Allocate the new layer. + bridging_layer = &layer_allocate(layer_storage, layer_storage_mutex, sltTopContact); + bridging_layer->idx_object_layer_above = layer_id; + bridging_layer->print_z = bridging_print_z; + if (bridging_print_z == m_slicing_params.first_print_layer_height) { + bridging_layer->bottom_z = 0; + bridging_layer->height = m_slicing_params.first_print_layer_height; + } else { + // Don't know the height yet. + bridging_layer->bottom_z = bridging_print_z; + bridging_layer->height = 0; + } + } } } } @@ -940,27 +1101,112 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ contact_polygons, // Trimming polygons, to trim the stretched support islands. slices_margin_cached, - // How much to offset the extracted contour outside of the grid. + // Grid resolution. m_object_config->support_material_spacing.value + m_support_material_flow.spacing(), Geometry::deg2rad(m_object_config->support_material_angle.value)); - // 1) infill polygons, expand them by half the extrusion width + a tiny bit of extra. - new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5); - // 2) Contact polygons will be projected down. To keep the interface and base layers to grow, return a contour a tiny bit smaller than the grid cells. - new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3)); + // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells. + new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3, true)); + // 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra. + if (layer_id == 0 || m_slicing_params.soluble_interface) { + // if (no_interface_offset == 0.f) { + new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, true); + } else { + // Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions. + Polygons dense_interface_polygons = diff(overhang_polygons, + offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS)); +// offset(lower_layer_polygons, no_interface_offset * 0.6f, SUPPORT_SURFACES_OFFSET_PARAMETERS)); + if (! dense_interface_polygons.empty()) { + //FIXME do it for the bridges only? + SupportGridPattern support_grid_pattern( + // Support islands, to be stretched into a grid. + dense_interface_polygons, + // Trimming polygons, to trim the stretched support islands. + slices_margin_cached, + // Grid resolution. + m_object_config->support_material_spacing.value + m_support_material_flow.spacing(), + Geometry::deg2rad(m_object_config->support_material_angle.value)); + new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, false); + } + } // Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded. // Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons. // Store the overhang polygons. // The overhang polygons are used in the path generator for planning of the contact loops. - // if (this->has_contact_loops()) + // if (this->has_contact_loops()). Compared to "polygons", "overhang_polygons" are snug. new_layer.overhang_polygons = new Polygons(std::move(overhang_polygons)); - contact_out[layer_id] = &new_layer; + contact_out[layer_id * 2] = &new_layer; + if (bridging_layer != nullptr) { + bridging_layer->polygons = new_layer.polygons; + bridging_layer->contact_polygons = new Polygons(*new_layer.contact_polygons); + bridging_layer->overhang_polygons = new Polygons(*new_layer.overhang_polygons); + contact_out[layer_id * 2 + 1] = bridging_layer; + } } } }); + // Compress contact_out, remove the nullptr items. remove_nulls(contact_out); + // Sort the layers, as one layer may produce bridging and non-bridging contact layers with different print_z. + std::sort(contact_out.begin(), contact_out.end(), [](const MyLayer *l1, const MyLayer *l2) { return l1->print_z < l2->print_z; }); + + // Merge close contact layers conservatively: If two layers are closer than the minimum allowed print layer height (the min_layer_height parameter), + // the top contact layer is merged into the bottom contact layer. + { + int i = 0; + int k = 0; + { + // Find the span of layers, which are to be printed at the first layer height. + int j = 0; + for (; j < contact_out.size() && contact_out[j]->print_z < m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON; ++ j); + if (j > 0) { + // Merge the contact_out layers (0) to (j - 1) into the contact_out[0]. + MyLayer &dst = *contact_out.front(); + for (int u = 1; u < j; ++ u) { + MyLayer &src = *contact_out[u]; + // The union_() does not support move semantic yet, but maybe one day it will. + dst.polygons = union_(dst.polygons, std::move(src.polygons)); + *dst.contact_polygons = union_(*dst.contact_polygons, std::move(*src.contact_polygons)); + *dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons)); + // Source polygon is no more needed, it will not be refrenced. Release its data. + src.reset(); + } + // Snap the first layer to the 1st layer height. + dst.print_z = m_slicing_params.first_print_layer_height; + dst.height = m_slicing_params.first_print_layer_height; + dst.bottom_z = 0; + ++ k; + } + i = j; + } + for (; i < int(contact_out.size()); ++ k) { + // Find the span of layers closer than m_support_layer_height_min. + int j = i + 1; + coordf_t zmax = contact_out[i]->print_z + m_support_layer_height_min + EPSILON; + for (; j < contact_out.size() && contact_out[j]->print_z < zmax; ++ j) ; + if (i + 1 < j) { + // Merge the contact_out layers (i + 1) to (j - 1) into the contact_out[i]. + MyLayer &dst = *contact_out[i]; + for (int u = i + 1; u < j; ++ u) { + MyLayer &src = *contact_out[u]; + // The union_() does not support move semantic yet, but maybe one day it will. + dst.polygons = union_(dst.polygons, std::move(src.polygons)); + *dst.contact_polygons = union_(*dst.contact_polygons, std::move(*src.contact_polygons)); + *dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons)); + // Source polygon is no more needed, it will not be refrenced. Release its data. + src.reset(); + } + } + if (k < i) + contact_out[k] = contact_out[i]; + i = j; + } + if (k < contact_out.size()) + contact_out.erase(contact_out.begin() + k, contact_out.end()); + } + BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - end"; return contact_out; @@ -996,7 +1242,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id; const Layer &layer = *object.get_layer(layer_id); // Collect projections of all contact areas above or at the same level as this top surface. - for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) { + for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z > layer.print_z - EPSILON; -- contact_idx) { Polygons polygons_new; // Contact surfaces are expanded away from the object, trimmed by the object. // Use a slight positive offset to overlap the touching regions. @@ -1004,7 +1250,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // Merge and collect the contact polygons. The contact polygons are inflated, but not extended into a grid form. polygons_append(polygons_new, offset(*top_contacts[contact_idx]->contact_polygons, SCALED_EPSILON)); #else - // Consume the contact_polygons. The contact polygons are already expanded into a grid form. + // Consume the contact_polygons. The contact polygons are already expanded into a grid form, and they are a tiny bit smaller + // than the grid cells. polygons_append(polygons_new, std::move(*top_contacts[contact_idx]->contact_polygons)); #endif // These are the overhang surfaces. They are touching the object and they are not expanded away from the object. @@ -1016,9 +1263,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta continue; Polygons projection_raw = union_(projection); - // Top surfaces of this layer, to be used to stop the surface volume from growing down. tbb::task_group task_group; if (! m_object_config->support_material_buildplate_only) + // Find the bottom contact layers above the top surfaces of this layer. task_group.run([this, &object, &top_contacts, contact_idx, &layer, layer_id, &layer_storage, &layer_support_areas, &bottom_contacts, &projection_raw] { Polygons top = collect_region_slices_by_type(layer, stTop); #ifdef SLIC3R_DEBUG @@ -1046,28 +1293,34 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // Grow top surfaces so that interface and support generation are generated // with some spacing from object - it looks we don't need the actual // top shapes so this can be done here + //FIXME calculate layer height based on the actual thickness of the layer: + // If the layer is extruded with no bridging flow, support just the normal extrusions. layer_new.height = m_slicing_params.soluble_interface ? // Align the interface layer with the object's layer height. object.layers()[layer_id + 1]->height : // Place a bridge flow interface layer over the top surface. + //FIXME Check whether the bottom bridging surfaces are extruded correctly (no bridging flow correction applied?) + // According to Jindrich the bottom surfaces work well. + //FIXME test the bridging flow instead? m_support_material_interface_flow.nozzle_diameter; layer_new.print_z = m_slicing_params.soluble_interface ? object.layers()[layer_id + 1]->print_z : layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value; layer_new.bottom_z = layer.print_z; layer_new.idx_object_layer_below = layer_id; layer_new.bridging = ! m_slicing_params.soluble_interface; - //FIXME how much to inflate the top surface? + //FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow. + //FIXME why is the offset positive? It will be trimmed by the object later on anyway, but then it just wastes CPU clocks. layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS); if (! m_slicing_params.soluble_interface) { // Walk the top surfaces, snap the top of the new bottom surface to the closest top of the top surface, // so there will be no support surfaces generated with thickness lower than m_support_layer_height_min. for (size_t top_idx = size_t(std::max(0, contact_idx)); - top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + m_support_layer_height_min; + top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min + EPSILON; ++ top_idx) { - if (top_contacts[top_idx]->print_z > layer_new.print_z - m_support_layer_height_min) { + if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min - EPSILON) { // A top layer has been found, which is close to the new bottom layer. coordf_t diff = layer_new.print_z - top_contacts[top_idx]->print_z; - assert(std::abs(diff) <= m_support_layer_height_min); + assert(std::abs(diff) <= this->m_support_layer_height_min + EPSILON); if (diff > 0.) { // The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer. assert(diff < layer_new.height + EPSILON); @@ -1091,10 +1344,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta union_ex(layer_new.polygons, false)); #endif /* SLIC3R_DEBUG */ // Trim the already created base layers above the current layer intersecting with the new bottom contacts layer. + //FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage? touching = offset(touching, float(SCALED_EPSILON)); for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++ layer_id_above) { const Layer &layer_above = *object.layers()[layer_id_above]; - if (layer_above.print_z > layer_new.print_z + EPSILON) + if (layer_above.print_z > layer_new.print_z - EPSILON) break; if (! layer_support_areas[layer_id_above].empty()) { #ifdef SLIC3R_DEBUG @@ -1147,7 +1401,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta projection, // Trimming polygons, to trim the stretched support islands. trimming, - // How much to offset the extracted contour outside of the grid. + // Grid spacing. m_object_config->support_material_spacing.value + m_support_material_flow.spacing(), Geometry::deg2rad(m_object_config->support_material_angle.value)); tbb::task_group task_group_inner; @@ -1158,7 +1412,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta , &layer #endif /* SLIC3R_DEBUG */ ] { - layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25); + layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25, true); #ifdef SLIC3R_DEBUG Slic3r::SVG::export_expolygons( debug_out_path("support-layer_support_area-gridded-%d-%lf.svg", iRun, layer.print_z), @@ -1172,7 +1426,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta , &layer #endif /* SLIC3R_DEBUG */ ] { - projection_new = support_grid_pattern.extract_support(-5); + projection_new = support_grid_pattern.extract_support(-5, true); #ifdef SLIC3R_DEBUG Slic3r::SVG::export_expolygons( debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z), @@ -1185,7 +1439,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta task_group.wait(); } std::reverse(bottom_contacts.begin(), bottom_contacts.end()); - trim_support_layers_by_object(object, bottom_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy); +// trim_support_layers_by_object(object, bottom_contacts, 0., 0., m_gap_xy); + trim_support_layers_by_object(object, bottom_contacts, + m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, + m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy); + } // ! top_contacts.empty() return bottom_contacts; @@ -1502,9 +1760,6 @@ void PrintObjectSupportMaterial::generate_base_layers( assert(idx_intermediate == 0 || layer_intermediate.print_z >= intermediate_layers[idx_intermediate - 1]->print_z); // Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new. - idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above, - [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; }); - // New polygons for layer_intermediate. Polygons polygons_new; @@ -1523,12 +1778,10 @@ void PrintObjectSupportMaterial::generate_base_layers( // 3) base.print_z > top.print_z && base.bottom_z >= top.bottom_z -> Overlap, which will be solved inside generate_toolpaths() by reducing the base layer height where it overlaps the top layer. No trimming needed here. // 4) base.print_z > top.bottom_z && base.bottom_z < top.bottom_z -> Base overlaps with top.bottom_z. This must not happen. // 5) base.print_z <= top.print_z && base.bottom_z >= top.bottom_z -> Base is fully inside top. Trim base by top. - int idx_top_contact_overlapping = idx_top_contact_above; - while (idx_top_contact_overlapping >= 0 && - top_contacts[idx_top_contact_overlapping]->bottom_z > layer_intermediate.print_z - EPSILON) - -- idx_top_contact_overlapping; + idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above, + [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; }); // Collect all the top_contact layer intersecting with this layer. - for (; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) { + for ( int idx_top_contact_overlapping = idx_top_contact_above; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) { MyLayer &layer_top_overlapping = *top_contacts[idx_top_contact_overlapping]; if (layer_top_overlapping.print_z < layer_intermediate.bottom_z + EPSILON) break; @@ -1608,7 +1861,10 @@ void PrintObjectSupportMaterial::generate_base_layers( ++ iRun; #endif /* SLIC3R_DEBUG */ - trim_support_layers_by_object(object, intermediate_layers, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, m_gap_xy); +// trim_support_layers_by_object(object, intermediate_layers, 0., 0., m_gap_xy); + this->trim_support_layers_by_object(object, intermediate_layers, + m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, + m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy); } void PrintObjectSupportMaterial::trim_support_layers_by_object( @@ -1653,19 +1909,23 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( const Layer &object_layer = *object.layers()[i]; if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON) break; - polygons_append(polygons_trimming, (Polygons)object_layer.slices); + polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); } if (! m_slicing_params.soluble_interface) { // Collect all bottom surfaces, which will be extruded with a bridging flow. for (; i < object.layers().size(); ++ i) { const Layer &object_layer = *object.layers()[i]; bool some_region_overlaps = false; - for (LayerRegion* region : object_layer.regions()) { - coordf_t nozzle_dmr = region->region()->nozzle_dmr_avg(*m_print_config); - if (object_layer.print_z - nozzle_dmr > support_layer.print_z + gap_extra_above - EPSILON) + for (LayerRegion *region : object_layer.regions()) { + coordf_t bridging_height = region->region()->bridging_height_avg(*this->m_print_config); + if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON) break; some_region_overlaps = true; - polygons_append(polygons_trimming, to_polygons(region->slices.filter_by_type(stBottomBridge))); + polygons_append(polygons_trimming, + offset(to_expolygons(region->fill_surfaces.filter_by_type(stBottomBridge)), + gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); + if (region->region()->config().overhangs.value) + SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming); } if (! some_region_overlaps) break; @@ -1675,9 +1935,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( // perimeter's width. $support contains the full shape of support // material, thus including the width of its foremost extrusion. // We leave a gap equal to a full extrusion width. - support_layer.polygons = diff( - support_layer.polygons, - offset(polygons_trimming, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); + support_layer.polygons = diff(support_layer.polygons, polygons_trimming); } }); BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end"; @@ -1800,11 +2058,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_int coordf_t top_z = intermediate_layers[std::min(intermediate_layers.size()-1, idx_intermediate_layer + m_object_config->support_material_interface_layers - 1)]->print_z; coordf_t bottom_z = intermediate_layers[std::max(0, int(idx_intermediate_layer) - int(m_object_config->support_material_interface_layers) + 1)]->bottom_z; // Move idx_top_contact_first up until above the current print_z. - idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); + idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); // - EPSILON // Collect the top contact areas above this intermediate layer, below top_z. Polygons polygons_top_contact_projected; for (size_t idx_top_contact = idx_top_contact_first; idx_top_contact < top_contacts.size(); ++ idx_top_contact) { const MyLayer &top_contact_layer = *top_contacts[idx_top_contact]; + //FIXME maybe this adds one interface layer in excess? if (top_contact_layer.bottom_z - EPSILON > top_z) break; polygons_append(polygons_top_contact_projected, top_contact_layer.polygons); @@ -1861,8 +2120,8 @@ static inline void fill_expolygons_generate_paths( fill_params.density = density; fill_params.complete = true; fill_params.dont_adjust = true; - for (ExPolygons::const_iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) { - Surface surface(stInternal, *it_expolygon); + for (const ExPolygon &expoly : expolygons) { + Surface surface(stInternal, expoly); extrusion_entities_append_paths( dst, filler->fill_surface(&surface, fill_params), @@ -1883,8 +2142,8 @@ static inline void fill_expolygons_generate_paths( fill_params.density = density; fill_params.complete = true; fill_params.dont_adjust = true; - for (ExPolygons::iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) { - Surface surface(stInternal, std::move(*it_expolygon)); + for (ExPolygon &expoly : expolygons) { + Surface surface(stInternal, std::move(expoly)); extrusion_entities_append_paths( dst, filler->fill_surface(&surface, fill_params), @@ -2359,7 +2618,7 @@ void modulate_extrusion_by_overlapping_layers( (fragment_end.is_start ? &polyline.points.front() : &polyline.points.back()); } private: - ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) = delete; + ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {} const std::vector &m_path_fragments; }; const coord_t search_radius = 7; @@ -2711,6 +2970,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( continue; //FIXME When paralellizing, each thread shall have its own copy of the fillers. bool interface_as_base = (&layer_ex == &interface_layer) && m_object_config->support_material_interface_layers.value == 0; + //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore + // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) Flow interface_flow( float(layer_ex.layer->bridging ? layer_ex.layer->height : (interface_as_base ? m_support_material_flow.width : m_support_material_interface_flow.width)), float(layer_ex.layer->height), diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index e20d7c69fb..2e1a05946f 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -12,6 +12,7 @@ class PrintConfig; class PrintObjectConfig; // how much we extend support around the actual contact area +//FIXME this should be dependent on the nozzle diameter! #define SUPPORT_MATERIAL_MARGIN 1.5 // This class manages raft and supports for a single PrintObject. @@ -71,6 +72,21 @@ public: overhang_polygons = nullptr; } + void reset() { + layer_type = sltUnknown; + print_z = 0.; + bottom_z = 0.; + height = 0.; + idx_object_layer_above = size_t(-1); + idx_object_layer_below = size_t(-1); + bridging = false; + polygons.clear(); + delete contact_polygons; + contact_polygons = nullptr; + delete overhang_polygons; + overhang_polygons = nullptr; + } + bool operator==(const MyLayer &layer2) const { return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging; } diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp index 29cfeb1dba..9544748e93 100644 --- a/xs/src/libslic3r/SurfaceCollection.hpp +++ b/xs/src/libslic3r/SurfaceCollection.hpp @@ -37,6 +37,11 @@ public: void clear() { surfaces.clear(); } bool empty() const { return surfaces.empty(); } + bool has(SurfaceType type) const { + for (const Surface &surface : this->surfaces) + if (surface.surface_type == type) return true; + return false; + } void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; } void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); } diff --git a/xs/src/libslic3r/Technologies.hpp b/xs/src/libslic3r/Technologies.hpp new file mode 100644 index 0000000000..5c4f8617de --- /dev/null +++ b/xs/src/libslic3r/Technologies.hpp @@ -0,0 +1,12 @@ +#ifndef _technologies_h_ +#define _technologies_h_ + +// 1.42.0 techs +#define ENABLE_1_42_0 1 + +// Add z coordinate to model instances' offset +#define ENABLE_MODELINSTANCE_3D_OFFSET (1 && ENABLE_1_42_0) + +#endif // _technologies_h_ + + diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 7b8f85b6be..01adac3375 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -21,16 +21,20 @@ #include +// for SLIC3R_DEBUG_SLICE_PROCESSING +#include "libslic3r.h" + #if 0 #define DEBUG #define _DEBUG #undef NDEBUG + #define SLIC3R_DEBUG +// #define SLIC3R_TRIANGLEMESH_DEBUG #endif #include -#ifdef SLIC3R_DEBUG -// #define SLIC3R_TRIANGLEMESH_DEBUG +#if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING) #include "SVG.hpp" #endif @@ -156,7 +160,6 @@ void TriangleMesh::repair() BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished"; } - float TriangleMesh::volume() { if (this->stl.stats.volume == -1) @@ -320,7 +323,7 @@ bool TriangleMesh::has_multiple_patches() const facet_visited[facet_idx] = true; for (int j = 0; j < 3; ++ j) { int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j]; - if (! facet_visited[neighbor_idx]) + if (neighbor_idx != -1 && ! facet_visited[neighbor_idx]) facet_queue[facet_queue_cnt ++] = neighbor_idx; } } @@ -363,7 +366,7 @@ size_t TriangleMesh::number_of_patches() const facet_visited[facet_idx] = true; for (int j = 0; j < 3; ++ j) { int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j]; - if (! facet_visited[neighbor_idx]) + if (neighbor_idx != -1 && ! facet_visited[neighbor_idx]) facet_queue[facet_queue_cnt ++] = neighbor_idx; } } @@ -623,6 +626,20 @@ void TriangleMesh::require_shared_vertices() BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices"; stl_generate_shared_vertices(&(this->stl)); } +#ifdef _DEBUG + // Verify validity of neighborship data. + for (int facet_idx = 0; facet_idx < stl.stats.number_of_facets; ++facet_idx) { + const stl_neighbors &nbr = stl.neighbors_start[facet_idx]; + const int *vertices = stl.v_indices[facet_idx].vertex; + for (int nbr_idx = 0; nbr_idx < 3; ++nbr_idx) { + int nbr_face = this->stl.neighbors_start[facet_idx].neighbor[nbr_idx]; + if (nbr_face != -1) { + assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3]); + assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]); + } + } + } +#endif /* _DEBUG */ BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end"; } @@ -699,13 +716,13 @@ void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type } } // Assign an edge index to the 1st face. - this->facets_edges[edge_i.face * 3 + std::abs(edge_i.face_edge) - 1] = num_edges; + this->facets_edges[edge_i.face * 3 + std::abs(edge_i.face_edge) - 1] = num_edges; if (found) { EdgeToFace &edge_j = edges_map[j]; - this->facets_edges[edge_j.face * 3 + std::abs(edge_j.face_edge) - 1] = num_edges; - // Mark the edge as connected. - edge_j.face = -1; - } + this->facets_edges[edge_j.face * 3 + std::abs(edge_j.face_edge) - 1] = num_edges; + // Mark the edge as connected. + edge_j.face = -1; + } ++ num_edges; if ((i & 0x0ffff) == 0) throw_on_cancel(); @@ -781,13 +798,30 @@ void TriangleMeshSlicer::slice(const std::vector &z, std::vector FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0](2), facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1](2), facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2](2)); printf("z: min = %.2f, max = %.2f\n", min_z, max_z); - #endif + #endif /* SLIC3R_TRIANGLEMESH_DEBUG */ // find layer extents std::vector::const_iterator min_layer, max_layer; min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z - #ifdef SLIC3R_DEBUG + #ifdef SLIC3R_TRIANGLEMESH_DEBUG printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin())); - #endif + #endif /* SLIC3R_TRIANGLEMESH_DEBUG */ for (std::vector::const_iterator it = min_layer; it != max_layer + 1; ++it) { std::vector::size_type layer_idx = it - z.begin(); IntersectionLine il; - if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il)) { + if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il) == TriangleMeshSlicer::Slicing) { boost::lock_guard l(*lines_mutex); if (il.edge_type == feHorizontal) { - // Insert all three edges of the face. + // Insert all marked edges of the face. The marked edges do not share an edge with another horizontal face + // (they may not have a nighbor, or their neighbor is vertical) const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex; const bool reverse = this->mesh->stl.facet_start[facet_idx].normal(2) < 0; - for (int j = 0; j < 3; ++ j) { - int a_id = vertices[j % 3]; - int b_id = vertices[(j+1) % 3]; - if (reverse) - std::swap(a_id, b_id); - const stl_vertex &a = this->v_scaled_shared[a_id]; - const stl_vertex &b = this->v_scaled_shared[b_id]; - il.a(0) = a(0); - il.a(1) = a(1); - il.b(0) = b(0); - il.b(1) = b(1); - il.a_id = a_id; - il.b_id = b_id; - (*lines)[layer_idx].emplace_back(il); - } + for (int j = 0; j < 3; ++ j) + if (il.flags & ((IntersectionLine::EDGE0_NO_NEIGHBOR | IntersectionLine::EDGE0_FOLD) << j)) { + int a_id = vertices[j % 3]; + int b_id = vertices[(j+1) % 3]; + if (reverse) + std::swap(a_id, b_id); + const stl_vertex &a = this->v_scaled_shared[a_id]; + const stl_vertex &b = this->v_scaled_shared[b_id]; + il.a(0) = a(0); + il.a(1) = a(1); + il.b(0) = b(0); + il.b(1) = b(1); + il.a_id = a_id; + il.b_id = b_id; + assert(il.a != il.b); + // This edge will not be used as a seed for loop extraction if it was added due to a fold of two overlapping horizontal faces. + il.set_no_seed((IntersectionLine::EDGE0_FOLD << j) != 0); + (*lines)[layer_idx].emplace_back(il); + } } else (*lines)[layer_idx].emplace_back(il); } @@ -861,7 +900,7 @@ void TriangleMeshSlicer::slice(const std::vector &z, std::vector& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { #ifdef SLIC3R_TRIANGLEMESH_DEBUG - printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]); + printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]); #endif throw_on_cancel(); this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]); @@ -871,23 +910,22 @@ void TriangleMeshSlicer::slice(const std::vector &z, std::vectormesh->stl.v_indices[facet_idx].vertex; int i = (facet.vertex[1](2) == min_z) ? 1 : ((facet.vertex[2](2) == min_z) ? 2 : 0); - for (int j = i; j - i < 3; ++ j) { // loop through facet edges + for (int j = i; j - i < 3; ++j ) { // loop through facet edges int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)]; - const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex; int a_id = vertices[j % 3]; int b_id = vertices[(j+1) % 3]; const stl_vertex &a = this->v_scaled_shared[a_id]; @@ -900,116 +938,279 @@ bool TriangleMeshSlicer::slice_facet( const stl_vertex &v1 = this->v_scaled_shared[vertices[1]]; const stl_vertex &v2 = this->v_scaled_shared[vertices[2]]; bool swap = false; + const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal; + // We may ignore this edge for slicing purposes, but we may still use it for object cutting. + FacetSliceType result = Slicing; + const stl_neighbors &nbr = this->mesh->stl.neighbors_start[facet_idx]; if (min_z == max_z) { // All three vertices are aligned with slice_z. line_out->edge_type = feHorizontal; - if (this->mesh->stl.facet_start[facet_idx].normal(2) < 0) { + // Mark neighbor edges, which do not have a neighbor. + uint32_t edges = 0; + for (int nbr_idx = 0; nbr_idx != 3; ++ nbr_idx) { + // If the neighbor with an edge starting with a vertex idx (nbr_idx - 2) shares no + // opposite face, add it to the edges to process when slicing. + if (nbr.neighbor[nbr_idx] == -1) { + // Mark this edge to be added to the slice. + edges |= (IntersectionLine::EDGE0_NO_NEIGHBOR << nbr_idx); + } +#if 1 + else if (normal(2) > 0) { + // Produce edges for opposite faced overlapping horizontal faces aka folds. + // This method often produces connecting lines (noise) at the cutting plane. + // Produce the edges for the top facing face of the pair of top / bottom facing faces. + + // Index of a neighbor face. + const int nbr_face = nbr.neighbor[nbr_idx]; + const int *nbr_vertices = this->mesh->stl.v_indices[nbr_face].vertex; + int idx_vertex_opposite = nbr_vertices[nbr.which_vertex_not[nbr_idx]]; + const stl_vertex &c2 = this->v_scaled_shared[idx_vertex_opposite]; + if (c2(2) == slice_z) { + // Edge shared by facet_idx and nbr_face. + int a_id = vertices[nbr_idx]; + int b_id = vertices[(nbr_idx + 1) % 3]; + int c1_id = vertices[(nbr_idx + 2) % 3]; + const stl_vertex &a = this->v_scaled_shared[a_id]; + const stl_vertex &b = this->v_scaled_shared[b_id]; + const stl_vertex &c1 = this->v_scaled_shared[c1_id]; + // Verify that the two neighbor faces share a common edge. + assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id); + assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id); + double n1 = (double(c1(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c1(1)) - double(a(1))) * (double(b(0)) - double(a(0))); + double n2 = (double(c2(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c2(1)) - double(a(1))) * (double(b(0)) - double(a(0))); + if (n1 * n2 > 0) + // The two faces overlap. This indicates an invalid mesh geometry (non-manifold), + // but these are the real world objects, and leaving out these edges leads to missing contours. + edges |= (IntersectionLine::EDGE0_FOLD << nbr_idx); + } + } +#endif + } + // Use some edges of this triangle for slicing only if at least one of its edge does not have an opposite face. + result = (edges == 0) ? Cutting : Slicing; + line_out->flags |= edges; + if (normal(2) < 0) { // If normal points downwards this is a bottom horizontal facet so we reverse its point order. swap = true; } - } else if (v0(2) < slice_z || v1(2) < slice_z || v2(2) < slice_z) { - // Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane. - line_out->edge_type = feTop; - swap = true; } else { - // Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane. - line_out->edge_type = feBottom; + // Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane. + int nbr_idx = j % 3; + int nbr_face = nbr.neighbor[nbr_idx]; + // Is the third vertex below the cutting plane? + bool third_below = v0(2) < slice_z || v1(2) < slice_z || v2(2) < slice_z; + // Is this a concave corner? + if (nbr_face == -1) { +#ifdef _DEBUG + printf("Face has no neighbor!\n"); +#endif + } else { + assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id); + assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id); + int idx_vertex_opposite = this->mesh->stl.v_indices[nbr_face].vertex[nbr.which_vertex_not[nbr_idx]]; + const stl_vertex &c = this->v_scaled_shared[idx_vertex_opposite]; + if (c(2) == slice_z) { + double normal_nbr = (double(c(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c(1)) - double(a(1))) * (double(b(0)) - double(a(0))); +#if 0 + if ((normal_nbr < 0) == third_below) { + printf("Flipped normal?\n"); + } +#endif + result = + // A vertical face shares edge with a horizontal face. Verify, whether the shared edge makes a convex or concave corner. + // Unfortunately too often there are flipped normals, which brake our assumption. Let's rather return every edge, + // and leth the code downstream hopefully handle it. + #if 1 + // Ignore concave corners for slicing. + // This method has the unfortunate property, that folds in a horizontal plane create concave corners, + // leading to broken contours, if these concave corners are not replaced by edges of the folds, see above. + ((normal_nbr < 0) == third_below) ? Cutting : Slicing; + #else + // Use concave corners for slicing. This leads to the test 01_trianglemesh.t "slicing a top tangent plane includes its area" failing, + // and rightly so. + Slicing; + #endif + } else { + // For a pair of faces touching exactly at the cutting plane, ignore one of them. An arbitrary rule is to ignore the face with a higher index. + result = (facet_idx < nbr_face) ? Slicing : Cutting; + } + } + if (third_below) { + line_out->edge_type = feTop; + swap = true; + } else + line_out->edge_type = feBottom; } line_out->a = to_2d(swap ? b : a).cast(); line_out->b = to_2d(swap ? a : b).cast(); line_out->a_id = swap ? b_id : a_id; line_out->b_id = swap ? a_id : b_id; - return true; + assert(line_out->a != line_out->b); + return result; } if (a(2) == slice_z) { // Only point a alings with the cutting plane. - points_on_layer[num_points_on_layer ++] = num_points; - IntersectionPoint &point = points[num_points ++]; - point(0) = a(0); - point(1) = a(1); - point.point_id = a_id; + if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) { + point_on_layer = num_points; + IntersectionPoint &point = points[num_points ++]; + point(0) = a(0); + point(1) = a(1); + point.point_id = a_id; + } } else if (b(2) == slice_z) { // Only point b alings with the cutting plane. - points_on_layer[num_points_on_layer ++] = num_points; - IntersectionPoint &point = points[num_points ++]; - point(0) = b(0); - point(1) = b(1); - point.point_id = b_id; + if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) { + point_on_layer = num_points; + IntersectionPoint &point = points[num_points ++]; + point(0) = b(0); + point(1) = b(1); + point.point_id = b_id; + } } else if ((a(2) < slice_z && b(2) > slice_z) || (b(2) < slice_z && a(2) > slice_z)) { // A general case. The face edge intersects the cutting plane. Calculate the intersection point. - IntersectionPoint &point = points[num_points ++]; - point(0) = b(0) + (a(0) - b(0)) * (slice_z - b(2)) / (a(2) - b(2)); - point(1) = b(1) + (a(1) - b(1)) * (slice_z - b(2)) / (a(2) - b(2)); - point.edge_id = edge_id; + assert(a_id != b_id); + // Sort the edge to give a consistent answer. + const stl_vertex *pa = &a; + const stl_vertex *pb = &b; + if (a_id > b_id) { + std::swap(a_id, b_id); + std::swap(pa, pb); + } + IntersectionPoint &point = points[num_points]; + double t = (double(slice_z) - double((*pb)(2))) / (double((*pa)(2)) - double((*pb)(2))); + if (t <= 0.) { + if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) { + point(0) = (*pa)(0); + point(1) = (*pa)(1); + point_on_layer = num_points ++; + point.point_id = a_id; + } + } else if (t >= 1.) { + if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) { + point(0) = (*pb)(0); + point(1) = (*pb)(1); + point_on_layer = num_points ++; + point.point_id = b_id; + } + } else { + point(0) = coord_t(floor(double((*pb)(0)) + (double((*pa)(0)) - double((*pb)(0))) * t + 0.5)); + point(1) = coord_t(floor(double((*pb)(1)) + (double((*pa)(1)) - double((*pb)(1))) * t + 0.5)); + point.edge_id = edge_id; + ++ num_points; + } } } - // We can't have only one point on layer because each vertex gets detected - // twice (once for each edge), and we can't have three points on layer, - // because we assume this code is not getting called for horizontal facets. - assert(num_points_on_layer == 0 || num_points_on_layer == 2); - if (num_points_on_layer > 0) { - assert(points[points_on_layer[0]].point_id == points[points_on_layer[1]].point_id); - assert(num_points == 2 || num_points == 3); - if (num_points < 3) - // This triangle touches the cutting plane with a single vertex. Ignore it. - return false; - // Erase one of the duplicate points. - -- num_points; - for (int i = points_on_layer[1]; i < num_points; ++ i) - points[i] = points[i + 1]; - } - - // Facets must intersect each plane 0 or 2 times. - assert(num_points == 0 || num_points == 2); + // Facets must intersect each plane 0 or 2 times, or it may touch the plane at a single vertex only. + assert(num_points < 3); if (num_points == 2) { - line_out->edge_type = feNone; + line_out->edge_type = feGeneral; line_out->a = (Point)points[1]; line_out->b = (Point)points[0]; line_out->a_id = points[1].point_id; line_out->b_id = points[0].point_id; line_out->edge_a_id = points[1].edge_id; line_out->edge_b_id = points[0].edge_id; - return true; + // Not a zero lenght edge. + //FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t. + //assert(line_out->a != line_out->b); + // The plane cuts at least one edge in a general position. + assert(line_out->a_id == -1 || line_out->b_id == -1); + assert(line_out->edge_a_id != -1 || line_out->edge_b_id != -1); + // General slicing position, use the segment for both slicing and object cutting. +#if 0 + if (line_out->a_id != -1 && line_out->b_id != -1) { + // Solving a degenerate case, where both the intersections snapped to an edge. + // Correctly classify the face as below or above based on the position of the 3rd point. + int i = vertices[0]; + if (i == line_out->a_id || i == line_out->b_id) + i = vertices[1]; + if (i == line_out->a_id || i == line_out->b_id) + i = vertices[2]; + assert(i != line_out->a_id && i != line_out->b_id); + line_out->edge_type = (this->v_scaled_shared[i].z < slice_z) ? feTop : feBottom; + } +#endif + return Slicing; + } + return NoSlice; +} + +//FIXME Should this go away? For valid meshes the function slice_facet() returns Slicing +// and sets edges of vertical triangles to produce only a single edge per pair of neighbor faces. +// So the following code makes only sense now to handle degenerate meshes with more than two faces +// sharing a single edge. +static inline void remove_tangent_edges(std::vector &lines) +{ + std::vector by_vertex_pair; + by_vertex_pair.reserve(lines.size()); + for (IntersectionLine& line : lines) + if (line.edge_type != feGeneral && line.a_id != -1) + // This is a face edge. Check whether there is its neighbor stored in lines. + by_vertex_pair.emplace_back(&line); + auto edges_lower_sorted = [](const IntersectionLine *l1, const IntersectionLine *l2) { + // Sort vertices of l1, l2 lexicographically + int l1a = l1->a_id; + int l1b = l1->b_id; + int l2a = l2->a_id; + int l2b = l2->b_id; + if (l1a > l1b) + std::swap(l1a, l1b); + if (l2a > l2b) + std::swap(l2a, l2b); + // Lexicographical "lower" operator on lexicographically sorted vertices should bring equal edges together when sored. + return l1a < l2a || (l1a == l2a && l1b < l2b); + }; + std::sort(by_vertex_pair.begin(), by_vertex_pair.end(), edges_lower_sorted); + for (auto line = by_vertex_pair.begin(); line != by_vertex_pair.end(); ++ line) { + IntersectionLine &l1 = **line; + if (! l1.skip()) { + // Iterate as long as line and line2 edges share the same end points. + for (auto line2 = line + 1; line2 != by_vertex_pair.end() && ! edges_lower_sorted(*line, *line2); ++ line2) { + // Lines must share the end points. + assert(! edges_lower_sorted(*line, *line2)); + assert(! edges_lower_sorted(*line2, *line)); + IntersectionLine &l2 = **line2; + if (l2.skip()) + continue; + if (l1.a_id == l2.a_id) { + assert(l1.b_id == l2.b_id); + l2.set_skip(); + // If they are both oriented upwards or downwards (like a 'V'), + // then we can remove both edges from this layer since it won't + // affect the sliced shape. + // If one of them is oriented upwards and the other is oriented + // downwards, let's only keep one of them (it doesn't matter which + // one since all 'top' lines were reversed at slicing). + if (l1.edge_type == l2.edge_type) { + l1.set_skip(); + break; + } + } else { + assert(l1.a_id == l2.b_id && l1.b_id == l2.a_id); + // If this edge joins two horizontal facets, remove both of them. + if (l1.edge_type == feHorizontal && l2.edge_type == feHorizontal) { + l1.set_skip(); + l2.set_skip(); + break; + } + } + } + } } - return false; } void TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* loops) const { - // Remove tangent edges. - //FIXME This is O(n^2) in rare cases when many faces intersect the cutting plane. - for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++ line) - if (! line->skip && line->edge_type != feNone) { - // This line is af facet edge. There may be a duplicate line with the same end vertices. - // If the line is is an edge connecting two facets, find another facet edge - // having the same endpoints but in reverse order. - for (IntersectionLines::iterator line2 = line + 1; line2 != lines.end(); ++ line2) - if (! line2->skip && line2->edge_type != feNone) { - // Are these facets adjacent? (sharing a common edge on this layer) - if (line->a_id == line2->a_id && line->b_id == line2->b_id) { - line2->skip = true; - /* if they are both oriented upwards or downwards (like a 'V') - then we can remove both edges from this layer since it won't - affect the sliced shape */ - /* if one of them is oriented upwards and the other is oriented - downwards, let's only keep one of them (it doesn't matter which - one since all 'top' lines were reversed at slicing) */ - if (line->edge_type == line2->edge_type) { - line->skip = true; - break; - } - } else if (line->a_id == line2->b_id && line->b_id == line2->a_id) { - /* if this edge joins two horizontal facets, remove both of them */ - if (line->edge_type == feHorizontal && line2->edge_type == feHorizontal) { - line->skip = true; - line2->skip = true; - break; - } - } - } - } +#if 0 +//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t. +//#ifdef _DEBUG + for (const Line &l : lines) + assert(l.a != l.b); +#endif /* _DEBUG */ + + remove_tangent_edges(lines); struct OpenPolyline { OpenPolyline() {}; @@ -1032,7 +1233,7 @@ void TriangleMeshSlicer::make_loops(std::vector &lines, Polygo by_edge_a_id.reserve(lines.size()); by_a_id.reserve(lines.size()); for (IntersectionLine &line : lines) { - if (! line.skip) { + if (! line.skip()) { if (line.edge_a_id != -1) by_edge_a_id.emplace_back(&line); if (line.a_id != -1) @@ -1049,13 +1250,14 @@ void TriangleMeshSlicer::make_loops(std::vector &lines, Polygo // take first spare line and start a new loop IntersectionLine *first_line = nullptr; for (; it_line_seed != lines.end(); ++ it_line_seed) - if (! it_line_seed->skip) { + if (it_line_seed->is_seed_candidate()) { + //if (! it_line_seed->skip()) { first_line = &(*it_line_seed ++); break; } if (first_line == nullptr) break; - first_line->skip = true; + first_line->set_skip(); Points loop_pts; loop_pts.emplace_back(first_line->a); IntersectionLine *last_line = first_line; @@ -1076,7 +1278,7 @@ void TriangleMeshSlicer::make_loops(std::vector &lines, Polygo if (it_begin != by_edge_a_id.end()) { auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower); for (auto it_line = it_begin; it_line != it_end; ++ it_line) - if (! (*it_line)->skip) { + if (! (*it_line)->skip()) { next_line = *it_line; break; } @@ -1088,7 +1290,7 @@ void TriangleMeshSlicer::make_loops(std::vector &lines, Polygo if (it_begin != by_a_id.end()) { auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower); for (auto it_line = it_begin; it_line != it_end; ++ it_line) - if (! (*it_line)->skip) { + if (! (*it_line)->skip()) { next_line = *it_line; break; } @@ -1119,7 +1321,7 @@ void TriangleMeshSlicer::make_loops(std::vector &lines, Polygo */ loop_pts.emplace_back(next_line->a); last_line = next_line; - next_line->skip = true; + next_line->set_skip(); } } } @@ -1186,12 +1388,12 @@ void TriangleMeshSlicer::make_loops(std::vector &lines, Polygo } } } - if (next_start == nullptr) { - // The current loop could not be closed. Unmark the segment. - opl.consumed = false; - break; - } - // Attach this polyline to the end of the initial polyline. + if (next_start == nullptr) { + // The current loop could not be closed. Unmark the segment. + opl.consumed = false; + break; + } + // Attach this polyline to the end of the initial polyline. if (next_start->start) { auto it = next_start->polyline->points.begin(); std::copy(++ it, next_start->polyline->points.end(), back_inserter(opl.points)); @@ -1211,8 +1413,8 @@ void TriangleMeshSlicer::make_loops(std::vector &lines, Polygo if ((ip1.edge_id != -1 && ip1.edge_id == ip2.edge_id) || (ip1.point_id != -1 && ip1.point_id == ip2.point_id)) { // The current loop is complete. Add it to the output. - /*assert(opl.points.front().point_id == opl.points.back().point_id); - assert(opl.points.front().edge_id == opl.points.back().edge_id);*/ + //assert(opl.points.front().point_id == opl.points.back().point_id); + //assert(opl.points.front().edge_id == opl.points.back().edge_id); // Remove the duplicate last point. opl.points.pop_back(); if (opl.points.size() >= 3) { @@ -1227,9 +1429,9 @@ void TriangleMeshSlicer::make_loops(std::vector &lines, Polygo loops->emplace_back(std::move(opl.points)); } opl.points.clear(); - break; + break; } - // Continue with the current loop. + // Continue with the current loop. } } } @@ -1277,15 +1479,15 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector &l if (slice_idx == -1) // Ignore this hole. continue; - assert(current_contour_area < std::numeric_limits::max() && current_contour_area >= -hole->area()); - (*slices)[slice_idx].holes.emplace_back(std::move(*hole)); + assert(current_contour_area < std::numeric_limits::max() && current_contour_area >= -hole->area()); + (*slices)[slice_idx].holes.emplace_back(std::move(*hole)); } #if 0 // If the input mesh is not valid, the holes may intersect with the external contour. // Rather subtract them from the outer contour. Polygons poly; - for (auto it_slice = slices->begin(); it_slice != slices->end(); ++ it_slice) { + for (auto it_slice = slices->begin(); it_slice != slices->end(); ++ it_slice) { if (it_slice->holes.empty()) { poly.emplace_back(std::move(it_slice->contour)); } else { @@ -1295,7 +1497,7 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector &l it->reverse(); polygons_append(poly, diff(contours, it_slice->holes)); } - } + } // If the input mesh is not valid, the input contours may intersect. *slices = union_ex(poly); #endif @@ -1412,7 +1614,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) // intersect facet with cutting plane IntersectionLine line; - if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line)) { + if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line) != TriangleMeshSlicer::NoSlice) { // Save intersection lines for generating correct triangulations. if (line.edge_type == feTop) { lower_lines.emplace_back(line); diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index ed3e6022dc..f6e0baea98 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -83,7 +83,7 @@ private: enum FacetEdgeType { // A general case, the cutting plane intersect a face at two different edges. - feNone, + feGeneral, // Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane. feTop, // Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane. @@ -117,6 +117,14 @@ public: class IntersectionLine : public Line { public: + IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feGeneral), flags(0) {} + + bool skip() const { return (this->flags & SKIP) != 0; } + void set_skip() { this->flags |= SKIP; } + + bool is_seed_candidate() const { return (this->flags & NO_SEED) == 0 && ! this->skip(); } + void set_no_seed(bool set) { if (set) this->flags |= NO_SEED; else this->flags &= ~NO_SEED; } + // Inherits Point a, b // For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1. // Vertex indices of the line end points. @@ -125,11 +133,23 @@ public: // Source mesh edges of the line end points. int edge_a_id; int edge_b_id; - // feNone, feTop, feBottom, feHorizontal + // feGeneral, feTop, feBottom, feHorizontal FacetEdgeType edge_type; - // Used by TriangleMeshSlicer::make_loops() to skip duplicate edges. - bool skip; - IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feNone), skip(false) {}; + // Used by TriangleMeshSlicer::slice() to skip duplicate edges. + enum { + // Triangle edge added, because it has no neighbor. + EDGE0_NO_NEIGHBOR = 0x001, + EDGE1_NO_NEIGHBOR = 0x002, + EDGE2_NO_NEIGHBOR = 0x004, + // Triangle edge added, because it makes a fold with another horizontal edge. + EDGE0_FOLD = 0x010, + EDGE1_FOLD = 0x020, + EDGE2_FOLD = 0x040, + // The edge cannot be a seed of a greedy loop extraction (folds are not safe to become seeds). + NO_SEED = 0x100, + SKIP = 0x200, + }; + uint32_t flags; }; typedef std::vector IntersectionLines; typedef std::vector IntersectionLinePtrs; @@ -144,7 +164,12 @@ public: void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel); void slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; void slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; - bool slice_facet(float slice_z, const stl_facet &facet, const int facet_idx, + enum FacetSliceType { + NoSlice = 0, + Slicing = 1, + Cutting = 2 + }; + FacetSliceType slice_facet(float slice_z, const stl_facet &facet, const int facet_idx, const float min_z, const float max_z, IntersectionLine *line_out) const; void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const; diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index dd05891dde..0d2df5a0b0 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -9,6 +9,7 @@ namespace Slic3r { extern void set_logging_level(unsigned int level); extern void trace(unsigned int level, const char *message); +extern void disable_multi_threading(); // Set a path with GUI resource files. void set_var_dir(const std::string &path); diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 925e93031c..cf0a19f7af 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -13,6 +13,8 @@ #include #include +#include "Technologies.hpp" + #define SLIC3R_FORK_NAME "Slic3r Prusa Edition" #define SLIC3R_VERSION "1.41.0" #define SLIC3R_BUILD "UNKNOWN" diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 4c2d65605d..46ee65a35a 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -25,6 +25,8 @@ #include #include +#include + namespace Slic3r { static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error; @@ -83,6 +85,14 @@ void trace(unsigned int level, const char *message) (::boost::log::keywords::severity = severity)) << message; } +void disable_multi_threading() +{ + // Disable parallelization so the Shiny profiler works + static tbb::task_scheduler_init *tbb_init = nullptr; + if (tbb_init == nullptr) + tbb_init = new tbb::task_scheduler_init(1); +} + static std::string g_var_dir; void set_var_dir(const std::string &dir) diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index 5e259c3672..4a36b5d7f1 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -11,12 +11,11 @@ #include #include -#include -#include #include +#include +#include #include #include -#include namespace Slic3r { @@ -44,15 +43,6 @@ namespace GUI { PresetBundle* get_preset_bundle(); } -static const PrintObjectStep STEP_SLICE = posSlice; -static const PrintObjectStep STEP_PERIMETERS = posPerimeters; -static const PrintObjectStep STEP_PREPARE_INFILL = posPrepareInfill; -static const PrintObjectStep STEP_INFILL = posInfill; -static const PrintObjectStep STEP_SUPPORTMATERIAL = posSupportMaterial; -static const PrintStep STEP_SKIRT = psSkirt; -static const PrintStep STEP_BRIM = psBrim; -static const PrintStep STEP_WIPE_TOWER = psWipeTower; - AppControllerBoilerplate::ProgresIndicatorPtr AppControllerBoilerplate::global_progress_indicator() { ProgresIndicatorPtr ret; @@ -72,343 +62,8 @@ void AppControllerBoilerplate::global_progress_indicator( pri_data_->m.unlock(); } -#if 0 -void PrintController::make_skirt() -{ - assert(print_ != nullptr); - - // prerequisites - for(auto obj : print_->objects()) make_perimeters(obj); - for(auto obj : print_->objects()) infill(obj); - for(auto obj : print_->objects()) gen_support_material(obj); - - if(!print_->state.is_done(STEP_SKIRT)) { - print_->state.set_started(STEP_SKIRT); - print_->skirt.clear(); - if(print_->has_skirt()) print_->_make_skirt(); - - print_->state.set_done(STEP_SKIRT); - } -} - -void PrintController::make_brim() -{ - assert(print_ != nullptr); - - // prerequisites - for(auto obj : print_->objects()) make_perimeters(obj); - for(auto obj : print_->objects()) infill(obj); - for(auto obj : print_->objects()) gen_support_material(obj); - make_skirt(); - - if(!print_->state.is_done(STEP_BRIM)) { - print_->state.set_started(STEP_BRIM); - - // since this method must be idempotent, we clear brim paths *before* - // checking whether we need to generate them - print_->brim.clear(); - - if(print_->config.brim_width > 0) print_->_make_brim(); - - print_->state.set_done(STEP_BRIM); - } -} - -void PrintController::make_wipe_tower() -{ - assert(print_ != nullptr); - - // prerequisites - for(auto obj : print_->objects()) make_perimeters(obj); - for(auto obj : print_->objects()) infill(obj); - for(auto obj : print_->objects()) gen_support_material(obj); - make_skirt(); - make_brim(); - - if(!print_->state.is_done(STEP_WIPE_TOWER)) { - print_->state.set_started(STEP_WIPE_TOWER); - - // since this method must be idempotent, we clear brim paths *before* - // checking whether we need to generate them - print_->brim.clear(); - - if(print_->has_wipe_tower()) print_->_make_wipe_tower(); - - print_->state.set_done(STEP_WIPE_TOWER); - } -} - -void PrintController::slice(PrintObject *pobj) -{ - assert(pobj != nullptr && print_ != nullptr); - - if(pobj->state.is_done(STEP_SLICE)) return; - - pobj->state.set_started(STEP_SLICE); - - pobj->_slice(); - - auto msg = pobj->_fix_slicing_errors(); - if(!msg.empty()) report_issue(IssueType::WARN, msg); - - // simplify slices if required - if (print_->config.resolution) - pobj->_simplify_slices(scale_(print_->config.resolution)); - - - if(pobj->layers.empty()) - report_issue(IssueType::ERR, - _(L("No layers were detected. You might want to repair your " - "STL file(s) or check their size or thickness and retry")) - ); - - pobj->state.set_done(STEP_SLICE); -} - -void PrintController::make_perimeters(PrintObject *pobj) -{ - assert(pobj != nullptr); - - slice(pobj); - - if (!pobj->state.is_done(STEP_PERIMETERS)) { - pobj->_make_perimeters(); - } -} - -void PrintController::infill(PrintObject *pobj) -{ - assert(pobj != nullptr); - - make_perimeters(pobj); - - if (!pobj->state.is_done(STEP_PREPARE_INFILL)) { - pobj->state.set_started(STEP_PREPARE_INFILL); - - pobj->_prepare_infill(); - - pobj->state.set_done(STEP_PREPARE_INFILL); - } - - pobj->_infill(); -} - -void PrintController::gen_support_material(PrintObject *pobj) -{ - assert(pobj != nullptr); - - // prerequisites - slice(pobj); - - if(!pobj->state.is_done(STEP_SUPPORTMATERIAL)) { - pobj->state.set_started(STEP_SUPPORTMATERIAL); - - pobj->clear_support_layers(); - - if((pobj->config.support_material || pobj->config.raft_layers > 0) - && pobj->layers.size() > 1) { - pobj->_generate_support_material(); - } - - pobj->state.set_done(STEP_SUPPORTMATERIAL); - } -} - -PrintController::PngExportData -PrintController::query_png_export_data(const DynamicPrintConfig& conf) -{ - PngExportData ret; - - auto zippath = query_destination_path("Output zip file", "*.zip", "out"); - - ret.zippath = zippath; - - ret.width_mm = conf.opt_float("display_width"); - ret.height_mm = conf.opt_float("display_height"); - - ret.width_px = conf.opt_int("display_pixels_x"); - ret.height_px = conf.opt_int("display_pixels_y"); - - auto opt_corr = conf.opt("printer_correction"); - - if(opt_corr) { - ret.corr_x = opt_corr->values[0]; - ret.corr_y = opt_corr->values[1]; - ret.corr_z = opt_corr->values[2]; - } - - ret.exp_time_first_s = conf.opt_float("initial_exposure_time"); - ret.exp_time_s = conf.opt_float("exposure_time"); - - return ret; -} - -void PrintController::slice(AppControllerBoilerplate::ProgresIndicatorPtr pri) -{ - auto st = pri->state(); - - Slic3r::trace(3, "Starting the slicing process."); - - pri->update(st+20, _(L("Generating perimeters"))); - for(auto obj : print_->objects()) make_perimeters(obj); - - pri->update(st+60, _(L("Infilling layers"))); - for(auto obj : print_->objects()) infill(obj); - - pri->update(st+70, _(L("Generating support material"))); - for(auto obj : print_->objects()) gen_support_material(obj); - - pri->message_fmt(_(L("Weight: %.1fg, Cost: %.1f")), - print_->total_weight, print_->total_cost); - pri->state(st+85); - - - pri->update(st+88, _(L("Generating skirt"))); - make_skirt(); - - - pri->update(st+90, _(L("Generating brim"))); - make_brim(); - - pri->update(st+95, _(L("Generating wipe tower"))); - make_wipe_tower(); - - pri->update(st+100, _(L("Done"))); - - // time to make some statistics.. - - Slic3r::trace(3, _(L("Slicing process finished."))); -} - -void PrintController::slice() -{ - auto pri = global_progress_indicator(); - if(!pri) pri = create_progress_indicator(100, L("Slicing")); - slice(pri); -} - -void PrintController::slice_to_png() -{ - using Pointf3 = Vec3d; - - auto presetbundle = GUI::get_preset_bundle(); - - assert(presetbundle); - - auto pt = presetbundle->printers.get_selected_preset().printer_technology(); - if(pt != ptSLA) { - report_issue(IssueType::ERR, _("Printer technology is not SLA!"), - _("Error")); - return; - } - - auto conf = presetbundle->full_config(); - conf.validate(); - - auto exd = query_png_export_data(conf); - if(exd.zippath.empty()) return; - - try { - print_->apply_config(conf); - print_->validate(); - } catch(std::exception& e) { - report_issue(IssueType::ERR, e.what(), "Error"); - return; - } - - // TODO: copy the model and work with the copy only - bool correction = false; - if(exd.corr_x != 1.0 || exd.corr_y != 1.0 || exd.corr_z != 1.0) { - correction = true; - print_->invalidate_all_steps(); - - for(auto po : print_->objects()) { - po->model_object()->scale( - Pointf3(exd.corr_x, exd.corr_y, exd.corr_z) - ); - po->model_object()->invalidate_bounding_box(); - po->reload_model_instances(); - po->invalidate_all_steps(); - } - } - - // Turn back the correction scaling on the model. - auto scale_back = [this, correction, exd]() { - if(correction) { // scale the model back - print_->invalidate_all_steps(); - for(auto po : print_->objects()) { - po->model_object()->scale( - Pointf3(1.0/exd.corr_x, 1.0/exd.corr_y, 1.0/exd.corr_z) - ); - po->model_object()->invalidate_bounding_box(); - po->reload_model_instances(); - po->invalidate_all_steps(); - } - } - }; - - auto print_bb = print_->bounding_box(); - Vec2d punsc = unscale(print_bb.size()); - - // If the print does not fit into the print area we should cry about it. - if(px(punsc) > exd.width_mm || py(punsc) > exd.height_mm) { - std::stringstream ss; - - ss << _(L("Print will not fit and will be truncated!")) << "\n" - << _(L("Width needed: ")) << px(punsc) << " mm\n" - << _(L("Height needed: ")) << py(punsc) << " mm\n"; - - if(!report_issue(IssueType::WARN_Q, ss.str(), _(L("Warning")))) { - scale_back(); - return; - } - } - -// std::async(supports_asynch()? std::launch::async : std::launch::deferred, -// [this, exd, scale_back]() -// { - - auto pri = create_progress_indicator( - 200, _(L("Slicing to zipped png files..."))); - - try { - pri->update(0, _(L("Slicing..."))); - slice(pri); - } catch (std::exception& e) { - pri->cancel(); - report_issue(IssueType::ERR, e.what(), _(L("Exception occured"))); - scale_back(); - return; - } - - auto pbak = print_->progressindicator; - print_->progressindicator = pri; - - try { - print_to( *print_, exd.zippath, - exd.width_mm, exd.height_mm, - exd.width_px, exd.height_px, - exd.exp_time_s, exd.exp_time_first_s); - - } catch (std::exception& e) { - pri->cancel(); - report_issue(IssueType::ERR, e.what(), _(L("Exception occured"))); - } - - print_->progressindicator = pbak; - scale_back(); - -// }); -} - -const PrintConfig &PrintController::config() const -{ - return print_->config; -} -#endif - void ProgressIndicator::message_fmt( - const wxString &fmtstr, ...) { + const std::string &fmtstr, ...) { std::stringstream ss; va_list args; va_start(args, fmtstr); @@ -438,6 +93,11 @@ void AppController::arrange_model() { using Coord = libnest2d::TCoord; + if(arranging_.load()) return; + + // to prevent UI reentrancies + arranging_.store(true); + unsigned count = 0; for(auto obj : model_->objects) count += obj->instances.size(); @@ -451,8 +111,8 @@ void AppController::arrange_model() // Set the range of the progress to the object count pind->max(count); - pind->on_cancel([](){ - std::cout << "Cannot be cancelled!" << std::endl; + pind->on_cancel([this](){ + arranging_.store(false); }); } @@ -466,7 +126,7 @@ void AppController::arrange_model() for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1))); - if(pind) pind->update(0, _(L("Arranging objects..."))); + if(pind) pind->update(0, L("Arranging objects...")); try { arr::BedShapeHint hint; @@ -478,24 +138,30 @@ void AppController::arrange_model() bed, hint, false, // create many piles not just one pile - [pind, count](unsigned rem) { + [this, pind, count](unsigned rem) { if(pind) - pind->update(count - rem, _(L("Arranging objects..."))); - }); + pind->update(count - rem, L("Arranging objects...")); + + process_events(); + }, [this] () { return !arranging_.load(); }); } catch(std::exception& e) { std::cerr << e.what() << std::endl; report_issue(IssueType::ERR, - _(L("Could not arrange model objects! " - "Some geometries may be invalid.")), - _(L("Exception occurred"))); + L("Could not arrange model objects! " + "Some geometries may be invalid."), + L("Exception occurred")); } // Restore previous max value if(pind) { pind->max(pmax); - pind->update(0, _(L("Arranging done."))); + pind->update(0, arranging_.load() ? L("Arranging done.") : + L("Arranging canceled.")); + pind->on_cancel(/*remove cancel function*/); } + + arranging_.store(false); } } diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp index 740f9a6301..71472835eb 100644 --- a/xs/src/slic3r/AppController.hpp +++ b/xs/src/slic3r/AppController.hpp @@ -18,6 +18,7 @@ class Print; class PrintObject; class PrintConfig; class ProgressStatusBar; +class DynamicPrintConfig; /** * @brief A boilerplate class for creating application logic. It should provide @@ -48,7 +49,7 @@ public: AppControllerBoilerplate(); ~AppControllerBoilerplate(); - using Path = wxString; + using Path = std::string; using PathList = std::vector; /// Common runtime issue types @@ -69,20 +70,20 @@ public: * @return Returns a list of paths choosed by the user. */ PathList query_destination_paths( - const wxString& title, + const std::string& title, const std::string& extensions) const; /** * @brief Same as query_destination_paths but works for directories only. */ PathList query_destination_dirs( - const wxString& title) const; + const std::string& title) const; /** * @brief Same as query_destination_paths but returns only one path. */ Path query_destination_path( - const wxString& title, + const std::string& title, const std::string& extensions, const std::string& hint = "") const; @@ -97,11 +98,11 @@ public: * title. */ bool report_issue(IssueType issuetype, - const wxString& description, - const wxString& brief); + const std::string& description, + const std::string& brief); bool report_issue(IssueType issuetype, - const wxString& description); + const std::string& description); /** * @brief Return the global progress indicator for the current controller. @@ -152,12 +153,12 @@ protected: */ ProgresIndicatorPtr create_progress_indicator( unsigned statenum, - const wxString& title, - const wxString& firstmsg) const; + const std::string& title, + const std::string& firstmsg) const; ProgresIndicatorPtr create_progress_indicator( unsigned statenum, - const wxString& title) const; + const std::string& title) const; // This is a global progress indicator placeholder. In the Slic3r UI it can // contain the progress indicator on the statusbar. @@ -170,43 +171,6 @@ protected: */ class PrintController: public AppControllerBoilerplate { Print *print_ = nullptr; -protected: - - void make_skirt(); - void make_brim(); - void make_wipe_tower(); - - void make_perimeters(PrintObject *pobj); - void infill(PrintObject *pobj); - void gen_support_material(PrintObject *pobj); - - // Data structure with the png export input data - struct PngExportData { - std::string zippath; // output zip file - unsigned long width_px = 1440; // resolution - rows - unsigned long height_px = 2560; // resolution columns - double width_mm = 68.0, height_mm = 120.0; // dimensions in mm - double exp_time_first_s = 35.0; // first exposure time - double exp_time_s = 8.0; // global exposure time - double corr_x = 1.0; // offsetting in x - double corr_y = 1.0; // offsetting in y - double corr_z = 1.0; // offsetting in y - }; - - // Should display a dialog with the input fields for printing to png - PngExportData query_png_export_data(const DynamicPrintConfig&); - - // The previous export data, to pre-populate the dialog - PngExportData prev_expdata_; - - /** - * @brief Slice one pront object. - * @param pobj The print object. - */ - void slice(PrintObject *pobj); - - void slice(ProgresIndicatorPtr pri); - public: // Must be public for perl to use it @@ -221,15 +185,8 @@ public: return PrintController::Ptr( new PrintController(print) ); } - /** - * @brief Slice the loaded print scene. - */ - void slice(); - - /** - * @brief Slice the print into zipped png files. - */ - void slice_to_png(); + void slice() {} + void slice_to_png() {} const PrintConfig& config() const; }; @@ -253,6 +210,7 @@ public: class AppController: public AppControllerBoilerplate { Model *model_ = nullptr; PrintController::Ptr printctl; + std::atomic arranging_; public: /** @@ -288,7 +246,7 @@ public: * In perl we have a progress indicating status bar on the bottom of the * window which is defined and created in perl. We can pass the ID-s of the * gauge and the statusbar id and make a wrapper implementation of the - * IProgressIndicator interface so we can use this GUI widget from C++. + * ProgressIndicator interface so we can use this GUI widget from C++. * * This function should be called from perl. * diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp index bd2859fceb..4d67d5f66d 100644 --- a/xs/src/slic3r/AppControllerWx.cpp +++ b/xs/src/slic3r/AppControllerWx.cpp @@ -28,16 +28,16 @@ bool AppControllerBoilerplate::supports_asynch() const void AppControllerBoilerplate::process_events() { - wxSafeYield(); + wxYieldIfNeeded(); } AppControllerBoilerplate::PathList AppControllerBoilerplate::query_destination_paths( - const wxString &title, + const std::string &title, const std::string &extensions) const { - wxFileDialog dlg(wxTheApp->GetTopWindow(), title ); + wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) ); dlg.SetWildcard(extensions); dlg.ShowModal(); @@ -53,11 +53,11 @@ AppControllerBoilerplate::query_destination_paths( AppControllerBoilerplate::Path AppControllerBoilerplate::query_destination_path( - const wxString &title, + const std::string &title, const std::string &extensions, const std::string& hint) const { - wxFileDialog dlg(wxTheApp->GetTopWindow(), title ); + wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) ); dlg.SetWildcard(extensions); dlg.SetFilename(hint); @@ -72,8 +72,8 @@ AppControllerBoilerplate::query_destination_path( } bool AppControllerBoilerplate::report_issue(IssueType issuetype, - const wxString &description, - const wxString &brief) + const std::string &description, + const std::string &brief) { auto icon = wxICON_INFORMATION; auto style = wxOK|wxCENTRE; @@ -85,15 +85,15 @@ bool AppControllerBoilerplate::report_issue(IssueType issuetype, case IssueType::FATAL: icon = wxICON_ERROR; } - auto ret = wxMessageBox(description, brief, icon | style); + auto ret = wxMessageBox(_(description), _(brief), icon | style); return ret != wxCANCEL; } bool AppControllerBoilerplate::report_issue( AppControllerBoilerplate::IssueType issuetype, - const wxString &description) + const std::string &description) { - return report_issue(issuetype, description, wxString()); + return report_issue(issuetype, description, std::string()); } wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent); @@ -126,7 +126,9 @@ class GuiProgressIndicator: void _state( unsigned st) { if(!gauge_.IsShown()) gauge_.ShowModal(); Base::state(st); - gauge_.Update(static_cast(st), message_); + if(!gauge_.Update(static_cast(st), message_)) { + cancel(); + } } public: @@ -140,7 +142,8 @@ public: inline GuiProgressIndicator(int range, const wxString& title, const wxString& firstmsg) : gauge_(title, firstmsg, range, wxTheApp->GetTopWindow(), - wxPD_APP_MODAL | wxPD_AUTO_HIDE), + wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT), + message_(firstmsg), range_(range), title_(title) { @@ -152,11 +155,6 @@ public: this, id_); } - virtual void cancel() override { - update(max(), "Abort"); - ProgressIndicator::cancel(); - } - virtual void state(float val) override { state(static_cast(val)); } @@ -171,26 +169,28 @@ public: } else _state(st); } - virtual void message(const wxString & msg) override { - message_ = msg; + virtual void message(const std::string & msg) override { + message_ = _(msg); } - virtual void messageFmt(const wxString& fmt, ...) { + virtual void messageFmt(const std::string& fmt, ...) { va_list arglist; va_start(arglist, fmt); - message_ = wxString::Format(wxString(fmt), arglist); + message_ = wxString::Format(_(fmt), arglist); va_end(arglist); } - virtual void title(const wxString & title) override { - title_ = title; + virtual void title(const std::string & title) override { + title_ = _(title); } }; } AppControllerBoilerplate::ProgresIndicatorPtr AppControllerBoilerplate::create_progress_indicator( - unsigned statenum, const wxString& title, const wxString& firstmsg) const + unsigned statenum, + const std::string& title, + const std::string& firstmsg) const { auto pri = std::make_shared(statenum, title, firstmsg); @@ -203,10 +203,10 @@ AppControllerBoilerplate::create_progress_indicator( } AppControllerBoilerplate::ProgresIndicatorPtr -AppControllerBoilerplate::create_progress_indicator(unsigned statenum, - const wxString &title) const +AppControllerBoilerplate::create_progress_indicator( + unsigned statenum, const std::string &title) const { - return create_progress_indicator(statenum, title, wxString()); + return create_progress_indicator(statenum, title, std::string()); } namespace { @@ -214,7 +214,7 @@ namespace { class Wrapper: public ProgressIndicator, public wxEvtHandler { ProgressStatusBar *sbar_; using Base = ProgressIndicator; - std::string message_; + wxString message_; AppControllerBoilerplate& ctl_; void showProgress(bool show = true) { @@ -271,18 +271,18 @@ public: } } - virtual void message(const wxString & msg) override { - message_ = msg; + virtual void message(const std::string & msg) override { + message_ = _(msg); } - virtual void message_fmt(const wxString& fmt, ...) override { + virtual void message_fmt(const std::string& fmt, ...) override { va_list arglist; va_start(arglist, fmt); - message_ = wxString::Format(fmt, arglist); + message_ = wxString::Format(_(fmt), arglist); va_end(arglist); } - virtual void title(const wxString & /*title*/) override {} + virtual void title(const std::string & /*title*/) override {} virtual void on_cancel(CancelFn fn) override { sbar_->set_cancel_callback(fn); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index a488bfcded..b9f6a59bc0 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -648,7 +648,7 @@ std::vector GLVolumeCollection::load_object( const ModelVolume *model_volume = model_object->volumes[volume_idx]; int extruder_id = -1; - if (!model_volume->modifier) + if (model_volume->is_model_part()) { extruder_id = model_volume->config.has("extruder") ? model_volume->config.option("extruder")->getInt() : 0; if (extruder_id == 0) @@ -661,7 +661,16 @@ std::vector GLVolumeCollection::load_object( volumes_idx.push_back(int(this->volumes.size())); float color[4]; memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); - color[3] = model_volume->modifier ? 0.5f : 1.f; + if (model_volume->is_support_blocker()) { + color[0] = 1.0f; + color[1] = 0.2f; + color[2] = 0.2f; + } else if (model_volume->is_support_enforcer()) { + color[0] = 0.2f; + color[1] = 0.2f; + color[2] = 1.0f; + } + color[3] = model_volume->is_model_part() ? 1.f : 0.5f; this->volumes.emplace_back(new GLVolume(color)); GLVolume &v = *this->volumes.back(); if (use_VBOs) @@ -675,16 +684,20 @@ std::vector GLVolumeCollection::load_object( v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx; v.set_select_group_id(select_by); v.set_drag_group_id(drag_by); - if (!model_volume->modifier) + if (model_volume->is_model_part()) { v.set_convex_hull(model_volume->get_convex_hull()); v.layer_height_texture = layer_height_texture; if (extruder_id != -1) v.extruder_id = extruder_id; } - v.is_modifier = model_volume->modifier; - v.shader_outside_printer_detection_enabled = !model_volume->modifier; + v.is_modifier = ! model_volume->is_model_part(); + v.shader_outside_printer_detection_enabled = model_volume->is_model_part(); +#if ENABLE_MODELINSTANCE_3D_OFFSET + v.set_offset(instance->get_offset()); +#else v.set_offset(Vec3d(instance->offset(0), instance->offset(1), 0.0)); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET v.set_rotation(instance->rotation); v.set_scaling_factor(instance->scaling_factor); } @@ -2164,6 +2177,11 @@ int _3DScene::get_first_volume_id(wxGLCanvas* canvas, int obj_idx) return s_canvas_mgr.get_first_volume_id(canvas, obj_idx); } +int _3DScene::get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx) +{ + return s_canvas_mgr.get_in_object_volume_id(canvas, scene_vol_idx); +} + void _3DScene::reload_scene(wxGLCanvas* canvas, bool force) { s_canvas_mgr.reload_scene(canvas, force); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index b53fd8a235..f2d1c07868 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -577,6 +577,7 @@ public: static std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); static int get_first_volume_id(wxGLCanvas* canvas, int obj_idx); + static int get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx); static void reload_scene(wxGLCanvas* canvas, bool force); diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp index 0f77b1953c..d7307cc320 100644 --- a/xs/src/slic3r/GUI/AppConfig.cpp +++ b/xs/src/slic3r/GUI/AppConfig.cpp @@ -60,6 +60,14 @@ void AppConfig::set_defaults() if (get("remember_output_path").empty()) set("remember_output_path", "1"); + + // Remove legacy window positions/sizes + erase("", "main_frame_maximized"); + erase("", "main_frame_pos"); + erase("", "main_frame_size"); + erase("", "object_settings_maximized"); + erase("", "object_settings_pos"); + erase("", "object_settings_size"); } void AppConfig::load() diff --git a/xs/src/slic3r/GUI/AppConfig.hpp b/xs/src/slic3r/GUI/AppConfig.hpp index b742176ed3..5af635a12c 100644 --- a/xs/src/slic3r/GUI/AppConfig.hpp +++ b/xs/src/slic3r/GUI/AppConfig.hpp @@ -72,6 +72,14 @@ public: bool has(const std::string &key) const { return this->has("", key); } + void erase(const std::string §ion, const std::string &key) + { + auto it = m_storage.find(section); + if (it != m_storage.end()) { + it->second.erase(key); + } + } + void clear_section(const std::string §ion) { m_storage[section].clear(); } diff --git a/xs/src/slic3r/GUI/ConfigWizard.cpp b/xs/src/slic3r/GUI/ConfigWizard.cpp index 9f736ded86..e784d85256 100644 --- a/xs/src/slic3r/GUI/ConfigWizard.cpp +++ b/xs/src/slic3r/GUI/ConfigWizard.cpp @@ -409,11 +409,10 @@ PageFirmware::PageFirmware(ConfigWizard *parent) : void PageFirmware::apply_custom_config(DynamicPrintConfig &config) { - ConfigOptionEnum opt; - auto sel = gcode_picker->GetSelection(); - if (sel != wxNOT_FOUND && opt.deserialize(gcode_picker->GetString(sel).ToStdString())) { - config.set_key_value("gcode_flavor", &opt); + if (sel >= 0 && sel < gcode_opt.enum_labels.size()) { + auto *opt = new ConfigOptionEnum(static_cast(sel)); + config.set_key_value("gcode_flavor", opt); } } @@ -871,10 +870,11 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) : // If the screen is smaller, resize wizrad to match, which will enable scrollbars. auto wizard_size = GetSize(); unsigned width, height; - GUI::get_current_screen_size(width, height); - wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN))); - wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN))); - SetMinSize(wizard_size); + if (GUI::get_current_screen_size(this, width, height)) { + wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN))); + wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN))); + SetMinSize(wizard_size); + } Fit(); p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_prev(); }); diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp index d0cd9f8cf6..d5ac64d900 100644 --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp +++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp @@ -367,7 +367,7 @@ void FirmwareDialog::priv::wait_for_mmu_bootloader(unsigned retries) auto ports = Utils::scan_serial_ports_extended(); ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) { - return port.id_vendor != USB_VID_PRUSA && port.id_product != USB_PID_MMU_BOOT; + return port.id_vendor != USB_VID_PRUSA || port.id_product != USB_PID_MMU_BOOT; }), ports.end()); if (ports.size() == 1) { @@ -390,23 +390,22 @@ void FirmwareDialog::priv::mmu_reboot(const SerialPortInfo &port) void FirmwareDialog::priv::lookup_port_mmu() { + static const auto msg_not_found = + "The Multi Material Control device was not found.\n" + "If the device is connected, please press the Reset button next to the USB connector ..."; + BOOST_LOG_TRIVIAL(info) << "Flashing MMU 2.0, looking for VID/PID 0x2c99/3 or 0x2c99/4 ..."; auto ports = Utils::scan_serial_ports_extended(); ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) { - return port.id_vendor != USB_VID_PRUSA && + return port.id_vendor != USB_VID_PRUSA || port.id_product != USB_PID_MMU_BOOT && port.id_product != USB_PID_MMU_APP; }), ports.end()); if (ports.size() == 0) { BOOST_LOG_TRIVIAL(info) << "MMU 2.0 device not found, asking the user to press Reset and waiting for the device to show up ..."; - - queue_status(_(L( - "The Multi Material Control device was not found.\n" - "If the device is connected, please press the Reset button next to the USB connector ..." - ))); - + queue_status(_(L(msg_not_found))); wait_for_mmu_bootloader(30); } else if (ports.size() > 1) { BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found"; @@ -417,6 +416,13 @@ void FirmwareDialog::priv::lookup_port_mmu() BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/4 at `%1%`, rebooting the device ...") % ports[0].port; mmu_reboot(ports[0]); wait_for_mmu_bootloader(10); + + if (! port) { + // The device in bootloader mode was not found, inform the user and wait some more... + BOOST_LOG_TRIVIAL(info) << "MMU 2.0 bootloader device not found after reboot, asking the user to press Reset and waiting for the device to show up ..."; + queue_status(_(L(msg_not_found))); + wait_for_mmu_bootloader(30); + } } else { port = ports[0]; } @@ -702,7 +708,8 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) : panel->SetSizer(vsizer); auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:"))); - p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY); + p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY, wxEmptyString, wxFileSelectorPromptStr, + "Hex files (*.hex)|*.hex|All files|*.*"); auto *label_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:"))); p->port_picker = new wxComboBox(panel, wxID_ANY); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index ae42f014b9..cb32509163 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1142,8 +1142,10 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) if (!gizmo->init()) return false; +#if !ENABLE_MODELINSTANCE_3D_OFFSET // temporary disable z grabber gizmo->disable_grabber(2); +#endif // !ENABLE_MODELINSTANCE_3D_OFFSET m_gizmos.insert(GizmosMap::value_type(Move, gizmo)); @@ -2377,7 +2379,11 @@ void GLCanvas3D::update_gizmos_data() ModelInstance* model_instance = model_object->instances[0]; if (model_instance != nullptr) { +#if ENABLE_MODELINSTANCE_3D_OFFSET + m_gizmos.set_position(model_instance->get_offset()); +#else m_gizmos.set_position(Vec3d(model_instance->offset(0), model_instance->offset(1), 0.0)); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET m_gizmos.set_scale(model_instance->scaling_factor); m_gizmos.set_angle_z(model_instance->rotation); m_gizmos.set_flattening_data(model_object); @@ -2495,6 +2501,11 @@ int GLCanvas3D::get_first_volume_id(int obj_idx) const return -1; } +int GLCanvas3D::get_in_object_volume_id(int scene_vol_idx) const +{ + return ((0 <= scene_vol_idx) && (scene_vol_idx < (int)m_volumes.volumes.size())) ? m_volumes.volumes[scene_vol_idx]->volume_idx() : -1; +} + void GLCanvas3D::reload_scene(bool force) { if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr)) @@ -5361,8 +5372,12 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) ModelObject* model_object = m_model->objects[obj_idx]; if (model_object != nullptr) { +#if ENABLE_MODELINSTANCE_3D_OFFSET + model_object->instances[instance_idx]->set_offset(volume->get_offset()); +#else const Vec3d& offset = volume->get_offset(); model_object->instances[instance_idx]->offset = Vec2d(offset(0), offset(1)); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET model_object->invalidate_bounding_box(); update_position_values(); object_moved = true; @@ -5409,6 +5424,7 @@ void GLCanvas3D::_on_select(int volume_idx, int object_idx) } m_on_select_object_callback.call(obj_id, vol_id); + Slic3r::GUI::select_current_volume(obj_id, vol_id); } std::vector GLCanvas3D::_parse_colors(const std::vector& colors) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index a200e7fdbe..528f73fc1e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -105,7 +105,6 @@ class GLCanvas3D void reset() { first_volumes.clear(); } }; -public: struct Camera { enum EType : unsigned char @@ -441,7 +440,6 @@ public: void render(const GLCanvas3D& canvas) const; }; -private: wxGLCanvas* m_canvas; wxGLContext* m_context; LegendTexture m_legend_texture; @@ -605,6 +603,7 @@ public: std::vector load_object(const Model& model, int obj_idx); int get_first_volume_id(int obj_idx) const; + int get_in_object_volume_id(int scene_vol_idx) const; void reload_scene(bool force); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 3445d4b652..495f494250 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -548,6 +548,12 @@ int GLCanvas3DManager::get_first_volume_id(wxGLCanvas* canvas, int obj_idx) cons return (it != m_canvases.end()) ? it->second->get_first_volume_id(obj_idx) : -1; } +int GLCanvas3DManager::get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_in_object_volume_id(scene_vol_idx) : -1; +} + void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index b808c022e9..4922b61714 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -138,6 +138,7 @@ public: std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); int get_first_volume_id(wxGLCanvas* canvas, int obj_idx) const; + int get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx) const; void reload_scene(wxGLCanvas* canvas, bool force); diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 4aa5ab32f9..e23958c1d1 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -20,90 +20,91 @@ static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f namespace Slic3r { namespace GUI { - // returns the intersection of the given ray with the plane parallel to plane XY and passing through the given center - // coordinates are local to the plane - Vec3d intersection_on_plane_xy(const Linef3& ray, const Vec3d& center) +// returns the intersection of the given ray with the plane parallel to plane XY and passing through the given center +// coordinates are local to the plane +Vec3d intersection_on_plane_xy(const Linef3& ray, const Vec3d& center) +{ + Transform3d m = Transform3d::Identity(); + m.translate(-center); + Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0)); + return Vec3d(mouse_pos_2d(0), mouse_pos_2d(1), 0.0); +} + +// returns the intersection of the given ray with the plane parallel to plane XZ and passing through the given center +// coordinates are local to the plane +Vec3d intersection_on_plane_xz(const Linef3& ray, const Vec3d& center) +{ + Transform3d m = Transform3d::Identity(); + m.rotate(Eigen::AngleAxisd(-0.5 * (double)PI, Vec3d::UnitX())); + m.translate(-center); + Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0)); + return Vec3d(mouse_pos_2d(0), 0.0, mouse_pos_2d(1)); +} + +// returns the intersection of the given ray with the plane parallel to plane YZ and passing through the given center +// coordinates are local to the plane +Vec3d intersection_on_plane_yz(const Linef3& ray, const Vec3d& center) +{ + Transform3d m = Transform3d::Identity(); + m.rotate(Eigen::AngleAxisd(-0.5f * (double)PI, Vec3d::UnitY())); + m.translate(-center); + Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0)); + + return Vec3d(0.0, mouse_pos_2d(1), -mouse_pos_2d(0)); +} + +// return an index: +// 0 for plane XY +// 1 for plane XZ +// 2 for plane YZ +// which indicates which plane is best suited for intersecting the given unit vector +// giving precedence to the plane with the given index +unsigned int select_best_plane(const Vec3d& unit_vector, unsigned int preferred_plane) +{ + unsigned int ret = preferred_plane; + + // 1st checks if the given vector is not parallel to the given preferred plane + double dot_to_normal = 0.0; + switch (ret) { - Transform3d m = Transform3d::Identity(); - m.translate(-center); - Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0)); - return Vec3d(mouse_pos_2d(0), mouse_pos_2d(1), 0.0); + case 0: // plane xy + { + dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitZ())); + break; + } + case 1: // plane xz + { + dot_to_normal = std::abs(unit_vector.dot(-Vec3d::UnitY())); + break; + } + case 2: // plane yz + { + dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitX())); + break; + } + default: + { + break; + } } - // returns the intersection of the given ray with the plane parallel to plane XZ and passing through the given center - // coordinates are local to the plane - Vec3d intersection_on_plane_xz(const Linef3& ray, const Vec3d& center) + // if almost parallel, select the plane whose normal direction is closest to the given vector direction, + // otherwise return the given preferred plane index + if (dot_to_normal < 0.1) { - Transform3d m = Transform3d::Identity(); - m.rotate(Eigen::AngleAxisd(-0.5 * (double)PI, Vec3d::UnitX())); - m.translate(-center); - Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0)); - return Vec3d(mouse_pos_2d(0), 0.0, mouse_pos_2d(1)); + typedef std::map ProjsMap; + ProjsMap projs_map; + projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitZ())), 0)); // plane xy + projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(-Vec3d::UnitY())), 1)); // plane xz + projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitX())), 2)); // plane yz + ret = projs_map.rbegin()->second; } - // returns the intersection of the given ray with the plane parallel to plane YZ and passing through the given center - // coordinates are local to the plane - Vec3d intersection_on_plane_yz(const Linef3& ray, const Vec3d& center) - { - Transform3d m = Transform3d::Identity(); - m.rotate(Eigen::AngleAxisd(-0.5f * (double)PI, Vec3d::UnitY())); - m.translate(-center); - Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0)); - - return Vec3d(0.0, mouse_pos_2d(1), -mouse_pos_2d(0)); - } - - // return an index: - // 0 for plane XY - // 1 for plane XZ - // 2 for plane YZ - // which indicates which plane is best suited for intersecting the given unit vector - // giving precedence to the plane with the given index - unsigned int select_best_plane(const Vec3d& unit_vector, unsigned int preferred_plane) - { - unsigned int ret = preferred_plane; - - // 1st checks if the given vector is not parallel to the given preferred plane - double dot_to_normal = 0.0; - switch (ret) - { - case 0: // plane xy - { - dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitZ())); - break; - } - case 1: // plane xz - { - dot_to_normal = std::abs(unit_vector.dot(-Vec3d::UnitY())); - break; - } - case 2: // plane yz - { - dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitX())); - break; - } - default: - { - break; - } - } - - // if almost parallel, select the plane whose normal direction is closest to the given vector direction, - // otherwise return the given preferred plane index - if (dot_to_normal < 0.1) - { - typedef std::map ProjsMap; - ProjsMap projs_map; - projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitZ())), 0)); // plane xy - projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(-Vec3d::UnitY())), 1)); // plane xz - projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitX())), 2)); // plane yz - ret = projs_map.rbegin()->second; - } - - return ret; - } + return ret; +} - const float GLGizmoBase::Grabber::HalfSize = 2.0f; +const float GLGizmoBase::Grabber::SizeFactor = 0.025f; +const float GLGizmoBase::Grabber::MinHalfSize = 1.5f; const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f; GLGizmoBase::Grabber::Grabber() @@ -117,7 +118,7 @@ GLGizmoBase::Grabber::Grabber() color[2] = 1.0f; } -void GLGizmoBase::Grabber::render(bool hover) const +void GLGizmoBase::Grabber::render(bool hover, const BoundingBoxf3& box) const { float render_color[3]; if (hover) @@ -129,12 +130,15 @@ void GLGizmoBase::Grabber::render(bool hover) const else ::memcpy((void*)render_color, (const void*)color, 3 * sizeof(float)); - render(render_color, true); + render(box, render_color, true); } -void GLGizmoBase::Grabber::render(const float* render_color, bool use_lighting) const +void GLGizmoBase::Grabber::render(const BoundingBoxf3& box, const float* render_color, bool use_lighting) const { - float half_size = dragging ? HalfSize * DraggingScaleFactor : HalfSize; + float max_size = (float)box.max_size(); + float half_size = dragging ? max_size * SizeFactor * DraggingScaleFactor : max_size * SizeFactor; + half_size = std::max(half_size, MinHalfSize); + if (use_lighting) ::glEnable(GL_LIGHTING); @@ -291,16 +295,16 @@ float GLGizmoBase::picking_color_component(unsigned int id) const return (float)color / 255.0f; } -void GLGizmoBase::render_grabbers() const +void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const { for (int i = 0; i < (int)m_grabbers.size(); ++i) { if (m_grabbers[i].enabled) - m_grabbers[i].render(m_hover_id == i); + m_grabbers[i].render((m_hover_id == i), box); } } -void GLGizmoBase::render_grabbers_for_picking() const +void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const { for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) { @@ -309,7 +313,7 @@ void GLGizmoBase::render_grabbers_for_picking() const m_grabbers[i].color[0] = 1.0f; m_grabbers[i].color[1] = 1.0f; m_grabbers[i].color[2] = picking_color_component(i); - m_grabbers[i].render_for_picking(); + m_grabbers[i].render_for_picking(box); } } } @@ -440,7 +444,7 @@ void GLGizmoRotate::on_render(const BoundingBoxf3& box) const if (m_hover_id != -1) render_angle(); - render_grabber(); + render_grabber(box); ::glPopMatrix(); } @@ -452,7 +456,7 @@ void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const ::glPushMatrix(); transform_to_local(); - render_grabbers_for_picking(); + render_grabbers_for_picking(box); ::glPopMatrix(); } @@ -544,7 +548,7 @@ void GLGizmoRotate::render_angle() const ::glEnd(); } -void GLGizmoRotate::render_grabber() const +void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const { double grabber_radius = (double)(m_radius + GrabberOffset); m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0); @@ -558,7 +562,7 @@ void GLGizmoRotate::render_grabber() const ::glEnd(); ::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 3 * sizeof(float)); - render_grabbers(); + render_grabbers(box); } void GLGizmoRotate::transform_to_local() const @@ -689,6 +693,7 @@ void GLGizmoRotate3D::on_render(const BoundingBoxf3& box) const } const float GLGizmoScale3D::Offset = 5.0f; +const Vec3d GLGizmoScale3D::OffsetVec = (double)GLGizmoScale3D::Offset * Vec3d::Ones(); GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent) : GLGizmoBase(parent) @@ -738,7 +743,7 @@ void GLGizmoScale3D::on_start_dragging(const BoundingBoxf3& box) { m_starting_drag_position = m_grabbers[m_hover_id].center; m_show_starting_box = true; - m_starting_box = box; + m_starting_box = BoundingBoxf3(box.min - OffsetVec, box.max + OffsetVec); } } @@ -772,9 +777,7 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const ::glEnable(GL_DEPTH_TEST); - Vec3d offset_vec = (double)Offset * Vec3d::Ones(); - - m_box = BoundingBoxf3(box.min - offset_vec, box.max + offset_vec); + m_box = BoundingBoxf3(box.min - OffsetVec, box.max + OffsetVec); const Vec3d& center = m_box.center(); // x axis @@ -829,7 +832,7 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const render_grabbers_connection(4, 5); } // draw grabbers - render_grabbers(); + render_grabbers(m_box); } else if ((m_hover_id == 0) || (m_hover_id == 1)) { @@ -846,8 +849,8 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const ::glColor3fv(m_grabbers[0].color); render_grabbers_connection(0, 1); // draw grabbers - m_grabbers[0].render(true); - m_grabbers[1].render(true); + m_grabbers[0].render(true, m_box); + m_grabbers[1].render(true, m_box); } else if ((m_hover_id == 2) || (m_hover_id == 3)) { @@ -864,8 +867,8 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const ::glColor3fv(m_grabbers[2].color); render_grabbers_connection(2, 3); // draw grabbers - m_grabbers[2].render(true); - m_grabbers[3].render(true); + m_grabbers[2].render(true, m_box); + m_grabbers[3].render(true, m_box); } else if ((m_hover_id == 4) || (m_hover_id == 5)) { @@ -882,8 +885,8 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const ::glColor3fv(m_grabbers[4].color); render_grabbers_connection(4, 5); // draw grabbers - m_grabbers[4].render(true); - m_grabbers[5].render(true); + m_grabbers[4].render(true, m_box); + m_grabbers[5].render(true, m_box); } else if (m_hover_id >= 6) { @@ -899,7 +902,7 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const // draw grabbers for (int i = 6; i < 10; ++i) { - m_grabbers[i].render(true); + m_grabbers[i].render(true, m_box); } } } @@ -908,7 +911,7 @@ void GLGizmoScale3D::on_render_for_picking(const BoundingBoxf3& box) const { ::glDisable(GL_DEPTH_TEST); - render_grabbers_for_picking(); + render_grabbers_for_picking(box); } void GLGizmoScale3D::render_box(const BoundingBoxf3& box) const @@ -1029,6 +1032,7 @@ GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent) , m_position(Vec3d::Zero()) , m_starting_drag_position(Vec3d::Zero()) , m_starting_box_center(Vec3d::Zero()) + , m_starting_box_bottom_center(Vec3d::Zero()) { } @@ -1062,17 +1066,19 @@ void GLGizmoMove3D::on_start_dragging(const BoundingBoxf3& box) { m_starting_drag_position = m_grabbers[m_hover_id].center; m_starting_box_center = box.center(); + m_starting_box_bottom_center = box.center(); + m_starting_box_bottom_center(2) = box.min(2); } } void GLGizmoMove3D::on_update(const Linef3& mouse_ray) { if (m_hover_id == 0) - m_position(0) = 2.0 * m_starting_box_center(0) + calc_displacement(1, mouse_ray) - m_starting_drag_position(0); + m_position(0) = 2.0 * m_starting_box_center(0) + calc_projection(X, 1, mouse_ray) - m_starting_drag_position(0); else if (m_hover_id == 1) - m_position(1) = 2.0 * m_starting_box_center(1) + calc_displacement(2, mouse_ray) - m_starting_drag_position(1); + m_position(1) = 2.0 * m_starting_box_center(1) + calc_projection(Y, 2, mouse_ray) - m_starting_drag_position(1); else if (m_hover_id == 2) - m_position(2) = 2.0 * m_starting_box_center(2) + calc_displacement(1, mouse_ray) - m_starting_drag_position(2); + m_position(2) = 2.0 * m_starting_box_bottom_center(2) + calc_projection(Z, 1, mouse_ray) - m_starting_drag_position(2); } void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const @@ -1118,7 +1124,7 @@ void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const } // draw grabbers - render_grabbers(); + render_grabbers(box); } else { @@ -1130,7 +1136,7 @@ void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const ::glEnd(); // draw grabber - m_grabbers[m_hover_id].render(true); + m_grabbers[m_hover_id].render(true, box); } } @@ -1138,43 +1144,43 @@ void GLGizmoMove3D::on_render_for_picking(const BoundingBoxf3& box) const { ::glDisable(GL_DEPTH_TEST); - render_grabbers_for_picking(); + render_grabbers_for_picking(box); } -double GLGizmoMove3D::calc_displacement(unsigned int preferred_plane_id, const Linef3& mouse_ray) const +double GLGizmoMove3D::calc_projection(Axis axis, unsigned int preferred_plane_id, const Linef3& mouse_ray) const { - double displacement = 0.0; + double projection = 0.0; - Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; + Vec3d starting_vec = (axis == Z) ? m_starting_drag_position - m_starting_box_bottom_center : m_starting_drag_position - m_starting_box_center; double len_starting_vec = starting_vec.norm(); if (len_starting_vec == 0.0) - return displacement; + return projection; Vec3d starting_vec_dir = starting_vec.normalized(); Vec3d mouse_dir = mouse_ray.unit_vector(); unsigned int plane_id = select_best_plane(mouse_dir, preferred_plane_id); - switch (plane_id) + switch (plane_id) { case 0: { - displacement = starting_vec_dir.dot(intersection_on_plane_xy(mouse_ray, m_starting_box_center)); + projection = starting_vec_dir.dot(intersection_on_plane_xy(mouse_ray, (axis == Z) ? m_starting_box_bottom_center : m_starting_box_center)); break; } case 1: { - displacement = starting_vec_dir.dot(intersection_on_plane_xz(mouse_ray, m_starting_box_center)); + projection = starting_vec_dir.dot(intersection_on_plane_xz(mouse_ray, (axis == Z) ? m_starting_box_bottom_center : m_starting_box_center)); break; } case 2: { - displacement = starting_vec_dir.dot(intersection_on_plane_yz(mouse_ray, m_starting_box_center)); + projection = starting_vec_dir.dot(intersection_on_plane_yz(mouse_ray, (axis == Z) ? m_starting_box_bottom_center : m_starting_box_center)); break; } } - return displacement; + return projection; } GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent) @@ -1230,10 +1236,19 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const else ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f); +#if ENABLE_MODELINSTANCE_3D_OFFSET + for (Vec3d offset : m_instances_positions) { + offset += dragged_offset; +#else for (Vec2d offset : m_instances_positions) { offset += to_2d(dragged_offset); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET ::glPushMatrix(); +#if ENABLE_MODELINSTANCE_3D_OFFSET + ::glTranslated(offset(0), offset(1), offset(2)); +#else ::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET ::glBegin(GL_POLYGON); for (const Vec3d& vertex : m_planes[i].vertices) ::glVertex3f((GLfloat)vertex(0), (GLfloat)vertex(1), (GLfloat)vertex(2)); @@ -1252,9 +1267,17 @@ void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const for (unsigned int i = 0; i < m_planes.size(); ++i) { ::glColor3f(1.0f, 1.0f, picking_color_component(i)); +#if ENABLE_MODELINSTANCE_3D_OFFSET + for (const Vec3d& offset : m_instances_positions) { +#else for (const Vec2d& offset : m_instances_positions) { +#endif // ENABLE_MODELINSTANCE_3D_OFFSET ::glPushMatrix(); +#if ENABLE_MODELINSTANCE_3D_OFFSET + ::glTranslated(offset(0), offset(1), offset(2)); +#else ::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET ::glBegin(GL_POLYGON); for (const Vec3d& vertex : m_planes[i].vertices) ::glVertex3f((GLfloat)vertex(0), (GLfloat)vertex(1), (GLfloat)vertex(2)); @@ -1272,7 +1295,11 @@ void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) if (m_model_object && !m_model_object->instances.empty()) { m_instances_positions.clear(); for (const auto* instance : m_model_object->instances) +#if ENABLE_MODELINSTANCE_3D_OFFSET + m_instances_positions.emplace_back(instance->get_offset()); +#else m_instances_positions.emplace_back(instance->offset); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET } if (is_plane_update_necessary()) diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index 8760b1c7df..2430b5af54 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -22,7 +22,8 @@ class GLGizmoBase protected: struct Grabber { - static const float HalfSize; + static const float SizeFactor; + static const float MinHalfSize; static const float DraggingScaleFactor; Vec3d center; @@ -33,11 +34,11 @@ protected: Grabber(); - void render(bool hover) const; - void render_for_picking() const { render(color, false); } + void render(bool hover, const BoundingBoxf3& box) const; + void render_for_picking(const BoundingBoxf3& box) const { render(box, color, false); } private: - void render(const float* render_color, bool use_lighting) const; + void render(const BoundingBoxf3& box, const float* render_color, bool use_lighting) const; void render_face(float half_size) const; }; @@ -109,8 +110,8 @@ protected: virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0; float picking_color_component(unsigned int id) const; - void render_grabbers() const; - void render_grabbers_for_picking() const; + void render_grabbers(const BoundingBoxf3& box) const; + void render_grabbers_for_picking(const BoundingBoxf3& box) const; void set_tooltip(const std::string& tooltip) const; std::string format(float value, unsigned int decimals) const; @@ -163,7 +164,7 @@ private: void render_snap_radii() const; void render_reference_radius() const; void render_angle() const; - void render_grabber() const; + void render_grabber(const BoundingBoxf3& box) const; void transform_to_local() const; // returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate @@ -234,6 +235,7 @@ protected: class GLGizmoScale3D : public GLGizmoBase { static const float Offset; + static const Vec3d OffsetVec; mutable BoundingBoxf3 m_box; @@ -285,6 +287,7 @@ class GLGizmoMove3D : public GLGizmoBase Vec3d m_position; Vec3d m_starting_drag_position; Vec3d m_starting_box_center; + Vec3d m_starting_box_bottom_center; public: explicit GLGizmoMove3D(GLCanvas3D& parent); @@ -300,7 +303,7 @@ protected: virtual void on_render_for_picking(const BoundingBoxf3& box) const; private: - double calc_displacement(unsigned int preferred_plane_id, const Linef3& mouse_ray) const; + double calc_projection(Axis axis, unsigned int preferred_plane_id, const Linef3& mouse_ray) const; }; class GLGizmoFlatten : public GLGizmoBase @@ -326,7 +329,11 @@ private: SourceDataSummary m_source_data; std::vector m_planes; +#if ENABLE_MODELINSTANCE_3D_OFFSET + Pointf3s m_instances_positions; +#else std::vector m_instances_positions; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET Vec3d m_starting_center; const ModelObject* m_model_object = nullptr; diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 7ce858aec1..decdb5691d 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #if __APPLE__ #import @@ -121,7 +122,6 @@ wxPanel *g_wxPlater = nullptr; AppConfig *g_AppConfig = nullptr; PresetBundle *g_PresetBundle= nullptr; PresetUpdater *g_PresetUpdater = nullptr; -_3DScene *g_3DScene = nullptr; wxColour g_color_label_modified; wxColour g_color_label_sys; wxColour g_color_label_default; @@ -141,14 +141,9 @@ wxButton* g_wiping_dialog_button = nullptr; //showed/hided controls according to the view mode wxWindow *g_right_panel = nullptr; wxBoxSizer *g_frequently_changed_parameters_sizer = nullptr; -wxBoxSizer *g_expert_mode_part_sizer = nullptr; -wxBoxSizer *g_scrolled_window_sizer = nullptr; +wxBoxSizer *g_info_sizer = nullptr; wxBoxSizer *g_object_list_sizer = nullptr; -wxButton *g_btn_export_gcode = nullptr; -wxButton *g_btn_export_stl = nullptr; -wxButton *g_btn_reslice = nullptr; -wxButton *g_btn_print = nullptr; -wxButton *g_btn_send_gcode = nullptr; +std::vector g_buttons; wxStaticBitmap *g_manifold_warning_icon = nullptr; bool g_show_print_info = false; bool g_show_manifold_warning_icon = false; @@ -241,27 +236,36 @@ void set_preset_updater(PresetUpdater *updater) g_PresetUpdater = updater; } -void set_3DScene(_3DScene *scene) +enum ActionButtons { - g_3DScene = scene; -} + abExportGCode, + abReslice, + abPrint, + abSendGCode, +}; -void set_objects_from_perl( wxWindow* parent, wxBoxSizer *frequently_changed_parameters_sizer, - wxBoxSizer *expert_mode_part_sizer, wxBoxSizer *scrolled_window_sizer, +void set_objects_from_perl( wxWindow* parent, + wxBoxSizer *frequently_changed_parameters_sizer, + wxBoxSizer *info_sizer, wxButton *btn_export_gcode, - wxButton *btn_export_stl, wxButton *btn_reslice, - wxButton *btn_print, wxButton *btn_send_gcode, + wxButton *btn_reslice, + wxButton *btn_print, + wxButton *btn_send_gcode, wxStaticBitmap *manifold_warning_icon) { - g_right_panel = parent; + g_right_panel = parent->GetParent(); g_frequently_changed_parameters_sizer = frequently_changed_parameters_sizer; - g_expert_mode_part_sizer = expert_mode_part_sizer; - g_scrolled_window_sizer = scrolled_window_sizer; - g_btn_export_gcode = btn_export_gcode; - g_btn_export_stl = btn_export_stl; - g_btn_reslice = btn_reslice; - g_btn_print = btn_print; - g_btn_send_gcode = btn_send_gcode; + g_info_sizer = info_sizer; + + g_buttons.push_back(btn_export_gcode); + g_buttons.push_back(btn_reslice); + g_buttons.push_back(btn_print); + g_buttons.push_back(btn_send_gcode); + + // Update font style for buttons + for (auto btn : g_buttons) + btn->SetFont(bold_font()); + g_manifold_warning_icon = manifold_warning_icon; } @@ -273,6 +277,15 @@ void set_show_print_info(bool show) void set_show_manifold_warning_icon(bool show) { g_show_manifold_warning_icon = show; + if (!g_manifold_warning_icon) + return; + + // update manifold_warning_icon showing + if (show && !g_info_sizer->IsShown(static_cast(0))) + g_show_manifold_warning_icon = false; + + g_manifold_warning_icon->Show(g_show_manifold_warning_icon); + g_manifold_warning_icon->GetParent()->Layout(); } void set_objects_list_sizer(wxBoxSizer *objects_list_sizer){ @@ -963,12 +976,11 @@ wxString from_u8(const std::string &str) return wxString::FromUTF8(str.c_str()); } -void add_expert_mode_part( wxWindow* parent, wxBoxSizer* sizer, - Model &model, - int event_object_selection_changed, - int event_object_settings_changed, - int event_remove_object, - int event_update_scene) +void set_model_events_from_perl(Model &model, + int event_object_selection_changed, + int event_object_settings_changed, + int event_remove_object, + int event_update_scene) { set_event_object_selection_changed(event_object_selection_changed); set_event_object_settings_changed(event_object_settings_changed); @@ -1105,7 +1117,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl }; optgroup->append_line(line); - sizer->Add(optgroup->sizer, 0, wxEXPAND | wxBOTTOM, 2); + sizer->Add(optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT, 2); m_optgroups.push_back(optgroup);// ogFrequentlyChangingParameters @@ -1132,24 +1144,23 @@ void show_frequently_changed_parameters(bool show) void show_buttons(bool show) { - g_btn_export_stl->Show(show); - g_btn_reslice->Show(show); + g_buttons[abReslice]->Show(show); for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) { TabPrinter *tab = dynamic_cast(g_wxTabPanel->GetPage(i)); if (!tab) continue; if (g_PresetBundle->printers.get_selected_preset().printer_technology() == ptFFF) { - g_btn_print->Show(show && !tab->m_config->opt_string("serial_port").empty()); - g_btn_send_gcode->Show(show && !tab->m_config->opt_string("print_host").empty()); + g_buttons[abPrint]->Show(show && !tab->m_config->opt_string("serial_port").empty()); + g_buttons[abSendGCode]->Show(show && !tab->m_config->opt_string("print_host").empty()); } break; } } -void show_info_sizer(bool show) +void show_info_sizer(const bool show) { - g_scrolled_window_sizer->Show(static_cast(0), show); - g_scrolled_window_sizer->Show(1, show && g_show_print_info); + g_info_sizer->Show(static_cast(0), show); + g_info_sizer->Show(1, show && g_show_print_info); g_manifold_warning_icon->Show(show && g_show_manifold_warning_icon); } @@ -1162,31 +1173,22 @@ void show_object_name(bool show) void update_mode() { - wxWindowUpdateLocker noUpdates(g_right_panel); - - // TODO There is a not the best place of it! - //*** Update style of the "Export G-code" button**** - if (g_btn_export_gcode->GetFont() != bold_font()){ - g_btn_export_gcode->SetBackgroundColour(wxColour(252, 77, 1)); - g_btn_export_gcode->SetFont(bold_font()); - } - // *********************************** + wxWindowUpdateLocker noUpdates(g_right_panel->GetParent()); ConfigMenuIDs mode = get_view_mode(); -// show_frequently_changed_parameters(mode >= ConfigMenuModeRegular); -// g_expert_mode_part_sizer->Show(mode == ConfigMenuModeExpert); g_object_list_sizer->Show(mode == ConfigMenuModeExpert); show_info_sizer(mode == ConfigMenuModeExpert); show_buttons(mode == ConfigMenuModeExpert); show_object_name(mode == ConfigMenuModeSimple); + show_manipulation_sizer(mode == ConfigMenuModeSimple); // TODO There is a not the best place of it! // *** Update showing of the collpane_settings // show_collpane_settings(mode == ConfigMenuModeExpert); // ************************* g_right_panel->Layout(); - g_right_panel->GetParent()->GetParent()->Layout(); + g_right_panel->GetParent()->Layout(); } bool is_expert_mode(){ @@ -1254,12 +1256,77 @@ int get_export_option(wxFileDialog* dlg) } -void get_current_screen_size(unsigned &width, unsigned &height) +bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height) { - wxDisplay display(wxDisplay::GetFromWindow(g_wxMainFrame)); + const auto idx = wxDisplay::GetFromWindow(window); + if (idx == wxNOT_FOUND) { + return false; + } + + wxDisplay display(idx); const auto disp_size = display.GetClientArea(); width = disp_size.GetWidth(); height = disp_size.GetHeight(); + + return true; +} + +void save_window_size(wxTopLevelWindow *window, const std::string &name) +{ + const wxSize size = window->GetSize(); + const wxPoint pos = window->GetPosition(); + const auto maximized = window->IsMaximized() ? "1" : "0"; + + g_AppConfig->set((boost::format("window_%1%_size") % name).str(), (boost::format("%1%;%2%") % size.GetWidth() % size.GetHeight()).str()); + g_AppConfig->set((boost::format("window_%1%_maximized") % name).str(), maximized); +} + +void restore_window_size(wxTopLevelWindow *window, const std::string &name) +{ + // XXX: This still doesn't behave nicely in some situations (mostly on Linux). + // The problem is that it's hard to obtain window position with respect to screen geometry reliably + // from wxWidgets. Sometimes wxWidgets claim a window is located on a different screen than on which + // it's actually visible. I suspect this has something to do with window initialization (maybe we + // restore window geometry too early), but haven't yet found a workaround. + + const auto display_idx = wxDisplay::GetFromWindow(window); + if (display_idx == wxNOT_FOUND) { return; } + + const auto display = wxDisplay(display_idx).GetClientArea(); + std::vector pair; + + try { + const auto key_size = (boost::format("window_%1%_size") % name).str(); + if (g_AppConfig->has(key_size)) { + if (unescape_strings_cstyle(g_AppConfig->get(key_size), pair) && pair.size() == 2) { + auto width = boost::lexical_cast(pair[0]); + auto height = boost::lexical_cast(pair[1]); + + window->SetSize(width, height); + } + } + } catch(const boost::bad_lexical_cast &) {} + + // Maximizing should be the last thing to do. + // This ensure the size and position are sane when the user un-maximizes the window. + const auto key_maximized = (boost::format("window_%1%_maximized") % name).str(); + if (g_AppConfig->get(key_maximized) == "1") { + window->Maximize(true); + } +} + +void enable_action_buttons(bool enable) +{ + if (g_buttons.empty()) + return; + + // Update background colour for buttons + const wxColour bgrd_color = enable ? wxColour(224, 224, 224/*255, 96, 0*/) : wxColour(204, 204, 204); + + for (auto btn : g_buttons) { + btn->Enable(enable); + btn->SetBackgroundColour(bgrd_color); + } } void about() diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 05ed580a35..02327b3258 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -27,6 +27,7 @@ class wxButton; class wxFileDialog; class wxStaticBitmap; class wxFont; +class wxTopLevelWindow; namespace Slic3r { @@ -38,7 +39,6 @@ class AppConfig; class PresetUpdater; class DynamicPrintConfig; class TabIface; -class _3DScene; #define _(s) Slic3r::GUI::I18N::translate((s)) @@ -106,13 +106,10 @@ void set_plater(wxPanel *plater); void set_app_config(AppConfig *app_config); void set_preset_bundle(PresetBundle *preset_bundle); void set_preset_updater(PresetUpdater *updater); -void set_3DScene(_3DScene *scene); void set_objects_from_perl( wxWindow* parent, wxBoxSizer *frequently_changed_parameters_sizer, - wxBoxSizer *expert_mode_part_sizer, - wxBoxSizer *scrolled_window_sizer, + wxBoxSizer *info_sizer, wxButton *btn_export_gcode, - wxButton *btn_export_stl, wxButton *btn_reslice, wxButton *btn_print, wxButton *btn_send_gcode, @@ -196,6 +193,8 @@ bool select_language(wxArrayString & names, wxArrayLong & identifiers); // update right panel of the Plater according to view mode void update_mode(); +void show_info_sizer(const bool show); + std::vector& get_tabs_list(); bool checked_tab(Tab* tab); void delete_tab_from_list(Tab* tab); @@ -214,12 +213,11 @@ wxString L_str(const std::string &str); // Return wxString from std::string in UTF8 wxString from_u8(const std::string &str); -void add_expert_mode_part( wxWindow* parent, wxBoxSizer* sizer, - Model &model, - int event_object_selection_changed, - int event_object_settings_changed, - int event_remove_object, - int event_update_scene); +void set_model_events_from_perl(Model &model, + int event_object_selection_changed, + int event_object_settings_changed, + int event_remove_object, + int event_update_scene); void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer); // Update view mode according to selected menu void update_mode(); @@ -236,7 +234,15 @@ void add_export_option(wxFileDialog* dlg, const std::string& format); int get_export_option(wxFileDialog* dlg); // Returns the dimensions of the screen on which the main frame is displayed -void get_current_screen_size(unsigned &width, unsigned &height); +bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height); + +// Save window size and maximized status into AppConfig +void save_window_size(wxTopLevelWindow *window, const std::string &name); +// Restore the above +void restore_window_size(wxTopLevelWindow *window, const std::string &name); + +// Update buttons view according to enable/disable +void enable_action_buttons(bool enable); // Display an About dialog extern void about(); diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp index 0c7b7a5d8f..ae34359ce9 100644 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp +++ b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp @@ -13,6 +13,9 @@ #include "Geometry.hpp" #include "slic3r/Utils/FixModelByWin10.hpp" +#include +#include "3DScene.hpp" + namespace Slic3r { namespace GUI @@ -23,10 +26,12 @@ wxSizer *m_sizer_object_movers = nullptr; wxDataViewCtrl *m_objects_ctrl = nullptr; PrusaObjectDataViewModel *m_objects_model = nullptr; wxCollapsiblePane *m_collpane_settings = nullptr; +PrusaDoubleSlider *m_slider = nullptr; +wxGLCanvas *m_preview_canvas = nullptr; -wxIcon m_icon_modifiermesh; -wxIcon m_icon_solidmesh; -wxIcon m_icon_manifold_warning; +wxBitmap m_icon_modifiermesh; +wxBitmap m_icon_solidmesh; +wxBitmap m_icon_manifold_warning; wxBitmap m_bmp_cog; wxBitmap m_bmp_split; @@ -64,8 +69,6 @@ bool m_part_settings_changed = false; wxString g_selected_extruder = ""; #endif //__WXOSX__ -// typedef std::map t_category_icon; -typedef std::map t_category_icon; inline t_category_icon& get_category_icon() { static t_category_icon CATEGORY_ICON; if (CATEGORY_ICON.empty()){ @@ -138,11 +141,11 @@ void set_objects_from_model(Model &model) { } void init_mesh_icons(){ - m_icon_modifiermesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG); - m_icon_solidmesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG); + m_icon_modifiermesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG); + m_icon_solidmesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG); // init icon for manifold warning - m_icon_manifold_warning = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG); + m_icon_manifold_warning = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG); // init bitmap for "Split to sub-objects" context menu m_bmp_split = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("split.png")), wxBITMAP_TYPE_PNG); @@ -233,7 +236,7 @@ wxDataViewColumn* object_ctrl_create_extruder_column(int extruders_count) void create_objects_ctrl(wxWindow* win, wxBoxSizer*& objects_sz) { m_objects_ctrl = new wxDataViewCtrl(win, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_objects_ctrl->SetInitialSize(wxSize(-1, 150)); // TODO - Set correct height according to the opened/closed objects + m_objects_ctrl->SetMinSize(wxSize(-1, 150)); // TODO - Set correct height according to the opened/closed objects objects_sz = new wxBoxSizer(wxVERTICAL); objects_sz->Add(m_objects_ctrl, 1, wxGROW | wxLEFT, 20); @@ -245,9 +248,10 @@ void create_objects_ctrl(wxWindow* win, wxBoxSizer*& objects_sz) m_objects_ctrl->EnableDropTarget(wxDF_UNICODETEXT); #endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE - // column 0(Icon+Text) of the view control: - m_objects_ctrl->AppendIconTextColumn(_(L("Name")), 0, wxDATAVIEW_CELL_INERT, 200, - wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE); + // column 0(Icon+Text) of the view control: + // And Icon can be consisting of several bitmaps + m_objects_ctrl->AppendColumn(new wxDataViewColumn(_(L("Name")), new PrusaBitmapTextRenderer(), + 0, 200, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE)); // column 1 of the view control: m_objects_ctrl->AppendTextColumn(_(L("Copy")), 1, wxDATAVIEW_CELL_INERT, 45, @@ -278,7 +282,7 @@ wxBoxSizer* create_objects_list(wxWindow *win) m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, [](wxDataViewEvent& event) { object_ctrl_context_menu(); - event.Skip(); +// event.Skip(); }); m_objects_ctrl->Bind(wxEVT_CHAR, [](wxKeyEvent& event) { object_ctrl_key_event(event); }); // doesn't work on OSX @@ -316,15 +320,15 @@ wxBoxSizer* create_edit_object_buttons(wxWindow* win) //*** button's functions btn_load_part->Bind(wxEVT_BUTTON, [win](wxEvent&) { - on_btn_load(win); +// on_btn_load(win); }); btn_load_modifier->Bind(wxEVT_BUTTON, [win](wxEvent&) { - on_btn_load(win, true); +// on_btn_load(win, true); }); btn_load_lambda_modifier->Bind(wxEVT_BUTTON, [win](wxEvent&) { - on_btn_load(win, true, true); +// on_btn_load(win, true, true); }); btn_delete ->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_del(); }); @@ -660,7 +664,7 @@ void add_object_to_list(const std::string &name, ModelObject* model_object) int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + stats.facets_added + stats.facets_reversed + stats.backwards_edges; if (errors > 0) { - const wxDataViewIconText data(item_name, m_icon_manifold_warning); + const PrusaDataViewBitmapText data(item_name, m_icon_manifold_warning); wxVariant variant; variant << data; m_objects_model->SetValue(variant, item, 0); @@ -724,15 +728,26 @@ void select_current_object(int idx) { g_prevent_list_events = true; m_objects_ctrl->UnselectAll(); - if (idx < 0) { - g_prevent_list_events = false; - return; - } - m_objects_ctrl->Select(m_objects_model->GetItemById(idx)); + if (idx>=0) + m_objects_ctrl->Select(m_objects_model->GetItemById(idx)); part_selection_changed(); g_prevent_list_events = false; } +void select_current_volume(int idx, int vol_idx) +{ + if (vol_idx < 0) { + select_current_object(idx); + return; + } + g_prevent_list_events = true; + m_objects_ctrl->UnselectAll(); + if (idx >= 0) + m_objects_ctrl->Select(m_objects_model->GetItemByVolumeId(idx, vol_idx)); + part_selection_changed(); + g_prevent_list_events = false; +} + void remove() { auto item = m_objects_ctrl->GetSelection(); @@ -758,8 +773,17 @@ void object_ctrl_selection_changed() if (m_event_object_selection_changed > 0) { wxCommandEvent event(m_event_object_selection_changed); - event.SetInt(int(m_objects_model->GetParent(m_objects_ctrl->GetSelection()) != wxDataViewItem(0))); - event.SetId(m_selected_object_id); + event.SetId(m_selected_object_id); // set $obj_idx + const wxDataViewItem item = m_objects_ctrl->GetSelection(); + if (!item || m_objects_model->GetParent(item) == wxDataViewItem(0)) + event.SetInt(-1); // set $vol_idx + else { + const int vol_idx = m_objects_model->GetVolumeIdByItem(item); + if (vol_idx == -2) // is settings item + event.SetInt(m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item))); // set $vol_idx + else + event.SetInt(vol_idx); + } get_main_frame()->ProcessWindowEvent(event); } @@ -772,9 +796,9 @@ void object_ctrl_context_menu() { wxDataViewItem item; wxDataViewColumn* col; - printf("object_ctrl_context_menu\n"); +// printf("object_ctrl_context_menu\n"); const wxPoint pt = get_mouse_position_in_control(); - printf("mouse_position_in_control: x = %d, y = %d\n", pt.x, pt.y); +// printf("mouse_position_in_control: x = %d, y = %d\n", pt.x, pt.y); m_objects_ctrl->HitTest(pt, item, col); if (!item) #ifdef __WXOSX__ // #ys_FIXME temporary workaround for OSX @@ -788,9 +812,9 @@ void object_ctrl_context_menu() #else return; #endif // __WXOSX__ - printf("item exists\n"); +// printf("item exists\n"); const wxString title = col->GetTitle(); - printf("title = *%s*\n", title.data().AsChar()); +// printf("title = *%s*\n", title.data().AsChar()); if (title == " ") show_context_menu(); @@ -836,6 +860,15 @@ void object_ctrl_item_value_change(wxDataViewEvent& event) } } +void show_manipulation_og(const bool show) +{ + wxGridSizer* grid_sizer = get_optgroup(ogFrequentlyObjectSettings)->get_grid_sizer(); + if (show == grid_sizer->IsShown(2)) + return; + for (size_t id = 2; id < 12; id++) + grid_sizer->Show(id, show); +} + //update_optgroup void update_settings_list() { @@ -856,14 +889,19 @@ void update_settings_list() m_option_sizer->Clear(true); - if (m_config) + bool show_manipulations = true; + const auto item = m_objects_ctrl->GetSelection(); + if (m_config && m_objects_model->IsSettingsItem(item)) { auto extra_column = [](wxWindow* parent, const Line& line) { auto opt_key = (line.get_options())[0].opt_id; //we assume that we have one option per line - auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("erase.png")), wxBITMAP_TYPE_PNG), + auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(from_u8(var("colorchange_delete_on.png")), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); +#ifdef __WXMSW__ + btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); +#endif // __WXMSW__ btn->Bind(wxEVT_BUTTON, [opt_key](wxEvent &event){ (*m_config)->erase(opt_key); wxTheApp->CallAfter([]() { update_settings_list(); }); @@ -873,60 +911,75 @@ void update_settings_list() std::map> cat_options; auto opt_keys = (*m_config)->keys(); - if (opt_keys.size() == 1 && opt_keys[0] == "extruder") - return; + m_og_settings.resize(0); + std::vector categories; + if (!(opt_keys.size() == 1 && opt_keys[0] == "extruder"))// return; + { + auto extruders_cnt = get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA ? 1 : + get_preset_bundle()->printers.get_edited_preset().config.option("nozzle_diameter")->values.size(); - auto extruders_cnt = get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA ? 1 : - get_preset_bundle()->printers.get_edited_preset().config.option("nozzle_diameter")->values.size(); + for (auto& opt_key : opt_keys) { + auto category = (*m_config)->def()->get(opt_key)->category; + if (category.empty() || + (category == "Extruders" && extruders_cnt == 1)) continue; - for (auto& opt_key : opt_keys) { - auto category = (*m_config)->def()->get(opt_key)->category; - if (category.empty() || - (category == "Extruders" && extruders_cnt==1)) continue; + std::vector< std::string > new_category; - std::vector< std::string > new_category; + auto& cat_opt = cat_options.find(category) == cat_options.end() ? new_category : cat_options.at(category); + cat_opt.push_back(opt_key); + if (cat_opt.size() == 1) + cat_options[category] = cat_opt; + } - auto& cat_opt = cat_options.find(category) == cat_options.end() ? new_category : cat_options.at(category); - cat_opt.push_back(opt_key); - if (cat_opt.size() == 1) - cat_options[category] = cat_opt; - } + for (auto& cat : cat_options) { + if (cat.second.size() == 1 && cat.second[0] == "extruder") + continue; + auto optgroup = std::make_shared(parent, cat.first, *m_config, false, ogDEFAULT, extra_column); + optgroup->label_width = 150; + optgroup->sidetext_width = 70; - m_og_settings.resize(0); - for (auto& cat : cat_options) { - if (cat.second.size() == 1 && cat.second[0] == "extruder") - continue; + for (auto& opt : cat.second) + { + if (opt == "extruder") + continue; + Option option = optgroup->get_option(opt); + option.opt.width = 70; + optgroup->append_single_option_line(option); + } + optgroup->reload_config(); + m_option_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0); + m_og_settings.push_back(optgroup); - auto optgroup = std::make_shared(parent, cat.first, *m_config, false, ogDEFAULT, extra_column); - optgroup->label_width = 100; - optgroup->sidetext_width = 70; + categories.push_back(cat.first); + } + } - for (auto& opt : cat.second) - { - if (opt == "extruder") - continue; - Option option = optgroup->get_option(opt); - option.opt.width = 70; - optgroup->append_single_option_line(option); - } - optgroup->reload_config(); - m_option_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0); - m_og_settings.push_back(optgroup); - } + if (m_og_settings.empty()) { + m_objects_ctrl->Select(m_objects_model->Delete(item)); + part_selection_changed(); + } + else { + if (!categories.empty()) + m_objects_model->UpdateSettingsDigest(item, categories); + show_manipulations = false; + } } + show_manipulation_og(show_manipulations); + show_info_sizer(show_manipulations && item && m_objects_model->GetParent(item) == wxDataViewItem(0)); + #ifdef __linux__ no_updates.reset(nullptr); #endif - /*get_right_panel()*/parent->Layout(); - get_right_panel()->GetParent()->GetParent()->Layout(); + parent->Layout(); + get_right_panel()->GetParent()->Layout(); } void get_settings_choice(wxMenu *menu, int id, bool is_part) { - auto category_name = menu->GetLabel(id); + const auto category_name = menu->GetLabel(id); wxArrayString names; wxArrayInt selections; @@ -974,15 +1027,35 @@ void get_settings_choice(wxMenu *menu, int id, bool is_part) (*m_config)->set_key_value(opt_key, m_default_config.get()->option(opt_key)->clone()); } + + // Add settings item for object + const auto item = m_objects_ctrl->GetSelection(); + if (item) { + const auto settings_item = m_objects_model->HasSettings(item); + m_objects_ctrl->Select(settings_item ? settings_item : + m_objects_model->AddSettingsChild(item)); +#ifndef __WXOSX__ + part_selection_changed(); +#endif //no __WXOSX__ + } + else update_settings_list(); } -bool cur_item_hase_children() -{ - wxDataViewItemArray children; - if (m_objects_model->GetChildren(m_objects_ctrl->GetSelection(), children) > 0) - return true; - return false; +void menu_item_add_generic(wxMenuItem* &menu, int id) { + auto sub_menu = new wxMenu; + + std::vector menu_items = { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }; + for (auto& item : menu_items) + sub_menu->Append(new wxMenuItem(sub_menu, ++id, _(item))); + +#ifndef __WXMSW__ + sub_menu->Bind(wxEVT_MENU, [sub_menu](wxEvent &event) { + load_lambda(sub_menu->GetLabel(event.GetId()).ToStdString()); + }); +#endif //no __WXMSW__ + + menu->SetSubMenu(sub_menu); } wxMenuItem* menu_item_split(wxMenu* menu, int id) { @@ -991,11 +1064,11 @@ wxMenuItem* menu_item_split(wxMenu* menu, int id) { return menu_item; } -wxMenuItem* menu_item_settings(wxMenu* menu, int id) { +wxMenuItem* menu_item_settings(wxMenu* menu, int id, const bool is_part) { auto menu_item = new wxMenuItem(menu, id, _(L("Add settings"))); menu_item->SetBitmap(m_bmp_cog); - auto sub_menu = create_add_settings_popupmenu(false); + auto sub_menu = create_add_settings_popupmenu(is_part); menu_item->SetSubMenu(sub_menu); return menu_item; } @@ -1005,44 +1078,54 @@ wxMenu *create_add_part_popupmenu() wxMenu *menu = new wxMenu; std::vector menu_items = { L("Add part"), L("Add modifier"), L("Add generic") }; - wxWindowID config_id_base = wxWindow::NewControlId(menu_items.size()+2); + wxWindowID config_id_base = wxWindow::NewControlId(menu_items.size()+4+2); int i = 0; for (auto& item : menu_items) { auto menu_item = new wxMenuItem(menu, config_id_base + i, _(item)); menu_item->SetBitmap(i == 0 ? m_icon_solidmesh : m_icon_modifiermesh); - menu->Append(menu_item); + if (item == "Add generic") + menu_item_add_generic(menu_item, config_id_base + i); + menu->Append(menu_item); i++; } menu->AppendSeparator(); - auto menu_item = menu_item_split(menu, config_id_base + i); + auto menu_item = menu_item_split(menu, config_id_base + i + 4); menu->Append(menu_item); - menu_item->Enable(!cur_item_hase_children()); + menu_item->Enable(is_splittable_object(false)); menu->AppendSeparator(); // Append settings popupmenu - menu->Append(menu_item_settings(menu, config_id_base + i + 1)); + menu->Append(menu_item_settings(menu, config_id_base + i + 5, false)); - wxWindow* win = get_tab_panel()->GetPage(0); - - menu->Bind(wxEVT_MENU, [config_id_base, win, menu](wxEvent &event){ + menu->Bind(wxEVT_MENU, [config_id_base, menu](wxEvent &event){ switch (event.GetId() - config_id_base) { case 0: - on_btn_load(win); + on_btn_load(); break; case 1: - on_btn_load(win, true); + on_btn_load(true); break; case 2: - on_btn_load(win, true, true); +// on_btn_load(true, true); break; - case 3: + case 3: + case 4: + case 5: + case 6: +#ifdef __WXMSW__ + load_lambda(menu->GetLabel(event.GetId()).ToStdString()); +#endif // __WXMSW__ + break; + case 7: //3: on_btn_split(false); break; - default:{ + default: +#ifdef __WXMSW__ get_settings_choice(menu, event.GetId(), false); - break;} +#endif // __WXMSW__ + break; } }); @@ -1054,11 +1137,13 @@ wxMenu *create_part_settings_popupmenu() wxMenu *menu = new wxMenu; wxWindowID config_id_base = wxWindow::NewControlId(2); - menu->Append(menu_item_split(menu, config_id_base)); + auto menu_item = menu_item_split(menu, config_id_base); + menu->Append(menu_item); + menu_item->Enable(is_splittable_object(true)); menu->AppendSeparator(); // Append settings popupmenu - menu->Append(menu_item_settings(menu, config_id_base + 1)); + menu->Append(menu_item_settings(menu, config_id_base + 1, true)); menu->Bind(wxEVT_MENU, [config_id_base, menu](wxEvent &event){ switch (event.GetId() - config_id_base) { @@ -1090,35 +1175,35 @@ wxMenu *create_add_settings_popupmenu(bool is_part) wxNullBitmap : categories.at(cat.first)); menu->Append(menu_item); } - - menu->Bind(wxEVT_MENU, [menu](wxEvent &event) { - get_settings_choice(menu, event.GetId(), true); +#ifndef __WXMSW__ + menu->Bind(wxEVT_MENU, [menu,is_part](wxEvent &event) { + get_settings_choice(menu, event.GetId(), is_part); }); - +#endif //no __WXMSW__ return menu; } void show_context_menu() { - auto item = m_objects_ctrl->GetSelection(); + const auto item = m_objects_ctrl->GetSelection(); if (item) { - if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { - auto menu = create_add_part_popupmenu(); - get_tab_panel()->GetPage(0)->PopupMenu(menu); - } - else { - auto menu = create_part_settings_popupmenu(); - get_tab_panel()->GetPage(0)->PopupMenu(menu); - } + if (m_objects_model->IsSettingsItem(item)) + return; + const auto menu = m_objects_model->GetParent(item) == wxDataViewItem(0) ? + create_add_part_popupmenu() : + create_part_settings_popupmenu(); + get_tab_panel()->GetPage(0)->PopupMenu(menu); } } // ****** -void load_part( wxWindow* parent, ModelObject* model_object, +void load_part( ModelObject* model_object, wxArrayString& part_names, const bool is_modifier) { + wxWindow* parent = get_tab_panel()->GetPage(0); + wxArrayString input_files; open_model(parent, input_files); for (int i = 0; i < input_files.size(); ++i) { @@ -1137,7 +1222,7 @@ void load_part( wxWindow* parent, ModelObject* model_object, for ( auto object : model.objects) { for (auto volume : object->volumes) { auto new_volume = model_object->add_volume(*volume); - new_volume->modifier = is_modifier; + new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); boost::filesystem::path(input_file).filename().string(); new_volume->name = boost::filesystem::path(input_file).filename().string(); @@ -1156,10 +1241,10 @@ void load_part( wxWindow* parent, ModelObject* model_object, } } -void load_lambda( wxWindow* parent, ModelObject* model_object, +void load_lambda( ModelObject* model_object, wxArrayString& part_names, const bool is_modifier) { - auto dlg = new LambdaObjectDialog(parent); + auto dlg = new LambdaObjectDialog(m_objects_ctrl->GetMainWindow()); if (dlg->ShowModal() == wxID_CANCEL) { return; } @@ -1195,7 +1280,8 @@ void load_lambda( wxWindow* parent, ModelObject* model_object, mesh.repair(); auto new_volume = model_object->add_volume(mesh); - new_volume->modifier = is_modifier; + new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); + new_volume->name = name; // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); @@ -1205,7 +1291,50 @@ void load_lambda( wxWindow* parent, ModelObject* model_object, m_parts_changed = true; } -void on_btn_load(wxWindow* parent, bool is_modifier /*= false*/, bool is_lambda/* = false*/) +void load_lambda(const std::string& type_name) +{ + if (m_selected_object_id < 0) return; + + auto dlg = new LambdaObjectDialog(m_objects_ctrl->GetMainWindow(), type_name); + if (dlg->ShowModal() == wxID_CANCEL) + return; + + const std::string name = "lambda-"+type_name; + TriangleMesh mesh; + + const auto params = dlg->ObjectParameters(); + if (type_name == _("Box")) + mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]); + else if (type_name == _("Cylinder")) + mesh = make_cylinder(params.cyl_r, params.cyl_h); + else if (type_name == _("Sphere")) + mesh = make_sphere(params.sph_rho); + else if (type_name == _("Slab")){ + const auto& size = (*m_objects)[m_selected_object_id]->bounding_box().size(); + mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h); + // box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z + mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z); + } + mesh.repair(); + + auto new_volume = (*m_objects)[m_selected_object_id]->add_volume(mesh); + new_volume->set_type(ModelVolume::PARAMETER_MODIFIER); + + new_volume->name = name; + // set a default extruder value, since user can't add it manually + new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); + + m_parts_changed = true; + parts_changed(m_selected_object_id); + + m_objects_ctrl->Select(m_objects_model->AddChild(m_objects_ctrl->GetSelection(), + name, m_icon_modifiermesh)); +#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME + object_ctrl_selection_changed(); +#endif //no __WXOSX__ //__WXMSW__ +} + +void on_btn_load(bool is_modifier /*= false*/, bool is_lambda/* = false*/) { auto item = m_objects_ctrl->GetSelection(); if (!item) @@ -1219,19 +1348,54 @@ void on_btn_load(wxWindow* parent, bool is_modifier /*= false*/, bool is_lambda/ if (obj_idx < 0) return; wxArrayString part_names; if (is_lambda) - load_lambda(parent, (*m_objects)[obj_idx], part_names, is_modifier); + load_lambda((*m_objects)[obj_idx], part_names, is_modifier); else - load_part(parent, (*m_objects)[obj_idx], part_names, is_modifier); + load_part((*m_objects)[obj_idx], part_names, is_modifier); parts_changed(obj_idx); for (int i = 0; i < part_names.size(); ++i) m_objects_ctrl->Select( m_objects_model->AddChild(item, part_names.Item(i), is_modifier ? m_icon_modifiermesh : m_icon_solidmesh)); -// part_selection_changed(); -#ifdef __WXMSW__ +#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME object_ctrl_selection_changed(); -#endif //__WXMSW__ +#endif //no __WXOSX__//__WXMSW__ +} + +void remove_settings_from_config() +{ + auto opt_keys = (*m_config)->keys(); + if (opt_keys.size() == 1 && opt_keys[0] == "extruder") + return; + int extruder = -1; + if ((*m_config)->has("extruder")) + extruder = (*m_config)->option("extruder")->value; + + (*m_config)->clear(); + + if (extruder >=0 ) + (*m_config)->set_key_value("extruder", new ConfigOptionInt(extruder)); +} + +bool remove_subobject_from_object(const int volume_id) +{ + const auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; + + // if user is deleting the last solid part, throw error + int solid_cnt = 0; + for (auto vol : (*m_objects)[m_selected_object_id]->volumes) + if (vol->is_model_part()) + ++solid_cnt; + if (volume->is_model_part() && solid_cnt == 1) { + Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last solid part from this object."))); + return false; + } + + (*m_objects)[m_selected_object_id]->delete_volume(volume_id); + m_parts_changed = true; + + parts_changed(m_selected_object_id); + return true; } void on_btn_del() @@ -1239,50 +1403,67 @@ void on_btn_del() auto item = m_objects_ctrl->GetSelection(); if (!item) return; - auto volume_id = m_objects_model->GetVolumeIdByItem(item); - if (volume_id < 0) + const auto volume_id = m_objects_model->GetVolumeIdByItem(item); + if (volume_id ==-1) return; - auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; - - // if user is deleting the last solid part, throw error - int solid_cnt = 0; - for (auto vol : (*m_objects)[m_selected_object_id]->volumes) - if (!vol->modifier) - ++solid_cnt; - if (!volume->modifier && solid_cnt == 1) { - Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last solid part from this object."))); - return; - } - - (*m_objects)[m_selected_object_id]->delete_volume(volume_id); - m_parts_changed = true; - - parts_changed(m_selected_object_id); + + if (volume_id ==-2) + remove_settings_from_config(); + else if (!remove_subobject_from_object(volume_id)) + return; m_objects_ctrl->Select(m_objects_model->Delete(item)); part_selection_changed(); -// #ifdef __WXMSW__ -// object_ctrl_selection_changed(); -// #endif //__WXMSW__ +} + +bool get_volume_by_item(const bool split_part, const wxDataViewItem& item, ModelVolume*& volume) +{ + if (!item || m_selected_object_id < 0) + return false; + const auto volume_id = m_objects_model->GetVolumeIdByItem(item); + if (volume_id < 0) { + if (split_part) return false; + volume = (*m_objects)[m_selected_object_id]->volumes[0]; + } + else + volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; + if (volume) + return true; + return false; +} + +bool is_splittable_object(const bool split_part) +{ + const wxDataViewItem item = m_objects_ctrl->GetSelection(); + if (!item) return false; + + wxDataViewItemArray children; + if (!split_part && m_objects_model->GetChildren(item, children) > 0) + return false; + + ModelVolume* volume; + if (!get_volume_by_item(split_part, item, volume) || !volume) + return false; + + TriangleMeshPtrs meshptrs = volume->mesh.split(); + if (meshptrs.size() <= 1) { + delete meshptrs.front(); + return false; + } + + return true; } void on_btn_split(const bool split_part) { - auto item = m_objects_ctrl->GetSelection(); + const auto item = m_objects_ctrl->GetSelection(); if (!item || m_selected_object_id<0) return; - auto volume_id = m_objects_model->GetVolumeIdByItem(item); ModelVolume* volume; - if (volume_id < 0) { - if (split_part) return; - else - volume = (*m_objects)[m_selected_object_id]->volumes[0]; } - else - volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; - DynamicPrintConfig& config = get_preset_bundle()->printers.get_edited_preset().config; - auto nozzle_dmrs_cnt = config.option("nozzle_diameter")->values.size(); - auto split_rez = volume->split(nozzle_dmrs_cnt); - if (split_rez == 1) { + if (!get_volume_by_item(split_part, item, volume)) return; + DynamicPrintConfig& config = get_preset_bundle()->printers.get_edited_preset().config; + const auto nozzle_dmrs_cnt = config.option("nozzle_diameter")->values.size(); + if (volume->split(nozzle_dmrs_cnt) == 1) { wxMessageBox(_(L("The selected object couldn't be split because it contains only one part."))); return; } @@ -1295,8 +1476,9 @@ void on_btn_split(const bool split_part) for (auto id = 0; id < model_object->volumes.size(); id++) m_objects_model->AddChild(parent, model_object->volumes[id]->name, - model_object->volumes[id]->modifier ? m_icon_modifiermesh : m_icon_solidmesh, - model_object->volumes[id]->config.option("extruder")->value, + model_object->volumes[id]->is_modifier() ? m_icon_modifiermesh : m_icon_solidmesh, + model_object->volumes[id]->config.has("extruder") ? + model_object->volumes[id]->config.option("extruder")->value : 0, false); m_objects_ctrl->Expand(parent); @@ -1305,10 +1487,14 @@ void on_btn_split(const bool split_part) for (auto id = 0; id < model_object->volumes.size(); id++) m_objects_model->AddChild(item, model_object->volumes[id]->name, m_icon_solidmesh, - model_object->volumes[id]->config.option("extruder")->value, + model_object->volumes[id]->config.has("extruder") ? + model_object->volumes[id]->config.option("extruder")->value : 0, false); m_objects_ctrl->Expand(item); } + + m_parts_changed = true; + parts_changed(m_selected_object_id); } void on_btn_move_up(){ @@ -1389,33 +1575,49 @@ void part_selection_changed() auto item = m_objects_ctrl->GetSelection(); int obj_idx = -1; auto og = get_optgroup(ogFrequentlyObjectSettings); + m_config = nullptr; + wxString object_name = wxEmptyString; if (item) { + const bool is_settings_item = m_objects_model->IsSettingsItem(item); bool is_part = false; - if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { + wxString og_name = wxEmptyString; + if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { obj_idx = m_objects_model->GetIdByItem(item); - og->set_name(" " + _(L("Object Settings")) + " "); + og_name = _(L("Object manipulation")); m_config = std::make_shared(&(*m_objects)[obj_idx]->config); } else { auto parent = m_objects_model->GetParent(item); - // Take ID of the parent object to "inform" perl-side which object have to be selected on the scene + // Take ID of the parent object to "inform" perl-side which object have to be selected on the scene obj_idx = m_objects_model->GetIdByItem(parent); - og->set_name(" " + _(L("Part Settings")) + " "); - is_part = true; - auto volume_id = m_objects_model->GetVolumeIdByItem(item); - m_config = std::make_shared(&(*m_objects)[obj_idx]->volumes[volume_id]->config); + if (is_settings_item) { + if (m_objects_model->GetParent(parent) == wxDataViewItem(0)) { + og_name = _(L("Object Settings to modify")); + m_config = std::make_shared(&(*m_objects)[obj_idx]->config); + } + else { + og_name = _(L("Part Settings to modify")); + is_part = true; + auto main_parent = m_objects_model->GetParent(parent); + obj_idx = m_objects_model->GetIdByItem(main_parent); + const auto volume_id = m_objects_model->GetVolumeIdByItem(parent); + m_config = std::make_shared(&(*m_objects)[obj_idx]->volumes[volume_id]->config); + } + } + else { + og_name = _(L("Part manipulation")); + is_part = true; + const auto volume_id = m_objects_model->GetVolumeIdByItem(item); + m_config = std::make_shared(&(*m_objects)[obj_idx]->volumes[volume_id]->config); + } } - auto config = m_config; - og->set_value("object_name", m_objects_model->GetName(item)); + og->set_name(" " + og_name + " "); + object_name = m_objects_model->GetName(item); m_default_config = std::make_shared(*DynamicPrintConfig::new_from_defaults_keys(get_options(is_part))); } - else { - wxString empty_str = wxEmptyString; - og->set_value("object_name", empty_str); - m_config = nullptr; - } + og->set_value("object_name", object_name); update_settings_list(); @@ -1561,9 +1763,15 @@ void update_position_values() auto og = get_optgroup(ogFrequentlyObjectSettings); auto instance = (*m_objects)[m_selected_object_id]->instances.front(); +#if ENABLE_MODELINSTANCE_3D_OFFSET + og->set_value("position_x", int(instance->get_offset(X))); + og->set_value("position_y", int(instance->get_offset(Y))); + og->set_value("position_z", int(instance->get_offset(Z))); +#else og->set_value("position_x", int(instance->offset(0))); og->set_value("position_y", int(instance->offset(1))); og->set_value("position_z", 0); +#endif // ENABLE_MODELINSTANCE_3D_OFFSET } void update_position_values(const Vec3d& position) @@ -1638,7 +1846,7 @@ void on_begin_drag(wxDataViewEvent &event) wxDataViewItem item(event.GetItem()); // only allow drags for item, not containers - if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { + if (m_objects_model->GetParent(item) == wxDataViewItem(0) || m_objects_model->IsSettingsItem(item)) { event.Veto(); return; } @@ -1662,7 +1870,7 @@ void on_drop_possible(wxDataViewEvent &event) // only allow drags for item or background, not containers if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) || - event.GetDataFormat() != wxDF_UNICODETEXT) + event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->IsSettingsItem(item)) event.Veto(); } @@ -1672,7 +1880,7 @@ void on_drop(wxDataViewEvent &event) // only allow drops for item, not containers if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) || - event.GetDataFormat() != wxDF_UNICODETEXT) { + event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->IsSettingsItem(item)) { event.Veto(); return; } @@ -1700,6 +1908,9 @@ void on_drop(wxDataViewEvent &event) for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id+=delta, cnt++) std::swap(volumes[id], volumes[id +delta]); + m_parts_changed = true; + parts_changed(m_selected_object_id); + g_prevent_list_events = false; } @@ -1716,5 +1927,115 @@ void update_objects_list_extruder_column(int extruders_count) set_extruder_column_hidden(extruders_count <= 1); } +void create_double_slider(wxWindow* parent, wxBoxSizer* sizer, wxGLCanvas* canvas) +{ + m_slider = new PrusaDoubleSlider(parent, wxID_ANY, 0, 0, 0, 100); + sizer->Add(m_slider, 0, wxEXPAND, 0); + + m_preview_canvas = canvas; + m_preview_canvas->Bind(wxEVT_KEY_DOWN, update_double_slider_from_canvas); + + m_slider->Bind(wxEVT_SCROLL_CHANGED, [parent](wxEvent& event) { + _3DScene::set_toolpaths_range(m_preview_canvas, m_slider->GetLowerValueD() - 1e-6, m_slider->GetHigherValueD() + 1e-6); + if (parent->IsShown()) + m_preview_canvas->Refresh(); + }); +} + +void fill_slider_values(std::vector> &values, + const std::vector &layers_z) +{ + std::vector layers_all_z = _3DScene::get_current_print_zs(m_preview_canvas, false); + if (layers_all_z.size() == layers_z.size()) + for (int i = 0; i < layers_z.size(); i++) + values.push_back(std::pair(i+1, layers_z[i])); + else if (layers_all_z.size() > layers_z.size()) { + int cur_id = 0; + for (int i = 0; i < layers_z.size(); i++) + for (int j = cur_id; j < layers_all_z.size(); j++) + if (layers_z[i] - 1e-6 < layers_all_z[j] && layers_all_z[j] < layers_z[i] + 1e-6) { + values.push_back(std::pair(j+1, layers_z[i])); + cur_id = j; + break; + } + } +} + +void set_double_slider_thumbs( const bool force_sliders_full_range, + const std::vector &layers_z, + const double z_low, const double z_high) +{ + // Force slider full range only when slider is created. + // Support selected diapason on the all next steps + if (/*force_sliders_full_range*/z_high == 0.0) { + m_slider->SetLowerValue(0); + m_slider->SetHigherValue(layers_z.size() - 1); + return; + } + + for (int i = layers_z.size() - 1; i >= 0; i--) + if (z_low >= layers_z[i]) { + m_slider->SetLowerValue(i); + break; + } + for (int i = layers_z.size() - 1; i >= 0 ; i--) + if (z_high >= layers_z[i]) { + m_slider->SetHigherValue(i); + break; + } +} + +void update_double_slider(bool force_sliders_full_range) +{ + std::vector> values; + std::vector layers_z = _3DScene::get_current_print_zs(m_preview_canvas, true); + fill_slider_values(values, layers_z); + + const double z_low = m_slider->GetLowerValueD(); + const double z_high = m_slider->GetHigherValueD(); + m_slider->SetMaxValue(layers_z.size() - 1); + m_slider->SetSliderValues(values); + + set_double_slider_thumbs(force_sliders_full_range, layers_z, z_low, z_high); +} + +void reset_double_slider() +{ + m_slider->SetHigherValue(0); + m_slider->SetLowerValue(0); +} + +void update_double_slider_from_canvas(wxKeyEvent& event) +{ + if (event.HasModifiers()) { + event.Skip(); + return; + } + + const auto key = event.GetKeyCode(); + + if (key == 'U' || key == 'D') { + const int new_pos = key == 'U' ? m_slider->GetHigherValue() + 1 : m_slider->GetHigherValue() - 1; + m_slider->SetHigherValue(new_pos); + if (event.ShiftDown()) m_slider->SetLowerValue(m_slider->GetHigherValue()); + } + else if (key == 'S') + m_slider->ChangeOneLayerLock(); + else + event.Skip(); +} + +void show_manipulation_sizer(const bool is_simple_mode) +{ + auto item = m_objects_ctrl->GetSelection(); + if (!item || !is_simple_mode) + return; + + if (m_objects_model->IsSettingsItem(item)) { + m_objects_ctrl->Select(m_objects_model->GetParent(item)); + part_selection_changed(); + } +} + } //namespace GUI } //namespace Slic3r \ No newline at end of file diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp b/xs/src/slic3r/GUI/GUI_ObjectParts.hpp index 8a1499e030..e66b4d1db8 100644 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp +++ b/xs/src/slic3r/GUI/GUI_ObjectParts.hpp @@ -9,13 +9,15 @@ class wxArrayString; class wxMenu; class wxDataViewEvent; class wxKeyEvent; -class wxControl; +class wxGLCanvas; +class wxBitmap; namespace Slic3r { class ModelObject; class Model; namespace GUI { +//class wxGLCanvas; enum ogGroup{ ogFrequentlyChangingParameters, @@ -44,6 +46,9 @@ struct OBJECT_PARAMETERS double slab_z = 0.0; }; +typedef std::map t_category_icon; +inline t_category_icon& get_category_icon(); + void add_collapsible_panes(wxWindow* parent, wxBoxSizer* sizer); void add_objects_list(wxWindow* parent, wxBoxSizer* sizer); void add_object_settings(wxWindow* parent, wxBoxSizer* sizer); @@ -66,16 +71,17 @@ void set_object_count(int idx, int count); void unselect_objects(); // Select current object in the list on c++ side void select_current_object(int idx); +// Select current volume in the list on c++ side +void select_current_volume(int idx, int vol_idx); // Remove objects/sub-object from the list void remove(); -//void create_double_slider(wxWindow* parent, wxControl* slider); - void object_ctrl_selection_changed(); void object_ctrl_context_menu(); void object_ctrl_key_event(wxKeyEvent& event); void object_ctrl_item_value_change(wxDataViewEvent& event); void show_context_menu(); +bool is_splittable_object(const bool split_part); void init_mesh_icons(); void set_event_object_selection_changed(const int& event); @@ -87,13 +93,14 @@ void set_objects_from_model(Model &model); bool is_parts_changed(); bool is_part_settings_changed(); -void load_part( wxWindow* parent, ModelObject* model_object, +void load_part( ModelObject* model_object, wxArrayString& part_names, const bool is_modifier); -void load_lambda(wxWindow* parent, ModelObject* model_object, - wxArrayString& part_names, const bool is_modifier); +void load_lambda( ModelObject* model_object, + wxArrayString& part_names, const bool is_modifier); +void load_lambda( const std::string& type_name); -void on_btn_load(wxWindow* parent, bool is_modifier = false, bool is_lambda = false); +void on_btn_load(bool is_modifier = false, bool is_lambda = false); void on_btn_del(); void on_btn_split(const bool split_part); void on_btn_move_up(); @@ -126,6 +133,15 @@ void on_drop(wxDataViewEvent &event); // update extruder column for objects_ctrl according to extruders count void update_objects_list_extruder_column(int extruders_count); +// Create/Update/Reset double slider on 3dPreview +void create_double_slider(wxWindow* parent, wxBoxSizer* sizer, wxGLCanvas* canvas); +void update_double_slider(bool force_sliders_full_range); +void reset_double_slider(); +// update DoubleSlider after keyDown in canvas +void update_double_slider_from_canvas(wxKeyEvent& event); + +void show_manipulation_sizer(const bool is_simple_mode); + } //namespace GUI } //namespace Slic3r #endif //slic3r_GUI_ObjectParts_hpp_ \ No newline at end of file diff --git a/xs/src/slic3r/GUI/LambdaObjectDialog.cpp b/xs/src/slic3r/GUI/LambdaObjectDialog.cpp index 7543821c0e..7d741be7ff 100644 --- a/xs/src/slic3r/GUI/LambdaObjectDialog.cpp +++ b/xs/src/slic3r/GUI/LambdaObjectDialog.cpp @@ -10,10 +10,12 @@ namespace GUI { static wxString dots("…", wxConvUTF8); -LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent) +LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent, + const wxString type_name): + m_type_name(type_name) { Create(parent, wxID_ANY, _(L("Lambda Object")), - wxDefaultPosition, wxDefaultSize, + parent->GetScreenPosition(), wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); // instead of double dim[3] = { 1.0, 1.0, 1.0 }; @@ -24,11 +26,20 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent) sizer = new wxBoxSizer(wxVERTICAL); // modificator options - m_modificator_options_book = new wxChoicebook( this, wxID_ANY, wxDefaultPosition, - wxDefaultSize, wxCHB_TOP); - sizer->Add(m_modificator_options_book, 1, wxEXPAND| wxALL, 10); + if (m_type_name == wxEmptyString) { + m_modificator_options_book = new wxChoicebook( this, wxID_ANY, wxDefaultPosition, + wxDefaultSize, wxCHB_TOP); + sizer->Add(m_modificator_options_book, 1, wxEXPAND | wxALL, 10); + } + else { + m_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); + sizer->Add(m_panel, 1, wxEXPAND | wxALL, 10); + } + ConfigOptionDef def; + def.width = 70; auto optgroup = init_modificator_options_page(_(L("Box"))); + if (optgroup){ optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){ int opt_id = opt_key == "l" ? 0 : opt_key == "w" ? 1 : @@ -37,8 +48,6 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent) object_parameters.dim[opt_id] = boost::any_cast(value); }; - ConfigOptionDef def; - def.width = 70; def.type = coFloat; def.default_value = new ConfigOptionFloat{ 1.0 }; def.label = L("L"); @@ -52,8 +61,10 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent) def.label = L("H"); option = Option(def, "h"); optgroup->append_single_option_line(option); + } optgroup = init_modificator_options_page(_(L("Cylinder"))); + if (optgroup){ optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){ int val = boost::any_cast(value); if (opt_key == "cyl_r") @@ -66,14 +77,16 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent) def.type = coInt; def.default_value = new ConfigOptionInt{ 1 }; def.label = L("Radius"); - option = Option(def, "cyl_r"); + auto option = Option(def, "cyl_r"); optgroup->append_single_option_line(option); def.label = L("Height"); option = Option(def, "cyl_h"); optgroup->append_single_option_line(option); + } optgroup = init_modificator_options_page(_(L("Sphere"))); + if (optgroup){ optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){ if (opt_key == "sph_rho") object_parameters.sph_rho = boost::any_cast(value); @@ -83,10 +96,12 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent) def.type = coFloat; def.default_value = new ConfigOptionFloat{ 1.0 }; def.label = L("Rho"); - option = Option(def, "sph_rho"); + auto option = Option(def, "sph_rho"); optgroup->append_single_option_line(option); + } optgroup = init_modificator_options_page(_(L("Slab"))); + if (optgroup){ optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){ double val = boost::any_cast(value); if (opt_key == "slab_z") @@ -96,13 +111,16 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent) else return; }; + def.type = coFloat; + def.default_value = new ConfigOptionFloat{ 1.0 }; def.label = L("H"); - option = Option(def, "slab_h"); + auto option = Option(def, "slab_h"); optgroup->append_single_option_line(option); def.label = L("Initial Z"); option = Option(def, "slab_z"); optgroup->append_single_option_line(option); + } Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent e) { @@ -127,8 +145,7 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent) } })); - - auto button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL); + const auto button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL); wxButton* btn_OK = static_cast(FindWindowById(wxID_OK, this)); btn_OK->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { @@ -155,9 +172,11 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent) // Called from the constructor. // Create a panel for a rectangular / circular / custom bed shape. -ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(wxString title){ +ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(const wxString& title){ + if (!m_type_name.IsEmpty() && m_type_name != title) + return nullptr; - auto panel = new wxPanel(m_modificator_options_book); + auto panel = m_type_name.IsEmpty() ? new wxPanel(m_modificator_options_book) : m_panel; ConfigOptionsGroupShp optgroup; optgroup = std::make_shared(panel, _(L("Add")) + " " +title + " " +dots); @@ -165,8 +184,12 @@ ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(wxString m_optgroups.push_back(optgroup); - panel->SetSizerAndFit(optgroup->sizer); - m_modificator_options_book->AddPage(panel, title); + if (m_type_name.IsEmpty()) { + panel->SetSizerAndFit(optgroup->sizer); + m_modificator_options_book->AddPage(panel, title); + } + else + panel->SetSizer(optgroup->sizer); return optgroup; } diff --git a/xs/src/slic3r/GUI/LambdaObjectDialog.hpp b/xs/src/slic3r/GUI/LambdaObjectDialog.hpp index a70c124490..8f3e8cd80d 100644 --- a/xs/src/slic3r/GUI/LambdaObjectDialog.hpp +++ b/xs/src/slic3r/GUI/LambdaObjectDialog.hpp @@ -7,6 +7,8 @@ #include #include +class wxPanel; + namespace Slic3r { namespace GUI @@ -14,16 +16,19 @@ namespace GUI using ConfigOptionsGroupShp = std::shared_ptr; class LambdaObjectDialog : public wxDialog { - wxChoicebook* m_modificator_options_book; + wxChoicebook* m_modificator_options_book = nullptr; std::vector m_optgroups; + wxString m_type_name; + wxPanel* m_panel = nullptr; public: - LambdaObjectDialog(wxWindow* parent); + LambdaObjectDialog(wxWindow* parent, + const wxString type_name = wxEmptyString); ~LambdaObjectDialog(){} bool CanClose() { return true; } // ??? OBJECT_PARAMETERS& ObjectParameters(){ return object_parameters; } - ConfigOptionsGroupShp init_modificator_options_page(wxString title); + ConfigOptionsGroupShp init_modificator_options_page(const wxString& title); // Note whether the window was already closed, so a pending update is not executed. bool m_already_closed = false; diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp index 81d02a4cd5..ea22b2cb5a 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.cpp +++ b/xs/src/slic3r/GUI/OptionsGroup.cpp @@ -158,7 +158,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* // if we have an extra column, build it if (extra_column) { if (extra_column) { - grid_sizer->Add(extra_column(parent(), line), 0, wxALIGN_CENTER_VERTICAL, 0); + grid_sizer->Add(extra_column(parent(), line), 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 3); } else { // if the callback provides no sizer for the extra cell, put a spacer diff --git a/xs/src/slic3r/GUI/OptionsGroup.hpp b/xs/src/slic3r/GUI/OptionsGroup.hpp index 5a18803a6a..4941e54539 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.hpp +++ b/xs/src/slic3r/GUI/OptionsGroup.hpp @@ -161,8 +161,10 @@ public: ogDrawFlag flag = ogDEFAULT, column_t extra_clmn = nullptr) : m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt), staticbox(title!=""), m_flag(flag), extra_column(extra_clmn){ - stb = new wxStaticBox(_parent, wxID_ANY, title); - stb->SetFont(bold_font()); + if (staticbox) { + stb = new wxStaticBox(_parent, wxID_ANY, title); + stb->SetFont(bold_font()); + } sizer = (staticbox ? new wxStaticBoxSizer(stb, wxVERTICAL) : new wxBoxSizer(wxVERTICAL)); auto num_columns = 1U; if (label_width != 0) num_columns++; diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 92912a3c44..9911caa5b8 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -295,7 +295,7 @@ const std::vector& Preset::print_options() "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", "bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", - "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers", + "min_skirt_length", "brim_width", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", diff --git a/xs/src/slic3r/GUI/ProgressIndicator.hpp b/xs/src/slic3r/GUI/ProgressIndicator.hpp index 106f5e7ead..0cf8b4a17c 100644 --- a/xs/src/slic3r/GUI/ProgressIndicator.hpp +++ b/xs/src/slic3r/GUI/ProgressIndicator.hpp @@ -4,8 +4,6 @@ #include #include -#include - namespace Slic3r { /** @@ -44,13 +42,13 @@ public: } /// Message shown on the next status update. - virtual void message(const wxString&) = 0; + virtual void message(const std::string&) = 0; /// Title of the operation. - virtual void title(const wxString&) = 0; + virtual void title(const std::string&) = 0; /// Formatted message for the next status update. Works just like sprintf. - virtual void message_fmt(const wxString& fmt, ...); + virtual void message_fmt(const std::string& fmt, ...); /// Set up a cancel callback for the operation if feasible. virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; } @@ -62,7 +60,7 @@ public: virtual void cancel() { cancelfunc_(); } /// Convenience function to call message and status update in one function. - void update(float st, const wxString& msg) { + void update(float st, const std::string& msg) { message(msg); state(st); } }; diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.cpp b/xs/src/slic3r/GUI/ProgressStatusBar.cpp index 67fcd1dcc6..363e34cb2b 100644 --- a/xs/src/slic3r/GUI/ProgressStatusBar.cpp +++ b/xs/src/slic3r/GUI/ProgressStatusBar.cpp @@ -134,7 +134,7 @@ void ProgressStatusBar::embed(wxFrame *frame) mf->SetStatusBar(self); } -void ProgressStatusBar::set_status_text(const std::string& txt) +void ProgressStatusBar::set_status_text(const wxString& txt) { self->SetStatusText(wxString::FromUTF8(txt.c_str())); } diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.hpp b/xs/src/slic3r/GUI/ProgressStatusBar.hpp index 91c180ba6b..3075805da7 100644 --- a/xs/src/slic3r/GUI/ProgressStatusBar.hpp +++ b/xs/src/slic3r/GUI/ProgressStatusBar.hpp @@ -13,6 +13,7 @@ class wxTimerEvent; class wxStatusBar; class wxWindow; class wxFrame; +class wxString; namespace Slic3r { @@ -46,7 +47,7 @@ public: inline void remove_cancel_callback() { set_cancel_callback(); } void run(int rate); void embed(wxFrame *frame = nullptr); - void set_status_text(const std::string& txt); + void set_status_text(const wxString& txt); // Temporary methods to satisfy Perl side void show_cancel_button(); diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index 83b834219d..e0db63803d 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -880,6 +880,7 @@ void TabPrint::build() page = add_options_page(_(L("Support material")), "building.png"); optgroup = page->new_optgroup(_(L("Support material"))); optgroup->append_single_option_line("support_material"); + optgroup->append_single_option_line("support_material_auto"); optgroup->append_single_option_line("support_material_threshold"); optgroup->append_single_option_line("support_material_enforce_layers"); @@ -1219,13 +1220,15 @@ void TabPrint::update() bool have_raft = m_config->opt_int("raft_layers") > 0; bool have_support_material = m_config->opt_bool("support_material") || have_raft; + bool have_support_material_auto = have_support_material && m_config->opt_bool("support_material_auto"); bool have_support_interface = m_config->opt_int("support_material_interface_layers") > 0; bool have_support_soluble = have_support_material && m_config->opt_float("support_material_contact_distance") == 0; - for (auto el : {"support_material_threshold", "support_material_pattern", "support_material_with_sheath", + for (auto el : {"support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_angle", "support_material_interface_layers", "dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance", "support_material_xy_spacing" }) get_field(el)->toggle(have_support_material); + get_field("support_material_threshold")->toggle(have_support_material_auto); for (auto el : {"support_material_interface_spacing", "support_material_interface_extruder", "support_material_interface_speed", "support_material_interface_contact_loops" }) diff --git a/xs/src/slic3r/GUI/wxExtensions.cpp b/xs/src/slic3r/GUI/wxExtensions.cpp index e646ed0ec2..13730a497a 100644 --- a/xs/src/slic3r/GUI/wxExtensions.cpp +++ b/xs/src/slic3r/GUI/wxExtensions.cpp @@ -2,6 +2,7 @@ #include "GUI.hpp" #include "../../libslic3r/Utils.hpp" +#include "BitmapCache.hpp" #include #include @@ -357,11 +358,53 @@ void PrusaObjectDataViewModelNode::set_part_action_icon() { m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG); } +Slic3r::GUI::BitmapCache *m_bitmap_cache = nullptr; +bool PrusaObjectDataViewModelNode::update_settings_digest(const std::vector& categories) +{ + if (m_type != "settings" || m_opt_categories == categories) + return false; + + m_opt_categories = categories; + m_name = wxEmptyString; + m_icon = m_empty_icon; + + auto categories_icon = Slic3r::GUI::get_category_icon(); + + for (auto& cat : m_opt_categories) + m_name += cat + "; "; + + wxBitmap *bmp = m_bitmap_cache->find(m_name.ToStdString()); + if (bmp == nullptr) { + std::vector bmps; + for (auto& cat : m_opt_categories) + bmps.emplace_back(categories_icon.find(cat) == categories_icon.end() ? + wxNullBitmap : categories_icon.at(cat)); + bmp = m_bitmap_cache->insert(m_name.ToStdString(), bmps); + } + + m_bmp = *bmp; + + return true; +} + // ***************************************************************************** // ---------------------------------------------------------------------------- // PrusaObjectDataViewModel // ---------------------------------------------------------------------------- +PrusaObjectDataViewModel::PrusaObjectDataViewModel() +{ + m_bitmap_cache = new Slic3r::GUI::BitmapCache; +} + +PrusaObjectDataViewModel::~PrusaObjectDataViewModel() +{ + for (auto object : m_objects) + delete object; + delete m_bitmap_cache; + m_bitmap_cache = nullptr; +} + wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name) { auto root = new PrusaObjectDataViewModelNode(name); @@ -386,34 +429,50 @@ wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name, const int ins wxDataViewItem PrusaObjectDataViewModel::AddChild( const wxDataViewItem &parent_item, const wxString &name, - const wxIcon& icon, + const wxBitmap& icon, const int extruder/* = 0*/, const bool create_frst_child/* = true*/) { PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID(); if (!root) return wxDataViewItem(0); - wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder); + const wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder); - if (root->GetChildren().Count() == 0 && create_frst_child) + if (create_frst_child && (root->GetChildren().Count() == 0 || + (root->GetChildren().Count() == 1 && root->GetNthChild(0)->m_type == "settings"))) { - auto icon_solid_mesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG); - auto node = new PrusaObjectDataViewModelNode(root, root->m_name, icon_solid_mesh, extruder_str, 0); + const auto icon_solid_mesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG); + const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, icon_solid_mesh, extruder_str, 0); root->Append(node); // notify control - wxDataViewItem child((void*)node); + const wxDataViewItem child((void*)node); ItemAdded(parent_item, child); } - auto volume_id = root->GetChildCount(); - auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, volume_id); + const auto volume_id = root->GetChildCount() > 0 && root->GetNthChild(0)->m_type == "settings" ? + root->GetChildCount() - 1 : root->GetChildCount(); + + const auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, volume_id); root->Append(node); // notify control - wxDataViewItem child((void*)node); + const wxDataViewItem child((void*)node); ItemAdded(parent_item, child); return child; } +wxDataViewItem PrusaObjectDataViewModel::AddSettingsChild(const wxDataViewItem &parent_item) +{ + PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID(); + if (!root) return wxDataViewItem(0); + + const auto node = new PrusaObjectDataViewModelNode(root); + root->Insert(node, 0); + // notify control + const wxDataViewItem child((void*)node); + ItemAdded(parent_item, child); + return child; +} + wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item) { auto ret_item = wxDataViewItem(0); @@ -438,7 +497,7 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item) //update volume_id value for remaining child-nodes auto children = node_parent->GetChildren(); - for (size_t i = 0; i < node_parent->GetChildCount(); i++) + for (size_t i = 0; i < node_parent->GetChildCount() && v_id>=0; i++) { auto volume_id = children[i]->GetVolumeId(); if (volume_id > v_id) @@ -460,9 +519,10 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item) delete node; // set m_containet to FALSE if parent has no child - if (node_parent && node_parent->GetChildCount() == 0){ + if (node_parent) { #ifndef __WXGTK__ - node_parent->m_container = false; + if (node_parent->GetChildCount() == 0) + node_parent->m_container = false; #endif //__WXGTK__ ret_item = parent; } @@ -522,6 +582,26 @@ wxDataViewItem PrusaObjectDataViewModel::GetItemById(int obj_idx) } +wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_idx) +{ + if (obj_idx >= m_objects.size()) { + printf("Error! Out of objects range.\n"); + return wxDataViewItem(0); + } + + auto parent = m_objects[obj_idx]; + if (parent->GetChildCount() == 0) { + printf("Error! Object has no one volume.\n"); + return wxDataViewItem(0); + } + + for (size_t i = 0; i < parent->GetChildCount(); i++) + if (parent->GetNthChild(i)->m_volume_id == volume_idx) + return wxDataViewItem(parent->GetNthChild(i)); + + return wxDataViewItem(0); +} + int PrusaObjectDataViewModel::GetIdByItem(wxDataViewItem& item) { wxASSERT(item.IsOk()); @@ -534,7 +614,7 @@ int PrusaObjectDataViewModel::GetIdByItem(wxDataViewItem& item) return it - m_objects.begin(); } -int PrusaObjectDataViewModel::GetVolumeIdByItem(wxDataViewItem& item) +int PrusaObjectDataViewModel::GetVolumeIdByItem(const wxDataViewItem& item) { wxASSERT(item.IsOk()); @@ -568,6 +648,12 @@ wxIcon& PrusaObjectDataViewModel::GetIcon(const wxDataViewItem &item) const return node->m_icon; } +wxBitmap& PrusaObjectDataViewModel::GetBitmap(const wxDataViewItem &item) const +{ + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + return node->m_bmp; +} + void PrusaObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const { wxASSERT(item.IsOk()); @@ -576,8 +662,8 @@ void PrusaObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem switch (col) { case 0:{ - const wxDataViewIconText data(node->m_name, node->m_icon); - variant << data; + const PrusaDataViewBitmapText data(node->m_name, node->m_bmp); + variant << data; break;} case 1: variant = node->m_copy; @@ -667,26 +753,35 @@ wxDataViewItem PrusaObjectDataViewModel::ReorganizeChildren(int current_volume_i if (!node_parent) // happens if item.IsOk()==false return ret_item; - PrusaObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id); + const size_t shift = node_parent->GetChildren().Item(0)->m_type == "settings" ? 1 : 0; + + PrusaObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id+shift); node_parent->GetChildren().Remove(deleted_node); ItemDeleted(parent, wxDataViewItem(deleted_node)); - node_parent->Insert(deleted_node, new_volume_id); + node_parent->Insert(deleted_node, new_volume_id+shift); ItemAdded(parent, wxDataViewItem(deleted_node)); + const auto settings_item = HasSettings(wxDataViewItem(deleted_node)); + if (settings_item) + ItemAdded(wxDataViewItem(deleted_node), settings_item); //update volume_id value for child-nodes auto children = node_parent->GetChildren(); int id_frst = current_volume_id < new_volume_id ? current_volume_id : new_volume_id; int id_last = current_volume_id > new_volume_id ? current_volume_id : new_volume_id; for (int id = id_frst; id <= id_last; ++id) - children[id]->SetVolumeId(id); + children[id+shift]->SetVolumeId(id); - return wxDataViewItem(node_parent->GetNthChild(new_volume_id)); + return wxDataViewItem(node_parent->GetNthChild(new_volume_id+shift)); } -// bool MyObjectTreeModel::IsEnabled(const wxDataViewItem &item, unsigned int col) const -// { -// -// } +bool PrusaObjectDataViewModel::IsEnabled(const wxDataViewItem &item, unsigned int col) const +{ + wxASSERT(item.IsOk()); + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + + // disable extruder selection for the "Settings" item + return !(col == 2 && node->m_extruder.IsEmpty()); +} wxDataViewItem PrusaObjectDataViewModel::GetParent(const wxDataViewItem &item) const { @@ -738,6 +833,87 @@ unsigned int PrusaObjectDataViewModel::GetChildren(const wxDataViewItem &parent, return count; } +wxDataViewItem PrusaObjectDataViewModel::HasSettings(const wxDataViewItem &item) const +{ + if (!item.IsOk()) + return wxDataViewItem(0); + + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + if (node->GetChildCount() == 0) + return wxDataViewItem(0); + + auto& children = node->GetChildren(); + if (children[0]->m_type == "settings") + return wxDataViewItem((void*)children[0]);; + + return wxDataViewItem(0); +} + +bool PrusaObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const +{ + if (!item.IsOk()) + return false; + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + return node->m_type == "settings"; +} + + + +void PrusaObjectDataViewModel::UpdateSettingsDigest(const wxDataViewItem &item, + const std::vector& categories) +{ + if (!item.IsOk()) return; + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + if (!node->update_settings_digest(categories)) + return; + ItemChanged(item); +} + +IMPLEMENT_VARIANT_OBJECT(PrusaDataViewBitmapText) +// --------------------------------------------------------- +// PrusaIconTextRenderer +// --------------------------------------------------------- + +bool PrusaBitmapTextRenderer::SetValue(const wxVariant &value) +{ + m_value << value; + return true; +} + +bool PrusaBitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const +{ + return false; +} + +bool PrusaBitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) +{ + int xoffset = 0; + + const wxBitmap& icon = m_value.GetBitmap(); + if (icon.IsOk()) + { + dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); + xoffset = icon.GetWidth() + 4; + } + + RenderText(m_value.GetText(), xoffset, rect, dc, state); + + return true; +} + +wxSize PrusaBitmapTextRenderer::GetSize() const +{ + if (!m_value.GetText().empty()) + { + wxSize size = GetTextExtent(m_value.GetText()); + + if (m_value.GetBitmap().IsOk()) + size.x += m_value.GetBitmap().GetWidth() + 4; + return size; + } + return wxSize(80, 20); +} + // ---------------------------------------------------------------------------- // PrusaDoubleSlider @@ -829,16 +1005,28 @@ wxSize PrusaDoubleSlider::DoGetBestSize() const void PrusaDoubleSlider::SetLowerValue(const int lower_val) { + m_selection = ssLower; m_lower_value = lower_val; + correct_lower_value(); Refresh(); Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); } void PrusaDoubleSlider::SetHigherValue(const int higher_val) { + m_selection = ssHigher; m_higher_value = higher_val; + correct_higher_value(); Refresh(); Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); } void PrusaDoubleSlider::SetMaxValue(const int max_value) @@ -905,6 +1093,13 @@ void PrusaDoubleSlider::get_size(int *w, int *h) is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim; } +double PrusaDoubleSlider::get_double_value(const SelectedSlider& selection) const +{ + if (m_values.empty()) + return 0.0; + return m_values[selection == ssLower ? m_lower_value : m_higher_value].second; +} + void PrusaDoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos) { const double step = get_scroll_step(); @@ -1010,7 +1205,7 @@ wxString PrusaDoubleSlider::get_label(const SelectedSlider& selection) const void PrusaDoubleSlider::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const { - if (m_is_one_layer && selection != m_selection || !selection) + if ((m_is_one_layer || m_higher_value==m_lower_value) && selection != m_selection || !selection) return; wxCoord text_width, text_height; const wxString label = get_label(selection); @@ -1178,6 +1373,20 @@ bool PrusaDoubleSlider::is_point_in_rect(const wxPoint& pt, const wxRect& rect) return false; } +void PrusaDoubleSlider::ChangeOneLayerLock() +{ + m_is_one_layer = !m_is_one_layer; + m_selection == ssLower ? correct_lower_value() : correct_higher_value(); + if (!m_selection) m_selection = ssHigher; + + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + void PrusaDoubleSlider::OnLeftDown(wxMouseEvent& event) { this->CaptureMouse(); @@ -1245,6 +1454,10 @@ void PrusaDoubleSlider::OnMotion(wxMouseEvent& event) Refresh(); Update(); event.Skip(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); } void PrusaDoubleSlider::OnLeftUp(wxMouseEvent& event) diff --git a/xs/src/slic3r/GUI/wxExtensions.hpp b/xs/src/slic3r/GUI/wxExtensions.hpp index 76e59f2eb7..51c02035c0 100644 --- a/xs/src/slic3r/GUI/wxExtensions.hpp +++ b/xs/src/slic3r/GUI/wxExtensions.hpp @@ -144,6 +144,49 @@ public: #endif //__WXMSW__ // ***************************************************************************** + +// ---------------------------------------------------------------------------- +// PrusaDataViewBitmapText: helper class used by PrusaBitmapTextRenderer +// ---------------------------------------------------------------------------- + +class PrusaDataViewBitmapText : public wxObject +{ +public: + PrusaDataViewBitmapText(const wxString &text = wxEmptyString, + const wxBitmap& bmp = wxNullBitmap) : + m_text(text), m_bmp(bmp) + { } + + PrusaDataViewBitmapText(const PrusaDataViewBitmapText &other) + : wxObject(), + m_text(other.m_text), + m_bmp(other.m_bmp) + { } + + void SetText(const wxString &text) { m_text = text; } + wxString GetText() const { return m_text; } + void SetBitmap(const wxIcon &icon) { m_bmp = icon; } + const wxBitmap &GetBitmap() const { return m_bmp; } + + bool IsSameAs(const PrusaDataViewBitmapText& other) const { + return m_text == other.m_text && m_bmp.IsSameAs(other.m_bmp); + } + + bool operator==(const PrusaDataViewBitmapText& other) const { + return IsSameAs(other); + } + + bool operator!=(const PrusaDataViewBitmapText& other) const { + return !IsSameAs(other); + } + +private: + wxString m_text; + wxBitmap m_bmp; +}; +DECLARE_VARIANT_OBJECT(PrusaDataViewBitmapText) + + // ---------------------------------------------------------------------------- // PrusaObjectDataViewModelNode: a node inside PrusaObjectDataViewModel // ---------------------------------------------------------------------------- @@ -155,7 +198,9 @@ class PrusaObjectDataViewModelNode { PrusaObjectDataViewModelNode* m_parent; MyObjectTreeModelNodePtrArray m_children; - wxIcon m_empty_icon; + wxIcon m_empty_icon; + wxBitmap m_empty_bmp; + std::vector< std::string > m_opt_categories; public: PrusaObjectDataViewModelNode(const wxString &name, const int instances_count=1) { m_parent = NULL; @@ -174,18 +219,31 @@ public: PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent, const wxString& sub_obj_name, - const wxIcon& icon, + const wxBitmap& bmp, const wxString& extruder, const int volume_id=-1) { m_parent = parent; m_name = sub_obj_name; m_copy = wxEmptyString; - m_icon = icon; + m_bmp = bmp; m_type = "volume"; m_volume_id = volume_id; - m_extruder = extruder; + m_extruder = extruder; +#ifdef __WXGTK__ + // it's necessary on GTK because of control have to know if this item will be container + // in another case you couldn't to add subitem for this item + // it will be produce "segmentation fault" + m_container = true; +#endif //__WXGTK__ set_part_action_icon(); - } + } + + PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent) : + m_parent(parent), + m_name("Settings to modified"), + m_copy(wxEmptyString), + m_type("settings"), + m_extruder(wxEmptyString) {} ~PrusaObjectDataViewModelNode() { @@ -200,9 +258,10 @@ public: wxString m_name; wxIcon& m_icon = m_empty_icon; + wxBitmap& m_bmp = m_empty_bmp; wxString m_copy; std::string m_type; - int m_volume_id; + int m_volume_id = -2; bool m_container = false; wxString m_extruder = "default"; wxBitmap m_action_icon; @@ -226,6 +285,8 @@ public: } void Insert(PrusaObjectDataViewModelNode* child, unsigned int n) { + if (!m_container) + m_container = true; m_children.Insert(child, n); } void Append(PrusaObjectDataViewModelNode* child) @@ -258,9 +319,9 @@ public: switch (col) { case 0:{ - wxDataViewIconText data; + PrusaDataViewBitmapText data; data << variant; - m_icon = data.GetIcon(); + m_bmp = data.GetBitmap(); m_name = data.GetText(); return true;} case 1: @@ -281,6 +342,11 @@ public: { m_icon = icon; } + + void SetBitmap(const wxBitmap &icon) + { + m_bmp = icon; + } void SetType(const std::string& type){ m_type = type; @@ -326,6 +392,7 @@ public: // Set action icons for node void set_object_action_icon(); void set_part_action_icon(); + bool update_settings_digest(const std::vector& categories); }; // ---------------------------------------------------------------------------- @@ -336,26 +403,24 @@ class PrusaObjectDataViewModel :public wxDataViewModel { std::vector m_objects; public: - PrusaObjectDataViewModel(){} - ~PrusaObjectDataViewModel() - { - for (auto object : m_objects) - delete object; - } + PrusaObjectDataViewModel(); + ~PrusaObjectDataViewModel(); wxDataViewItem Add(const wxString &name); wxDataViewItem Add(const wxString &name, const int instances_count); wxDataViewItem AddChild(const wxDataViewItem &parent_item, const wxString &name, - const wxIcon& icon, - const int = 0, + const wxBitmap& icon, + const int extruder = 0, const bool create_frst_child = true); + wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); wxDataViewItem Delete(const wxDataViewItem &item); void DeleteAll(); void DeleteChildren(wxDataViewItem& parent); wxDataViewItem GetItemById(int obj_idx); + wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx); int GetIdByItem(wxDataViewItem& item); - int GetVolumeIdByItem(wxDataViewItem& item); + int GetVolumeIdByItem(const wxDataViewItem& item); bool IsEmpty() { return m_objects.empty(); } // helper method for wxLog @@ -363,6 +428,7 @@ public: wxString GetName(const wxDataViewItem &item) const; wxString GetCopy(const wxDataViewItem &item) const; wxIcon& GetIcon(const wxDataViewItem &item) const; + wxBitmap& GetBitmap(const wxDataViewItem &item) const; // helper methods to change the model @@ -383,8 +449,7 @@ public: int new_volume_id, const wxDataViewItem &parent); -// virtual bool IsEnabled(const wxDataViewItem &item, -// unsigned int col) const override; + virtual bool IsEnabled(const wxDataViewItem &item, unsigned int col) const override; virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override; virtual bool IsContainer(const wxDataViewItem &item) const override; @@ -394,8 +459,35 @@ public: // Is the container just a header or an item with all columns // In our case it is an item with all columns virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } + + wxDataViewItem HasSettings(const wxDataViewItem &item) const; + bool IsSettingsItem(const wxDataViewItem &item) const; + void UpdateSettingsDigest(const wxDataViewItem &item, const std::vector& categories); }; +// ---------------------------------------------------------------------------- +// PrusaBitmapTextRenderer +// ---------------------------------------------------------------------------- + +class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer +{ +public: + PrusaBitmapTextRenderer( wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, + int align = wxDVR_DEFAULT_ALIGNMENT): + wxDataViewCustomRenderer(wxT("wxObject"), mode, align) {} + + bool SetValue(const wxVariant &value); + bool GetValue(wxVariant &value) const; + + virtual bool Render(wxRect cell, wxDC *dc, int state); + virtual wxSize GetSize() const; + + virtual bool HasEditorCtrl() const { return false; } + +private: +// wxDataViewIconText m_value; + PrusaDataViewBitmapText m_value; +}; // ---------------------------------------------------------------------------- @@ -516,7 +608,7 @@ public: int maxValue, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, - long style = wxSL_HORIZONTAL, + long style = wxSL_VERTICAL, const wxValidator& val = wxDefaultValidator, const wxString& name = wxEmptyString); ~PrusaDoubleSlider(){} @@ -528,6 +620,8 @@ public: return m_higher_value; } int GetActiveValue() const; + double GetLowerValueD() const { return get_double_value(ssLower); } + double GetHigherValueD() const { return get_double_value(ssHigher); } wxSize DoGetBestSize() const override; void SetLowerValue(const int lower_val); void SetHigherValue(const int higher_val); @@ -538,6 +632,7 @@ public: void SetSliderValues(const std::vector>& values) { m_values = values; } + void ChangeOneLayerLock(); void OnPaint(wxPaintEvent& ){ render();} void OnLeftDown(wxMouseEvent& event); @@ -583,6 +678,7 @@ protected: wxCoord get_position_from_value(const int value); wxSize get_size(); void get_size(int *w, int *h); + double get_double_value(const SelectedSlider& selection) const; private: int m_min_value; diff --git a/xs/src/slic3r/Utils/Serial.cpp b/xs/src/slic3r/Utils/Serial.cpp index 183119b442..601719b50e 100644 --- a/xs/src/slic3r/Utils/Serial.cpp +++ b/xs/src/slic3r/Utils/Serial.cpp @@ -231,7 +231,12 @@ std::vector scan_serial_ports_extended() spi.port = path; #ifdef __linux__ auto friendly_name = sysfs_tty_prop(name, "product"); - spi.friendly_name = friendly_name ? (boost::format("%1% (%2%)") % *friendly_name % path).str() : path; + if (friendly_name) { + spi.is_printer = looks_like_printer(*friendly_name); + spi.friendly_name = (boost::format("%1% (%2%)") % *friendly_name % path).str(); + } else { + spi.friendly_name = path; + } auto vid = sysfs_tty_prop_hex(name, "idVendor"); auto pid = sysfs_tty_prop_hex(name, "idProduct"); if (vid && pid) { diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index 6700a47657..a4d656616e 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -96,26 +96,21 @@ void add_frequently_changed_parameters(SV *ui_parent, SV *ui_sizer, SV *ui_p_siz void set_print_callback_event(Print *print, int id) %code%{ Slic3r::GUI::set_print_callback_event(print, id); %}; -void add_expert_mode_part( SV *ui_parent, SV *ui_sizer, - Model *model, - int event_object_selection_changed, - int event_object_settings_changed, - int event_remove_object, - int event_update_scene) - %code%{ Slic3r::GUI::add_expert_mode_part( (wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"), - (wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"), - *model, - event_object_selection_changed, - event_object_settings_changed, - event_remove_object, - event_update_scene); %}; +void set_model_events_from_perl(Model *model, + int event_object_selection_changed, + int event_object_settings_changed, + int event_remove_object, + int event_update_scene) + %code%{ Slic3r::GUI::set_model_events_from_perl(*model, + event_object_selection_changed, + event_object_settings_changed, + event_remove_object, + event_update_scene); %}; void set_objects_from_perl( SV *ui_parent, SV *frequently_changed_parameters_sizer, - SV *expert_mode_part_sizer, - SV *scrolled_window_sizer, + SV *info_sizer, SV *btn_export_gcode, - SV *btn_export_stl, SV *btn_reslice, SV *btn_print, SV *btn_send_gcode, @@ -123,10 +118,8 @@ void set_objects_from_perl( SV *ui_parent, %code%{ Slic3r::GUI::set_objects_from_perl( (wxWindow *)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"), (wxBoxSizer *)wxPli_sv_2_object(aTHX_ frequently_changed_parameters_sizer, "Wx::BoxSizer"), - (wxBoxSizer *)wxPli_sv_2_object(aTHX_ expert_mode_part_sizer, "Wx::BoxSizer"), - (wxBoxSizer *)wxPli_sv_2_object(aTHX_ scrolled_window_sizer, "Wx::BoxSizer"), + (wxBoxSizer *)wxPli_sv_2_object(aTHX_ info_sizer, "Wx::BoxSizer"), (wxButton *)wxPli_sv_2_object(aTHX_ btn_export_gcode, "Wx::Button"), - (wxButton *)wxPli_sv_2_object(aTHX_ btn_export_stl, "Wx::Button"), (wxButton *)wxPli_sv_2_object(aTHX_ btn_reslice, "Wx::Button"), (wxButton *)wxPli_sv_2_object(aTHX_ btn_print, "Wx::Button"), (wxButton *)wxPli_sv_2_object(aTHX_ btn_send_gcode, "Wx::Button"), @@ -161,6 +154,9 @@ void unselect_objects() void select_current_object(int idx) %code%{ Slic3r::GUI::select_current_object(idx); %}; +void select_current_volume(int idx, int vol_idx) + %code%{ Slic3r::GUI::select_current_volume(idx, vol_idx); %}; + void remove_obj() %code%{ Slic3r::GUI::remove(); %}; @@ -179,16 +175,28 @@ void desktop_open_datadir_folder() void fix_model_by_win10_sdk_gui(ModelObject *model_object_src, Print *print, Model *model_dst) %code%{ Slic3r::fix_model_by_win10_sdk_gui(*model_object_src, *print, *model_dst); %}; -void set_3DScene(SV *scene) - %code%{ Slic3r::GUI::set_3DScene((_3DScene *)wxPli_sv_2_object(aTHX_ scene, "Slic3r::Model::3DScene") ); %}; - void register_on_request_update_callback(SV* callback) %code%{ Slic3r::GUI::g_on_request_update_callback.register_callback(callback); %}; void deregister_on_request_update_callback() %code%{ Slic3r::GUI::g_on_request_update_callback.deregister_callback(); %}; -//void create_double_slider(SV *ui_parent, SV *ui_ds) -// %code%{ Slic3r::GUI::create_double_slider( (wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"), -// (wxControl*)wxPli_sv_2_object(aTHX_ ui_ds, "Wx::Control")); %}; +void create_double_slider(SV *ui_parent, SV *ui_sizer, SV *ui_canvas) + %code%{ Slic3r::GUI::create_double_slider( (wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"), + (wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"), + (wxGLCanvas*)wxPli_sv_2_object(aTHX_ ui_canvas, "Wx::GLCanvas")); %}; +void update_double_slider(bool force_sliders_full_range) + %code%{ Slic3r::GUI::update_double_slider(force_sliders_full_range); %}; + +void reset_double_slider() + %code%{ Slic3r::GUI::reset_double_slider(); %}; + +void enable_action_buttons(bool enable) + %code%{ Slic3r::GUI::enable_action_buttons(enable); %}; + +void save_window_size(SV *window, std::string name) + %code%{ Slic3r::GUI::save_window_size((wxTopLevelWindow*)wxPli_sv_2_object(aTHX_ window, "Wx::TopLevelWindow"), name); %}; + +void restore_window_size(SV *window, std::string name) + %code%{ Slic3r::GUI::restore_window_size((wxTopLevelWindow*)wxPli_sv_2_object(aTHX_ window, "Wx::TopLevelWindow"), name); %}; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 4c3c5501e1..c3e4ba3b7c 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -767,6 +767,15 @@ get_first_volume_id(canvas, obj_idx) OUTPUT: RETVAL +int +get_in_object_volume_id(canvas, scene_vol_idx) + SV *canvas; + int scene_vol_idx; + CODE: + RETVAL = _3DScene::get_in_object_volume_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), scene_vol_idx); + OUTPUT: + RETVAL + std::vector load_model(canvas, model, obj_idx) SV *canvas; diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp index 4f09fb5214..efd6c9ae63 100644 --- a/xs/xsp/Layer.xsp +++ b/xs/xsp/Layer.xsp @@ -17,8 +17,6 @@ %code%{ RETVAL = &THIS->thin_fills; %}; Ref fill_surfaces() %code%{ RETVAL = &THIS->fill_surfaces; %}; - Ref perimeter_surfaces() - %code%{ RETVAL = &THIS->perimeter_surfaces; %}; Polygons bridged() %code%{ RETVAL = THIS->bridged; %}; Ref unsupported_bridge_edges() diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index a5925775df..101faf5643 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -340,9 +340,19 @@ ModelMaterial::attributes() %code%{ RETVAL = &THIS->mesh; %}; bool modifier() - %code%{ RETVAL = THIS->modifier; %}; + %code%{ RETVAL = THIS->is_modifier(); %}; void set_modifier(bool modifier) - %code%{ THIS->modifier = modifier; %}; + %code%{ THIS->set_type(modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); %}; + bool model_part() + %code%{ RETVAL = THIS->is_model_part(); %}; + bool support_enforcer() + %code%{ RETVAL = THIS->is_support_enforcer(); %}; + void set_support_enforcer() + %code%{ THIS->set_type(ModelVolume::SUPPORT_ENFORCER); %}; + bool support_blocker() + %code%{ RETVAL = THIS->is_support_blocker(); %}; + void set_support_blocker() + %code%{ THIS->set_type(ModelVolume::SUPPORT_BLOCKER); %}; size_t split(unsigned int max_extruders); @@ -358,15 +368,28 @@ ModelMaterial::attributes() %code%{ RETVAL = THIS->rotation; %}; double scaling_factor() %code%{ RETVAL = THIS->scaling_factor; %}; +#if ENABLE_MODELINSTANCE_3D_OFFSET + Vec2d* offset() + %code%{ RETVAL = new Vec2d(THIS->get_offset(X), THIS->get_offset(Y)); %}; +#else Ref offset() %code%{ RETVAL = &THIS->offset; %}; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET void set_rotation(double val) %code%{ THIS->rotation = val; THIS->get_object()->invalidate_bounding_box(); %}; void set_scaling_factor(double val) %code%{ THIS->scaling_factor = val; THIS->get_object()->invalidate_bounding_box(); %}; +#if ENABLE_MODELINSTANCE_3D_OFFSET + void set_offset(Vec2d *offset) + %code%{ + THIS->set_offset(X, (*offset)(0)); + THIS->set_offset(Y, (*offset)(1)); + %}; +#else void set_offset(Vec2d *offset) %code%{ THIS->offset = *offset; %}; +#endif // ENABLE_MODELINSTANCE_3D_OFFSET void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; void transform_polygon(Polygon* polygon) const; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index f29cb52fc4..0424d8b7b2 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -103,6 +103,12 @@ _constant() %code%{ RETVAL = THIS->print_statistics().total_weight; %}; double total_cost() %code%{ RETVAL = THIS->print_statistics().total_cost; %}; + double total_wipe_tower_cost() + %code%{ RETVAL = THIS->print_statistics().total_wipe_tower_cost; %}; + double total_wipe_tower_filament() + %code%{ RETVAL = THIS->print_statistics().total_wipe_tower_filament; %}; + int wipe_tower_number_of_toolchanges() + %code%{ RETVAL = THIS->wipe_tower_data().number_of_toolchanges; %}; PrintObjectPtrs* objects() %code%{ RETVAL = const_cast(&THIS->objects()); %}; void clear_objects(); diff --git a/xs/xsp/ProgressStatusBar.xsp b/xs/xsp/ProgressStatusBar.xsp index c089cfd7c1..703a53b679 100644 --- a/xs/xsp/ProgressStatusBar.xsp +++ b/xs/xsp/ProgressStatusBar.xsp @@ -3,6 +3,7 @@ %{ #include #include "slic3r/GUI/ProgressStatusBar.hpp" +#include "slic3r/GUI/GUI.hpp" %} %name{Slic3r::GUI::ProgressStatusBar} class ProgressStatusBar { @@ -37,7 +38,7 @@ %code%{ THIS->embed(); %}; void SetStatusText(const char *txt) - %code%{ THIS->set_status_text(txt); %}; + %code%{ THIS->set_status_text(_(txt)); %}; void SetCancelCallback(SV* callback) %code%{ THIS->m_perl_cancel_callback.register_callback(callback); THIS->show_cancel_button();%}; diff --git a/xs/xsp/XS.xsp b/xs/xsp/XS.xsp index e900532aaf..04969a7f92 100644 --- a/xs/xsp/XS.xsp +++ b/xs/xsp/XS.xsp @@ -48,6 +48,11 @@ trace(level, message) CODE: Slic3r::trace(level, message); +void +disable_multi_threading() + CODE: + Slic3r::disable_multi_threading(); + void set_var_dir(dir) char *dir; From 153bd108a209a102f4f99d20c531c8ebf3ef3a7a Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 17 Sep 2018 15:32:54 +0200 Subject: [PATCH 16/36] WIP fix of PostProcessor on Linux --- xs/src/libslic3r/GCode/PostProcessor.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/GCode/PostProcessor.cpp b/xs/src/libslic3r/GCode/PostProcessor.cpp index f3b28f3985..438d6d70b7 100644 --- a/xs/src/libslic3r/GCode/PostProcessor.cpp +++ b/xs/src/libslic3r/GCode/PostProcessor.cpp @@ -1,18 +1,23 @@ #include "PostProcessor.hpp" -namespace Slic3r { #ifdef WIN32 +namespace Slic3r { + //FIXME Ignore until we include boost::process void run_post_process_scripts(const std::string &path, const PrintConfig &config) { } +} // namespace Slic3r + #else #include +namespace Slic3r { + void run_post_process_scripts(const std::string &path, const PrintConfig &config) { if (config.post_process.values.empty()) @@ -50,6 +55,6 @@ void run_post_process_scripts(const std::string &path, const PrintConfig &config } } -#endif - } // namespace Slic3r + +#endif From 27bba453312d59898f773aa54d7b625e65fc501e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 17 Sep 2018 17:17:38 +0200 Subject: [PATCH 17/36] Fix of compilation on Linux, disabled -Wreorder --- xs/CMakeLists.txt | 2 +- xs/src/libslic3r/GCode/PostProcessor.cpp | 4 ++-- xs/src/libslic3r/Print.hpp | 1 + xs/src/libslic3r/utils.cpp | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 878ce4dfea..668e6a293a 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -11,7 +11,7 @@ list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/) if (CMAKE_SYSTEM_NAME STREQUAL "Linux") # Workaround for an old CMake, which does not understand CMAKE_CXX_STANDARD. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall" ) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-reorder" ) find_package(PkgConfig REQUIRED) endif() diff --git a/xs/src/libslic3r/GCode/PostProcessor.cpp b/xs/src/libslic3r/GCode/PostProcessor.cpp index 438d6d70b7..c04aeae3cb 100644 --- a/xs/src/libslic3r/GCode/PostProcessor.cpp +++ b/xs/src/libslic3r/GCode/PostProcessor.cpp @@ -1,7 +1,7 @@ #include "PostProcessor.hpp" - -#ifdef WIN32 +#if 1 +//#ifdef WIN32 namespace Slic3r { diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 19da875ab7..7f88110bc3 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -2,6 +2,7 @@ #define slic3r_Print_hpp_ #include "libslic3r.h" +#include #include #include #include diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 46ee65a35a..2fbfcae814 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -205,7 +205,7 @@ int rename_file(const std::string &from, const std::string &to) #else - boost::nowide::remove(from.c_str()); + boost::nowide::remove(to.c_str()); ec = boost::nowide::rename(from.c_str(), to.c_str()); #endif From 3ddaccb6410478ad02d8c0e02d6d8e6eb1785b9f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Sep 2018 10:09:58 +0200 Subject: [PATCH 18/36] Replaced CONFESS with throw std::exception in libslic3r, so now libslic3r should be compilable without Perl. --- src/slabasebed.cpp | 6 - src/slic3r.cpp | 2 - xs/CMakeLists.txt | 2 + xs/src/callback.cpp | 155 +++++++++++++++ xs/src/callback.hpp | 32 ++++ xs/src/libslic3r/BoundingBox.hpp | 4 +- xs/src/libslic3r/ExPolygon.cpp | 3 +- .../libslic3r/ExtrusionEntityCollection.hpp | 4 +- xs/src/libslic3r/Fill/FillBase.cpp | 2 +- xs/src/libslic3r/Flow.cpp | 4 +- xs/src/libslic3r/Format/AMF.cpp | 2 +- xs/src/libslic3r/GCode.cpp | 6 +- xs/src/libslic3r/GCode/PrintExtents.cpp | 2 +- xs/src/libslic3r/Geometry.cpp | 2 +- xs/src/libslic3r/Model.cpp | 5 +- xs/src/libslic3r/Polygon.cpp | 2 +- xs/src/libslic3r/Polyline.cpp | 3 +- xs/src/libslic3r/PolylineCollection.cpp | 3 +- xs/src/libslic3r/Print.cpp | 3 +- xs/src/libslic3r/PrintRegion.cpp | 4 +- xs/src/libslic3r/TriangleMesh.cpp | 8 +- xs/src/libslic3r/Utils.hpp | 20 -- xs/src/libslic3r/libslic3r.h | 8 - xs/src/libslic3r/utils.cpp | 181 ------------------ xs/src/slic3r/GUI/3DScene.cpp | 2 +- .../slic3r/GUI/BackgroundSlicingProcess.cpp | 1 + xs/src/slic3r/GUI/GLToolbar.hpp | 2 +- xs/src/slic3r/GUI/GUI.hpp | 2 +- xs/src/slic3r/GUI/ProgressStatusBar.hpp | 2 +- xs/src/xsinit.h | 35 ++++ 30 files changed, 261 insertions(+), 246 deletions(-) create mode 100644 xs/src/callback.cpp create mode 100644 xs/src/callback.hpp diff --git a/src/slabasebed.cpp b/src/slabasebed.cpp index b11486f90a..255ce2cc34 100644 --- a/src/slabasebed.cpp +++ b/src/slabasebed.cpp @@ -10,12 +10,6 @@ const std::string USAGE_STR = { "Usage: slabasebed stlfilename.stl" }; -void confess_at(const char * /*file*/, - int /*line*/, - const char * /*func*/, - const char * /*pat*/, - ...) {} - int main(const int argc, const char *argv[]) { using namespace Slic3r; using std::cout; using std::endl; diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 4cff9b38bb..085b396862 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -14,8 +14,6 @@ using namespace Slic3r; -void confess_at(const char *file, int line, const char *func, const char *pat, ...){} - int main(int argc, char **argv) { diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 668e6a293a..69e03779a2 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -518,6 +518,8 @@ add_library(XS ${XS_SHARED_LIBRARY_TYPE} ${LIBDIR}/libslic3r/utils.cpp ${LIBDIR}/slic3r/GUI/wxPerlIface.cpp ${LIBDIR}/perlglue.cpp + ${LIBDIR}/callback.cpp + ${LIBDIR}/callback.hpp ${LIBDIR}/ppport.h ${LIBDIR}/xsinit.h ${CMAKE_CURRENT_LIST_DIR}/xsp/my.map diff --git a/xs/src/callback.cpp b/xs/src/callback.cpp new file mode 100644 index 0000000000..05b4f9e507 --- /dev/null +++ b/xs/src/callback.cpp @@ -0,0 +1,155 @@ +#include "callback.hpp" + +#include + +void PerlCallback::register_callback(void *sv) +{ + if (! SvROK((SV*)sv) || SvTYPE(SvRV((SV*)sv)) != SVt_PVCV) + croak("Not a Callback %_ for PerlFunction", (SV*)sv); + if (m_callback) + SvSetSV((SV*)m_callback, (SV*)sv); + else + m_callback = newSVsv((SV*)sv); +} + +void PerlCallback::deregister_callback() +{ + if (m_callback) { + sv_2mortal((SV*)m_callback); + m_callback = nullptr; + } +} + +void PerlCallback::call() const +{ + if (! m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(int i) const +{ + if (! m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSViv(i))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(int i, int j) const +{ + if (! m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSViv(i))); + XPUSHs(sv_2mortal(newSViv(j))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(const std::vector& ints) const +{ + if (! m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + for (int i : ints) + { + XPUSHs(sv_2mortal(newSViv(i))); + } + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(double a) const +{ + if (!m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVnv(a))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(double a, double b) const +{ + if (!m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVnv(a))); + XPUSHs(sv_2mortal(newSVnv(b))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(double a, double b, double c) const +{ + if (!m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVnv(a))); + XPUSHs(sv_2mortal(newSVnv(b))); + XPUSHs(sv_2mortal(newSVnv(c))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(double a, double b, double c, double d) const +{ + if (!m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVnv(a))); + XPUSHs(sv_2mortal(newSVnv(b))); + XPUSHs(sv_2mortal(newSVnv(c))); + XPUSHs(sv_2mortal(newSVnv(d))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(bool b) const +{ + call(b ? 1 : 0); +} \ No newline at end of file diff --git a/xs/src/callback.hpp b/xs/src/callback.hpp new file mode 100644 index 0000000000..9530829f8c --- /dev/null +++ b/xs/src/callback.hpp @@ -0,0 +1,32 @@ +#ifndef slic3r_PerlCallback_hpp_ +#define slic3r_PerlCallback_hpp_ + +#include + +#include "libslic3r.h" + +namespace Slic3r { + +class PerlCallback { +public: + PerlCallback(void *sv) : m_callback(nullptr) { this->register_callback(sv); } + PerlCallback() : m_callback(nullptr) {} + ~PerlCallback() { this->deregister_callback(); } + void register_callback(void *sv); + void deregister_callback(); + void call() const; + void call(int i) const; + void call(int i, int j) const; + void call(const std::vector& ints) const; + void call(double a) const; + void call(double a, double b) const; + void call(double a, double b, double c) const; + void call(double a, double b, double c, double d) const; + void call(bool b) const; +private: + void *m_callback; +}; + +} // namespace Slic3r + +#endif /* slic3r_PerlCallback_hpp_ */ diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp index db56c765cf..b3a1c2f5c8 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -21,7 +21,7 @@ public: BoundingBoxBase(const std::vector& points) : min(PointClass::Zero()), max(PointClass::Zero()) { if (points.empty()) - CONFESS("Empty point set supplied to BoundingBoxBase constructor"); + throw std::invalid_argument("Empty point set supplied to BoundingBoxBase constructor"); typename std::vector::const_iterator it = points.begin(); this->min = *it; @@ -65,7 +65,7 @@ public: BoundingBox3Base(const std::vector& points) { if (points.empty()) - CONFESS("Empty point set supplied to BoundingBox3Base constructor"); + throw std::invalid_argument("Empty point set supplied to BoundingBox3Base constructor"); typename std::vector::const_iterator it = points.begin(); this->min = *it; this->max = *it; diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp index 4294fe543a..00bb4ffe4e 100644 --- a/xs/src/libslic3r/ExPolygon.cpp +++ b/xs/src/libslic3r/ExPolygon.cpp @@ -437,7 +437,8 @@ ExPolygon::triangulate_pp(Polygons* polygons) const // perform triangulation std::list output; int res = TPPLPartition().Triangulate_MONO(&input, &output); - if (res != 1) CONFESS("Triangulation failed"); + if (res != 1) + throw std::runtime_error("Triangulation failed"); // convert output polygons for (std::list::iterator poly = output.begin(); poly != output.end(); ++poly) { diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp index ee0d3d5cd4..230c041601 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp @@ -88,7 +88,7 @@ public: // Following methods shall never be called on an ExtrusionEntityCollection. Polyline as_polyline() const { - CONFESS("Calling as_polyline() on a ExtrusionEntityCollection"); + throw std::runtime_error("Calling as_polyline() on a ExtrusionEntityCollection"); return Polyline(); }; @@ -98,7 +98,7 @@ public: } double length() const override { - CONFESS("Calling length() on a ExtrusionEntityCollection"); + throw std::runtime_error("Calling length() on a ExtrusionEntityCollection"); return 0.; } }; diff --git a/xs/src/libslic3r/Fill/FillBase.cpp b/xs/src/libslic3r/Fill/FillBase.cpp index 88645b3d95..7a99e84f71 100644 --- a/xs/src/libslic3r/Fill/FillBase.cpp +++ b/xs/src/libslic3r/Fill/FillBase.cpp @@ -34,7 +34,7 @@ Fill* Fill::new_from_type(const InfillPattern type) case ipArchimedeanChords: return new FillArchimedeanChords(); case ipHilbertCurve: return new FillHilbertCurve(); case ipOctagramSpiral: return new FillOctagramSpiral(); - default: CONFESS("unknown type"); return nullptr; + default: throw std::invalid_argument("unknown type");; } } diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp index 9ad482dafb..e71b935dbb 100644 --- a/xs/src/libslic3r/Flow.cpp +++ b/xs/src/libslic3r/Flow.cpp @@ -28,7 +28,7 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent { // we need layer height unless it's a bridge if (height <= 0 && bridge_flow_ratio == 0) - CONFESS("Invalid flow height supplied to new_from_config_width()"); + throw std::invalid_argument("Invalid flow height supplied to new_from_config_width()"); float w; if (bridge_flow_ratio > 0) { @@ -53,7 +53,7 @@ Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, { // we need layer height unless it's a bridge if (height <= 0 && !bridge) - CONFESS("Invalid flow height supplied to new_from_spacing()"); + throw std::invalid_argument("Invalid flow height supplied to new_from_spacing()"); // Calculate width from spacing. // For normal extrusons, extrusion width is wider than the spacing due to the rounding and squishing of the extrusions. // For bridge extrusions, the extrusions are placed with a tiny BRIDGE_EXTRA_SPACING gaps between the threads. diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp index 803d9ee54f..458ce79de7 100644 --- a/xs/src/libslic3r/Format/AMF.cpp +++ b/xs/src/libslic3r/Format/AMF.cpp @@ -800,7 +800,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c for (ModelVolume *volume : object->volumes) { vertices_offsets.push_back(num_vertices); if (! volume->mesh.repaired) - CONFESS("store_amf() requires repair()"); + throw std::runtime_error("store_amf() requires repair()"); auto &stl = volume->mesh.stl; if (stl.v_shared == nullptr) stl_generate_shared_vertices(&stl); diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 3c6e6de530..d10705c183 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -2204,7 +2204,7 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string des else if (const ExtrusionLoop* loop = dynamic_cast(&entity)) return this->extrude_loop(*loop, description, speed, lower_layer_edge_grid); else { - CONFESS("Invalid argument supplied to extrude()"); + throw std::invalid_argument("Invalid argument supplied to extrude()"); return ""; } } @@ -2389,7 +2389,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } else if (path.role() == erGapFill) { speed = m_config.get_abs_value("gap_fill_speed"); } else { - CONFESS("Invalid speed"); + throw std::invalid_argument("Invalid speed"); } } if (this->on_first_layer()) @@ -2715,7 +2715,7 @@ void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, co } else if (type != "infills") { - CONFESS("Unknown parameter!"); + throw std::invalid_argument("Unknown parameter!"); return; } diff --git a/xs/src/libslic3r/GCode/PrintExtents.cpp b/xs/src/libslic3r/GCode/PrintExtents.cpp index 00b8838b6c..92a58fdf06 100644 --- a/xs/src/libslic3r/GCode/PrintExtents.cpp +++ b/xs/src/libslic3r/GCode/PrintExtents.cpp @@ -93,7 +93,7 @@ static BoundingBoxf extrusionentity_extents(const ExtrusionEntity *extrusion_ent auto *extrusion_entity_collection = dynamic_cast(extrusion_entity); if (extrusion_entity_collection != nullptr) return extrusionentity_extents(*extrusion_entity_collection); - CONFESS("Unexpected extrusion_entity type in extrusionentity_extents()"); + throw std::runtime_error("Unexpected extrusion_entity type in extrusionentity_extents()"); return BoundingBoxf(); } diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp index b0ded2d04c..87b4e223d4 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/xs/src/libslic3r/Geometry.cpp @@ -432,7 +432,7 @@ Pointfs arrange(size_t num_parts, const Vec2d &part_size, coordf_t gap, const Bo size_t cellw = size_t(floor((bed_bbox.size()(0) + gap) / cell_size(0))); size_t cellh = size_t(floor((bed_bbox.size()(1) + gap) / cell_size(1))); if (num_parts > cellw * cellh) - CONFESS(PRINTF_ZU " parts won't fit in your print area!\n", num_parts); + throw std::invalid_argument(PRINTF_ZU " parts won't fit in your print area!\n", num_parts); // Get a bounding box of cellw x cellh cells, centered at the center of the bed. Vec2d cells_size(cellw * cell_size(0) - gap, cellh * cell_size(1) - gap); diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 8e8396e7a9..ec7447352b 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -340,7 +340,7 @@ void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb) Pointfs model_sizes(copies_num-1, to_2d(this->bounding_box().size())); Pointfs positions; if (! _arrange(model_sizes, dist, bb, positions)) - CONFESS("Cannot duplicate part as the resulting objects would not fit on the print bed.\n"); + throw std::invalid_argument("Cannot duplicate part as the resulting objects would not fit on the print bed.\n"); // note that this will leave the object count unaltered @@ -671,7 +671,8 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const BoundingBoxf3 bb; for (const ModelVolume *v : this->volumes) if (v->is_model_part()) { - if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances"); + if (this->instances.empty()) + throw std::invalid_argument("Can't call raw_bounding_box() with no instances"); bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true)); } return bb; diff --git a/xs/src/libslic3r/Polygon.cpp b/xs/src/libslic3r/Polygon.cpp index 14248d84ff..cf0783bae5 100644 --- a/xs/src/libslic3r/Polygon.cpp +++ b/xs/src/libslic3r/Polygon.cpp @@ -47,7 +47,7 @@ Polygon::split_at_vertex(const Point &point) const for (const Point &pt : this->points) if (pt == point) return this->split_at_index(&pt - &this->points.front()); - CONFESS("Point not found"); + throw std::invalid_argument("Point not found"); return Polyline(); } diff --git a/xs/src/libslic3r/Polyline.cpp b/xs/src/libslic3r/Polyline.cpp index e0cd5221c0..af155468ab 100644 --- a/xs/src/libslic3r/Polyline.cpp +++ b/xs/src/libslic3r/Polyline.cpp @@ -18,7 +18,8 @@ Polyline::operator Polylines() const Polyline::operator Line() const { - if (this->points.size() > 2) CONFESS("Can't convert polyline with more than two points to a line"); + if (this->points.size() > 2) + throw std::invalid_argument("Can't convert polyline with more than two points to a line"); return Line(this->points.front(), this->points.back()); } diff --git a/xs/src/libslic3r/PolylineCollection.cpp b/xs/src/libslic3r/PolylineCollection.cpp index 3f65ea6998..1304161c3f 100644 --- a/xs/src/libslic3r/PolylineCollection.cpp +++ b/xs/src/libslic3r/PolylineCollection.cpp @@ -77,7 +77,8 @@ Polylines PolylineCollection::_chained_path_from( Point PolylineCollection::leftmost_point(const Polylines &polylines) { - if (polylines.empty()) CONFESS("leftmost_point() called on empty PolylineCollection"); + if (polylines.empty()) + throw std::invalid_argument("leftmost_point() called on empty PolylineCollection"); Polylines::const_iterator it = polylines.begin(); Point p = it->leftmost_point(); for (++ it; it != polylines.end(); ++it) { diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 85634d1bb1..f8f6537ca8 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -819,7 +819,8 @@ BoundingBox Print::total_bounding_box() const double Print::skirt_first_layer_height() const { - if (m_objects.empty()) CONFESS("skirt_first_layer_height() can't be called without PrintObjects"); + if (m_objects.empty()) + throw std::invalid_argument("skirt_first_layer_height() can't be called without PrintObjects"); return m_objects.front()->config().get_abs_value("first_layer_height"); } diff --git a/xs/src/libslic3r/PrintRegion.cpp b/xs/src/libslic3r/PrintRegion.cpp index c112548b40..4ea777b4a4 100644 --- a/xs/src/libslic3r/PrintRegion.cpp +++ b/xs/src/libslic3r/PrintRegion.cpp @@ -25,7 +25,7 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir } else if (role == frTopSolidInfill) { config_width = m_config.top_infill_extrusion_width; } else { - CONFESS("Unknown role"); + throw std::invalid_argument("Unknown role"); } } if (config_width.value == 0) { @@ -42,7 +42,7 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir } else if (role == frSolidInfill || role == frTopSolidInfill) { extruder = m_config.solid_infill_extruder; } else { - CONFESS("Unknown role $role"); + throw std::invalid_argument("Unknown role"); } double nozzle_diameter = m_print->config().nozzle_diameter.get_at(extruder-1); diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 01adac3375..4bf13330f6 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -308,7 +308,8 @@ void TriangleMesh::rotate(double angle, Point* center) bool TriangleMesh::has_multiple_patches() const { // we need neighbors - if (!this->repaired) CONFESS("split() requires repair()"); + if (!this->repaired) + throw std::runtime_error("split() requires repair()"); if (this->stl.stats.number_of_facets == 0) return false; @@ -338,7 +339,8 @@ bool TriangleMesh::has_multiple_patches() const size_t TriangleMesh::number_of_patches() const { // we need neighbors - if (!this->repaired) CONFESS("split() requires repair()"); + if (!this->repaired) + throw std::runtime_error("split() requires repair()"); if (this->stl.stats.number_of_facets == 0) return false; @@ -382,7 +384,7 @@ TriangleMeshPtrs TriangleMesh::split() const // we need neighbors if (!this->repaired) - CONFESS("split() requires repair()"); + throw std::runtime_error("split() requires repair()"); // loop while we have remaining facets for (;;) { diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 0d2df5a0b0..c90ad76508 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -95,26 +95,6 @@ inline T next_highest_power_of_2(T v) extern std::string xml_escape(std::string text); -class PerlCallback { -public: - PerlCallback(void *sv) : m_callback(nullptr) { this->register_callback(sv); } - PerlCallback() : m_callback(nullptr) {} - ~PerlCallback() { this->deregister_callback(); } - void register_callback(void *sv); - void deregister_callback(); - void call() const; - void call(int i) const; - void call(int i, int j) const; - void call(const std::vector& ints) const; - void call(double a) const; - void call(double a, double b) const; - void call(double a, double b, double c) const; - void call(double a, double b, double c, double d) const; - void call(bool b) const; -private: - void *m_callback; -}; - } // namespace Slic3r #endif // slic3r_Utils_hpp_ diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index cf0a19f7af..5f2f3fba17 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -48,14 +48,6 @@ typedef double coordf_t; //inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); } #define scale_(val) ((val) / SCALING_FACTOR) #define SCALED_EPSILON scale_(EPSILON) -/* Implementation of CONFESS("foo"): */ -#ifdef _MSC_VER - #define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) -#else - #define CONFESS(...) confess_at(__FILE__, __LINE__, __func__, __VA_ARGS__) -#endif -void confess_at(const char *file, int line, const char *func, const char *pat, ...); -/* End implementation of CONFESS("foo"): */ // Which C++ version is supported? // For example, could optimized functions with move semantics be used? diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 2fbfcae814..4ff15175b0 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -233,187 +233,6 @@ int copy_file(const std::string &from, const std::string &to) } // namespace Slic3r -#include - -void -confess_at(const char *file, int line, const char *func, - const char *pat, ...) -{ - #ifdef SLIC3RXS - va_list args; - SV *error_sv = newSVpvf("Error in function %s at %s:%d: ", func, - file, line); - - va_start(args, pat); - sv_vcatpvf(error_sv, pat, &args); - va_end(args); - - sv_catpvn(error_sv, "\n\t", 2); - - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs( sv_2mortal(error_sv) ); - PUTBACK; - call_pv("Carp::confess", G_DISCARD); - FREETMPS; - LEAVE; - #endif -} - -void PerlCallback::register_callback(void *sv) -{ - if (! SvROK((SV*)sv) || SvTYPE(SvRV((SV*)sv)) != SVt_PVCV) - croak("Not a Callback %_ for PerlFunction", (SV*)sv); - if (m_callback) - SvSetSV((SV*)m_callback, (SV*)sv); - else - m_callback = newSVsv((SV*)sv); -} - -void PerlCallback::deregister_callback() -{ - if (m_callback) { - sv_2mortal((SV*)m_callback); - m_callback = nullptr; - } -} - -void PerlCallback::call() const -{ - if (! m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; -} - -void PerlCallback::call(int i) const -{ - if (! m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs(sv_2mortal(newSViv(i))); - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; -} - -void PerlCallback::call(int i, int j) const -{ - if (! m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs(sv_2mortal(newSViv(i))); - XPUSHs(sv_2mortal(newSViv(j))); - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; -} - -void PerlCallback::call(const std::vector& ints) const -{ - if (! m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - for (int i : ints) - { - XPUSHs(sv_2mortal(newSViv(i))); - } - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; -} - -void PerlCallback::call(double a) const -{ - if (!m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs(sv_2mortal(newSVnv(a))); - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; -} - -void PerlCallback::call(double a, double b) const -{ - if (!m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs(sv_2mortal(newSVnv(a))); - XPUSHs(sv_2mortal(newSVnv(b))); - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; -} - -void PerlCallback::call(double a, double b, double c) const -{ - if (!m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs(sv_2mortal(newSVnv(a))); - XPUSHs(sv_2mortal(newSVnv(b))); - XPUSHs(sv_2mortal(newSVnv(c))); - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; -} - -void PerlCallback::call(double a, double b, double c, double d) const -{ - if (!m_callback) - return; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs(sv_2mortal(newSVnv(a))); - XPUSHs(sv_2mortal(newSVnv(b))); - XPUSHs(sv_2mortal(newSVnv(c))); - XPUSHs(sv_2mortal(newSVnv(d))); - PUTBACK; - perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); - FREETMPS; - LEAVE; -} - -void PerlCallback::call(bool b) const -{ - call(b ? 1 : 0); -} - #ifdef WIN32 #ifndef NOMINMAX # define NOMINMAX diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index b9f6a59bc0..e6f0380426 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1687,7 +1687,7 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, if (extrusion_entity_collection != nullptr) extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, volume); else { - CONFESS("Unexpected extrusion_entity type in to_verts()"); + throw std::runtime_error("Unexpected extrusion_entity type in to_verts()"); } } } diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 338db3010b..99997e3905 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -7,6 +7,7 @@ // Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx. #include "../../libslic3r/Print.hpp" +#include "../../libslic3r/Utils.hpp" #include "../../libslic3r/GCode/PostProcessor.hpp" //#undef NDEBUG diff --git a/xs/src/slic3r/GUI/GLToolbar.hpp b/xs/src/slic3r/GUI/GLToolbar.hpp index c1501b10c2..65d6748ffe 100644 --- a/xs/src/slic3r/GUI/GLToolbar.hpp +++ b/xs/src/slic3r/GUI/GLToolbar.hpp @@ -2,7 +2,7 @@ #define slic3r_GLToolbar_hpp_ #include "../../slic3r/GUI/GLTexture.hpp" -#include "../../libslic3r/Utils.hpp" +#include "../../callback.hpp" #include #include diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 02327b3258..998b572b98 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -4,7 +4,7 @@ #include #include #include "PrintConfig.hpp" -#include "../../libslic3r/Utils.hpp" +#include "../../callback.hpp" #include "GUI_ObjectParts.hpp" #include diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.hpp b/xs/src/slic3r/GUI/ProgressStatusBar.hpp index 3075805da7..9a7f58eee7 100644 --- a/xs/src/slic3r/GUI/ProgressStatusBar.hpp +++ b/xs/src/slic3r/GUI/ProgressStatusBar.hpp @@ -4,7 +4,7 @@ #include #include -#include "../../libslic3r/Utils.hpp" +#include "../../callback.hpp" class wxTimer; class wxGauge; diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index c9e3636027..47d71a09fc 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -209,6 +209,41 @@ SV* to_SV(TriangleMesh* THIS); // Return a pointer to the associated wxWidgets object instance given by classname. extern void* wxPli_sv_2_object( pTHX_ SV* scalar, const char* classname ); +inline void confess_at(const char *file, int line, const char *func, const char *pat, ...) +{ + #ifdef SLIC3RXS + va_list args; + SV *error_sv = newSVpvf("Error in function %s at %s:%d: ", func, + file, line); + + va_start(args, pat); + sv_vcatpvf(error_sv, pat, &args); + va_end(args); + + sv_catpvn(error_sv, "\n\t", 2); + + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs( sv_2mortal(error_sv) ); + PUTBACK; + call_pv("Carp::confess", G_DISCARD); + FREETMPS; + LEAVE; + #endif +} + +#ifndef CONFESS +/* Implementation of CONFESS("foo"): */ +#ifdef _MSC_VER + #define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) +#else + #define CONFESS(...) confess_at(__FILE__, __LINE__, __func__, __VA_ARGS__) +#endif +/* End implementation of CONFESS("foo"): */ +#endif /* CONFESS */ + using namespace Slic3r; #endif From 5fa99fd903312aab34ce8db748af602237855517 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 18 Sep 2018 11:16:49 +0200 Subject: [PATCH 19/36] removing unnecessary artefacts FindFlann and duplicate c++11 standard definition in cmake. --- cmake/modules/FindFlann.cmake | 28 ---------------------------- xs/CMakeLists.txt | 4 ---- 2 files changed, 32 deletions(-) delete mode 100644 cmake/modules/FindFlann.cmake diff --git a/cmake/modules/FindFlann.cmake b/cmake/modules/FindFlann.cmake deleted file mode 100644 index 98674d230d..0000000000 --- a/cmake/modules/FindFlann.cmake +++ /dev/null @@ -1,28 +0,0 @@ -############################################################################### -# Find Flann -# -# This sets the following variables: -# FLANN_FOUND - True if FLANN was found. -# FLANN_INCLUDE_DIRS - Directories containing the FLANN include files. -# FLANN_LIBRARIES - Libraries needed to use FLANN. -# FLANN_DEFINITIONS - Compiler flags for FLANN. - -find_package(PkgConfig) -pkg_check_modules(PC_FLANN flann) -set(FLANN_DEFINITIONS ${PC_FLANN_CFLAGS_OTHER}) - -find_path(FLANN_INCLUDE_DIR flann/flann.hpp - HINTS ${PC_FLANN_INCLUDEDIR} ${PC_FLANN_INCLUDE_DIRS}) - -find_library(FLANN_LIBRARY flann_cpp - HINTS ${PC_FLANN_LIBDIR} ${PC_FLANN_LIBRARY_DIRS}) - -set(FLANN_INCLUDE_DIRS ${FLANN_INCLUDE_DIR}) -set(FLANN_LIBRARIES ${FLANN_LIBRARY}) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Flann DEFAULT_MSG - FLANN_LIBRARY FLANN_INCLUDE_DIR) - -mark_as_advanced(FLANN_LIBRARY FLANN_INCLUDE_DIR) - diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 69e03779a2..7fd81f6104 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -2,10 +2,6 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# Enable C11 language standard. -set(CMAKE_C_STANDARD 11) -set(CMAKE_C_STANDARD_REQUIRED ON) - # Add our own cmake module path. list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/) From e3e5948982786464c512a6837b27d7802b0fc56b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Sep 2018 13:35:05 +0200 Subject: [PATCH 20/36] 1st installment of preview ported in c++ --- lib/Slic3r/GUI/Plater.pm | 84 +++++- xs/CMakeLists.txt | 5 + xs/src/perlglue.cpp | 1 + xs/src/slic3r/GUI/GUI.cpp | 21 ++ xs/src/slic3r/GUI/GUI.hpp | 9 + xs/src/slic3r/GUI/GUI_Preview.cpp | 376 +++++++++++++++++++++++++ xs/src/slic3r/GUI/GUI_Preview.hpp | 91 ++++++ xs/src/slic3r/GUI/GUI_PreviewIface.cpp | 62 ++++ xs/src/slic3r/GUI/GUI_PreviewIface.hpp | 37 +++ xs/xsp/GUI.xsp | 3 + xs/xsp/GUI_Preview.xsp | 28 ++ xs/xsp/my.map | 10 +- xs/xsp/typemap.xspt | 1 + 13 files changed, 718 insertions(+), 10 deletions(-) create mode 100644 xs/src/slic3r/GUI/GUI_Preview.cpp create mode 100644 xs/src/slic3r/GUI/GUI_Preview.hpp create mode 100644 xs/src/slic3r/GUI/GUI_PreviewIface.cpp create mode 100644 xs/src/slic3r/GUI/GUI_PreviewIface.hpp create mode 100644 xs/xsp/GUI_Preview.xsp diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 5a8bf3f8a9..b0050b5b72 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -296,29 +296,52 @@ sub new { } }); - Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); }); +#====================================================================================================================================== + Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, + sub { + Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); + $self->{preview_iface}->set_viewport_from_scene($self->{canvas3D}); + }); +# Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); }); +#====================================================================================================================================== } Slic3r::GUI::register_on_request_update_callback(sub { $self->schedule_background_process; }); # Initialize 3D toolpaths preview if ($Slic3r::GUI::have_OpenGL) { +#====================================================================================================================================== + $self->{preview_iface} = Slic3r::GUI::create_preview_iface($self->{preview_notebook}, $self->{config}, $self->{print}, $self->{gcode_preview_data}); + $self->{preview_page_idx} = $self->{preview_notebook}->GetPageCount-1; +#====================================================================================================================================== $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{gcode_preview_data}, $self->{config}); Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 1); Slic3r::GUI::_3DScene::enable_dynamic_background($self->{preview3D}->canvas, 1); Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); }); +#====================================================================================================================================== + $self->{preview_iface}->register_on_viewport_changed_callback(sub { $self->{preview_iface}->set_viewport_into_scene($self->{canvas3D}); }); +#====================================================================================================================================== $self->{preview_notebook}->AddPage($self->{preview3D}, L('Preview')); $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1; } EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub { my $preview = $self->{preview_notebook}->GetCurrentPage; - if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D})) { +#====================================================================================================================================== + if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D}) && ($preview != $self->{preview_iface})) { +# if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D})) { +#====================================================================================================================================== $preview->OnActivate if $preview->can('OnActivate'); } elsif ($preview == $self->{preview3D}) { $self->{preview3D}->reload_print; # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) Slic3r::GUI::_3DScene::set_as_dirty($self->{preview3D}->canvas); +#====================================================================================================================================== + } elsif ($preview == $self->{preview_iface}) { + $self->{preview_iface}->reload_print; + # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) + $self->{preview_iface}->set_canvas_as_dirty; +#====================================================================================================================================== } elsif ($preview == $self->{canvas3D}) { if (Slic3r::GUI::_3DScene::is_reload_delayed($self->{canvas3D})) { my $selections = $self->collect_selections; @@ -461,7 +484,10 @@ sub new { $_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self)) for grep defined($_), - $self, $self->{canvas3D}, $self->{preview3D}, $self->{list}; +#====================================================================================================================================== + $self, $self->{canvas3D}, $self->{preview3D}, $self->{preview_iface}, $self->{list}; +# $self, $self->{canvas3D}, $self->{preview3D}, $self->{list}; +#====================================================================================================================================== # $self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D}; EVT_COMMAND($self, -1, $SLICING_COMPLETED_EVENT, sub { @@ -491,6 +517,9 @@ sub new { if ($self->{preview3D}) { Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape); } +#====================================================================================================================================== + $self->{preview_iface}->set_bed_shape($self->{config}->bed_shape) if ($self->{preview_iface}); +#====================================================================================================================================== $self->update; { @@ -1006,6 +1035,9 @@ sub remove { # Prevent toolpaths preview from rendering while we modify the Print object $self->{preview3D}->enabled(0) if $self->{preview3D}; +#====================================================================================================================================== + $self->{preview_iface}->set_enabled(0) if $self->{preview_iface}; +#====================================================================================================================================== # If no object index is supplied, remove the selected one. if (! defined $obj_idx) { @@ -1031,6 +1063,9 @@ sub reset { # Prevent toolpaths preview from rendering while we modify the Print object $self->{preview3D}->enabled(0) if $self->{preview3D}; +#====================================================================================================================================== + $self->{preview_iface}->set_enabled(0) if $self->{preview_iface}; +#====================================================================================================================================== @{$self->{objects}} = (); $self->{model}->clear_objects; @@ -1392,6 +1427,9 @@ sub async_apply_config { # Otherwise they will be just refreshed. $self->{gcode_preview_data}->reset; $self->{preview3D}->reload_print if $self->{preview3D}; +#====================================================================================================================================== + $self->{preview_iface}->reload_print if $self->{preview_iface}; +#====================================================================================================================================== # We also need to reload 3D scene because of the wipe tower preview box if ($self->{config}->wipe_tower) { my $selections = $self->collect_selections; @@ -1427,6 +1465,9 @@ sub stop_background_process { my ($self) = @_; $self->{background_slicing_process}->stop(); $self->{preview3D}->reload_print if $self->{preview3D}; +#====================================================================================================================================== + $self->{preview_iface}->reload_print if $self->{preview_iface}; +#====================================================================================================================================== } # Called by the "Slice now" button, which is visible only if the background processing is disabled. @@ -1533,6 +1574,9 @@ sub export_gcode { sub on_update_print_preview { my ($self) = @_; $self->{preview3D}->reload_print if $self->{preview3D}; +#====================================================================================================================================== + $self->{preview_iface}->reload_print if $self->{preview_iface}; +#====================================================================================================================================== # in case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth: my $selections = $self->collect_selections; @@ -1614,6 +1658,9 @@ sub on_process_completed { # refresh preview $self->{preview3D}->reload_print if $self->{preview3D}; +#====================================================================================================================================== + $self->{preview_iface}->reload_print if $self->{preview_iface}; +#====================================================================================================================================== } # Fill in the "Sliced info" box with the result of the G-code generator. @@ -1883,6 +1930,10 @@ sub update { Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0); $self->{preview3D}->reset_gcode_preview_data if $self->{preview3D}; $self->{preview3D}->reload_print if $self->{preview3D}; +#====================================================================================================================================== + $self->{preview_iface}->reset_gcode_preview_data if $self->{preview_iface}; + $self->{preview_iface}->reload_print if $self->{preview_iface}; +#====================================================================================================================================== $self->schedule_background_process; $self->Thaw; } @@ -1959,6 +2010,9 @@ sub on_config_change { # $self->{canvas}->update_bed_size; Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape) if $self->{canvas3D}; Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D}; +#====================================================================================================================================== + $self->{preview_iface}->set_bed_shape($self->{config}->bed_shape) if ($self->{preview_iface}); +#====================================================================================================================================== $update_scheduled = 1; } elsif ($opt_key =~ '^wipe_tower' || $opt_key eq 'single_extruder_multi_material') { $update_scheduled = 1; @@ -1994,12 +2048,18 @@ sub on_config_change { $update_scheduled = 1; my $extruder_colors = $config->get('extruder_colour'); $self->{preview3D}->set_number_extruders(scalar(@{$extruder_colors})); +#====================================================================================================================================== + $self->{preview_iface}->set_number_extruders(scalar(@{$extruder_colors})); +#====================================================================================================================================== } elsif ($opt_key eq 'max_print_height') { $update_scheduled = 1; } elsif ($opt_key eq 'printer_model') { # update to force bed selection (for texturing) Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape) if $self->{canvas3D}; Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D}; +#====================================================================================================================================== + $self->{preview_iface}->set_bed_shape($self->{config}->bed_shape) if ($self->{preview_iface}); +#====================================================================================================================================== $update_scheduled = 1; } } @@ -2450,12 +2510,24 @@ sub select_view { my ($self, $direction) = @_; my $idx_page = $self->{preview_notebook}->GetSelection; my $page = ($idx_page == &Wx::wxNOT_FOUND) ? L('3D') : $self->{preview_notebook}->GetPageText($idx_page); - if ($page eq L('Preview')) { - Slic3r::GUI::_3DScene::select_view($self->{preview3D}->canvas, $direction); - Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); +#====================================================================================================================================== + if (($page eq L('Preview')) || ($page eq L('_Preview_'))) { +#====================================================================================================================================== + if ($page eq L('Preview')) { + Slic3r::GUI::_3DScene::select_view($self->{preview3D}->canvas, $direction); + Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); +#====================================================================================================================================== + } else { + $self->{preview_iface}->select_view($direction); + $self->{preview_iface}->set_viewport_into_scene($self->{canvas3D}); + } +#====================================================================================================================================== } else { Slic3r::GUI::_3DScene::select_view($self->{canvas3D}, $direction); Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); +#====================================================================================================================================== + $self->{preview_iface}->set_viewport_from_scene($self->{canvas3D}); +#====================================================================================================================================== } } diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 7fd81f6104..8aa80278e5 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -222,6 +222,10 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/GUI.hpp ${LIBDIR}/slic3r/GUI/GUI_ObjectParts.cpp ${LIBDIR}/slic3r/GUI/GUI_ObjectParts.hpp + ${LIBDIR}/slic3r/GUI/GUI_Preview.cpp + ${LIBDIR}/slic3r/GUI/GUI_Preview.hpp + ${LIBDIR}/slic3r/GUI/GUI_PreviewIface.cpp + ${LIBDIR}/slic3r/GUI/GUI_PreviewIface.hpp ${LIBDIR}/slic3r/GUI/LambdaObjectDialog.cpp ${LIBDIR}/slic3r/GUI/LambdaObjectDialog.hpp ${LIBDIR}/slic3r/GUI/Tab.cpp @@ -463,6 +467,7 @@ set(XS_XSP_FILES ${XSP_DIR}/GUI_3DScene.xsp ${XSP_DIR}/GUI_Preset.xsp ${XSP_DIR}/GUI_Tab.xsp + ${XSP_DIR}/GUI_Preview.xsp ${XSP_DIR}/Layer.xsp ${XSP_DIR}/Line.xsp ${XSP_DIR}/Model.xsp diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 68fbcd6120..09a2998b20 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -63,6 +63,7 @@ REGISTER_CLASS(Preset, "GUI::Preset"); REGISTER_CLASS(PresetCollection, "GUI::PresetCollection"); REGISTER_CLASS(PresetBundle, "GUI::PresetBundle"); REGISTER_CLASS(TabIface, "GUI::Tab"); +REGISTER_CLASS(PreviewIface, "GUI::Preview"); REGISTER_CLASS(ProgressStatusBar, "GUI::ProgressStatusBar"); REGISTER_CLASS(PresetUpdater, "PresetUpdater"); REGISTER_CLASS(AppController, "AppController"); diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index decdb5691d..b3b52e0c6e 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -46,6 +46,10 @@ #include "Tab.hpp" #include "TabIface.hpp" +//############################################################################################################################################################## +#include "GUI_Preview.hpp" +#include "GUI_PreviewIface.hpp" +//############################################################################################################################################################## #include "AboutDialog.hpp" #include "AppConfig.hpp" #include "ConfigSnapshotDialog.hpp" @@ -148,6 +152,10 @@ wxStaticBitmap *g_manifold_warning_icon = nullptr; bool g_show_print_info = false; bool g_show_manifold_warning_icon = false; +//############################################################################################################################################################## +PreviewIface* g_preview = nullptr; // <<< FIXME ENRICO -> add code to delete the pointer when the application closes +//############################################################################################################################################################## + static void init_label_colours() { auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); @@ -665,6 +673,19 @@ TabIface* get_preset_tab_iface(char *name) return new TabIface(nullptr); } +//############################################################################################################################################################## +PreviewIface* create_preview_iface(wxNotebook* parent, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data) +{ + if (g_preview == nullptr) + { + Preview* panel = new Preview(parent, config, print, gcode_preview_data); + g_preview = new PreviewIface(panel); + } + + return g_preview; +} +//############################################################################################################################################################## + // opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element) void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/) { diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 998b572b98..a5fb4d44d8 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -39,6 +39,11 @@ class AppConfig; class PresetUpdater; class DynamicPrintConfig; class TabIface; +//############################################################################################################################################################## +class PreviewIface; +class Print; +class GCodePreviewData; +//############################################################################################################################################################## #define _(s) Slic3r::GUI::I18N::translate((s)) @@ -165,6 +170,10 @@ extern void open_preferences_dialog(int event_preferences); void create_preset_tabs(int event_value_change, int event_presets_changed); TabIface* get_preset_tab_iface(char *name); +//############################################################################################################################################################## +PreviewIface* create_preview_iface(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data); +//############################################################################################################################################################## + // add it at the end of the tab panel. void add_created_tab(Tab* panel, int event_value_change, int event_presets_changed); // Change option value in config diff --git a/xs/src/slic3r/GUI/GUI_Preview.cpp b/xs/src/slic3r/GUI/GUI_Preview.cpp new file mode 100644 index 0000000000..4e23538974 --- /dev/null +++ b/xs/src/slic3r/GUI/GUI_Preview.cpp @@ -0,0 +1,376 @@ +#include "../../libslic3r/libslic3r.h" +#include "GUI_Preview.hpp" +#include "GUI.hpp" +#include "AppConfig.hpp" +#include "3DScene.hpp" +#include "../../libslic3r/GCode/PreviewData.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace Slic3r { +namespace GUI { + +Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data) + : m_canvas(nullptr) + , m_double_slider_sizer(nullptr) + , m_label_view_type(nullptr) + , m_choice_view_type(nullptr) + , m_label_show_features(nullptr) + , m_combochecklist_features(nullptr) + , m_checkbox_travel(nullptr) + , m_checkbox_retractions(nullptr) + , m_checkbox_unretractions(nullptr) + , m_checkbox_shells(nullptr) + , m_config(config) + , m_print(print) + , m_gcode_preview_data(gcode_preview_data) + , m_number_extruders(1) + , m_preferred_color_mode("feature") + , m_loaded(false) + , m_enabled(false) + , m_force_sliders_full_range(false) +{ + if (init(notebook, config, print, gcode_preview_data)) + { + notebook->AddPage(this, _(L("_Preview_"))); + show_hide_ui_elements("none"); + reload_print(); + } +} + +bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data) +{ + if ((notebook == nullptr) || (config == nullptr) || (print == nullptr) || (gcode_preview_data == nullptr)) + return false; + + // creates this panel add append it to the given notebook as a new page + if (!Create(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize)) + return false; + + int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24, WX_GL_SAMPLE_BUFFERS, 1, WX_GL_SAMPLES, 4, 0 }; + + int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER; + const AppConfig* app_config = GUI::get_app_config(); + bool enable_multisample = (app_config != nullptr) && (app_config->get("use_legacy_opengl") != "1") && (wxVersion >= 30003); + + // if multisample is not enabled or supported by the graphic card, remove it from the attributes list + bool can_multisample = enable_multisample && wxGLCanvas::IsDisplaySupported(attribList); // <<< FIXME ENRICO IsDisplaySupported() seems not to work + if (!can_multisample) + attribList[4] = 0; + + m_canvas = new wxGLCanvas(this, wxID_ANY, attribList); + if (m_canvas == nullptr) + return false; + + _3DScene::add_canvas(m_canvas); + _3DScene::allow_multisample(m_canvas, can_multisample); + _3DScene::enable_shader(m_canvas, true); + _3DScene::set_config(m_canvas, m_config); + _3DScene::set_print(m_canvas, m_print); + _3DScene::enable_legend_texture(m_canvas, true); + _3DScene::enable_dynamic_background(m_canvas, true); + + m_double_slider_sizer = new wxBoxSizer(wxHORIZONTAL); + create_double_slider(this, m_double_slider_sizer, m_canvas); + + m_label_view_type = new wxStaticText(this, wxID_ANY, _(L("View"))); + + m_choice_view_type = new wxChoice(this, wxID_ANY); + m_choice_view_type->Append(_(L("Feature type"))); + m_choice_view_type->Append(_(L("Height"))); + m_choice_view_type->Append(_(L("Width"))); + m_choice_view_type->Append(_(L("Speed"))); + m_choice_view_type->Append(_(L("Volumetric flow rate"))); + m_choice_view_type->Append(_(L("Tool"))); + m_choice_view_type->SetSelection(0); + + m_label_show_features = new wxStaticText(this, wxID_ANY, _(L("Show"))); + + m_combochecklist_features = new wxComboCtrl(); + m_combochecklist_features->Create(this, wxID_ANY, _(L("Feature types")), wxDefaultPosition, wxSize(200, -1), wxCB_READONLY); + std::string feature_text = _(L("Feature types")); + std::string feature_items = _(L("Perimeter")) + "|" + + _(L("External perimeter")) + "|" + + _(L("Overhang perimeter")) + "|" + + _(L("Internal infill")) + "|" + + _(L("Solid infill")) + "|" + + _(L("Top solid infill")) + "|" + + _(L("Bridge infill")) + "|" + + _(L("Gap fill")) + "|" + + _(L("Skirt")) + "|" + + _(L("Support material")) + "|" + + _(L("Support material interface")) + "|" + + _(L("Wipe tower")) + "|" + + _(L("Custom")); + Slic3r::GUI::create_combochecklist(m_combochecklist_features, feature_text, feature_items, true); + + m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel"))); + m_checkbox_retractions = new wxCheckBox(this, wxID_ANY, _(L("Retractions"))); + m_checkbox_unretractions = new wxCheckBox(this, wxID_ANY, _(L("Unretractions"))); + m_checkbox_shells = new wxCheckBox(this, wxID_ANY, _(L("Shells"))); + + wxBoxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL); + top_sizer->Add(m_canvas, 1, wxALL | wxEXPAND, 0); + top_sizer->Add(m_double_slider_sizer, 0, wxEXPAND, 0); + + wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL); + bottom_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); + bottom_sizer->Add(m_choice_view_type, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + bottom_sizer->AddSpacer(10); + bottom_sizer->Add(m_label_show_features, 0, wxALIGN_CENTER_VERTICAL, 5); + bottom_sizer->Add(m_combochecklist_features, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + bottom_sizer->AddSpacer(20); + bottom_sizer->Add(m_checkbox_travel, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + bottom_sizer->AddSpacer(10); + bottom_sizer->Add(m_checkbox_retractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + bottom_sizer->AddSpacer(10); + bottom_sizer->Add(m_checkbox_unretractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + bottom_sizer->AddSpacer(10); + bottom_sizer->Add(m_checkbox_shells, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + + wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); + main_sizer->Add(top_sizer, 1, wxALL | wxEXPAND, 0); + main_sizer->Add(bottom_sizer, 0, wxALL | wxEXPAND, 0); + + SetSizer(main_sizer); + SetMinSize(GetSize()); + GetSizer()->SetSizeHints(this); + + bind_event_handlers(); + + // sets colors for gcode preview extrusion roles + std::vector extrusion_roles_colors = { + "FFFF66", // Perimeter + "FFA500", // External perimeter + "0000FF", // Overhang perimeter + "B1302A", // Internal infill + "D732D7", // Solid infill + "FF1A1A", // Top solid infill + "9999FF", // Bridge infill + "FFFFFF", // Gap fill + "845321", // Skirt + "00FF00", // Support material + "008000", // Support material interface + "B3E3AB", // Wipe tower + "28CC94" // Custom + }; + m_gcode_preview_data->set_extrusion_paths_colors(extrusion_roles_colors); + + return true; +} + +Preview::~Preview() +{ + unbind_event_handlers(); + + if (m_canvas != nullptr) + { + _3DScene::remove_canvas(m_canvas); + delete m_canvas; + } +} + +void Preview::register_on_viewport_changed_callback(void* callback) +{ + if ((m_canvas != nullptr) && (callback != nullptr)) + _3DScene::register_on_viewport_changed_callback(m_canvas, callback); +} + +void Preview::set_number_extruders(unsigned int number_extruders) +{ + if (m_number_extruders != number_extruders) + { + m_number_extruders = number_extruders; + int type = 0; // color by a feature type + if (number_extruders > 1) + { + int tool_idx = m_choice_view_type->FindString(_(L("Tool"))); + int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type + m_choice_view_type->SetSelection(type); + if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) + m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; + + m_preferred_color_mode = (type == tool_idx) ? "tool_or_feature" : "feature"; + } + } +} + +void Preview::reset_gcode_preview_data() +{ + m_gcode_preview_data->reset(); + _3DScene::reset_legend_texture(); +} + +void Preview::set_canvas_as_dirty() +{ + if (m_canvas != nullptr) + _3DScene::set_as_dirty(m_canvas); +} + +void Preview::set_enabled(bool enabled) +{ + m_enabled = enabled; +} + +void Preview::set_bed_shape(const Pointfs& shape) +{ + if (m_canvas != nullptr) + _3DScene::set_bed_shape(m_canvas, shape); +} + +void Preview::select_view(const std::string& direction) +{ + if (m_canvas != nullptr) + _3DScene::select_view(m_canvas, direction); +} + +void Preview::set_viewport_from_scene(wxGLCanvas* canvas) +{ + if ((m_canvas != nullptr) && (canvas != nullptr)) + _3DScene::set_viewport_from_scene(m_canvas, canvas); +} + +void Preview::set_viewport_into_scene(wxGLCanvas* canvas) +{ + if ((m_canvas != nullptr) && (canvas != nullptr)) + _3DScene::set_viewport_from_scene(canvas, m_canvas); +} + +void Preview::set_drop_target(wxDropTarget* target) +{ + if (target != nullptr) + SetDropTarget(target); +} + +void Preview::load_print() +{ +} + +void Preview::reload_print(bool force) +{ + _3DScene::reset_volumes(m_canvas); + m_loaded = false; + + if (!IsShown() && !force) + return; + + load_print(); +} + +void Preview::refresh_print() +{ + m_loaded = false; + + if (!IsShown()) + return; + + load_print(); +} + +void Preview::bind_event_handlers() +{ + this->Bind(wxEVT_SIZE, &Preview::on_size, this); + m_choice_view_type->Bind(wxEVT_CHOICE, &Preview::on_choice_view_type, this); + m_combochecklist_features->Bind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_features, this); + m_checkbox_travel->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_travel, this); + m_checkbox_retractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this); + m_checkbox_unretractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this); + m_checkbox_shells->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this); +} + +void Preview::unbind_event_handlers() +{ + this->Unbind(wxEVT_SIZE, &Preview::on_size, this); + m_choice_view_type->Unbind(wxEVT_CHOICE, &Preview::on_choice_view_type, this); + m_combochecklist_features->Unbind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_features, this); + m_checkbox_travel->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_travel, this); + m_checkbox_retractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this); + m_checkbox_unretractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this); + m_checkbox_shells->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this); +} + +void Preview::show_hide_ui_elements(const std::string& what) +{ + bool enable = (what == "full"); + m_label_show_features->Enable(enable); + m_combochecklist_features->Enable(enable); + m_checkbox_travel->Enable(enable); + m_checkbox_retractions->Enable(enable); + m_checkbox_unretractions->Enable(enable); + m_checkbox_shells->Enable(enable); + + enable = (what != "none"); + m_label_view_type->Enable(enable); + m_choice_view_type->Enable(enable); +} + +void Preview::reset_sliders() +{ + m_enabled = false; + reset_double_slider(); + m_double_slider_sizer->Hide((size_t)0); +} + +void Preview::update_sliders() +{ + m_enabled = true; + update_double_slider(m_force_sliders_full_range); + m_double_slider_sizer->Show((size_t)0); + Layout(); +} + +void Preview::on_size(wxSizeEvent& evt) +{ + evt.Skip(); + Refresh(); +} + +void Preview::on_choice_view_type(wxCommandEvent& evt) +{ + m_preferred_color_mode = (m_choice_view_type->GetStringSelection() == L("Tool")) ? "tool" : "feature"; + int selection = m_choice_view_type->GetCurrentSelection(); + if ((0 <= selection) && (selection < (int)GCodePreviewData::Extrusion::Num_View_Types)) + m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)selection; + + reload_print(); +} + +void Preview::on_combochecklist_features(wxCommandEvent& evt) +{ + int flags = Slic3r::GUI::combochecklist_get_flags(m_combochecklist_features); + m_gcode_preview_data->extrusion.role_flags = (unsigned int)flags; + refresh_print(); +} + +void Preview::on_checkbox_travel(wxCommandEvent& evt) +{ + m_gcode_preview_data->travel.is_visible = m_checkbox_travel->IsChecked(); + refresh_print(); +} + +void Preview::on_checkbox_retractions(wxCommandEvent& evt) +{ + m_gcode_preview_data->retraction.is_visible = m_checkbox_retractions->IsChecked(); + refresh_print(); +} + +void Preview::on_checkbox_unretractions(wxCommandEvent& evt) +{ + m_gcode_preview_data->unretraction.is_visible = m_checkbox_unretractions->IsChecked(); + refresh_print(); +} + +void Preview::on_checkbox_shells(wxCommandEvent& evt) +{ + m_gcode_preview_data->shell.is_visible = m_checkbox_shells->IsChecked(); + refresh_print(); +} + +} // namespace GUI +} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GUI_Preview.hpp b/xs/src/slic3r/GUI/GUI_Preview.hpp new file mode 100644 index 0000000000..bdd69a0754 --- /dev/null +++ b/xs/src/slic3r/GUI/GUI_Preview.hpp @@ -0,0 +1,91 @@ +#ifndef slic3r_GUI_Preview_hpp_ +#define slic3r_GUI_Preview_hpp_ + +#include +#include "../../libslic3r/Point.hpp" + +#include + +class wxNotebook; +class wxGLCanvas; +class wxBoxSizer; +class wxStaticText; +class wxChoice; +class wxComboCtrl; +class wxCheckBox; + +namespace Slic3r { + +class DynamicPrintConfig; +class Print; +class GCodePreviewData; + +namespace GUI { + +class Preview : public wxPanel +{ + wxGLCanvas* m_canvas; + wxBoxSizer* m_double_slider_sizer; + wxStaticText* m_label_view_type; + wxChoice* m_choice_view_type; + wxStaticText* m_label_show_features; + wxComboCtrl* m_combochecklist_features; + wxCheckBox* m_checkbox_travel; + wxCheckBox* m_checkbox_retractions; + wxCheckBox* m_checkbox_unretractions; + wxCheckBox* m_checkbox_shells; + + DynamicPrintConfig* m_config; + Print* m_print; + GCodePreviewData* m_gcode_preview_data; + + unsigned int m_number_extruders; + std::string m_preferred_color_mode; + + bool m_loaded; + bool m_enabled; + bool m_force_sliders_full_range; + +public: + Preview(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data); + virtual ~Preview(); + + void register_on_viewport_changed_callback(void* callback); + void set_number_extruders(unsigned int number_extruders); + void reset_gcode_preview_data(); + void set_canvas_as_dirty(); + void set_enabled(bool enabled); + void set_bed_shape(const Pointfs& shape); + void select_view(const std::string& direction); + void set_viewport_from_scene(wxGLCanvas* canvas); + void set_viewport_into_scene(wxGLCanvas* canvas); + void set_drop_target(wxDropTarget* target); + + void load_print(); + void reload_print(bool force = false); + void refresh_print(); + +private: + bool init(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data); + + void bind_event_handlers(); + void unbind_event_handlers(); + + void show_hide_ui_elements(const std::string& what); + + void reset_sliders(); + void update_sliders(); + + void on_size(wxSizeEvent& evt); + void on_choice_view_type(wxCommandEvent& evt); + void on_combochecklist_features(wxCommandEvent& evt); + void on_checkbox_travel(wxCommandEvent& evt); + void on_checkbox_retractions(wxCommandEvent& evt); + void on_checkbox_unretractions(wxCommandEvent& evt); + void on_checkbox_shells(wxCommandEvent& evt); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GUI_Preview_hpp_ diff --git a/xs/src/slic3r/GUI/GUI_PreviewIface.cpp b/xs/src/slic3r/GUI/GUI_PreviewIface.cpp new file mode 100644 index 0000000000..9048beb338 --- /dev/null +++ b/xs/src/slic3r/GUI/GUI_PreviewIface.cpp @@ -0,0 +1,62 @@ +#include "../../libslic3r/libslic3r.h" +#include "GUI_PreviewIface.hpp" +#include "GUI_Preview.hpp" + +namespace Slic3r { + +void PreviewIface::register_on_viewport_changed_callback(void* callback) +{ + m_preview->register_on_viewport_changed_callback(callback); +} + +void PreviewIface::set_number_extruders(unsigned int number_extruders) +{ + m_preview->set_number_extruders(number_extruders); +} + +void PreviewIface::reset_gcode_preview_data() +{ + m_preview->reset_gcode_preview_data(); +} + +void PreviewIface::reload_print(bool force) +{ + m_preview->reload_print(force); +} + +void PreviewIface::set_canvas_as_dirty() +{ + m_preview->set_canvas_as_dirty(); +} + +void PreviewIface::set_enabled(bool enabled) +{ + m_preview->set_enabled(enabled); +} + +void PreviewIface::set_bed_shape(const Pointfs& shape) +{ + m_preview->set_bed_shape(shape); +} + +void PreviewIface::select_view(const std::string& direction) +{ + m_preview->select_view(direction); +} + +void PreviewIface::set_viewport_from_scene(wxGLCanvas* canvas) +{ + m_preview->set_viewport_from_scene(canvas); +} + +void PreviewIface::set_viewport_into_scene(wxGLCanvas* canvas) +{ + m_preview->set_viewport_into_scene(canvas); +} + +void PreviewIface::set_drop_target(wxDropTarget* target) +{ + m_preview->set_drop_target(target); +} + +} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GUI_PreviewIface.hpp b/xs/src/slic3r/GUI/GUI_PreviewIface.hpp new file mode 100644 index 0000000000..86f155bd5a --- /dev/null +++ b/xs/src/slic3r/GUI/GUI_PreviewIface.hpp @@ -0,0 +1,37 @@ +#ifndef slic3r_GUI_PreviewIface_hpp_ +#define slic3r_GUI_PreviewIface_hpp_ + +#include "../../libslic3r/Point.hpp" + +class wxGLCanvas; +class wxDropTarget; + +namespace Slic3r { + +namespace GUI { +class Preview; +} // namespace GUI + +class PreviewIface +{ + GUI::Preview* m_preview; + +public: + explicit PreviewIface(GUI::Preview* preview) : m_preview(preview) {} + + void register_on_viewport_changed_callback(void* callback); + void set_number_extruders(unsigned int number_extruders); + void reset_gcode_preview_data(); + void reload_print(bool force = false); + void set_canvas_as_dirty(); + void set_enabled(bool enabled); + void set_bed_shape(const Pointfs& shape); + void select_view(const std::string& direction); + void set_viewport_from_scene(wxGLCanvas* canvas); + void set_viewport_into_scene(wxGLCanvas* canvas); + void set_drop_target(wxDropTarget* target); +}; + +} // namespace Slic3r + +#endif // slic3r_GUI_PreviewIface_hpp_ diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index a4d656616e..22e2ff1d71 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -59,6 +59,9 @@ void show_error_id(int id, std::string msg) TabIface* get_preset_tab(char *name) %code%{ RETVAL=Slic3r::GUI::get_preset_tab_iface(name); %}; +PreviewIface* create_preview_iface(SV *ui, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data) + %code%{ RETVAL=Slic3r::GUI::create_preview_iface((wxNotebook*)wxPli_sv_2_object(aTHX_ ui, "Wx::Notebook"), config, print, gcode_preview_data); %}; + bool load_language() %code%{ RETVAL=Slic3r::GUI::load_language(); %}; diff --git a/xs/xsp/GUI_Preview.xsp b/xs/xsp/GUI_Preview.xsp new file mode 100644 index 0000000000..da50c0d21c --- /dev/null +++ b/xs/xsp/GUI_Preview.xsp @@ -0,0 +1,28 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "slic3r/GUI/GUI_PreviewIface.hpp" +%} + +%name{Slic3r::GUI::Preview} class PreviewIface { + + void register_on_viewport_changed_callback(SV* callback) + %code%{ THIS->register_on_viewport_changed_callback((void*)callback); %}; + + void set_number_extruders(unsigned int number_extruders); + void reset_gcode_preview_data(); + void reload_print(bool force = false); + void set_canvas_as_dirty(); + void set_enabled(bool enabled); + void set_bed_shape(Pointfs shape); + void select_view(std::string direction); + void set_viewport_from_scene(SV *ui) + %code%{ THIS->set_viewport_from_scene((wxGLCanvas*)wxPli_sv_2_object(aTHX_ ui, "Wx::GLCanvas")); %}; + + void set_viewport_into_scene(SV *ui) + %code%{ THIS->set_viewport_into_scene((wxGLCanvas*)wxPli_sv_2_object(aTHX_ ui, "Wx::GLCanvas")); %}; + + void SetDropTarget(SV *ui) + %code%{ THIS->set_drop_target((wxDropTarget*)wxPli_sv_2_object(aTHX_ ui, "Wx::DropTarget")); %}; +}; \ No newline at end of file diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 408966607e..a91c407261 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -235,10 +235,12 @@ PresetCollection* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T PresetBundle* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T -TabIface* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -ProgressStatusBar* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T +TabIface* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +PreviewIface* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +ProgressStatusBar* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T PresetUpdater* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 9dd3722b26..0209ce92dd 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -215,6 +215,7 @@ %typemap{PresetHints*}; %typemap{Ref}{simple}; %typemap{TabIface*}; +%typemap{PreviewIface*}; %typemap{ProgressStatusBar*}; %typemap{PrintRegionPtrs*}; From abdaaf6ede02814e67f9a52b26cc075a79a673c9 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Sep 2018 15:50:52 +0200 Subject: [PATCH 21/36] Method Preview::load_print() ported to c++ --- lib/Slic3r/GUI/Plater.pm | 5 +- xs/src/slic3r/GUI/GUI_Preview.cpp | 112 +++++++++++++++++++++++++++++- 2 files changed, 114 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index b0050b5b72..18c93f1c13 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -328,7 +328,8 @@ sub new { EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub { my $preview = $self->{preview_notebook}->GetCurrentPage; #====================================================================================================================================== - if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D}) && ($preview != $self->{preview_iface})) { + my $page_id = $self->{preview_notebook}->GetSelection; + if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D}) && ($page_id != $self->{preview_page_idx})) { # if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D})) { #====================================================================================================================================== $preview->OnActivate if $preview->can('OnActivate'); @@ -337,7 +338,7 @@ sub new { # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) Slic3r::GUI::_3DScene::set_as_dirty($self->{preview3D}->canvas); #====================================================================================================================================== - } elsif ($preview == $self->{preview_iface}) { + } elsif ($page_id == $self->{preview_page_idx}) { $self->{preview_iface}->reload_print; # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) $self->{preview_iface}->set_canvas_as_dirty; diff --git a/xs/src/slic3r/GUI/GUI_Preview.cpp b/xs/src/slic3r/GUI/GUI_Preview.cpp index 4e23538974..2548a743b6 100644 --- a/xs/src/slic3r/GUI/GUI_Preview.cpp +++ b/xs/src/slic3r/GUI/GUI_Preview.cpp @@ -4,6 +4,7 @@ #include "AppConfig.hpp" #include "3DScene.hpp" #include "../../libslic3r/GCode/PreviewData.hpp" +#include "PresetBundle.hpp" #include #include @@ -13,6 +14,9 @@ #include #include +// this include must follow the wxWidgets ones or it won't compile on Windows -> see http://trac.wxwidgets.org/ticket/2421 +#include "../../libslic3r/Print.hpp" + namespace Slic3r { namespace GUI { @@ -40,7 +44,7 @@ Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, { notebook->AddPage(this, _(L("_Preview_"))); show_hide_ui_elements("none"); - reload_print(); + load_print(); } } @@ -250,6 +254,112 @@ void Preview::set_drop_target(wxDropTarget* target) void Preview::load_print() { + if (m_loaded) + return; + + // we require that there's at least one object and the posSlice step + // is performed on all of them(this ensures that _shifted_copies was + // populated and we know the number of layers) + unsigned int n_layers = 0; + if (m_print->is_step_done(posSlice)) + { + std::set zs; + for (const PrintObject* print_object : m_print->objects()) + { + const LayerPtrs& layers = print_object->layers(); + const SupportLayerPtrs& support_layers = print_object->support_layers(); + for (const Layer* layer : layers) + { + zs.insert(layer->print_z); + } + for (const SupportLayer* layer : support_layers) + { + zs.insert(layer->print_z); + } + } + + n_layers = (unsigned int)zs.size(); + } + + if (n_layers == 0) + { + reset_sliders(); + _3DScene::reset_legend_texture(); + if (m_canvas) + m_canvas->Refresh(); + + return; + } + + if (m_preferred_color_mode == "tool_or_feature") + { + // It is left to Slic3r to decide whether the print shall be colored by the tool or by the feature. + // Color by feature if it is a single extruder print. + unsigned int number_extruders = (unsigned int)m_print->extruders().size(); + int tool_idx = m_choice_view_type->FindString(_(L("Tool"))); + int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type + m_choice_view_type->SetSelection(type); + if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) + m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; + // If the->SetSelection changed the following line, revert it to "decide yourself". + m_preferred_color_mode = "tool_or_feature"; + } + + // Collect colors per extruder. + std::vector colors; + if (!m_gcode_preview_data->empty() || (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Tool)) + { + const ConfigOptionStrings* extruders_opt = dynamic_cast(m_config->option("extruder_colour")); + const ConfigOptionStrings* filamemts_opt = dynamic_cast(m_config->option("filament_colour")); + unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size()); + + unsigned char rgb[3]; + for (unsigned int i = 0; i < colors_count; ++i) + { + std::string color = m_config->opt_string("extruder_colour", i); + if (!PresetBundle::parse_color(color, rgb)) + { + color = m_config->opt_string("filament_colour", i); + if (!PresetBundle::parse_color(color, rgb)) + color = "#FFFFFF"; + } + + colors.push_back(color); + } + } + + if (IsShown() && (m_canvas != nullptr)) + { + // used to set the sliders to the extremes of the current zs range + m_force_sliders_full_range = false; + + if (m_gcode_preview_data->empty()) + { + // load skirt and brim + _3DScene::load_preview(m_canvas, colors); + show_hide_ui_elements("simple"); + } + else + { + m_force_sliders_full_range = (_3DScene::get_volumes_count(m_canvas) == 0); + _3DScene::load_gcode_preview(m_canvas, m_gcode_preview_data, colors); + show_hide_ui_elements("full"); + + // recalculates zs and update sliders accordingly + n_layers = (unsigned int)_3DScene::get_current_print_zs(m_canvas, true).size(); + if (n_layers == 0) + { + // all layers filtered out + reset_sliders(); + m_canvas->Refresh(); + } + } + + if (n_layers > 0) + update_sliders(); + + m_loaded = true; + } } void Preview::reload_print(bool force) From 9daae9413af4b72e567b4458734d756c98f256a6 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Sep 2018 16:13:18 +0200 Subject: [PATCH 22/36] Perl version of preview removed from Slic3r --- lib/Slic3r/GUI/Plater.pm | 72 ++++++++++++++----------------- xs/src/slic3r/GUI/GUI_Preview.cpp | 2 +- 2 files changed, 33 insertions(+), 41 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 18c93f1c13..7bfdadd0ef 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -299,7 +299,6 @@ sub new { #====================================================================================================================================== Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { - Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); $self->{preview_iface}->set_viewport_from_scene($self->{canvas3D}); }); # Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); }); @@ -313,15 +312,13 @@ sub new { #====================================================================================================================================== $self->{preview_iface} = Slic3r::GUI::create_preview_iface($self->{preview_notebook}, $self->{config}, $self->{print}, $self->{gcode_preview_data}); $self->{preview_page_idx} = $self->{preview_notebook}->GetPageCount-1; -#====================================================================================================================================== - $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{gcode_preview_data}, $self->{config}); - Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 1); - Slic3r::GUI::_3DScene::enable_dynamic_background($self->{preview3D}->canvas, 1); - Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); }); -#====================================================================================================================================== $self->{preview_iface}->register_on_viewport_changed_callback(sub { $self->{preview_iface}->set_viewport_into_scene($self->{canvas3D}); }); -#====================================================================================================================================== - $self->{preview_notebook}->AddPage($self->{preview3D}, L('Preview')); +# $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{gcode_preview_data}, $self->{config}); +# Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 1); +# Slic3r::GUI::_3DScene::enable_dynamic_background($self->{preview3D}->canvas, 1); +# Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); }); +# $self->{preview_notebook}->AddPage($self->{preview3D}, L('Preview')); +#====================================================================================================================================== $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1; } @@ -329,19 +326,19 @@ sub new { my $preview = $self->{preview_notebook}->GetCurrentPage; #====================================================================================================================================== my $page_id = $self->{preview_notebook}->GetSelection; - if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D}) && ($page_id != $self->{preview_page_idx})) { + if (($preview != $self->{canvas3D}) && ($page_id != $self->{preview_page_idx})) { # if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D})) { #====================================================================================================================================== $preview->OnActivate if $preview->can('OnActivate'); - } elsif ($preview == $self->{preview3D}) { - $self->{preview3D}->reload_print; - # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) - Slic3r::GUI::_3DScene::set_as_dirty($self->{preview3D}->canvas); #====================================================================================================================================== } elsif ($page_id == $self->{preview_page_idx}) { $self->{preview_iface}->reload_print; # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) $self->{preview_iface}->set_canvas_as_dirty; +# } elsif ($preview == $self->{preview3D}) { +# $self->{preview3D}->reload_print; +# # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) +# Slic3r::GUI::_3DScene::set_as_dirty($self->{preview3D}->canvas); #====================================================================================================================================== } elsif ($preview == $self->{canvas3D}) { if (Slic3r::GUI::_3DScene::is_reload_delayed($self->{canvas3D})) { @@ -486,7 +483,7 @@ sub new { $_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self)) for grep defined($_), #====================================================================================================================================== - $self, $self->{canvas3D}, $self->{preview3D}, $self->{preview_iface}, $self->{list}; + $self, $self->{canvas3D}, $self->{preview_iface}, $self->{list}; # $self, $self->{canvas3D}, $self->{preview3D}, $self->{list}; #====================================================================================================================================== # $self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D}; @@ -515,11 +512,11 @@ sub new { Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape); Slic3r::GUI::_3DScene::zoom_to_bed($self->{canvas3D}); } - if ($self->{preview3D}) { - Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape); - } #====================================================================================================================================== $self->{preview_iface}->set_bed_shape($self->{config}->bed_shape) if ($self->{preview_iface}); +# if ($self->{preview3D}) { +# Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape); +# } #====================================================================================================================================== $self->update; @@ -1035,9 +1032,9 @@ sub remove { $self->stop_background_process; # Prevent toolpaths preview from rendering while we modify the Print object - $self->{preview3D}->enabled(0) if $self->{preview3D}; #====================================================================================================================================== $self->{preview_iface}->set_enabled(0) if $self->{preview_iface}; +# $self->{preview3D}->enabled(0) if $self->{preview3D}; #====================================================================================================================================== # If no object index is supplied, remove the selected one. @@ -1063,9 +1060,9 @@ sub reset { $self->stop_background_process; # Prevent toolpaths preview from rendering while we modify the Print object - $self->{preview3D}->enabled(0) if $self->{preview3D}; #====================================================================================================================================== $self->{preview_iface}->set_enabled(0) if $self->{preview_iface}; +# $self->{preview3D}->enabled(0) if $self->{preview3D}; #====================================================================================================================================== @{$self->{objects}} = (); @@ -1427,9 +1424,9 @@ sub async_apply_config { # 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->{preview3D}->reload_print if $self->{preview3D}; #====================================================================================================================================== $self->{preview_iface}->reload_print if $self->{preview_iface}; +# $self->{preview3D}->reload_print if $self->{preview3D}; #====================================================================================================================================== # We also need to reload 3D scene because of the wipe tower preview box if ($self->{config}->wipe_tower) { @@ -1465,9 +1462,9 @@ sub start_background_process { sub stop_background_process { my ($self) = @_; $self->{background_slicing_process}->stop(); - $self->{preview3D}->reload_print if $self->{preview3D}; #====================================================================================================================================== $self->{preview_iface}->reload_print if $self->{preview_iface}; +# $self->{preview3D}->reload_print if $self->{preview3D}; #====================================================================================================================================== } @@ -1574,9 +1571,9 @@ sub export_gcode { # This message should be called by the background process synchronously. sub on_update_print_preview { my ($self) = @_; - $self->{preview3D}->reload_print if $self->{preview3D}; #====================================================================================================================================== $self->{preview_iface}->reload_print if $self->{preview_iface}; +# $self->{preview3D}->reload_print if $self->{preview3D}; #====================================================================================================================================== # in case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth: @@ -1658,9 +1655,9 @@ sub on_process_completed { $self->object_list_changed; # refresh preview - $self->{preview3D}->reload_print if $self->{preview3D}; #====================================================================================================================================== $self->{preview_iface}->reload_print if $self->{preview_iface}; +# $self->{preview3D}->reload_print if $self->{preview3D}; #====================================================================================================================================== } @@ -1929,11 +1926,11 @@ sub update { my $selections = $self->collect_selections; Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0); - $self->{preview3D}->reset_gcode_preview_data if $self->{preview3D}; - $self->{preview3D}->reload_print if $self->{preview3D}; #====================================================================================================================================== $self->{preview_iface}->reset_gcode_preview_data if $self->{preview_iface}; $self->{preview_iface}->reload_print if $self->{preview_iface}; +# $self->{preview3D}->reset_gcode_preview_data if $self->{preview3D}; +# $self->{preview3D}->reload_print if $self->{preview3D}; #====================================================================================================================================== $self->schedule_background_process; $self->Thaw; @@ -2010,9 +2007,9 @@ sub on_config_change { if ($opt_key eq 'bed_shape') { # $self->{canvas}->update_bed_size; Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape) if $self->{canvas3D}; - Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D}; #====================================================================================================================================== $self->{preview_iface}->set_bed_shape($self->{config}->bed_shape) if ($self->{preview_iface}); +# Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D}; #====================================================================================================================================== $update_scheduled = 1; } elsif ($opt_key =~ '^wipe_tower' || $opt_key eq 'single_extruder_multi_material') { @@ -2048,18 +2045,18 @@ sub on_config_change { } elsif ($opt_key eq 'extruder_colour') { $update_scheduled = 1; my $extruder_colors = $config->get('extruder_colour'); - $self->{preview3D}->set_number_extruders(scalar(@{$extruder_colors})); #====================================================================================================================================== $self->{preview_iface}->set_number_extruders(scalar(@{$extruder_colors})); +# $self->{preview3D}->set_number_extruders(scalar(@{$extruder_colors})); #====================================================================================================================================== } elsif ($opt_key eq 'max_print_height') { $update_scheduled = 1; } elsif ($opt_key eq 'printer_model') { # update to force bed selection (for texturing) Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape) if $self->{canvas3D}; - Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D}; #====================================================================================================================================== $self->{preview_iface}->set_bed_shape($self->{config}->bed_shape) if ($self->{preview_iface}); +# Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D}; #====================================================================================================================================== $update_scheduled = 1; } @@ -2511,23 +2508,18 @@ sub select_view { my ($self, $direction) = @_; my $idx_page = $self->{preview_notebook}->GetSelection; my $page = ($idx_page == &Wx::wxNOT_FOUND) ? L('3D') : $self->{preview_notebook}->GetPageText($idx_page); + if ($page eq L('Preview')) { #====================================================================================================================================== - if (($page eq L('Preview')) || ($page eq L('_Preview_'))) { -#====================================================================================================================================== - if ($page eq L('Preview')) { - Slic3r::GUI::_3DScene::select_view($self->{preview3D}->canvas, $direction); - Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); -#====================================================================================================================================== - } else { - $self->{preview_iface}->select_view($direction); - $self->{preview_iface}->set_viewport_into_scene($self->{canvas3D}); - } + $self->{preview_iface}->select_view($direction); + $self->{preview_iface}->set_viewport_into_scene($self->{canvas3D}); +# Slic3r::GUI::_3DScene::select_view($self->{preview3D}->canvas, $direction); +# Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); #====================================================================================================================================== } else { Slic3r::GUI::_3DScene::select_view($self->{canvas3D}, $direction); - Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); #====================================================================================================================================== $self->{preview_iface}->set_viewport_from_scene($self->{canvas3D}); +# Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); #====================================================================================================================================== } } diff --git a/xs/src/slic3r/GUI/GUI_Preview.cpp b/xs/src/slic3r/GUI/GUI_Preview.cpp index 2548a743b6..52d20e18a5 100644 --- a/xs/src/slic3r/GUI/GUI_Preview.cpp +++ b/xs/src/slic3r/GUI/GUI_Preview.cpp @@ -42,7 +42,7 @@ Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, { if (init(notebook, config, print, gcode_preview_data)) { - notebook->AddPage(this, _(L("_Preview_"))); + notebook->AddPage(this, _(L("Preview"))); show_hide_ui_elements("none"); load_print(); } From 4d6fb52047e80017be3fc28cb49da7860d5a731a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 18 Sep 2018 19:13:56 +0200 Subject: [PATCH 23/36] Removed explicit dependency of wxWidgets from PrintExport.hpp --- xs/src/libslic3r/Print.cpp | 32 +++++------ xs/src/libslic3r/PrintExport.hpp | 91 +++++++++++++++++++++++++------- 2 files changed, 87 insertions(+), 36 deletions(-) diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index f8f6537ca8..cdc12d2d15 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -1341,22 +1341,22 @@ std::string Print::output_filepath(const std::string &path) const void Print::export_png(const std::string &dirpath) { - size_t idx = 0; - for (PrintObject *obj : m_objects) { - obj->slice(); - this->set_status(int(floor(idx * 100. / m_objects.size() + 0.5)), "Slicing..."); - ++ idx; - } - this->set_status(90, "Exporting zipped archive..."); - print_to(*this, - dirpath, - float(m_config.bed_size_x.value), - float(m_config.bed_size_y.value), - int(m_config.pixel_width.value), - int(m_config.pixel_height.value), - float(m_config.exp_time.value), - float(m_config.exp_time_first.value)); - this->set_status(100, "Done."); +// size_t idx = 0; +// for (PrintObject *obj : m_objects) { +// obj->slice(); +// this->set_status(int(floor(idx * 100. / m_objects.size() + 0.5)), "Slicing..."); +// ++ idx; +// } +// this->set_status(90, "Exporting zipped archive..."); +// print_to(*this, +// dirpath, +// float(m_config.bed_size_x.value), +// float(m_config.bed_size_y.value), +// int(m_config.pixel_width.value), +// int(m_config.pixel_height.value), +// float(m_config.exp_time.value), +// float(m_config.exp_time_first.value)); +// this->set_status(100, "Done."); } // Returns extruder this eec should be printed with, according to PrintRegion config diff --git a/xs/src/libslic3r/PrintExport.hpp b/xs/src/libslic3r/PrintExport.hpp index 7c3871251f..8286b71659 100644 --- a/xs/src/libslic3r/PrintExport.hpp +++ b/xs/src/libslic3r/PrintExport.hpp @@ -7,9 +7,9 @@ #include #include -#include -#include -#include +//#include +//#include +//#include #include @@ -32,7 +32,7 @@ enum class FilePrinterFormat { * different implementations of this class template for each supported format. * */ -template +template class FilePrinter { public: @@ -73,10 +73,35 @@ public: void saveLayer(unsigned lyr, const std::string& path); }; +template struct VeryFalse { static const bool value = false; }; + +// This has to be explicitly implemented in the gui layer or a default zlib +// based implementation is needed. +template class Zipper { +public: + + Zipper(const std::string& /*zipfile_path*/) { + static_assert(Backend>::value, + "No zipper implementation provided!"); + } + + void next_entry(const std::string& /*fname*/) {} + + bool is_ok() { return false; } + + std::string get_name() { return ""; } + + template Zipper& operator<<(const T& /*arg*/) { + return *this; + } + + void close() {} +}; + // Implementation for PNG raster output // Be aware that if a large number of layers are allocated, it can very well // exhaust the available memory especially on 32 bit platform. -template<> class FilePrinter { +template class FilePrinter { struct Layer { Raster first; @@ -148,7 +173,7 @@ public: pxdim_(m.pxdim_) {} inline void layers(unsigned cnt) { if(cnt > 0) layers_rst_.resize(cnt); } - inline unsigned layers() const { return layers_rst_.size(); } + inline unsigned layers() const { return unsigned(layers_rst_.size()); } void printConfig(const Print& printconf) { print_ = &printconf; } @@ -184,37 +209,63 @@ public: inline void save(const std::string& path) { - wxFileName filepath(path); + Zipper zipper(path); - wxFFileOutputStream zipfile(path); + std::string project = zipper.get_name(); - std::string project = filepath.GetName().ToStdString(); - - if(!zipfile.IsOk()) { + if(!zipper.is_ok()) { BOOST_LOG_TRIVIAL(error) << "Can't create zip file for layers! " << path; return; } - wxZipOutputStream zipstream(zipfile); - wxStdOutputStream pngstream(zipstream); - - zipstream.PutNextEntry("config.ini"); - pngstream << createIniContent(project); + zipper.next_entry(project); + zipper << createIniContent(project); for(unsigned i = 0; i < layers_rst_.size(); i++) { if(layers_rst_[i].second.rdbuf()->in_avail() > 0) { char lyrnum[6]; std::sprintf(lyrnum, "%.5d", i); auto zfilename = project + lyrnum + ".png"; - zipstream.PutNextEntry(zfilename); - pngstream << layers_rst_[i].second.rdbuf(); + zipper.next_entry(zfilename); + zipper << layers_rst_[i].second.rdbuf(); layers_rst_[i].second.str(""); } } - zipstream.Close(); - zipfile.Close(); + zipper.close(); + +// wxFileName filepath(path); + +// wxFFileOutputStream zipfile(path); + +// std::string project = filepath.GetName().ToStdString(); + +// if(!zipfile.IsOk()) { +// BOOST_LOG_TRIVIAL(error) << "Can't create zip file for layers! " +// << path; +// return; +// } + +// wxZipOutputStream zipstream(zipfile); +// wxStdOutputStream pngstream(zipstream); + +// zipstream.PutNextEntry("config.ini"); +// pngstream << createIniContent(project); + +// for(unsigned i = 0; i < layers_rst_.size(); i++) { +// if(layers_rst_[i].second.rdbuf()->in_avail() > 0) { +// char lyrnum[6]; +// std::sprintf(lyrnum, "%.5d", i); +// auto zfilename = project + lyrnum + ".png"; +// zipstream.PutNextEntry(zfilename); +// pngstream << layers_rst_[i].second.rdbuf(); +// layers_rst_[i].second.str(""); +// } +// } + +// zipstream.Close(); +// zipfile.Close(); } void saveLayer(unsigned lyr, const std::string& path) { From e79b0a2f25f325ef8643587944ff0ce30f508c96 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 19 Sep 2018 08:59:11 +0200 Subject: [PATCH 24/36] Code cleanup --- lib/Slic3r/GUI/Plater.pm | 36 ------------------------------- xs/src/slic3r/GUI/GUI.cpp | 8 +------ xs/src/slic3r/GUI/GUI.hpp | 4 ---- xs/src/slic3r/GUI/GUI_Preview.cpp | 5 +++-- 4 files changed, 4 insertions(+), 49 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 7bfdadd0ef..c4c7224fbc 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -296,20 +296,17 @@ sub new { } }); -#====================================================================================================================================== Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { $self->{preview_iface}->set_viewport_from_scene($self->{canvas3D}); }); # Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); }); -#====================================================================================================================================== } Slic3r::GUI::register_on_request_update_callback(sub { $self->schedule_background_process; }); # Initialize 3D toolpaths preview if ($Slic3r::GUI::have_OpenGL) { -#====================================================================================================================================== $self->{preview_iface} = Slic3r::GUI::create_preview_iface($self->{preview_notebook}, $self->{config}, $self->{print}, $self->{gcode_preview_data}); $self->{preview_page_idx} = $self->{preview_notebook}->GetPageCount-1; $self->{preview_iface}->register_on_viewport_changed_callback(sub { $self->{preview_iface}->set_viewport_into_scene($self->{canvas3D}); }); @@ -318,19 +315,15 @@ sub new { # Slic3r::GUI::_3DScene::enable_dynamic_background($self->{preview3D}->canvas, 1); # Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); }); # $self->{preview_notebook}->AddPage($self->{preview3D}, L('Preview')); -#====================================================================================================================================== $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1; } EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub { my $preview = $self->{preview_notebook}->GetCurrentPage; -#====================================================================================================================================== my $page_id = $self->{preview_notebook}->GetSelection; if (($preview != $self->{canvas3D}) && ($page_id != $self->{preview_page_idx})) { # if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D})) { -#====================================================================================================================================== $preview->OnActivate if $preview->can('OnActivate'); -#====================================================================================================================================== } elsif ($page_id == $self->{preview_page_idx}) { $self->{preview_iface}->reload_print; # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) @@ -339,7 +332,6 @@ sub new { # $self->{preview3D}->reload_print; # # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) # Slic3r::GUI::_3DScene::set_as_dirty($self->{preview3D}->canvas); -#====================================================================================================================================== } elsif ($preview == $self->{canvas3D}) { if (Slic3r::GUI::_3DScene::is_reload_delayed($self->{canvas3D})) { my $selections = $self->collect_selections; @@ -482,10 +474,8 @@ sub new { $_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self)) for grep defined($_), -#====================================================================================================================================== $self, $self->{canvas3D}, $self->{preview_iface}, $self->{list}; # $self, $self->{canvas3D}, $self->{preview3D}, $self->{list}; -#====================================================================================================================================== # $self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D}; EVT_COMMAND($self, -1, $SLICING_COMPLETED_EVENT, sub { @@ -512,12 +502,10 @@ sub new { Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape); Slic3r::GUI::_3DScene::zoom_to_bed($self->{canvas3D}); } -#====================================================================================================================================== $self->{preview_iface}->set_bed_shape($self->{config}->bed_shape) if ($self->{preview_iface}); # if ($self->{preview3D}) { # Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape); # } -#====================================================================================================================================== $self->update; { @@ -1032,10 +1020,8 @@ sub remove { $self->stop_background_process; # Prevent toolpaths preview from rendering while we modify the Print object -#====================================================================================================================================== $self->{preview_iface}->set_enabled(0) if $self->{preview_iface}; # $self->{preview3D}->enabled(0) if $self->{preview3D}; -#====================================================================================================================================== # If no object index is supplied, remove the selected one. if (! defined $obj_idx) { @@ -1060,10 +1046,8 @@ sub reset { $self->stop_background_process; # Prevent toolpaths preview from rendering while we modify the Print object -#====================================================================================================================================== $self->{preview_iface}->set_enabled(0) if $self->{preview_iface}; # $self->{preview3D}->enabled(0) if $self->{preview3D}; -#====================================================================================================================================== @{$self->{objects}} = (); $self->{model}->clear_objects; @@ -1424,10 +1408,8 @@ sub async_apply_config { # 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->{preview_iface}->reload_print if $self->{preview_iface}; # $self->{preview3D}->reload_print if $self->{preview3D}; -#====================================================================================================================================== # We also need to reload 3D scene because of the wipe tower preview box if ($self->{config}->wipe_tower) { my $selections = $self->collect_selections; @@ -1462,10 +1444,8 @@ sub start_background_process { sub stop_background_process { my ($self) = @_; $self->{background_slicing_process}->stop(); -#====================================================================================================================================== $self->{preview_iface}->reload_print if $self->{preview_iface}; # $self->{preview3D}->reload_print if $self->{preview3D}; -#====================================================================================================================================== } # Called by the "Slice now" button, which is visible only if the background processing is disabled. @@ -1571,10 +1551,8 @@ sub export_gcode { # This message should be called by the background process synchronously. sub on_update_print_preview { my ($self) = @_; -#====================================================================================================================================== $self->{preview_iface}->reload_print if $self->{preview_iface}; # $self->{preview3D}->reload_print if $self->{preview3D}; -#====================================================================================================================================== # in case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth: my $selections = $self->collect_selections; @@ -1655,10 +1633,8 @@ sub on_process_completed { $self->object_list_changed; # refresh preview -#====================================================================================================================================== $self->{preview_iface}->reload_print if $self->{preview_iface}; # $self->{preview3D}->reload_print if $self->{preview3D}; -#====================================================================================================================================== } # Fill in the "Sliced info" box with the result of the G-code generator. @@ -1926,12 +1902,10 @@ sub update { my $selections = $self->collect_selections; Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0); -#====================================================================================================================================== $self->{preview_iface}->reset_gcode_preview_data if $self->{preview_iface}; $self->{preview_iface}->reload_print if $self->{preview_iface}; # $self->{preview3D}->reset_gcode_preview_data if $self->{preview3D}; # $self->{preview3D}->reload_print if $self->{preview3D}; -#====================================================================================================================================== $self->schedule_background_process; $self->Thaw; } @@ -2007,10 +1981,8 @@ sub on_config_change { if ($opt_key eq 'bed_shape') { # $self->{canvas}->update_bed_size; Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape) if $self->{canvas3D}; -#====================================================================================================================================== $self->{preview_iface}->set_bed_shape($self->{config}->bed_shape) if ($self->{preview_iface}); # Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D}; -#====================================================================================================================================== $update_scheduled = 1; } elsif ($opt_key =~ '^wipe_tower' || $opt_key eq 'single_extruder_multi_material') { $update_scheduled = 1; @@ -2045,19 +2017,15 @@ sub on_config_change { } elsif ($opt_key eq 'extruder_colour') { $update_scheduled = 1; my $extruder_colors = $config->get('extruder_colour'); -#====================================================================================================================================== $self->{preview_iface}->set_number_extruders(scalar(@{$extruder_colors})); # $self->{preview3D}->set_number_extruders(scalar(@{$extruder_colors})); -#====================================================================================================================================== } elsif ($opt_key eq 'max_print_height') { $update_scheduled = 1; } elsif ($opt_key eq 'printer_model') { # update to force bed selection (for texturing) Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape) if $self->{canvas3D}; -#====================================================================================================================================== $self->{preview_iface}->set_bed_shape($self->{config}->bed_shape) if ($self->{preview_iface}); # Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D}; -#====================================================================================================================================== $update_scheduled = 1; } } @@ -2509,18 +2477,14 @@ sub select_view { my $idx_page = $self->{preview_notebook}->GetSelection; my $page = ($idx_page == &Wx::wxNOT_FOUND) ? L('3D') : $self->{preview_notebook}->GetPageText($idx_page); if ($page eq L('Preview')) { -#====================================================================================================================================== $self->{preview_iface}->select_view($direction); $self->{preview_iface}->set_viewport_into_scene($self->{canvas3D}); # Slic3r::GUI::_3DScene::select_view($self->{preview3D}->canvas, $direction); # Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); -#====================================================================================================================================== } else { Slic3r::GUI::_3DScene::select_view($self->{canvas3D}, $direction); -#====================================================================================================================================== $self->{preview_iface}->set_viewport_from_scene($self->{canvas3D}); # Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); -#====================================================================================================================================== } } diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index b3b52e0c6e..24d459921d 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -46,10 +46,8 @@ #include "Tab.hpp" #include "TabIface.hpp" -//############################################################################################################################################################## #include "GUI_Preview.hpp" #include "GUI_PreviewIface.hpp" -//############################################################################################################################################################## #include "AboutDialog.hpp" #include "AppConfig.hpp" #include "ConfigSnapshotDialog.hpp" @@ -152,9 +150,7 @@ wxStaticBitmap *g_manifold_warning_icon = nullptr; bool g_show_print_info = false; bool g_show_manifold_warning_icon = false; -//############################################################################################################################################################## -PreviewIface* g_preview = nullptr; // <<< FIXME ENRICO -> add code to delete the pointer when the application closes -//############################################################################################################################################################## +PreviewIface* g_preview = nullptr; static void init_label_colours() { @@ -673,7 +669,6 @@ TabIface* get_preset_tab_iface(char *name) return new TabIface(nullptr); } -//############################################################################################################################################################## PreviewIface* create_preview_iface(wxNotebook* parent, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data) { if (g_preview == nullptr) @@ -684,7 +679,6 @@ PreviewIface* create_preview_iface(wxNotebook* parent, DynamicPrintConfig* confi return g_preview; } -//############################################################################################################################################################## // opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element) void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/) diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index a5fb4d44d8..8dfaf42c66 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -39,11 +39,9 @@ class AppConfig; class PresetUpdater; class DynamicPrintConfig; class TabIface; -//############################################################################################################################################################## class PreviewIface; class Print; class GCodePreviewData; -//############################################################################################################################################################## #define _(s) Slic3r::GUI::I18N::translate((s)) @@ -170,9 +168,7 @@ extern void open_preferences_dialog(int event_preferences); void create_preset_tabs(int event_value_change, int event_presets_changed); TabIface* get_preset_tab_iface(char *name); -//############################################################################################################################################################## PreviewIface* create_preview_iface(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data); -//############################################################################################################################################################## // add it at the end of the tab panel. void add_created_tab(Tab* panel, int event_value_change, int event_presets_changed); diff --git a/xs/src/slic3r/GUI/GUI_Preview.cpp b/xs/src/slic3r/GUI/GUI_Preview.cpp index 52d20e18a5..50d404e780 100644 --- a/xs/src/slic3r/GUI/GUI_Preview.cpp +++ b/xs/src/slic3r/GUI/GUI_Preview.cpp @@ -57,14 +57,15 @@ bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, Print* prin if (!Create(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize)) return false; - int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24, WX_GL_SAMPLE_BUFFERS, 1, WX_GL_SAMPLES, 4, 0 }; + int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24, WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0 }; int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER; const AppConfig* app_config = GUI::get_app_config(); bool enable_multisample = (app_config != nullptr) && (app_config->get("use_legacy_opengl") != "1") && (wxVersion >= 30003); // if multisample is not enabled or supported by the graphic card, remove it from the attributes list - bool can_multisample = enable_multisample && wxGLCanvas::IsDisplaySupported(attribList); // <<< FIXME ENRICO IsDisplaySupported() seems not to work + bool can_multisample = enable_multisample && wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample"); +// bool can_multisample = enable_multisample && wxGLCanvas::IsDisplaySupported(attribList); // <<< Alternative method: but IsDisplaySupported() seems not to work if (!can_multisample) attribList[4] = 0; From 51d504c7202488b57aa0710369f5240952ff3a0e Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 19 Sep 2018 10:34:21 +0200 Subject: [PATCH 25/36] Build fixes, conversion of wxString into utf-8 std::string --- xs/src/libslic3r/PrintExport.hpp | 4 ++-- xs/src/libslic3r/SupportMaterial.cpp | 5 ++++- xs/src/slic3r/GUI/GUI.cpp | 6 ++++++ xs/src/slic3r/GUI/GUI.hpp | 2 ++ xs/src/slic3r/GUI/GUI_Preview.cpp | 8 +++++--- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/xs/src/libslic3r/PrintExport.hpp b/xs/src/libslic3r/PrintExport.hpp index 8286b71659..4de12d3c2e 100644 --- a/xs/src/libslic3r/PrintExport.hpp +++ b/xs/src/libslic3r/PrintExport.hpp @@ -81,8 +81,8 @@ template class Zipper { public: Zipper(const std::string& /*zipfile_path*/) { - static_assert(Backend>::value, - "No zipper implementation provided!"); + // static_assert(Backend>::value, + // "No zipper implementation provided!"); } void next_entry(const std::string& /*fname*/) {} diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 2bcf597e6c..bd1a9f3fbc 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -2618,7 +2618,10 @@ void modulate_extrusion_by_overlapping_layers( (fragment_end.is_start ? &polyline.points.front() : &polyline.points.back()); } private: - ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {} + ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) { + return *this; + } + const std::vector &m_path_fragments; }; const coord_t search_radius = 7; diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 24d459921d..e5ed917b35 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -991,6 +991,12 @@ wxString from_u8(const std::string &str) return wxString::FromUTF8(str.c_str()); } +std::string into_u8(const wxString &str) +{ + auto buffer_utf8 = str.utf8_str(); + return std::string(buffer_utf8.data()); +} + void set_model_events_from_perl(Model &model, int event_object_selection_changed, int event_object_settings_changed, diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 8dfaf42c66..cbf83aa948 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -217,6 +217,8 @@ int combochecklist_get_flags(wxComboCtrl* comboCtrl); wxString L_str(const std::string &str); // Return wxString from std::string in UTF8 wxString from_u8(const std::string &str); +// Return std::string in UTF8 from wxString +std::string into_u8(const wxString &str); void set_model_events_from_perl(Model &model, int event_object_selection_changed, diff --git a/xs/src/slic3r/GUI/GUI_Preview.cpp b/xs/src/slic3r/GUI/GUI_Preview.cpp index 50d404e780..bfc8b56238 100644 --- a/xs/src/slic3r/GUI/GUI_Preview.cpp +++ b/xs/src/slic3r/GUI/GUI_Preview.cpp @@ -99,8 +99,9 @@ bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, Print* prin m_combochecklist_features = new wxComboCtrl(); m_combochecklist_features->Create(this, wxID_ANY, _(L("Feature types")), wxDefaultPosition, wxSize(200, -1), wxCB_READONLY); - std::string feature_text = _(L("Feature types")); - std::string feature_items = _(L("Perimeter")) + "|" + + std::string feature_text = GUI::into_u8(_(L("Feature types"))); + std::string feature_items = GUI::into_u8( + _(L("Perimeter")) + "|" + _(L("External perimeter")) + "|" + _(L("Overhang perimeter")) + "|" + _(L("Internal infill")) + "|" + @@ -112,7 +113,8 @@ bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, Print* prin _(L("Support material")) + "|" + _(L("Support material interface")) + "|" + _(L("Wipe tower")) + "|" + - _(L("Custom")); + _(L("Custom")) + ); Slic3r::GUI::create_combochecklist(m_combochecklist_features, feature_text, feature_items, true); m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel"))); From 4bf49fe990d8f894323d52d02b4bcf70e9eebb20 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Mon, 17 Sep 2018 13:34:29 +0200 Subject: [PATCH 26/36] Build: Make return value type mismatch an error on Unix --- xs/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 8aa80278e5..7d4c324f4e 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -16,6 +16,12 @@ if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals" ) endif() +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # On GCC and Clang, no return from a non-void function is a warning only. Here, we make it an error. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=return-type" ) +endif() + + # Where all the bundled libraries reside? set(LIBDIR ${CMAKE_CURRENT_SOURCE_DIR}/src/) # For the bundled boost libraries (boost::nowide) From 7708fb8ada2a71d69969c08ba6639992ddf5ccbc Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 19 Sep 2018 11:08:10 +0200 Subject: [PATCH 27/36] Zipper implementation in gui level for png export. --- xs/src/libslic3r/PrintExport.hpp | 70 +++--- xs/src/slic3r/AppController.cpp | 386 ++++++++++++++++++++++++++++++- xs/src/slic3r/AppController.hpp | 63 +++-- 3 files changed, 463 insertions(+), 56 deletions(-) diff --git a/xs/src/libslic3r/PrintExport.hpp b/xs/src/libslic3r/PrintExport.hpp index 4de12d3c2e..c3c59fc30b 100644 --- a/xs/src/libslic3r/PrintExport.hpp +++ b/xs/src/libslic3r/PrintExport.hpp @@ -7,10 +7,6 @@ #include #include -//#include -//#include -//#include - #include #include "Rasterizer/Rasterizer.hpp" @@ -81,14 +77,12 @@ template class Zipper { public: Zipper(const std::string& /*zipfile_path*/) { - // static_assert(Backend>::value, - // "No zipper implementation provided!"); + static_assert(VeryFalse::value, + "No zipper implementation provided!"); } void next_entry(const std::string& /*fname*/) {} - bool is_ok() { return false; } - std::string get_name() { return ""; } template Zipper& operator<<(const T& /*arg*/) { @@ -208,33 +202,32 @@ public: } inline void save(const std::string& path) { + try { + Zipper zipper(path); - Zipper zipper(path); + std::string project = zipper.get_name(); - std::string project = zipper.get_name(); + zipper.next_entry(project); + zipper << createIniContent(project); - if(!zipper.is_ok()) { + for(unsigned i = 0; i < layers_rst_.size(); i++) { + if(layers_rst_[i].second.rdbuf()->in_avail() > 0) { + char lyrnum[6]; + std::sprintf(lyrnum, "%.5d", i); + auto zfilename = project + lyrnum + ".png"; + zipper.next_entry(zfilename); + zipper << layers_rst_[i].second.rdbuf(); + layers_rst_[i].second.str(""); + } + } + + zipper.close(); + } catch(std::exception&) { BOOST_LOG_TRIVIAL(error) << "Can't create zip file for layers! " << path; return; } - zipper.next_entry(project); - zipper << createIniContent(project); - - for(unsigned i = 0; i < layers_rst_.size(); i++) { - if(layers_rst_[i].second.rdbuf()->in_avail() > 0) { - char lyrnum[6]; - std::sprintf(lyrnum, "%.5d", i); - auto zfilename = project + lyrnum + ".png"; - zipper.next_entry(zfilename); - zipper << layers_rst_[i].second.rdbuf(); - layers_rst_[i].second.str(""); - } - } - - zipper.close(); - // wxFileName filepath(path); // wxFFileOutputStream zipfile(path); @@ -294,7 +287,7 @@ inline coord_t py(const Point& p) { return p(1); } inline coordf_t px(const Vec2d& p) { return p(0); } inline coordf_t py(const Vec2d& p) { return p(1); } -template +template void print_to(Print& print, std::string dirpath, double width_mm, @@ -312,7 +305,9 @@ void print_to(Print& print, auto& objects = print.objects(); // Merge the sliced layers with the support layers - std::for_each(objects.cbegin(), objects.cend(), [&layers](const PrintObject *o) { + std::for_each(objects.cbegin(), objects.cend(), + [&layers](const PrintObject *o) + { for(const auto l : o->layers()) { auto& lyrs = layers[static_cast(scale_(l->print_z))]; lyrs.push_back(l); @@ -339,8 +334,8 @@ void print_to(Print& print, auto cy = scale_(height_mm)/2 - (py(print_bb.center()) - py(print_bb.min)); // Create the actual printer, forward any additional arguments to it. - FilePrinter printer(width_mm, height_mm, - std::forward(args)...); + FilePrinter printer(width_mm, height_mm, + std::forward(args)...); printer.printConfig(print); @@ -354,14 +349,11 @@ void print_to(Print& print, keys.reserve(layers.size()); for(auto& e : layers) keys.push_back(e.first); - //FIXME - int initstatus = //print.progressindicator? print.progressindicator->state() : - 0; - print.set_status(initstatus, jobdesc); + print.set_status(0, jobdesc); // Method that prints one layer auto process_layer = [&layers, &keys, &printer, &st_prev, &m, - &jobdesc, print_bb, dir, cx, cy, &print, initstatus] + &jobdesc, print_bb, dir, cx, cy, &print] (unsigned layer_id) { LayerPtrs lrange = layers[keys[layer_id]]; @@ -405,7 +397,7 @@ void print_to(Print& print, auto st = static_cast(layer_id*80.0/layers.size()); m.lock(); if( st - st_prev > 10) { - print.set_status(initstatus + st, jobdesc); + print.set_status(st, jobdesc); st_prev = st; } m.unlock(); @@ -424,9 +416,9 @@ void print_to(Print& print, // print.set_status(100, jobdesc); // Save the print into the file system. - print.set_status(initstatus + 90, "Writing layers to disk"); + print.set_status(90, "Writing layers to disk"); printer.save(dir); - print.set_status(initstatus + 100, "Writing layers completed"); + print.set_status(100, "Writing layers completed"); } } diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index 4a36b5d7f1..6728c6c00c 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -11,12 +11,17 @@ #include #include -#include #include #include +#include +#include #include #include +#include +#include +#include + namespace Slic3r { class AppControllerBoilerplate::PriData { @@ -43,6 +48,15 @@ namespace GUI { PresetBundle* get_preset_bundle(); } +static const PrintObjectStep STEP_SLICE = posSlice; +static const PrintObjectStep STEP_PERIMETERS = posPerimeters; +static const PrintObjectStep STEP_PREPARE_INFILL = posPrepareInfill; +static const PrintObjectStep STEP_INFILL = posInfill; +static const PrintObjectStep STEP_SUPPORTMATERIAL = posSupportMaterial; +static const PrintStep STEP_SKIRT = psSkirt; +static const PrintStep STEP_BRIM = psBrim; +static const PrintStep STEP_WIPE_TOWER = psWipeTower; + AppControllerBoilerplate::ProgresIndicatorPtr AppControllerBoilerplate::global_progress_indicator() { ProgresIndicatorPtr ret; @@ -62,6 +76,376 @@ void AppControllerBoilerplate::global_progress_indicator( pri_data_->m.unlock(); } +//void PrintController::make_skirt() +//{ +// assert(print_ != nullptr); + +// // prerequisites +// for(auto obj : print_->objects) make_perimeters(obj); +// for(auto obj : print_->objects) infill(obj); +// for(auto obj : print_->objects) gen_support_material(obj); + +// if(!print_->state.is_done(STEP_SKIRT)) { +// print_->state.set_started(STEP_SKIRT); +// print_->skirt.clear(); +// if(print_->has_skirt()) print_->_make_skirt(); + +// print_->state.set_done(STEP_SKIRT); +// } +//} + +//void PrintController::make_brim() +//{ +// assert(print_ != nullptr); + +// // prerequisites +// for(auto obj : print_->objects) make_perimeters(obj); +// for(auto obj : print_->objects) infill(obj); +// for(auto obj : print_->objects) gen_support_material(obj); +// make_skirt(); + +// if(!print_->state.is_done(STEP_BRIM)) { +// print_->state.set_started(STEP_BRIM); + +// // since this method must be idempotent, we clear brim paths *before* +// // checking whether we need to generate them +// print_->brim.clear(); + +// if(print_->config.brim_width > 0) print_->_make_brim(); + +// print_->state.set_done(STEP_BRIM); +// } +//} + +//void PrintController::make_wipe_tower() +//{ +// assert(print_ != nullptr); + +// // prerequisites +// for(auto obj : print_->objects) make_perimeters(obj); +// for(auto obj : print_->objects) infill(obj); +// for(auto obj : print_->objects) gen_support_material(obj); +// make_skirt(); +// make_brim(); + +// if(!print_->state.is_done(STEP_WIPE_TOWER)) { +// print_->state.set_started(STEP_WIPE_TOWER); + +// // since this method must be idempotent, we clear brim paths *before* +// // checking whether we need to generate them +// print_->brim.clear(); + +// if(print_->has_wipe_tower()) print_->_make_wipe_tower(); + +// print_->state.set_done(STEP_WIPE_TOWER); +// } +//} + +//void PrintController::slice(PrintObject *pobj) +//{ +// assert(pobj != nullptr && print_ != nullptr); + +// if(pobj->state.is_done(STEP_SLICE)) return; + +// pobj->state.set_started(STEP_SLICE); + +// pobj->_slice(); + +// auto msg = pobj->_fix_slicing_errors(); +// if(!msg.empty()) report_issue(IssueType::WARN, msg); + +// // simplify slices if required +// if (print_->config.resolution) +// pobj->_simplify_slices(scale_(print_->config.resolution)); + + +// if(pobj->layers.empty()) +// report_issue(IssueType::ERR, +// L("No layers were detected. You might want to repair your " +// "STL file(s) or check their size or thickness and retry") +// ); + +// pobj->state.set_done(STEP_SLICE); +//} + +//void PrintController::make_perimeters(PrintObject *pobj) +//{ +// assert(pobj != nullptr); + +// slice(pobj); + +// if (!pobj->state.is_done(STEP_PERIMETERS)) { +// pobj->_make_perimeters(); +// } +//} + +//void PrintController::infill(PrintObject *pobj) +//{ +// assert(pobj != nullptr); + +// make_perimeters(pobj); + +// if (!pobj->state.is_done(STEP_PREPARE_INFILL)) { +// pobj->state.set_started(STEP_PREPARE_INFILL); + +// pobj->_prepare_infill(); + +// pobj->state.set_done(STEP_PREPARE_INFILL); +// } + +// pobj->_infill(); +//} + +//void PrintController::gen_support_material(PrintObject *pobj) +//{ +// assert(pobj != nullptr); + +// // prerequisites +// slice(pobj); + +// if(!pobj->state.is_done(STEP_SUPPORTMATERIAL)) { +// pobj->state.set_started(STEP_SUPPORTMATERIAL); + +// pobj->clear_support_layers(); + +// if((pobj->config.support_material || pobj->config.raft_layers > 0) +// && pobj->layers.size() > 1) { +// pobj->_generate_support_material(); +// } + +// pobj->state.set_done(STEP_SUPPORTMATERIAL); +// } +//} + +PrintController::PngExportData +PrintController::query_png_export_data(const DynamicPrintConfig& conf) +{ + PngExportData ret; + + auto zippath = query_destination_path("Output zip file", "*.zip", "out"); + + ret.zippath = zippath; + + ret.width_mm = conf.opt_float("display_width"); + ret.height_mm = conf.opt_float("display_height"); + + ret.width_px = conf.opt_int("display_pixels_x"); + ret.height_px = conf.opt_int("display_pixels_y"); + + auto opt_corr = conf.opt("printer_correction"); + + if(opt_corr) { + ret.corr_x = opt_corr->values[0]; + ret.corr_y = opt_corr->values[1]; + ret.corr_z = opt_corr->values[2]; + } + + ret.exp_time_first_s = conf.opt_float("initial_exposure_time"); + ret.exp_time_s = conf.opt_float("exposure_time"); + + return ret; +} + +void PrintController::slice(AppControllerBoilerplate::ProgresIndicatorPtr pri) +{ + auto st = pri->state(); + + Slic3r::trace(3, "Starting the slicing process."); + + pri->update(st+20, L("Generating perimeters")); +// for(auto obj : print_->objects) make_perimeters(obj); + + pri->update(st+60, L("Infilling layers")); +// for(auto obj : print_->objects) infill(obj); + + pri->update(st+70, L("Generating support material")); +// for(auto obj : print_->objects) gen_support_material(obj); + +// pri->message_fmt(L("Weight: %.1fg, Cost: %.1f"), +// print_->total_weight, print_->total_cost); + pri->state(st+85); + + + pri->update(st+88, L("Generating skirt")); + make_skirt(); + + + pri->update(st+90, L("Generating brim")); + make_brim(); + + pri->update(st+95, L("Generating wipe tower")); + make_wipe_tower(); + + pri->update(st+100, L("Done")); + + // time to make some statistics.. + + Slic3r::trace(3, L("Slicing process finished.")); +} + +void PrintController::slice() +{ + auto pri = global_progress_indicator(); + if(!pri) pri = create_progress_indicator(100, L("Slicing")); + slice(pri); +} + +struct wxZipper {}; + +template<> class Zipper { + wxFileName m_fpath; + wxFFileOutputStream m_zipfile; + wxZipOutputStream m_zipstream; + wxStdOutputStream m_pngstream; +public: + + Zipper(const std::string& zipfile_path): + m_fpath(zipfile_path), + m_zipfile(zipfile_path), + m_zipstream(m_zipfile), + m_pngstream(m_zipstream) + { + if(!m_zipfile.IsOk()) + throw std::runtime_error(L("Cannot create zip file.")); + } + + void next_entry(const std::string& fname) { + m_zipstream.PutNextEntry(fname); + } + + std::string get_name() { + return m_fpath.GetName().ToStdString(); + } + + template Zipper& operator<<(const T& arg) { + m_pngstream << arg; return *this; + } + + void close() { + m_zipstream.Close(); + m_zipfile.Close(); + } +}; + +void PrintController::slice_to_png() +{ + using Pointf3 = Vec3d; + + auto presetbundle = GUI::get_preset_bundle(); + + assert(presetbundle); + + auto pt = presetbundle->printers.get_selected_preset().printer_technology(); + if(pt != ptSLA) { + report_issue(IssueType::ERR, L("Printer technology is not SLA!"), + L("Error")); + return; + } + + auto conf = presetbundle->full_config(); + conf.validate(); + + auto exd = query_png_export_data(conf); + if(exd.zippath.empty()) return; + + Print *print = print_; + + try { + print->apply_config(conf); + print->validate(); + } catch(std::exception& e) { + report_issue(IssueType::ERR, e.what(), "Error"); + return; + } + + // TODO: copy the model and work with the copy only + bool correction = false; + if(exd.corr_x != 1.0 || exd.corr_y != 1.0 || exd.corr_z != 1.0) { + correction = true; +// print->invalidate_all_steps(); + +// for(auto po : print->objects) { +// po->model_object()->scale( +// Pointf3(exd.corr_x, exd.corr_y, exd.corr_z) +// ); +// po->model_object()->invalidate_bounding_box(); +// po->reload_model_instances(); +// po->invalidate_all_steps(); +// } + } + + // Turn back the correction scaling on the model. + auto scale_back = [this, print, correction, exd]() { + if(correction) { // scale the model back +// print->invalidate_all_steps(); +// for(auto po : print->objects) { +// po->model_object()->scale( +// Pointf3(1.0/exd.corr_x, 1.0/exd.corr_y, 1.0/exd.corr_z) +// ); +// po->model_object()->invalidate_bounding_box(); +// po->reload_model_instances(); +// po->invalidate_all_steps(); +// } + } + }; + + auto print_bb = print->bounding_box(); + Vec2d punsc = unscale(print_bb.size()); + + // If the print does not fit into the print area we should cry about it. + if(px(punsc) > exd.width_mm || py(punsc) > exd.height_mm) { + std::stringstream ss; + + ss << L("Print will not fit and will be truncated!") << "\n" + << L("Width needed: ") << px(punsc) << " mm\n" + << L("Height needed: ") << py(punsc) << " mm\n"; + + if(!report_issue(IssueType::WARN_Q, ss.str(), L("Warning"))) { + scale_back(); + return; + } + } + + auto pri = create_progress_indicator( + 200, L("Slicing to zipped png files...")); + + pri->on_cancel([&print](){ print->cancel(); }); + + try { + pri->update(0, L("Slicing...")); + slice(pri); + } catch (std::exception& e) { + report_issue(IssueType::ERR, e.what(), L("Exception occurred")); + scale_back(); + if(print->canceled()) print->restart(); + return; + } + + auto initstate = unsigned(pri->state()); + print->set_status_callback([pri, initstate](int st, const std::string& msg) + { + pri->update(initstate + unsigned(st), msg); + }); + + try { + print_to( *print, exd.zippath, + exd.width_mm, exd.height_mm, + exd.width_px, exd.height_px, + exd.exp_time_s, exd.exp_time_first_s); + + } catch (std::exception& e) { + report_issue(IssueType::ERR, e.what(), L("Exception occurred")); + } + + scale_back(); + if(print->canceled()) print->restart(); +} + +const PrintConfig &PrintController::config() const +{ + return print_->config(); +} + void ProgressIndicator::message_fmt( const std::string &fmtstr, ...) { std::stringstream ss; diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp index 71472835eb..88d1f0ecac 100644 --- a/xs/src/slic3r/AppController.hpp +++ b/xs/src/slic3r/AppController.hpp @@ -165,12 +165,49 @@ protected: ProgresIndicatorPtr global_progressind_; }; -#if 0 /** * @brief Implementation of the printing logic. */ class PrintController: public AppControllerBoilerplate { Print *print_ = nullptr; + std::function rempools_; +protected: + + void make_skirt() {} + void make_brim() {} + void make_wipe_tower() {} + + void make_perimeters(PrintObject *pobj) {} + void infill(PrintObject *pobj) {} + void gen_support_material(PrintObject *pobj) {} + + // Data structure with the png export input data + struct PngExportData { + std::string zippath; // output zip file + unsigned long width_px = 1440; // resolution - rows + unsigned long height_px = 2560; // resolution columns + double width_mm = 68.0, height_mm = 120.0; // dimensions in mm + double exp_time_first_s = 35.0; // first exposure time + double exp_time_s = 8.0; // global exposure time + double corr_x = 1.0; // offsetting in x + double corr_y = 1.0; // offsetting in y + double corr_z = 1.0; // offsetting in y + }; + + // Should display a dialog with the input fields for printing to png + PngExportData query_png_export_data(const DynamicPrintConfig&); + + // The previous export data, to pre-populate the dialog + PngExportData prev_expdata_; + + /** + * @brief Slice one pront object. + * @param pobj The print object. + */ + void slice(PrintObject *pobj); + + void slice(ProgresIndicatorPtr pri); + public: // Must be public for perl to use it @@ -185,24 +222,18 @@ public: return PrintController::Ptr( new PrintController(print) ); } - void slice() {} - void slice_to_png() {} + /** + * @brief Slice the loaded print scene. + */ + void slice(); + + /** + * @brief Slice the print into zipped png files. + */ + void slice_to_png(); const PrintConfig& config() const; }; -#else -class PrintController: public AppControllerBoilerplate { -public: - using Ptr = std::unique_ptr; - explicit inline PrintController(Print *print){} - inline static Ptr create(Print *print) { - return PrintController::Ptr( new PrintController(print) ); - } - void slice() {} - void slice_to_png() {} - const PrintConfig& config() const { static PrintConfig cfg; return cfg; } -}; -#endif /** * @brief Top level controller. From 2056f4c3365b6b9c3eb89d3746882983ad7b42a8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 19 Sep 2018 13:43:15 +0200 Subject: [PATCH 28/36] Zipper concept clarified. --- xs/src/libslic3r/PrintExport.hpp | 61 ++++++++----------------------- xs/src/slic3r/AppController.cpp | 48 +++++++----------------- xs/src/slic3r/AppController.hpp | 17 +++++++++ xs/src/slic3r/AppControllerWx.cpp | 49 +++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 81 deletions(-) diff --git a/xs/src/libslic3r/PrintExport.hpp b/xs/src/libslic3r/PrintExport.hpp index c3c59fc30b..cd6d16f334 100644 --- a/xs/src/libslic3r/PrintExport.hpp +++ b/xs/src/libslic3r/PrintExport.hpp @@ -73,19 +73,21 @@ template struct VeryFalse { static const bool value = false; }; // This has to be explicitly implemented in the gui layer or a default zlib // based implementation is needed. -template class Zipper { +template class LayerWriter { public: - Zipper(const std::string& /*zipfile_path*/) { + LayerWriter(const std::string& /*zipfile_path*/) { static_assert(VeryFalse::value, - "No zipper implementation provided!"); + "No layer writer implementation provided!"); } void next_entry(const std::string& /*fname*/) {} std::string get_name() { return ""; } - template Zipper& operator<<(const T& /*arg*/) { + bool is_ok() { return false; } + + template LayerWriter& operator<<(const T& /*arg*/) { return *this; } @@ -203,62 +205,29 @@ public: inline void save(const std::string& path) { try { - Zipper zipper(path); + LayerWriter writer(path); - std::string project = zipper.get_name(); + std::string project = writer.get_name(); - zipper.next_entry(project); - zipper << createIniContent(project); + writer.next_entry("config.ini"); + writer << createIniContent(project); for(unsigned i = 0; i < layers_rst_.size(); i++) { if(layers_rst_[i].second.rdbuf()->in_avail() > 0) { char lyrnum[6]; std::sprintf(lyrnum, "%.5d", i); auto zfilename = project + lyrnum + ".png"; - zipper.next_entry(zfilename); - zipper << layers_rst_[i].second.rdbuf(); + writer.next_entry(zfilename); + writer << layers_rst_[i].second.rdbuf(); layers_rst_[i].second.str(""); } } - zipper.close(); - } catch(std::exception&) { - BOOST_LOG_TRIVIAL(error) << "Can't create zip file for layers! " - << path; + writer.close(); + } catch(std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); return; } - -// wxFileName filepath(path); - -// wxFFileOutputStream zipfile(path); - -// std::string project = filepath.GetName().ToStdString(); - -// if(!zipfile.IsOk()) { -// BOOST_LOG_TRIVIAL(error) << "Can't create zip file for layers! " -// << path; -// return; -// } - -// wxZipOutputStream zipstream(zipfile); -// wxStdOutputStream pngstream(zipstream); - -// zipstream.PutNextEntry("config.ini"); -// pngstream << createIniContent(project); - -// for(unsigned i = 0; i < layers_rst_.size(); i++) { -// if(layers_rst_[i].second.rdbuf()->in_avail() > 0) { -// char lyrnum[6]; -// std::sprintf(lyrnum, "%.5d", i); -// auto zfilename = project + lyrnum + ".png"; -// zipstream.PutNextEntry(zfilename); -// pngstream << layers_rst_[i].second.rdbuf(); -// layers_rst_[i].second.str(""); -// } -// } - -// zipstream.Close(); -// zipfile.Close(); } void saveLayer(unsigned lyr, const std::string& path) { diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index 6728c6c00c..b5c9702f13 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -18,9 +18,6 @@ #include #include -#include -#include -#include namespace Slic3r { @@ -290,41 +287,21 @@ void PrintController::slice() slice(pri); } -struct wxZipper {}; - -template<> class Zipper { - wxFileName m_fpath; - wxFFileOutputStream m_zipfile; - wxZipOutputStream m_zipstream; - wxStdOutputStream m_pngstream; +template<> class LayerWriter { + Zipper m_zip; public: - Zipper(const std::string& zipfile_path): - m_fpath(zipfile_path), - m_zipfile(zipfile_path), - m_zipstream(m_zipfile), - m_pngstream(m_zipstream) - { - if(!m_zipfile.IsOk()) - throw std::runtime_error(L("Cannot create zip file.")); + inline LayerWriter(const std::string& zipfile_path): m_zip(zipfile_path) {} + + inline void next_entry(const std::string& fname) { m_zip.next_entry(fname); } + + inline std::string get_name() const { return m_zip.get_name(); } + + template inline LayerWriter& operator<<(const T& arg) { + m_zip.stream() << arg; return *this; } - void next_entry(const std::string& fname) { - m_zipstream.PutNextEntry(fname); - } - - std::string get_name() { - return m_fpath.GetName().ToStdString(); - } - - template Zipper& operator<<(const T& arg) { - m_pngstream << arg; return *this; - } - - void close() { - m_zipstream.Close(); - m_zipfile.Close(); - } + inline void close() { m_zip.close(); } }; void PrintController::slice_to_png() @@ -428,7 +405,7 @@ void PrintController::slice_to_png() }); try { - print_to( *print, exd.zippath, + print_to( *print, exd.zippath, exd.width_mm, exd.height_mm, exd.width_px, exd.height_px, exd.exp_time_s, exd.exp_time_first_s); @@ -439,6 +416,7 @@ void PrintController::slice_to_png() scale_back(); if(print->canceled()) print->restart(); + print->set_status_default(); } const PrintConfig &PrintController::config() const diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp index 88d1f0ecac..c9ab6f15d0 100644 --- a/xs/src/slic3r/AppController.hpp +++ b/xs/src/slic3r/AppController.hpp @@ -165,6 +165,23 @@ protected: ProgresIndicatorPtr global_progressind_; }; +class Zipper { + struct Impl; + std::unique_ptr m_impl; +public: + + Zipper(const std::string& zipfilepath); + ~Zipper(); + + void next_entry(const std::string& fname); + + std::string get_name() const; + + std::ostream& stream(); + + void close(); +}; + /** * @brief Implementation of the printing logic. */ diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp index 4d67d5f66d..7de2a424f0 100644 --- a/xs/src/slic3r/AppControllerWx.cpp +++ b/xs/src/slic3r/AppControllerWx.cpp @@ -1,5 +1,9 @@ #include "AppController.hpp" +#include +#include +#include + #include #include @@ -98,6 +102,51 @@ bool AppControllerBoilerplate::report_issue( wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent); +struct Zipper::Impl { + wxFileName m_fpath; + wxFFileOutputStream m_zipfile; + wxZipOutputStream m_zipstream; + wxStdOutputStream m_pngstream; + + Impl(const std::string& zipfile_path): + m_fpath(zipfile_path), + m_zipfile(zipfile_path), + m_zipstream(m_zipfile), + m_pngstream(m_zipstream) + { + if(!m_zipfile.IsOk()) + throw std::runtime_error(L("Cannot create zip file.")); + } +}; + +Zipper::Zipper(const std::string &zipfilepath) +{ + m_impl.reset(new Impl(zipfilepath)); +} + +Zipper::~Zipper() {} + +void Zipper::next_entry(const std::string &fname) +{ + m_impl->m_zipstream.PutNextEntry(fname); +} + +std::string Zipper::get_name() const +{ + return m_impl->m_fpath.GetName().ToStdString(); +} + +std::ostream &Zipper::stream() +{ + return m_impl->m_pngstream; +} + +void Zipper::close() +{ + m_impl->m_zipstream.Close(); + m_impl->m_zipfile.Close(); +} + namespace { /* From 5f587eb362cdf169c484fec2b10af8e37d8beed5 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 19 Sep 2018 14:11:36 +0200 Subject: [PATCH 29/36] Fixed colors of extrusion paths --- xs/src/slic3r/GUI/GUI_Preview.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/xs/src/slic3r/GUI/GUI_Preview.cpp b/xs/src/slic3r/GUI/GUI_Preview.cpp index bfc8b56238..c66d1fd1f2 100644 --- a/xs/src/slic3r/GUI/GUI_Preview.cpp +++ b/xs/src/slic3r/GUI/GUI_Preview.cpp @@ -152,20 +152,20 @@ bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, Print* prin bind_event_handlers(); // sets colors for gcode preview extrusion roles - std::vector extrusion_roles_colors = { - "FFFF66", // Perimeter - "FFA500", // External perimeter - "0000FF", // Overhang perimeter - "B1302A", // Internal infill - "D732D7", // Solid infill - "FF1A1A", // Top solid infill - "9999FF", // Bridge infill - "FFFFFF", // Gap fill - "845321", // Skirt - "00FF00", // Support material - "008000", // Support material interface - "B3E3AB", // Wipe tower - "28CC94" // Custom + std::vector extrusion_roles_colors = { + "Perimeter", "FFFF66", + "External perimeter", "FFA500", + "Overhang perimeter", "0000FF", + "Internal infill", "B1302A", + "Solid infill", "D732D7", + "Top solid infill", "FF1A1A", + "Bridge infill", "9999FF", + "Gap fill", "FFFFFF", + "Skirt", "845321", + "Support material", "00FF00", + "Support material interface", "008000", + "Wipe tower", "B3E3AB", + "Custom", "28CC94" }; m_gcode_preview_data->set_extrusion_paths_colors(extrusion_roles_colors); From 1a0b72de2c5b07fa120f14fb96f139d557cc7c6c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 19 Sep 2018 14:32:38 +0200 Subject: [PATCH 30/36] png export recovered with the new print object interface. --- xs/src/slic3r/AppController.cpp | 177 +------------------------------- 1 file changed, 4 insertions(+), 173 deletions(-) diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index b5c9702f13..c9029f2be0 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -73,147 +73,6 @@ void AppControllerBoilerplate::global_progress_indicator( pri_data_->m.unlock(); } -//void PrintController::make_skirt() -//{ -// assert(print_ != nullptr); - -// // prerequisites -// for(auto obj : print_->objects) make_perimeters(obj); -// for(auto obj : print_->objects) infill(obj); -// for(auto obj : print_->objects) gen_support_material(obj); - -// if(!print_->state.is_done(STEP_SKIRT)) { -// print_->state.set_started(STEP_SKIRT); -// print_->skirt.clear(); -// if(print_->has_skirt()) print_->_make_skirt(); - -// print_->state.set_done(STEP_SKIRT); -// } -//} - -//void PrintController::make_brim() -//{ -// assert(print_ != nullptr); - -// // prerequisites -// for(auto obj : print_->objects) make_perimeters(obj); -// for(auto obj : print_->objects) infill(obj); -// for(auto obj : print_->objects) gen_support_material(obj); -// make_skirt(); - -// if(!print_->state.is_done(STEP_BRIM)) { -// print_->state.set_started(STEP_BRIM); - -// // since this method must be idempotent, we clear brim paths *before* -// // checking whether we need to generate them -// print_->brim.clear(); - -// if(print_->config.brim_width > 0) print_->_make_brim(); - -// print_->state.set_done(STEP_BRIM); -// } -//} - -//void PrintController::make_wipe_tower() -//{ -// assert(print_ != nullptr); - -// // prerequisites -// for(auto obj : print_->objects) make_perimeters(obj); -// for(auto obj : print_->objects) infill(obj); -// for(auto obj : print_->objects) gen_support_material(obj); -// make_skirt(); -// make_brim(); - -// if(!print_->state.is_done(STEP_WIPE_TOWER)) { -// print_->state.set_started(STEP_WIPE_TOWER); - -// // since this method must be idempotent, we clear brim paths *before* -// // checking whether we need to generate them -// print_->brim.clear(); - -// if(print_->has_wipe_tower()) print_->_make_wipe_tower(); - -// print_->state.set_done(STEP_WIPE_TOWER); -// } -//} - -//void PrintController::slice(PrintObject *pobj) -//{ -// assert(pobj != nullptr && print_ != nullptr); - -// if(pobj->state.is_done(STEP_SLICE)) return; - -// pobj->state.set_started(STEP_SLICE); - -// pobj->_slice(); - -// auto msg = pobj->_fix_slicing_errors(); -// if(!msg.empty()) report_issue(IssueType::WARN, msg); - -// // simplify slices if required -// if (print_->config.resolution) -// pobj->_simplify_slices(scale_(print_->config.resolution)); - - -// if(pobj->layers.empty()) -// report_issue(IssueType::ERR, -// L("No layers were detected. You might want to repair your " -// "STL file(s) or check their size or thickness and retry") -// ); - -// pobj->state.set_done(STEP_SLICE); -//} - -//void PrintController::make_perimeters(PrintObject *pobj) -//{ -// assert(pobj != nullptr); - -// slice(pobj); - -// if (!pobj->state.is_done(STEP_PERIMETERS)) { -// pobj->_make_perimeters(); -// } -//} - -//void PrintController::infill(PrintObject *pobj) -//{ -// assert(pobj != nullptr); - -// make_perimeters(pobj); - -// if (!pobj->state.is_done(STEP_PREPARE_INFILL)) { -// pobj->state.set_started(STEP_PREPARE_INFILL); - -// pobj->_prepare_infill(); - -// pobj->state.set_done(STEP_PREPARE_INFILL); -// } - -// pobj->_infill(); -//} - -//void PrintController::gen_support_material(PrintObject *pobj) -//{ -// assert(pobj != nullptr); - -// // prerequisites -// slice(pobj); - -// if(!pobj->state.is_done(STEP_SUPPORTMATERIAL)) { -// pobj->state.set_started(STEP_SUPPORTMATERIAL); - -// pobj->clear_support_layers(); - -// if((pobj->config.support_material || pobj->config.raft_layers > 0) -// && pobj->layers.size() > 1) { -// pobj->_generate_support_material(); -// } - -// pobj->state.set_done(STEP_SUPPORTMATERIAL); -// } -//} - PrintController::PngExportData PrintController::query_png_export_data(const DynamicPrintConfig& conf) { @@ -245,39 +104,11 @@ PrintController::query_png_export_data(const DynamicPrintConfig& conf) void PrintController::slice(AppControllerBoilerplate::ProgresIndicatorPtr pri) { - auto st = pri->state(); + print_->set_status_callback([pri](int st, const std::string& msg){ + pri->update(unsigned(st), msg); + }); - Slic3r::trace(3, "Starting the slicing process."); - - pri->update(st+20, L("Generating perimeters")); -// for(auto obj : print_->objects) make_perimeters(obj); - - pri->update(st+60, L("Infilling layers")); -// for(auto obj : print_->objects) infill(obj); - - pri->update(st+70, L("Generating support material")); -// for(auto obj : print_->objects) gen_support_material(obj); - -// pri->message_fmt(L("Weight: %.1fg, Cost: %.1f"), -// print_->total_weight, print_->total_cost); - pri->state(st+85); - - - pri->update(st+88, L("Generating skirt")); - make_skirt(); - - - pri->update(st+90, L("Generating brim")); - make_brim(); - - pri->update(st+95, L("Generating wipe tower")); - make_wipe_tower(); - - pri->update(st+100, L("Done")); - - // time to make some statistics.. - - Slic3r::trace(3, L("Slicing process finished.")); + print_->process(); } void PrintController::slice() From 3f0968fb0274956ad7fa84b0fa35de3821e8d114 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 19 Sep 2018 14:54:37 +0200 Subject: [PATCH 31/36] Refactoring member variable names for my classes to match our coding style. --- xs/src/libslic3r/PrintExport.hpp | 114 ++++++++++----------- xs/src/libslic3r/Rasterizer/Rasterizer.cpp | 90 ++++++++-------- xs/src/libslic3r/Rasterizer/Rasterizer.hpp | 4 +- xs/src/slic3r/AppController.cpp | 42 ++++---- xs/src/slic3r/AppController.hpp | 34 ++---- xs/src/slic3r/AppControllerWx.cpp | 96 ++++++++--------- xs/src/slic3r/GUI/ProgressIndicator.hpp | 18 ++-- xs/src/slic3r/GUI/ProgressStatusBar.cpp | 76 +++++++------- xs/src/slic3r/GUI/ProgressStatusBar.hpp | 12 +-- 9 files changed, 236 insertions(+), 250 deletions(-) diff --git a/xs/src/libslic3r/PrintExport.hpp b/xs/src/libslic3r/PrintExport.hpp index cd6d16f334..917a39e081 100644 --- a/xs/src/libslic3r/PrintExport.hpp +++ b/xs/src/libslic3r/PrintExport.hpp @@ -32,10 +32,10 @@ template class FilePrinter { public: - void printConfig(const Print&); + void print_config(const Print&); // Draw an ExPolygon which is a polygon inside a slice on the specified layer. - void drawPolygon(const ExPolygon& p, unsigned lyr); + void draw_polygon(const ExPolygon& p, unsigned lyr); // Tell the printer how many layers should it consider. void layers(unsigned layernum); @@ -47,26 +47,26 @@ public: * specified layer number than an appropriate number of layers will be * allocated in the printer. */ - void beginLayer(unsigned layer); + void begin_layer(unsigned layer); // Allocate a new layer on top of the last and switch to it. - void beginLayer(); + void begin_layer(); /* * Finish the selected layer. It means that no drawing is allowed on that * layer anymore. This fact can be used to prepare the file system output * data like png comprimation and so on. */ - void finishLayer(unsigned layer); + void finish_layer(unsigned layer); // Finish the top layer. - void finishLayer(); + void finish_layer(); // Save all the layers into the file (or dir) specified in the path argument void save(const std::string& path); // Save only the selected layer to the file specified in path argument. - void saveLayer(unsigned lyr, const std::string& path); + void save_layer(unsigned lyr, const std::string& path); }; template struct VeryFalse { static const bool value = false; }; @@ -112,22 +112,22 @@ template class FilePrinter { // We will save the compressed PNG data into stringstreams which can be done // in parallel. Later we can write every layer to the disk sequentially. - std::vector layers_rst_; - Raster::Resolution res_; - Raster::PixelDim pxdim_; - const Print *print_ = nullptr; - double exp_time_s_ = .0, exp_time_first_s_ = .0; + std::vector m_layers_rst; + Raster::Resolution m_res; + Raster::PixelDim m_pxdim; + const Print *m_print = nullptr; + double m_exp_time_s = .0, m_exp_time_first_s = .0; std::string createIniContent(const std::string& projectname) { - double layer_height = print_? - print_->default_object_config().layer_height.getFloat() : + double layer_height = m_print? + m_print->default_object_config().layer_height.getFloat() : 0.05; using std::string; using std::to_string; - auto expt_str = to_string(exp_time_s_); - auto expt_first_str = to_string(exp_time_first_s_); + auto expt_str = to_string(m_exp_time_s); + auto expt_first_str = to_string(m_exp_time_first_s); auto stepnum_str = to_string(static_cast(800*layer_height)); auto layerh_str = to_string(layer_height); @@ -155,51 +155,51 @@ public: inline FilePrinter(double width_mm, double height_mm, unsigned width_px, unsigned height_px, double exp_time, double exp_time_first): - res_(width_px, height_px), - pxdim_(width_mm/width_px, height_mm/height_px), - exp_time_s_(exp_time), - exp_time_first_s_(exp_time_first) + m_res(width_px, height_px), + m_pxdim(width_mm/width_px, height_mm/height_px), + m_exp_time_s(exp_time), + m_exp_time_first_s(exp_time_first) { } FilePrinter(const FilePrinter& ) = delete; FilePrinter(FilePrinter&& m): - layers_rst_(std::move(m.layers_rst_)), - res_(m.res_), - pxdim_(m.pxdim_) {} + m_layers_rst(std::move(m.m_layers_rst)), + m_res(m.m_res), + m_pxdim(m.m_pxdim) {} - inline void layers(unsigned cnt) { if(cnt > 0) layers_rst_.resize(cnt); } - inline unsigned layers() const { return unsigned(layers_rst_.size()); } + inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); } + inline unsigned layers() const { return unsigned(m_layers_rst.size()); } - void printConfig(const Print& printconf) { print_ = &printconf; } + void print_config(const Print& printconf) { m_print = &printconf; } - inline void drawPolygon(const ExPolygon& p, unsigned lyr) { - assert(lyr < layers_rst_.size()); - layers_rst_[lyr].first.draw(p); + inline void draw_polygon(const ExPolygon& p, unsigned lyr) { + assert(lyr < m_layers_rst.size()); + m_layers_rst[lyr].first.draw(p); } - inline void beginLayer(unsigned lyr) { - if(layers_rst_.size() <= lyr) layers_rst_.resize(lyr+1); - layers_rst_[lyr].first.reset(res_, pxdim_, ORIGIN); + inline void begin_layer(unsigned lyr) { + if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1); + m_layers_rst[lyr].first.reset(m_res, m_pxdim, ORIGIN); } - inline void beginLayer() { - layers_rst_.emplace_back(); - layers_rst_.front().first.reset(res_, pxdim_, ORIGIN); + inline void begin_layer() { + m_layers_rst.emplace_back(); + m_layers_rst.front().first.reset(m_res, m_pxdim, ORIGIN); } - inline void finishLayer(unsigned lyr_id) { - assert(lyr_id < layers_rst_.size()); - layers_rst_[lyr_id].first.save(layers_rst_[lyr_id].second, + inline void finish_layer(unsigned lyr_id) { + assert(lyr_id < m_layers_rst.size()); + m_layers_rst[lyr_id].first.save(m_layers_rst[lyr_id].second, Raster::Compression::PNG); - layers_rst_[lyr_id].first.reset(); + m_layers_rst[lyr_id].first.reset(); } - inline void finishLayer() { - if(!layers_rst_.empty()) { - layers_rst_.back().first.save(layers_rst_.back().second, + inline void finish_layer() { + if(!m_layers_rst.empty()) { + m_layers_rst.back().first.save(m_layers_rst.back().second, Raster::Compression::PNG); - layers_rst_.back().first.reset(); + m_layers_rst.back().first.reset(); } } @@ -212,14 +212,14 @@ public: writer.next_entry("config.ini"); writer << createIniContent(project); - for(unsigned i = 0; i < layers_rst_.size(); i++) { - if(layers_rst_[i].second.rdbuf()->in_avail() > 0) { + for(unsigned i = 0; i < m_layers_rst.size(); i++) { + if(m_layers_rst[i].second.rdbuf()->in_avail() > 0) { char lyrnum[6]; std::sprintf(lyrnum, "%.5d", i); auto zfilename = project + lyrnum + ".png"; writer.next_entry(zfilename); - writer << layers_rst_[i].second.rdbuf(); - layers_rst_[i].second.str(""); + writer << m_layers_rst[i].second.rdbuf(); + m_layers_rst[i].second.str(""); } } @@ -230,9 +230,9 @@ public: } } - void saveLayer(unsigned lyr, const std::string& path) { + void save_layer(unsigned lyr, const std::string& path) { unsigned i = lyr; - assert(i < layers_rst_.size()); + assert(i < m_layers_rst.size()); char lyrnum[6]; std::sprintf(lyrnum, "%.5d", lyr); @@ -240,19 +240,19 @@ public: std::fstream out(loc, std::fstream::out | std::fstream::binary); if(out.good()) { - layers_rst_[i].first.save(out, Raster::Compression::PNG); + m_layers_rst[i].first.save(out, Raster::Compression::PNG); } else { BOOST_LOG_TRIVIAL(error) << "Can't create file for layer"; } out.close(); - layers_rst_[i].first.reset(); + m_layers_rst[i].first.reset(); } }; // Let's shadow this eigen interface -inline coord_t px(const Point& p) { return p(0); } -inline coord_t py(const Point& p) { return p(1); } +inline coord_t px(const Point& p) { return p(0); } +inline coord_t py(const Point& p) { return p(1); } inline coordf_t px(const Vec2d& p) { return p(0); } inline coordf_t py(const Vec2d& p) { return p(1); } @@ -306,7 +306,7 @@ void print_to(Print& print, FilePrinter printer(width_mm, height_mm, std::forward(args)...); - printer.printConfig(print); + printer.print_config(print); printer.layers(layers.size()); // Allocate space for all the layers @@ -327,7 +327,7 @@ void print_to(Print& print, { LayerPtrs lrange = layers[keys[layer_id]]; - printer.beginLayer(layer_id); // Switch to the appropriate layer + printer.begin_layer(layer_id); // Switch to the appropriate layer for(Layer *lp : lrange) { Layer& l = *lp; @@ -348,7 +348,7 @@ void print_to(Print& print, slice.translate(-px(print_bb.min) + cx, -py(print_bb.min) + cy); - printer.drawPolygon(slice, layer_id); + printer.draw_polygon(slice, layer_id); } /*if(print.has_support_material() && layer_id > 0) { @@ -361,7 +361,7 @@ void print_to(Print& print, } - printer.finishLayer(layer_id); // Finish the layer for later saving it. + printer.finish_layer(layer_id); // Finish the layer for later saving it. auto st = static_cast(layer_id*80.0/layers.size()); m.lock(); diff --git a/xs/src/libslic3r/Rasterizer/Rasterizer.cpp b/xs/src/libslic3r/Rasterizer/Rasterizer.cpp index b0bf04343d..3ff3e09496 100644 --- a/xs/src/libslic3r/Rasterizer/Rasterizer.cpp +++ b/xs/src/libslic3r/Rasterizer/Rasterizer.cpp @@ -37,37 +37,37 @@ public: using Origin = Raster::Origin; private: - Raster::Resolution resolution_; - Raster::PixelDim pxdim_; - TBuffer buf_; - TRawBuffer rbuf_; - TPixelRenderer pixfmt_; - TRawRenderer raw_renderer_; - TRendererAA renderer_; - Origin o_; - std::function flipy_ = [](agg::path_storage&) {}; + Raster::Resolution m_resolution; + Raster::PixelDim m_pxdim; + TBuffer m_buf; + TRawBuffer m_rbuf; + TPixelRenderer m_pixfmt; + TRawRenderer m_raw_renderer; + TRendererAA m_renderer; + Origin m_o; + std::function m_flipy = [](agg::path_storage&) {}; public: inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd, Origin o): - resolution_(res), pxdim_(pd), - buf_(res.pixels()), - rbuf_(reinterpret_cast(buf_.data()), + m_resolution(res), m_pxdim(pd), + m_buf(res.pixels()), + m_rbuf(reinterpret_cast(m_buf.data()), res.width_px, res.height_px, res.width_px*TPixelRenderer::num_components), - pixfmt_(rbuf_), - raw_renderer_(pixfmt_), - renderer_(raw_renderer_), - o_(o) + m_pixfmt(m_rbuf), + m_raw_renderer(m_pixfmt), + m_renderer(m_raw_renderer), + m_o(o) { - renderer_.color(ColorWhite); + m_renderer.color(ColorWhite); // If we would like to play around with gamma // ras.gamma(agg::gamma_power(1.0)); clear(); - if(o_ == Origin::TOP_LEFT) flipy_ = [this](agg::path_storage& path) { - path.flip_y(0, resolution_.height_px); + if(m_o == Origin::TOP_LEFT) m_flipy = [this](agg::path_storage& path) { + path.flip_y(0, m_resolution.height_px); }; } @@ -76,35 +76,35 @@ public: agg::scanline_p8 scanlines; auto&& path = to_path(poly.contour); - flipy_(path); + m_flipy(path); ras.add_path(path); for(auto h : poly.holes) { auto&& holepath = to_path(h); - flipy_(holepath); + m_flipy(holepath); ras.add_path(holepath); } - agg::render_scanlines(ras, scanlines, renderer_); + agg::render_scanlines(ras, scanlines, m_renderer); } inline void clear() { - raw_renderer_.clear(ColorBlack); + m_raw_renderer.clear(ColorBlack); } - inline TBuffer& buffer() { return buf_; } + inline TBuffer& buffer() { return m_buf; } - inline const Raster::Resolution resolution() { return resolution_; } + inline const Raster::Resolution resolution() { return m_resolution; } - inline Origin origin() const /*noexcept*/ { return o_; } + inline Origin origin() const /*noexcept*/ { return m_o; } private: double getPx(const Point& p) { - return p(0) * SCALING_FACTOR/pxdim_.w_mm; + return p(0) * SCALING_FACTOR/m_pxdim.w_mm; } double getPy(const Point& p) { - return p(1) * SCALING_FACTOR/pxdim_.h_mm; + return p(1) * SCALING_FACTOR/m_pxdim.h_mm; } agg::path_storage to_path(const Polygon& poly) { @@ -124,57 +124,57 @@ const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255); const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0); Raster::Raster(const Resolution &r, const PixelDim &pd, Origin o): - impl_(new Impl(r, pd, o)) {} + m_impl(new Impl(r, pd, o)) {} Raster::Raster() {} Raster::~Raster() {} Raster::Raster(Raster &&m): - impl_(std::move(m.impl_)) {} + m_impl(std::move(m.m_impl)) {} void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd) { // Free up the unnecessary memory and make sure it stays clear after // an exception - auto o = impl_? impl_->origin() : Origin::TOP_LEFT; + auto o = m_impl? m_impl->origin() : Origin::TOP_LEFT; reset(r, pd, o); } void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, Raster::Origin o) { - impl_.reset(); - impl_.reset(new Impl(r, pd, o)); + m_impl.reset(); + m_impl.reset(new Impl(r, pd, o)); } void Raster::reset() { - impl_.reset(); + m_impl.reset(); } Raster::Resolution Raster::resolution() const { - if(impl_) return impl_->resolution(); + if(m_impl) return m_impl->resolution(); return Resolution(0, 0); } void Raster::clear() { - assert(impl_); - impl_->clear(); + assert(m_impl); + m_impl->clear(); } void Raster::draw(const ExPolygon &poly) { - assert(impl_); - impl_->draw(poly); + assert(m_impl); + m_impl->draw(poly); } void Raster::save(std::ostream& stream, Compression comp) { - assert(impl_); + assert(m_impl); switch(comp) { case Compression::PNG: { @@ -188,7 +188,7 @@ void Raster::save(std::ostream& stream, Compression comp) wr.write_info(); - auto& b = impl_->buffer(); + auto& b = m_impl->buffer(); auto ptr = reinterpret_cast( b.data() ); unsigned stride = sizeof(Impl::TBuffer::value_type) * resolution().width_px; @@ -201,12 +201,12 @@ void Raster::save(std::ostream& stream, Compression comp) } case Compression::RAW: { stream << "P5 " - << impl_->resolution().width_px << " " - << impl_->resolution().height_px << " " + << m_impl->resolution().width_px << " " + << m_impl->resolution().height_px << " " << "255 "; - stream.write(reinterpret_cast(impl_->buffer().data()), - impl_->buffer().size()*sizeof(Impl::TBuffer::value_type)); + stream.write(reinterpret_cast(m_impl->buffer().data()), + m_impl->buffer().size()*sizeof(Impl::TBuffer::value_type)); } } } diff --git a/xs/src/libslic3r/Rasterizer/Rasterizer.hpp b/xs/src/libslic3r/Rasterizer/Rasterizer.hpp index cbb39bc6bb..b6406b7705 100644 --- a/xs/src/libslic3r/Rasterizer/Rasterizer.hpp +++ b/xs/src/libslic3r/Rasterizer/Rasterizer.hpp @@ -18,7 +18,7 @@ class ExPolygon; */ class Raster { class Impl; - std::unique_ptr impl_; + std::unique_ptr m_impl; public: /// Supported compression types @@ -65,7 +65,7 @@ public: /** * Release the allocated resources. Drawing in this state ends in - * unspecified behaviour. + * unspecified behavior. */ void reset(); diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index c9029f2be0..f9c1262862 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -30,15 +30,15 @@ public: }; AppControllerBoilerplate::AppControllerBoilerplate() - :pri_data_(new PriData(std::this_thread::get_id())) {} + :m_pri_data(new PriData(std::this_thread::get_id())) {} AppControllerBoilerplate::~AppControllerBoilerplate() { - pri_data_.reset(); + m_pri_data.reset(); } bool AppControllerBoilerplate::is_main_thread() const { - return pri_data_->ui_thread == std::this_thread::get_id(); + return m_pri_data->ui_thread == std::this_thread::get_id(); } namespace GUI { @@ -58,9 +58,9 @@ AppControllerBoilerplate::ProgresIndicatorPtr AppControllerBoilerplate::global_progress_indicator() { ProgresIndicatorPtr ret; - pri_data_->m.lock(); - ret = global_progressind_; - pri_data_->m.unlock(); + m_pri_data->m.lock(); + ret = m_global_progressind; + m_pri_data->m.unlock(); return ret; } @@ -68,9 +68,9 @@ AppControllerBoilerplate::global_progress_indicator() { void AppControllerBoilerplate::global_progress_indicator( AppControllerBoilerplate::ProgresIndicatorPtr gpri) { - pri_data_->m.lock(); - global_progressind_ = gpri; - pri_data_->m.unlock(); + m_pri_data->m.lock(); + m_global_progressind = gpri; + m_pri_data->m.unlock(); } PrintController::PngExportData @@ -104,11 +104,11 @@ PrintController::query_png_export_data(const DynamicPrintConfig& conf) void PrintController::slice(AppControllerBoilerplate::ProgresIndicatorPtr pri) { - print_->set_status_callback([pri](int st, const std::string& msg){ + m_print->set_status_callback([pri](int st, const std::string& msg){ pri->update(unsigned(st), msg); }); - print_->process(); + m_print->process(); } void PrintController::slice() @@ -156,7 +156,7 @@ void PrintController::slice_to_png() auto exd = query_png_export_data(conf); if(exd.zippath.empty()) return; - Print *print = print_; + Print *print = m_print; try { print->apply_config(conf); @@ -252,7 +252,7 @@ void PrintController::slice_to_png() const PrintConfig &PrintController::config() const { - return print_->config(); + return m_print->config(); } void ProgressIndicator::message_fmt( @@ -286,13 +286,13 @@ void AppController::arrange_model() { using Coord = libnest2d::TCoord; - if(arranging_.load()) return; + if(m_arranging.load()) return; // to prevent UI reentrancies - arranging_.store(true); + m_arranging.store(true); unsigned count = 0; - for(auto obj : model_->objects) count += obj->instances.size(); + for(auto obj : m_model->objects) count += obj->instances.size(); auto pind = global_progress_indicator(); @@ -305,7 +305,7 @@ void AppController::arrange_model() pind->max(count); pind->on_cancel([this](){ - arranging_.store(false); + m_arranging.store(false); }); } @@ -326,7 +326,7 @@ void AppController::arrange_model() // TODO: from Sasha from GUI hint.type = arr::BedShapeType::WHO_KNOWS; - arr::arrange(*model_, + arr::arrange(*m_model, min_obj_distance, bed, hint, @@ -336,7 +336,7 @@ void AppController::arrange_model() pind->update(count - rem, L("Arranging objects...")); process_events(); - }, [this] () { return !arranging_.load(); }); + }, [this] () { return !m_arranging.load(); }); } catch(std::exception& e) { std::cerr << e.what() << std::endl; report_issue(IssueType::ERR, @@ -348,13 +348,13 @@ void AppController::arrange_model() // Restore previous max value if(pind) { pind->max(pmax); - pind->update(0, arranging_.load() ? L("Arranging done.") : + pind->update(0, m_arranging.load() ? L("Arranging done.") : L("Arranging canceled.")); pind->on_cancel(/*remove cancel function*/); } - arranging_.store(false); + m_arranging.store(false); } } diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp index c9ab6f15d0..df506c05b8 100644 --- a/xs/src/slic3r/AppController.hpp +++ b/xs/src/slic3r/AppController.hpp @@ -42,7 +42,7 @@ private: class PriData; // Some structure to store progress indication data // Pimpl data for thread safe progress indication features - std::unique_ptr pri_data_; + std::unique_ptr m_pri_data; public: @@ -67,7 +67,7 @@ public: * It should display a file chooser dialog in case of a UI application. * @param title Title of a possible query dialog. * @param extensions Recognized file extensions. - * @return Returns a list of paths choosed by the user. + * @return Returns a list of paths chosen by the user. */ PathList query_destination_paths( const std::string& title, @@ -162,7 +162,7 @@ protected: // This is a global progress indicator placeholder. In the Slic3r UI it can // contain the progress indicator on the statusbar. - ProgresIndicatorPtr global_progressind_; + ProgresIndicatorPtr m_global_progressind; }; class Zipper { @@ -186,18 +186,10 @@ public: * @brief Implementation of the printing logic. */ class PrintController: public AppControllerBoilerplate { - Print *print_ = nullptr; - std::function rempools_; + Print *m_print = nullptr; + std::function m_rempools; protected: - void make_skirt() {} - void make_brim() {} - void make_wipe_tower() {} - - void make_perimeters(PrintObject *pobj) {} - void infill(PrintObject *pobj) {} - void gen_support_material(PrintObject *pobj) {} - // Data structure with the png export input data struct PngExportData { std::string zippath; // output zip file @@ -215,20 +207,14 @@ protected: PngExportData query_png_export_data(const DynamicPrintConfig&); // The previous export data, to pre-populate the dialog - PngExportData prev_expdata_; - - /** - * @brief Slice one pront object. - * @param pobj The print object. - */ - void slice(PrintObject *pobj); + PngExportData m_prev_expdata; void slice(ProgresIndicatorPtr pri); public: // Must be public for perl to use it - explicit inline PrintController(Print *print): print_(print) {} + explicit inline PrintController(Print *print): m_print(print) {} PrintController(const PrintController&) = delete; PrintController(PrintController&&) = delete; @@ -256,9 +242,9 @@ public: * @brief Top level controller. */ class AppController: public AppControllerBoilerplate { - Model *model_ = nullptr; + Model *m_model = nullptr; PrintController::Ptr printctl; - std::atomic arranging_; + std::atomic m_arranging; public: /** @@ -275,7 +261,7 @@ public: * @param model A raw pointer to the model object. This can be used from * perl. */ - void set_model(Model *model) { model_ = model; } + void set_model(Model *model) { m_model = model; } /** * @brief Set the print object from perl. diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp index 7de2a424f0..9f52e52d2f 100644 --- a/xs/src/slic3r/AppControllerWx.cpp +++ b/xs/src/slic3r/AppControllerWx.cpp @@ -103,18 +103,18 @@ bool AppControllerBoilerplate::report_issue( wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent); struct Zipper::Impl { - wxFileName m_fpath; - wxFFileOutputStream m_zipfile; - wxZipOutputStream m_zipstream; - wxStdOutputStream m_pngstream; + wxFileName fpath; + wxFFileOutputStream zipfile; + wxZipOutputStream zipstream; + wxStdOutputStream pngstream; Impl(const std::string& zipfile_path): - m_fpath(zipfile_path), - m_zipfile(zipfile_path), - m_zipstream(m_zipfile), - m_pngstream(m_zipstream) + fpath(zipfile_path), + zipfile(zipfile_path), + zipstream(zipfile), + pngstream(zipstream) { - if(!m_zipfile.IsOk()) + if(!zipfile.IsOk()) throw std::runtime_error(L("Cannot create zip file.")); } }; @@ -128,23 +128,23 @@ Zipper::~Zipper() {} void Zipper::next_entry(const std::string &fname) { - m_impl->m_zipstream.PutNextEntry(fname); + m_impl->zipstream.PutNextEntry(fname); } std::string Zipper::get_name() const { - return m_impl->m_fpath.GetName().ToStdString(); + return m_impl->fpath.GetName().ToStdString(); } std::ostream &Zipper::stream() { - return m_impl->m_pngstream; + return m_impl->pngstream; } void Zipper::close() { - m_impl->m_zipstream.Close(); - m_impl->m_zipfile.Close(); + m_impl->zipstream.Close(); + m_impl->zipfile.Close(); } namespace { @@ -156,26 +156,26 @@ namespace { class GuiProgressIndicator: public ProgressIndicator, public wxEvtHandler { - wxProgressDialog gauge_; + wxProgressDialog m_gauge; using Base = ProgressIndicator; - wxString message_; - int range_; wxString title_; - bool is_asynch_ = false; + wxString m_message; + int m_range; wxString m_title; + bool m_is_asynch = false; - const int id_ = wxWindow::NewControlId(); + const int m_id = wxWindow::NewControlId(); // status update handler void _state( wxCommandEvent& evt) { unsigned st = evt.GetInt(); - message_ = evt.GetString(); + m_message = evt.GetString(); _state(st); } // Status update implementation void _state( unsigned st) { - if(!gauge_.IsShown()) gauge_.ShowModal(); + if(!m_gauge.IsShown()) m_gauge.ShowModal(); Base::state(st); - if(!gauge_.Update(static_cast(st), message_)) { + if(!m_gauge.Update(static_cast(st), m_message)) { cancel(); } } @@ -183,25 +183,25 @@ class GuiProgressIndicator: public: /// Setting whether it will be used from the UI thread or some worker thread - inline void asynch(bool is) { is_asynch_ = is; } + inline void asynch(bool is) { m_is_asynch = is; } /// Get the mode of parallel operation. - inline bool asynch() const { return is_asynch_; } + inline bool asynch() const { return m_is_asynch; } inline GuiProgressIndicator(int range, const wxString& title, const wxString& firstmsg) : - gauge_(title, firstmsg, range, wxTheApp->GetTopWindow(), + m_gauge(title, firstmsg, range, wxTheApp->GetTopWindow(), wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT), - message_(firstmsg), - range_(range), title_(title) + m_message(firstmsg), + m_range(range), m_title(title) { Base::max(static_cast(range)); Base::states(static_cast(range)); Bind(PROGRESS_STATUS_UPDATE_EVENT, &GuiProgressIndicator::_state, - this, id_); + this, m_id); } virtual void state(float val) override { @@ -210,27 +210,27 @@ public: void state(unsigned st) { // send status update event - if(is_asynch_) { - auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, id_); + if(m_is_asynch) { + auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, m_id); evt->SetInt(st); - evt->SetString(message_); + evt->SetString(m_message); wxQueueEvent(this, evt); } else _state(st); } virtual void message(const std::string & msg) override { - message_ = _(msg); + m_message = _(msg); } virtual void messageFmt(const std::string& fmt, ...) { va_list arglist; va_start(arglist, fmt); - message_ = wxString::Format(_(fmt), arglist); + m_message = wxString::Format(_(fmt), arglist); va_end(arglist); } virtual void title(const std::string & title) override { - title_ = _(title); + m_title = _(title); } }; } @@ -261,20 +261,20 @@ AppControllerBoilerplate::create_progress_indicator( namespace { class Wrapper: public ProgressIndicator, public wxEvtHandler { - ProgressStatusBar *sbar_; + ProgressStatusBar *m_sbar; using Base = ProgressIndicator; - wxString message_; - AppControllerBoilerplate& ctl_; + wxString m_message; + AppControllerBoilerplate& m_ctl; void showProgress(bool show = true) { - sbar_->show_progress(show); + m_sbar->show_progress(show); } void _state(unsigned st) { if( st <= ProgressIndicator::max() ) { Base::state(st); - sbar_->set_status_text(message_); - sbar_->set_progress(st); + m_sbar->set_status_text(m_message); + m_sbar->set_progress(st); } } @@ -289,10 +289,10 @@ public: inline Wrapper(ProgressStatusBar *sbar, AppControllerBoilerplate& ctl): - sbar_(sbar), ctl_(ctl) + m_sbar(sbar), m_ctl(ctl) { - Base::max(static_cast(sbar_->get_range())); - Base::states(static_cast(sbar_->get_range())); + Base::max(static_cast(m_sbar->get_range())); + Base::states(static_cast(m_sbar->get_range())); Bind(PROGRESS_STATUS_UPDATE_EVENT, &Wrapper::_state, @@ -305,13 +305,13 @@ public: virtual void max(float val) override { if(val > 1.0) { - sbar_->set_range(static_cast(val)); + m_sbar->set_range(static_cast(val)); ProgressIndicator::max(val); } } void state(unsigned st) { - if(!ctl_.is_main_thread()) { + if(!m_ctl.is_main_thread()) { auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, id_); evt->SetInt(st); wxQueueEvent(this, evt); @@ -321,20 +321,20 @@ public: } virtual void message(const std::string & msg) override { - message_ = _(msg); + m_message = _(msg); } virtual void message_fmt(const std::string& fmt, ...) override { va_list arglist; va_start(arglist, fmt); - message_ = wxString::Format(_(fmt), arglist); + m_message = wxString::Format(_(fmt), arglist); va_end(arglist); } virtual void title(const std::string & /*title*/) override {} virtual void on_cancel(CancelFn fn) override { - sbar_->set_cancel_callback(fn); + m_sbar->set_cancel_callback(fn); Base::on_cancel(fn); } diff --git a/xs/src/slic3r/GUI/ProgressIndicator.hpp b/xs/src/slic3r/GUI/ProgressIndicator.hpp index 0cf8b4a17c..280ba63ac8 100644 --- a/xs/src/slic3r/GUI/ProgressIndicator.hpp +++ b/xs/src/slic3r/GUI/ProgressIndicator.hpp @@ -14,31 +14,31 @@ public: using CancelFn = std::function; // Cancel function signature. private: - float state_ = .0f, max_ = 1.f, step_; - CancelFn cancelfunc_ = [](){}; + float m_state = .0f, m_max = 1.f, m_step; + CancelFn m_cancelfunc = [](){}; public: inline virtual ~ProgressIndicator() {} /// Get the maximum of the progress range. - float max() const { return max_; } + float max() const { return m_max; } /// Get the current progress state - float state() const { return state_; } + float state() const { return m_state; } /// Set the maximum of the progress range - virtual void max(float maxval) { max_ = maxval; } + virtual void max(float maxval) { m_max = maxval; } /// Set the current state of the progress. - virtual void state(float val) { state_ = val; } + virtual void state(float val) { m_state = val; } /** * @brief Number of states int the progress. Can be used instead of giving a * maximum value. */ virtual void states(unsigned statenum) { - step_ = max_ / statenum; + m_step = m_max / statenum; } /// Message shown on the next status update. @@ -51,13 +51,13 @@ public: virtual void message_fmt(const std::string& fmt, ...); /// Set up a cancel callback for the operation if feasible. - virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; } + virtual void on_cancel(CancelFn func = CancelFn()) { m_cancelfunc = func; } /** * Explicitly shut down the progress indicator and call the associated * callback. */ - virtual void cancel() { cancelfunc_(); } + virtual void cancel() { m_cancelfunc(); } /// Convenience function to call message and status update in one function. void update(float st, const std::string& msg) { diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.cpp b/xs/src/slic3r/GUI/ProgressStatusBar.cpp index 363e34cb2b..0ca86084b0 100644 --- a/xs/src/slic3r/GUI/ProgressStatusBar.cpp +++ b/xs/src/slic3r/GUI/ProgressStatusBar.cpp @@ -14,117 +14,117 @@ namespace Slic3r { ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id): self(new wxStatusBar(parent ? parent : GUI::get_main_frame(), id == -1? wxID_ANY : id)), - timer_(new wxTimer(self)), - prog_ (new wxGauge(self, + m_timer(new wxTimer(self)), + m_prog (new wxGauge(self, wxGA_HORIZONTAL, 100, wxDefaultPosition, wxDefaultSize)), - cancelbutton_(new wxButton(self, + m_cancelbutton(new wxButton(self, -1, "Cancel", wxDefaultPosition, wxDefaultSize)) { - prog_->Hide(); - cancelbutton_->Hide(); + m_prog->Hide(); + m_cancelbutton->Hide(); self->SetFieldsCount(3); int w[] = {-1, 150, 155}; self->SetStatusWidths(3, w); self->Bind(wxEVT_TIMER, [this](const wxTimerEvent&) { - if (prog_->IsShown()) timer_->Stop(); - if(is_busy()) prog_->Pulse(); + if (m_prog->IsShown()) m_timer->Stop(); + if(is_busy()) m_prog->Pulse(); }); self->Bind(wxEVT_SIZE, [this](wxSizeEvent& event){ wxRect rect; self->GetFieldRect(1, rect); auto offset = 0; - cancelbutton_->Move(rect.GetX() + offset, rect.GetY() + offset); - cancelbutton_->SetSize(rect.GetWidth() - offset, rect.GetHeight()); + m_cancelbutton->Move(rect.GetX() + offset, rect.GetY() + offset); + m_cancelbutton->SetSize(rect.GetWidth() - offset, rect.GetHeight()); self->GetFieldRect(2, rect); - prog_->Move(rect.GetX() + offset, rect.GetY() + offset); - prog_->SetSize(rect.GetWidth() - offset, rect.GetHeight()); + m_prog->Move(rect.GetX() + offset, rect.GetY() + offset); + m_prog->SetSize(rect.GetWidth() - offset, rect.GetHeight()); event.Skip(); }); - cancelbutton_->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) { - if(cancel_cb_) cancel_cb_(); + m_cancelbutton->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) { + if(m_cancel_cb) m_cancel_cb(); m_perl_cancel_callback.call(); - cancelbutton_->Hide(); + m_cancelbutton->Hide(); }); } ProgressStatusBar::~ProgressStatusBar() { - if(timer_->IsRunning()) timer_->Stop(); + if(m_timer->IsRunning()) m_timer->Stop(); } int ProgressStatusBar::get_progress() const { - return prog_->GetValue(); + return m_prog->GetValue(); } void ProgressStatusBar::set_progress(int val) { - if(!prog_->IsShown()) show_progress(true); + if(!m_prog->IsShown()) show_progress(true); - if(val == prog_->GetRange()) { - prog_->SetValue(0); + if(val == m_prog->GetRange()) { + m_prog->SetValue(0); show_progress(false); } else { - prog_->SetValue(val); + m_prog->SetValue(val); } } int ProgressStatusBar::get_range() const { - return prog_->GetRange(); + return m_prog->GetRange(); } void ProgressStatusBar::set_range(int val) { - if(val != prog_->GetRange()) { - prog_->SetRange(val); + if(val != m_prog->GetRange()) { + m_prog->SetRange(val); } } void ProgressStatusBar::show_progress(bool show) { - prog_->Show(show); - prog_->Pulse(); + m_prog->Show(show); + m_prog->Pulse(); } void ProgressStatusBar::start_busy(int rate) { - busy_ = true; + m_busy = true; show_progress(true); - if (!timer_->IsRunning()) { - timer_->Start(rate); + if (!m_timer->IsRunning()) { + m_timer->Start(rate); } } void ProgressStatusBar::stop_busy() { - timer_->Stop(); + m_timer->Stop(); show_progress(false); - prog_->SetValue(0); - busy_ = false; + m_prog->SetValue(0); + m_busy = false; } void ProgressStatusBar::set_cancel_callback(ProgressStatusBar::CancelFn ccb) { - cancel_cb_ = ccb; - if(ccb) cancelbutton_->Show(); - else cancelbutton_->Hide(); + m_cancel_cb = ccb; + if(ccb) m_cancelbutton->Show(); + else m_cancelbutton->Hide(); } void ProgressStatusBar::run(int rate) { - if(!timer_->IsRunning()) { - timer_->Start(rate); + if(!m_timer->IsRunning()) { + m_timer->Start(rate); } } @@ -141,12 +141,12 @@ void ProgressStatusBar::set_status_text(const wxString& txt) void ProgressStatusBar::show_cancel_button() { - cancelbutton_->Show(); + m_cancelbutton->Show(); } void ProgressStatusBar::hide_cancel_button() { - cancelbutton_->Hide(); + m_cancelbutton->Hide(); } } diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.hpp b/xs/src/slic3r/GUI/ProgressStatusBar.hpp index 9a7f58eee7..0d8560d3bb 100644 --- a/xs/src/slic3r/GUI/ProgressStatusBar.hpp +++ b/xs/src/slic3r/GUI/ProgressStatusBar.hpp @@ -24,9 +24,9 @@ namespace Slic3r { */ class ProgressStatusBar { wxStatusBar *self; // we cheat! It should be the base class but: perl! - wxTimer *timer_; - wxGauge *prog_; - wxButton *cancelbutton_; + wxTimer *m_timer; + wxGauge *m_prog; + wxButton *m_cancelbutton; public: /// Cancel callback function type @@ -42,7 +42,7 @@ public: void show_progress(bool); void start_busy(int = 100); void stop_busy(); - inline bool is_busy() const { return busy_; } + inline bool is_busy() const { return m_busy; } void set_cancel_callback(CancelFn = CancelFn()); inline void remove_cancel_callback() { set_cancel_callback(); } void run(int rate); @@ -55,8 +55,8 @@ public: PerlCallback m_perl_cancel_callback; private: - bool busy_ = false; - CancelFn cancel_cb_; + bool m_busy = false; + CancelFn m_cancel_cb; }; namespace GUI { From c9acd1252aa59236ed4a76edc73f29379e5c8e32 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 19 Sep 2018 15:39:54 +0200 Subject: [PATCH 32/36] reset transformation components to their default value by double clicking on gizmos' grabbers --- xs/src/libslic3r/Technologies.hpp | 2 ++ xs/src/slic3r/GUI/GLCanvas3D.cpp | 44 +++++++++++++++++++++++++++++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 3 +++ xs/src/slic3r/GUI/GLGizmo.cpp | 14 ++++++++++ xs/src/slic3r/GUI/GLGizmo.hpp | 20 ++++++++++++++ 5 files changed, 83 insertions(+) diff --git a/xs/src/libslic3r/Technologies.hpp b/xs/src/libslic3r/Technologies.hpp index 5c4f8617de..0343bb00c0 100644 --- a/xs/src/libslic3r/Technologies.hpp +++ b/xs/src/libslic3r/Technologies.hpp @@ -6,6 +6,8 @@ // Add z coordinate to model instances' offset #define ENABLE_MODELINSTANCE_3D_OFFSET (1 && ENABLE_1_42_0) +// Add double click on gizmo grabbers to reset transformation components to their default value +#define ENABLE_GIZMOS_RESET (1 && ENABLE_1_42_0) #endif // _technologies_h_ diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index cb32509163..c62652994c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1349,6 +1349,18 @@ void GLCanvas3D::Gizmos::update(const Linef3& mouse_ray) curr->update(mouse_ray); } +#if ENABLE_GIZMOS_RESET +void GLCanvas3D::Gizmos::process_double_click() +{ + if (!m_enabled) + return; + + GLGizmoBase* curr = _get_current(); + if (curr != nullptr) + curr->process_double_click(); +} +#endif // ENABLE_GIZMOS_RESET + GLCanvas3D::Gizmos::EType GLCanvas3D::Gizmos::get_current_type() const { return m_current; @@ -3043,6 +3055,38 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_toolbar_action_running = true; m_toolbar.do_action((unsigned int)toolbar_contains_mouse); } +#if ENABLE_GIZMOS_RESET + else if (evt.LeftDClick() && m_gizmos.grabber_contains_mouse()) + { + m_gizmos.process_double_click(); + switch (m_gizmos.get_current_type()) + { + case Gizmos::Scale: + { + m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale()); + update_scale_values(); + m_dirty = true; + break; + } + case Gizmos::Rotate: + { +#if ENABLE_MODELINSTANCE_3D_ROTATION + const Vec3d& rotation = m_gizmos.get_rotation(); + m_on_gizmo_rotate_3D_callback.call(rotation(0), rotation(1), rotation(2)); +#else + m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z()); +#endif //ENABLE_MODELINSTANCE_3D_ROTATION + update_rotation_values(); + m_dirty = true; + break; + } + default: + { + break; + } + } + } +#endif // ENABLE_GIZMOS_RESET else if (evt.LeftDown() || evt.RightDown()) { // If user pressed left or right button we first check whether this happened diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 528f73fc1e..2c830f58e4 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -366,6 +366,9 @@ class GLCanvas3D bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const; bool grabber_contains_mouse() const; void update(const Linef3& mouse_ray); +#if ENABLE_GIZMOS_RESET + void process_double_click(); +#endif // ENABLE_GIZMOS_RESET EType get_current_type() const; diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index e23958c1d1..1890a7bf81 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -759,6 +759,20 @@ void GLGizmoScale3D::on_update(const Linef3& mouse_ray) do_scale_uniform(mouse_ray); } +#if ENABLE_GIZMOS_RESET +void GLGizmoScale3D::on_process_double_click() +{ + if ((m_hover_id == 0) || (m_hover_id == 1)) + m_scale(0) = 1.0; + else if ((m_hover_id == 2) || (m_hover_id == 3)) + m_scale(1) = 1.0; + else if ((m_hover_id == 4) || (m_hover_id == 5)) + m_scale(2) = 1.0; + else if (m_hover_id >= 6) + m_scale = Vec3d::Ones(); +} +#endif // ENABLE_GIZMOS_RESET + void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const { if (m_grabbers[0].dragging || m_grabbers[1].dragging) diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index 2430b5af54..bf5838f722 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -94,6 +94,10 @@ public: void update(const Linef3& mouse_ray); +#if ENABLE_GIZMOS_RESET + void process_double_click() { on_process_double_click(); } +#endif // ENABLE_GIZMOS_RESET + void render(const BoundingBoxf3& box) const { on_render(box); } void render_for_picking(const BoundingBoxf3& box) const { on_render_for_picking(box); } @@ -106,6 +110,9 @@ protected: virtual void on_start_dragging(const BoundingBoxf3& box) {} virtual void on_stop_dragging() {} virtual void on_update(const Linef3& mouse_ray) = 0; +#if ENABLE_GIZMOS_RESET + virtual void on_process_double_click() {} +#endif // ENABLE_GIZMOS_RESET virtual void on_render(const BoundingBoxf3& box) const = 0; virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0; @@ -155,6 +162,9 @@ protected: virtual bool on_init(); virtual void on_start_dragging(const BoundingBoxf3& box); virtual void on_update(const Linef3& mouse_ray); +#if ENABLE_GIZMOS_RESET + virtual void on_process_double_click() { m_angle = 0.0; } +#endif // ENABLE_GIZMOS_RESET virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; @@ -222,6 +232,13 @@ protected: g.update(mouse_ray); } } +#if ENABLE_GIZMOS_RESET + virtual void on_process_double_click() + { + if (m_hover_id != -1) + m_gizmos[m_hover_id].process_double_click(); + } +#endif // ENABLE_GIZMOS_RESET virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const { @@ -265,6 +282,9 @@ protected: virtual void on_start_dragging(const BoundingBoxf3& box); virtual void on_stop_dragging() { m_show_starting_box = false; } virtual void on_update(const Linef3& mouse_ray); +#if ENABLE_GIZMOS_RESET + virtual void on_process_double_click(); +#endif // ENABLE_GIZMOS_RESET virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; From 2306c1589a816dbda8ede8620842f0f6b24c8e8a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 19 Sep 2018 18:02:04 +0200 Subject: [PATCH 33/36] Polymorphic AppController for cli and gui modes. --- slic3r.pl | 9 +- xs/src/libslic3r/ModelArrange.hpp | 155 ++++++++++----------- xs/src/slic3r/AppController.cpp | 50 ++++--- xs/src/slic3r/AppController.hpp | 217 +++++++++++++++++++++++------- xs/src/slic3r/AppControllerWx.cpp | 57 ++++---- xs/src/slic3r/GUI/GUI.cpp | 20 +++ xs/src/slic3r/GUI/GUI.hpp | 7 + xs/xsp/GUI.xsp | 6 + 8 files changed, 338 insertions(+), 183 deletions(-) diff --git a/slic3r.pl b/slic3r.pl index 38c10a9007..95427443b3 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -109,6 +109,7 @@ if ((!@ARGV || $opt{gui}) && !$opt{no_gui} && !$opt{save} && eval "require Slic3 $Slic3r::GUI::no_plater = $opt{no_plater}; $Slic3r::GUI::autosave = $opt{autosave}; } + Slic3r::GUI::set_gui_appctl(); $gui = Slic3r::GUI->new; #setlocale(LC_NUMERIC, 'C'); $gui->{mainframe}->load_config_file($_) for @{$opt{load}}; @@ -121,6 +122,9 @@ if ((!@ARGV || $opt{gui}) && !$opt{no_gui} && !$opt{save} && eval "require Slic3 die $@ if $@ && $opt{gui}; if (@ARGV) { # slicing from command line + Slic3r::GUI::set_cli_appctl(); + my $appctl = Slic3r::AppController->new(); + $config->validate; if ($opt{repair}) { @@ -210,7 +214,10 @@ if (@ARGV) { # slicing from command line $sprint->apply_config($config); if ($opt{export_png}) { - $sprint->export_png; + # $sprint->export_png; + $appctl->set_model($model); + $appctl->set_print($sprint->_print); + $appctl->print_ctl()->slice_to_png(); } else { my $t0 = [gettimeofday]; # The following call may die if the output_filename_format template substitution fails, diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index 372689c39d..4b91131ede 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -292,59 +292,59 @@ protected: using Distance = TCoord; using Pile = sl::Shapes; - Packer pck_; - PConfig pconf_; // Placement configuration - double bin_area_; - SpatIndex rtree_; - SpatIndex smallsrtree_; - double norm_; - Pile merged_pile_; - Box pilebb_; - ItemGroup remaining_; - ItemGroup items_; + Packer m_pck; + PConfig m_pconf; // Placement configuration + double m_bin_area; + SpatIndex m_rtree; + SpatIndex m_smallsrtree; + double m_norm; + Pile m_merged_pile; + Box m_pilebb; + ItemGroup m_remaining; + ItemGroup m_items; public: _ArrBase(const TBin& bin, Distance dist, std::function progressind, std::function stopcond): - pck_(bin, dist), bin_area_(sl::area(bin)), - norm_(std::sqrt(sl::area(bin))) + m_pck(bin, dist), m_bin_area(sl::area(bin)), + m_norm(std::sqrt(sl::area(bin))) { - fillConfig(pconf_); + fillConfig(m_pconf); - pconf_.before_packing = + m_pconf.before_packing = [this](const Pile& merged_pile, // merged pile const ItemGroup& items, // packed items const ItemGroup& remaining) // future items to be packed { - items_ = items; - merged_pile_ = merged_pile; - remaining_ = remaining; + m_items = items; + m_merged_pile = merged_pile; + m_remaining = remaining; - pilebb_ = sl::boundingBox(merged_pile); + m_pilebb = sl::boundingBox(merged_pile); - rtree_.clear(); - smallsrtree_.clear(); + m_rtree.clear(); + m_smallsrtree.clear(); // We will treat big items (compared to the print bed) differently auto isBig = [this](double a) { - return a/bin_area_ > BIG_ITEM_TRESHOLD ; + return a/m_bin_area > BIG_ITEM_TRESHOLD ; }; for(unsigned idx = 0; idx < items.size(); ++idx) { Item& itm = items[idx]; - if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx}); - smallsrtree_.insert({itm.boundingBox(), idx}); + if(isBig(itm.area())) m_rtree.insert({itm.boundingBox(), idx}); + m_smallsrtree.insert({itm.boundingBox(), idx}); } }; - pck_.progressIndicator(progressind); - pck_.stopCondition(stopcond); + m_pck.progressIndicator(progressind); + m_pck.stopCondition(stopcond); } template inline IndexedPackGroup operator()(Args&&...args) { - rtree_.clear(); - return pck_.executeIndexed(std::forward(args)...); + m_rtree.clear(); + return m_pck.executeIndexed(std::forward(args)...); } }; @@ -358,18 +358,18 @@ public: _ArrBase(bin, dist, progressind, stopcond) { - pconf_.object_function = [this, bin] (const Item &item) { + m_pconf.object_function = [this, bin] (const Item &item) { auto result = objfunc(bin.center(), - merged_pile_, - pilebb_, - items_, + m_merged_pile, + m_pilebb, + m_items, item, - bin_area_, - norm_, - rtree_, - smallsrtree_, - remaining_); + m_bin_area, + m_norm, + m_rtree, + m_smallsrtree, + m_remaining); double score = std::get<0>(result); auto& fullbb = std::get<1>(result); @@ -381,7 +381,7 @@ public: return score; }; - pck_.configure(pconf_); + m_pck.configure(m_pconf); } }; @@ -396,27 +396,27 @@ public: std::function stopcond): _ArrBase(bin, dist, progressind, stopcond) { - pconf_.object_function = [this, &bin] (const Item &item) { + m_pconf.object_function = [this, &bin] (const Item &item) { auto result = objfunc(bin.center(), - merged_pile_, - pilebb_, - items_, + m_merged_pile, + m_pilebb, + m_items, item, - bin_area_, - norm_, - rtree_, - smallsrtree_, - remaining_); + m_bin_area, + m_norm, + m_rtree, + m_smallsrtree, + m_remaining); double score = std::get<0>(result); auto isBig = [this](const Item& itm) { - return itm.area()/bin_area_ > BIG_ITEM_TRESHOLD ; + return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ; }; if(isBig(item)) { - auto mp = merged_pile_; + auto mp = m_merged_pile; mp.push_back(item.transformedShape()); auto chull = sl::convexHull(mp); double miss = Placer::overfit(chull, bin); @@ -427,7 +427,7 @@ public: return score; }; - pck_.configure(pconf_); + m_pck.configure(m_pconf); } }; @@ -439,25 +439,25 @@ public: std::function stopcond): _ArrBase(bin, dist, progressind, stopcond) { - pconf_.object_function = [this, &bin] (const Item &item) { + m_pconf.object_function = [this, &bin] (const Item &item) { auto binbb = sl::boundingBox(bin); auto result = objfunc(binbb.center(), - merged_pile_, - pilebb_, - items_, + m_merged_pile, + m_pilebb, + m_items, item, - bin_area_, - norm_, - rtree_, - smallsrtree_, - remaining_); + m_bin_area, + m_norm, + m_rtree, + m_smallsrtree, + m_remaining); double score = std::get<0>(result); return score; }; - pck_.configure(pconf_); + m_pck.configure(m_pconf); } }; @@ -469,22 +469,22 @@ public: std::function stopcond): _ArrBase(Box(0, 0), dist, progressind, stopcond) { - this->pconf_.object_function = [this] (const Item &item) { + this->m_pconf.object_function = [this] (const Item &item) { auto result = objfunc({0, 0}, - merged_pile_, - pilebb_, - items_, + m_merged_pile, + m_pilebb, + m_items, item, 0, - norm_, - rtree_, - smallsrtree_, - remaining_); + m_norm, + m_rtree, + m_smallsrtree, + m_remaining); return std::get<0>(result); }; - this->pck_.configure(pconf_); + this->m_pck.configure(m_pconf); } }; @@ -530,11 +530,11 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { item.rotation(objinst->rotation); item.translation( { #if ENABLE_MODELINSTANCE_3D_OFFSET - ClipperLib::cInt(objinst->get_offset(X) / SCALING_FACTOR), - ClipperLib::cInt(objinst->get_offset(Y) / SCALING_FACTOR) + ClipperLib::cInt(objinst->get_offset(X)/SCALING_FACTOR), + ClipperLib::cInt(objinst->get_offset(Y)/SCALING_FACTOR) #else - ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR), - ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR) + ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR), + ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR) #endif // ENABLE_MODELINSTANCE_3D_OFFSET }); ret.emplace_back(objinst, item); @@ -668,17 +668,19 @@ void applyResult( // Get the model instance from the shapemap using the index ModelInstance *inst_ptr = shapemap[idx].first; - // Get the tranformation data from the item object and scale it + // Get the transformation data from the item object and scale it // appropriately auto off = item.translation(); Radians rot = item.rotation(); #if ENABLE_MODELINSTANCE_3D_OFFSET - Vec3d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR, 0.0); + Vec3d foff(off.X*SCALING_FACTOR + batch_offset, + off.Y*SCALING_FACTOR, + 0.0); #else Vec2d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR); #endif // ENABLE_MODELINSTANCE_3D_OFFSET - // write the tranformation data into the model instance + // write the transformation data into the model instance inst_ptr->rotation = rot; #if ENABLE_MODELINSTANCE_3D_OFFSET inst_ptr->set_offset(foff); @@ -695,7 +697,7 @@ void applyResult( * The arrangement considers multiple bins (aka. print beds) for placing all * the items provided in the model argument. If the items don't fit on one * print bed, the remaining will be placed onto newly created print beds. - * The first_bin_only parameter, if set to true, disables this behaviour and + * The first_bin_only parameter, if set to true, disables this behavior and * makes sure that only one print bed is filled and the remaining items will be * untouched. When set to false, the items which could not fit onto the * print bed will be placed next to the print bed so the user should see a @@ -741,6 +743,7 @@ bool arrange(Model &model, coordf_t min_obj_distance, IndexedPackGroup result; + // If there is no hint about the shape, we will try to guess if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed); BoundingBox bbb(bed); diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index f9c1262862..2bb79cec1a 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -21,7 +21,7 @@ namespace Slic3r { -class AppControllerBoilerplate::PriData { +class AppControllerGui::PriData { public: std::mutex m; std::thread::id ui_thread; @@ -29,14 +29,14 @@ public: inline explicit PriData(std::thread::id uit): ui_thread(uit) {} }; -AppControllerBoilerplate::AppControllerBoilerplate() +AppControllerGui::AppControllerGui() :m_pri_data(new PriData(std::this_thread::get_id())) {} -AppControllerBoilerplate::~AppControllerBoilerplate() { +AppControllerGui::~AppControllerGui() { m_pri_data.reset(); } -bool AppControllerBoilerplate::is_main_thread() const +bool AppControllerGui::is_main_thread() const { return m_pri_data->ui_thread == std::this_thread::get_id(); } @@ -54,8 +54,7 @@ static const PrintStep STEP_SKIRT = psSkirt; static const PrintStep STEP_BRIM = psBrim; static const PrintStep STEP_WIPE_TOWER = psWipeTower; -AppControllerBoilerplate::ProgresIndicatorPtr -AppControllerBoilerplate::global_progress_indicator() { +ProgresIndicatorPtr AppControllerGui::global_progress_indicator() { ProgresIndicatorPtr ret; m_pri_data->m.lock(); @@ -65,8 +64,7 @@ AppControllerBoilerplate::global_progress_indicator() { return ret; } -void AppControllerBoilerplate::global_progress_indicator( - AppControllerBoilerplate::ProgresIndicatorPtr gpri) +void AppControllerGui::global_progress_indicator(ProgresIndicatorPtr gpri) { m_pri_data->m.lock(); m_global_progressind = gpri; @@ -78,7 +76,10 @@ PrintController::query_png_export_data(const DynamicPrintConfig& conf) { PngExportData ret; - auto zippath = query_destination_path("Output zip file", "*.zip", "out"); + auto c = GUI::get_appctl(); + auto zippath = c->query_destination_path("Output zip file", "*.zip", + "export-png", + "out"); ret.zippath = zippath; @@ -102,7 +103,7 @@ PrintController::query_png_export_data(const DynamicPrintConfig& conf) return ret; } -void PrintController::slice(AppControllerBoilerplate::ProgresIndicatorPtr pri) +void PrintController::slice(ProgresIndicatorPtr pri) { m_print->set_status_callback([pri](int st, const std::string& msg){ pri->update(unsigned(st), msg); @@ -113,8 +114,9 @@ void PrintController::slice(AppControllerBoilerplate::ProgresIndicatorPtr pri) void PrintController::slice() { - auto pri = global_progress_indicator(); - if(!pri) pri = create_progress_indicator(100, L("Slicing")); + auto ctl = GUI::get_appctl(); + auto pri = ctl->global_progress_indicator(); + if(!pri) pri = ctl->create_progress_indicator(100, L("Slicing")); slice(pri); } @@ -139,13 +141,15 @@ void PrintController::slice_to_png() { using Pointf3 = Vec3d; + auto ctl = GUI::get_appctl(); auto presetbundle = GUI::get_preset_bundle(); assert(presetbundle); + // FIXME: this crashes in command line mode auto pt = presetbundle->printers.get_selected_preset().printer_technology(); if(pt != ptSLA) { - report_issue(IssueType::ERR, L("Printer technology is not SLA!"), + ctl->report_issue(IssueType::ERR, L("Printer technology is not SLA!"), L("Error")); return; } @@ -162,7 +166,7 @@ void PrintController::slice_to_png() print->apply_config(conf); print->validate(); } catch(std::exception& e) { - report_issue(IssueType::ERR, e.what(), "Error"); + ctl->report_issue(IssueType::ERR, e.what(), "Error"); return; } @@ -208,13 +212,13 @@ void PrintController::slice_to_png() << L("Width needed: ") << px(punsc) << " mm\n" << L("Height needed: ") << py(punsc) << " mm\n"; - if(!report_issue(IssueType::WARN_Q, ss.str(), L("Warning"))) { + if(!ctl->report_issue(IssueType::WARN_Q, ss.str(), L("Warning"))) { scale_back(); return; } } - auto pri = create_progress_indicator( + auto pri = ctl->create_progress_indicator( 200, L("Slicing to zipped png files...")); pri->on_cancel([&print](){ print->cancel(); }); @@ -223,7 +227,7 @@ void PrintController::slice_to_png() pri->update(0, L("Slicing...")); slice(pri); } catch (std::exception& e) { - report_issue(IssueType::ERR, e.what(), L("Exception occurred")); + ctl->report_issue(IssueType::ERR, e.what(), L("Exception occurred")); scale_back(); if(print->canceled()) print->restart(); return; @@ -242,7 +246,7 @@ void PrintController::slice_to_png() exd.exp_time_s, exd.exp_time_first_s); } catch (std::exception& e) { - report_issue(IssueType::ERR, e.what(), L("Exception occurred")); + ctl->report_issue(IssueType::ERR, e.what(), L("Exception occurred")); } scale_back(); @@ -286,6 +290,8 @@ void AppController::arrange_model() { using Coord = libnest2d::TCoord; + auto ctl = GUI::get_appctl(); + if(m_arranging.load()) return; // to prevent UI reentrancies @@ -294,7 +300,7 @@ void AppController::arrange_model() unsigned count = 0; for(auto obj : m_model->objects) count += obj->instances.size(); - auto pind = global_progress_indicator(); + auto pind = ctl->global_progress_indicator(); float pmax = 1.0; @@ -331,15 +337,15 @@ void AppController::arrange_model() bed, hint, false, // create many piles not just one pile - [this, pind, count](unsigned rem) { + [this, pind, &ctl, count](unsigned rem) { if(pind) pind->update(count - rem, L("Arranging objects...")); - process_events(); + ctl->process_events(); }, [this] () { return !m_arranging.load(); }); } catch(std::exception& e) { std::cerr << e.what() << std::endl; - report_issue(IssueType::ERR, + ctl->report_issue(IssueType::ERR, L("Could not arrange model objects! " "Some geometries may be invalid."), L("Exception occurred")); diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp index df506c05b8..a34e5d035f 100644 --- a/xs/src/slic3r/AppController.hpp +++ b/xs/src/slic3r/AppController.hpp @@ -20,6 +20,21 @@ class PrintConfig; class ProgressStatusBar; class DynamicPrintConfig; +/// A Progress indicator object smart pointer +using ProgresIndicatorPtr = std::shared_ptr; + +using FilePath = std::string; +using FilePathList = std::vector; + +/// Common runtime issue types +enum class IssueType { + INFO, + WARN, + WARN_Q, // Warning with a question to continue + ERR, + FATAL +}; + /** * @brief A boilerplate class for creating application logic. It should provide * features as issue reporting and progress indication, etc... @@ -32,34 +47,12 @@ class DynamicPrintConfig; * UI toolkit dependencies. We can implement it with any UI framework or make it * a cli client. */ -class AppControllerBoilerplate { +class AppControllerBase { public: - /// A Progress indicator object smart pointer - using ProgresIndicatorPtr = std::shared_ptr; + using Ptr = std::shared_ptr; -private: - class PriData; // Some structure to store progress indication data - - // Pimpl data for thread safe progress indication features - std::unique_ptr m_pri_data; - -public: - - AppControllerBoilerplate(); - ~AppControllerBoilerplate(); - - using Path = std::string; - using PathList = std::vector; - - /// Common runtime issue types - enum class IssueType { - INFO, - WARN, - WARN_Q, // Warning with a question to continue - ERR, - FATAL - }; + inline virtual ~AppControllerBase() {} /** * @brief Query some paths from the user. @@ -69,23 +62,28 @@ public: * @param extensions Recognized file extensions. * @return Returns a list of paths chosen by the user. */ - PathList query_destination_paths( + virtual FilePathList query_destination_paths( const std::string& title, - const std::string& extensions) const; + const std::string& extensions, + const std::string& functionid = "", + const std::string& hint = "") const = 0; /** * @brief Same as query_destination_paths but works for directories only. */ - PathList query_destination_dirs( - const std::string& title) const; + virtual FilePathList query_destination_dirs( + const std::string& title, + const std::string& functionid = "", + const std::string& hint = "") const = 0; /** * @brief Same as query_destination_paths but returns only one path. */ - Path query_destination_path( + virtual FilePath query_destination_path( const std::string& title, const std::string& extensions, - const std::string& hint = "") const; + const std::string& functionid = "", + const std::string& hint = "") const = 0; /** * @brief Report an issue to the user be it fatal or recoverable. @@ -97,12 +95,9 @@ public: * @param brief A very brief description. Can be used for message dialog * title. */ - bool report_issue(IssueType issuetype, - const std::string& description, - const std::string& brief); - - bool report_issue(IssueType issuetype, - const std::string& description); + virtual bool report_issue(IssueType issuetype, + const std::string& description, + const std::string& brief) = 0; /** * @brief Return the global progress indicator for the current controller. @@ -110,9 +105,9 @@ public: * * Only one thread should use the global indicator at a time. */ - ProgresIndicatorPtr global_progress_indicator(); + virtual ProgresIndicatorPtr global_progress_indicator() = 0; - void global_progress_indicator(ProgresIndicatorPtr gpri); + virtual void global_progress_indicator(ProgresIndicatorPtr gpri) = 0; /** * @brief A predicate telling the caller whether it is the thread that @@ -122,7 +117,7 @@ public: * @return Return true for the same caller thread that created this * object and false for every other. */ - bool is_main_thread() const; + virtual bool is_main_thread() const = 0; /** * @brief The frontend supports asynch execution. @@ -138,11 +133,9 @@ public: * @return true if a job or method can be executed asynchronously, false * otherwise. */ - bool supports_asynch() const; + virtual bool supports_asynch() const = 0; - void process_events(); - -protected: + virtual void process_events() = 0; /** * @brief Create a new progress indicator and return a smart pointer to it. @@ -151,14 +144,138 @@ protected: * @param firstmsg The message for the first subtask to be displayed. * @return Smart pointer to the created object. */ - ProgresIndicatorPtr create_progress_indicator( + virtual ProgresIndicatorPtr create_progress_indicator( unsigned statenum, const std::string& title, - const std::string& firstmsg) const; + const std::string& firstmsg = "") const = 0; +}; - ProgresIndicatorPtr create_progress_indicator( +/** + * @brief Implementation of AppControllerBase for the GUI app + */ +class AppControllerGui: public AppControllerBase { +private: + class PriData; // Some structure to store progress indication data + + // Pimpl data for thread safe progress indication features + std::unique_ptr m_pri_data; + +public: + + AppControllerGui(); + + virtual ~AppControllerGui(); + + virtual FilePathList query_destination_paths( + const std::string& title, + const std::string& extensions, + const std::string& functionid, + const std::string& hint) const override; + + virtual FilePathList query_destination_dirs( + const std::string& /*title*/, + const std::string& /*functionid*/, + const std::string& /*hint*/) const override { return {}; } + + virtual FilePath query_destination_path( + const std::string& title, + const std::string& extensions, + const std::string& functionid, + const std::string& hint) const override; + + virtual bool report_issue(IssueType issuetype, + const std::string& description, + const std::string& brief = std::string()) override; + + virtual ProgresIndicatorPtr global_progress_indicator() override; + + virtual void global_progress_indicator(ProgresIndicatorPtr gpri) override; + + virtual bool is_main_thread() const override; + + virtual bool supports_asynch() const override; + + virtual void process_events() override; + + virtual ProgresIndicatorPtr create_progress_indicator( unsigned statenum, - const std::string& title) const; + const std::string& title, + const std::string& firstmsg) const override; + +protected: + + // This is a global progress indicator placeholder. In the Slic3r UI it can + // contain the progress indicator on the statusbar. + ProgresIndicatorPtr m_global_progressind; +}; + +class AppControllerCli: public AppControllerBase { + + class CliProgress : public ProgressIndicator { + std::string m_msg, m_title; + public: + virtual void message(const std::string& msg) override { + m_msg = msg; + } + + virtual void title(const std::string& title) override { + m_title = title; + } + }; + +public: + + AppControllerCli() { + std::cout << "Cli AppController ready..." << std::endl; + m_global_progressind = std::make_shared(); + } + + virtual ~AppControllerCli() {} + + virtual FilePathList query_destination_paths( + const std::string& /*title*/, + const std::string& /*extensions*/, + const std::string& /*functionid*/, + const std::string& /*hint*/) const override { return {}; } + + virtual FilePathList query_destination_dirs( + const std::string& /*title*/, + const std::string& /*functionid*/, + const std::string& /*hint*/) const override { return {}; } + + virtual FilePath query_destination_path( + const std::string& /*title*/, + const std::string& /*extensions*/, + const std::string& /*functionid*/, + const std::string& /*hint*/) const override { return "out.zip"; } + + virtual bool report_issue(IssueType /*issuetype*/, + const std::string& description, + const std::string& brief) override { + std::cerr << brief << ": " << description << std::endl; + return true; + } + + virtual ProgresIndicatorPtr global_progress_indicator() override { + return m_global_progressind; + } + + virtual void global_progress_indicator(ProgresIndicatorPtr) override {} + + virtual bool is_main_thread() const override { return true; } + + virtual bool supports_asynch() const override { return false; } + + virtual void process_events() override {} + + virtual ProgresIndicatorPtr create_progress_indicator( + unsigned /*statenum*/, + const std::string& /*title*/, + const std::string& /*firstmsg*/) const override { + return std::make_shared(); + } + +protected: // This is a global progress indicator placeholder. In the Slic3r UI it can // contain the progress indicator on the statusbar. @@ -185,7 +302,7 @@ public: /** * @brief Implementation of the printing logic. */ -class PrintController: public AppControllerBoilerplate { +class PrintController { Print *m_print = nullptr; std::function m_rempools; protected: @@ -241,7 +358,7 @@ public: /** * @brief Top level controller. */ -class AppController: public AppControllerBoilerplate { +class AppController { Model *m_model = nullptr; PrintController::Ptr printctl; std::atomic m_arranging; diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp index 9f52e52d2f..25cd739f33 100644 --- a/xs/src/slic3r/AppControllerWx.cpp +++ b/xs/src/slic3r/AppControllerWx.cpp @@ -25,40 +25,43 @@ namespace Slic3r { -bool AppControllerBoilerplate::supports_asynch() const +bool AppControllerGui::supports_asynch() const { return true; } -void AppControllerBoilerplate::process_events() +void AppControllerGui::process_events() { wxYieldIfNeeded(); } -AppControllerBoilerplate::PathList -AppControllerBoilerplate::query_destination_paths( +FilePathList AppControllerGui::query_destination_paths( const std::string &title, - const std::string &extensions) const + const std::string &extensions, + const std::string &/*functionid*/, + const std::string& hint) const { wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) ); dlg.SetWildcard(extensions); - dlg.ShowModal(); + dlg.SetFilename(hint); - wxArrayString paths; - dlg.GetPaths(paths); + FilePathList ret; - PathList ret(paths.size(), ""); - for(auto& p : paths) ret.push_back(p.ToStdString()); + if(dlg.ShowModal() == wxID_OK) { + wxArrayString paths; + dlg.GetPaths(paths); + for(auto& p : paths) ret.push_back(p.ToStdString()); + } return ret; } -AppControllerBoilerplate::Path -AppControllerBoilerplate::query_destination_path( +FilePath AppControllerGui::query_destination_path( const std::string &title, const std::string &extensions, + const std::string &/*functionid*/, const std::string& hint) const { wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) ); @@ -66,16 +69,16 @@ AppControllerBoilerplate::query_destination_path( dlg.SetFilename(hint); - Path ret; + FilePath ret; if(dlg.ShowModal() == wxID_OK) { - ret = Path(dlg.GetPath()); + ret = FilePath(dlg.GetPath()); } return ret; } -bool AppControllerBoilerplate::report_issue(IssueType issuetype, +bool AppControllerGui::report_issue(IssueType issuetype, const std::string &description, const std::string &brief) { @@ -93,13 +96,6 @@ bool AppControllerBoilerplate::report_issue(IssueType issuetype, return ret != wxCANCEL; } -bool AppControllerBoilerplate::report_issue( - AppControllerBoilerplate::IssueType issuetype, - const std::string &description) -{ - return report_issue(issuetype, description, std::string()); -} - wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent); struct Zipper::Impl { @@ -235,8 +231,7 @@ public: }; } -AppControllerBoilerplate::ProgresIndicatorPtr -AppControllerBoilerplate::create_progress_indicator( +ProgresIndicatorPtr AppControllerGui::create_progress_indicator( unsigned statenum, const std::string& title, const std::string& firstmsg) const @@ -251,20 +246,13 @@ AppControllerBoilerplate::create_progress_indicator( return pri; } -AppControllerBoilerplate::ProgresIndicatorPtr -AppControllerBoilerplate::create_progress_indicator( - unsigned statenum, const std::string &title) const -{ - return create_progress_indicator(statenum, title, std::string()); -} - namespace { class Wrapper: public ProgressIndicator, public wxEvtHandler { ProgressStatusBar *m_sbar; using Base = ProgressIndicator; wxString m_message; - AppControllerBoilerplate& m_ctl; + AppControllerBase& m_ctl; void showProgress(bool show = true) { m_sbar->show_progress(show); @@ -288,7 +276,7 @@ class Wrapper: public ProgressIndicator, public wxEvtHandler { public: inline Wrapper(ProgressStatusBar *sbar, - AppControllerBoilerplate& ctl): + AppControllerBase& ctl): m_sbar(sbar), m_ctl(ctl) { Base::max(static_cast(m_sbar->get_range())); @@ -344,7 +332,8 @@ public: void AppController::set_global_progress_indicator(ProgressStatusBar *prsb) { if(prsb) { - global_progress_indicator(std::make_shared(prsb, *this)); + auto ctl = GUI::get_appctl(); + ctl->global_progress_indicator(std::make_shared(prsb, *ctl)); } } diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index e5ed917b35..2ad155b7d7 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -1,4 +1,5 @@ #include "GUI.hpp" +#include "../AppController.hpp" #include "WipeTowerDialog.hpp" #include @@ -1405,4 +1406,23 @@ void desktop_open_datadir_folder() #endif } +namespace { +AppControllerPtr g_appctl; +} + +AppControllerPtr get_appctl() +{ + return g_appctl; +} + +void set_cli_appctl() +{ + g_appctl = std::make_shared(); +} + +void set_gui_appctl() +{ + g_appctl = std::make_shared(); +} + } } diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index cbf83aa948..04b4a42179 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -42,6 +42,9 @@ class TabIface; class PreviewIface; class Print; class GCodePreviewData; +class AppControllerBase; + +using AppControllerPtr = std::shared_ptr; #define _(s) Slic3r::GUI::I18N::translate((s)) @@ -129,6 +132,10 @@ ProgressStatusBar* get_progress_status_bar(); wxNotebook * get_tab_panel(); wxNotebook* get_tab_panel(); +AppControllerPtr get_appctl(); +void set_cli_appctl(); +void set_gui_appctl(); + const wxColour& get_label_clr_modified(); const wxColour& get_label_clr_sys(); const wxColour& get_label_clr_default(); diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index 22e2ff1d71..a1e3e4670d 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -35,6 +35,12 @@ bool is_windows10() void set_wxapp(SV *ui) %code%{ Slic3r::GUI::set_wxapp((wxApp*)wxPli_sv_2_object(aTHX_ ui, "Wx::App")); %}; +void set_gui_appctl() + %code%{ Slic3r::GUI::set_gui_appctl(); %}; + +void set_cli_appctl() + %code%{ Slic3r::GUI::set_cli_appctl(); %}; + void set_progress_status_bar(ProgressStatusBar *prs) %code%{ Slic3r::GUI::set_progress_status_bar(prs); %}; From f1e0dc2dd714c9afae5d978e633646c0db8a7c57 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 20 Sep 2018 09:50:49 +0200 Subject: [PATCH 34/36] Ignore mouse up event after double click on gizmos grabbers --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 16 +++++++++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index c62652994c..efb2b2dab1 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1095,6 +1095,9 @@ GLCanvas3D::Mouse::Drag::Drag() GLCanvas3D::Mouse::Mouse() : dragging(false) , position(DBL_MAX, DBL_MAX) +#if ENABLE_GIZMOS_RESET + , ignore_up_event(false) +#endif // ENABLE_GIZMOS_RESET { } @@ -3058,6 +3061,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) #if ENABLE_GIZMOS_RESET else if (evt.LeftDClick() && m_gizmos.grabber_contains_mouse()) { +#if ENABLE_GIZMOS_RESET + m_mouse.ignore_up_event = true; +#endif // ENABLE_GIZMOS_RESET m_gizmos.process_double_click(); switch (m_gizmos.get_current_type()) { @@ -3075,7 +3081,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_on_gizmo_rotate_3D_callback.call(rotation(0), rotation(1), rotation(2)); #else m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z()); -#endif //ENABLE_MODELINSTANCE_3D_ROTATION +#endif // ENABLE_MODELINSTANCE_3D_ROTATION update_rotation_values(); m_dirty = true; break; @@ -3421,12 +3427,20 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled()) { // deselect and propagate event through callback +#if ENABLE_GIZMOS_RESET + if (!m_mouse.ignore_up_event && m_picking_enabled && !m_toolbar_action_running) +#else if (m_picking_enabled && !m_toolbar_action_running) +#endif // ENABLE_GIZMOS_RESET { deselect_volumes(); _on_select(-1, -1); update_gizmos_data(); } +#if ENABLE_GIZMOS_RESET + else if (m_mouse.ignore_up_event) + m_mouse.ignore_up_event = false; +#endif // ENABLE_GIZMOS_RESET } else if (evt.LeftUp() && m_gizmos.is_dragging()) { diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 2c830f58e4..b123efccf4 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -315,6 +315,9 @@ class GLCanvas3D bool dragging; Vec2d position; Drag drag; +#if ENABLE_GIZMOS_RESET + bool ignore_up_event; +#endif // ENABLE_GIZMOS_RESET Mouse(); From 07274589a35948e135ccb2eed59626a41c61444b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 20 Sep 2018 15:00:40 +0200 Subject: [PATCH 35/36] 1st installment of ModelInstance 3D rotation components --- lib/Slic3r/GUI/Plater.pm | 54 ++++++++++++++++ xs/src/libslic3r/Format/3mf.cpp | 8 +++ xs/src/libslic3r/Format/AMF.cpp | 64 ++++++++++++++++++- xs/src/libslic3r/Format/PRUS.cpp | 16 ++++- xs/src/libslic3r/Model.cpp | 36 +++++++++++ xs/src/libslic3r/Model.hpp | 19 ++++++ xs/src/libslic3r/ModelArrange.hpp | 12 +++- xs/src/libslic3r/Technologies.hpp | 2 + xs/src/slic3r/GUI/3DScene.cpp | 70 ++++++++++++++++++++- xs/src/slic3r/GUI/3DScene.hpp | 14 ++++- xs/src/slic3r/GUI/GLCanvas3D.cpp | 82 ++++++++++++++++++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 21 ++++++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 16 +++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 5 ++ xs/src/slic3r/GUI/GLGizmo.cpp | 58 ++++++++++++++++- xs/src/slic3r/GUI/GLGizmo.hpp | 23 +++++++ xs/src/slic3r/GUI/GUI_ObjectParts.cpp | 14 +++++ xs/src/slic3r/GUI/GUI_ObjectParts.hpp | 3 + xs/xsp/GUI_3DScene.xsp | 14 +++++ xs/xsp/Model.xsp | 14 +++++ 20 files changed, 533 insertions(+), 12 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index c4c7224fbc..525cc79318 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -150,12 +150,64 @@ sub new { $self->rotate(rad2deg($angle), Z, 'absolute'); }; + # callback to react to gizmo rotate + my $on_gizmo_rotate_3D = sub { + my ($angle_x, $angle_y, $angle_z) = @_; + + my ($obj_idx, $object) = $self->selected_object; + return if !defined $obj_idx; + + my $model_object = $self->{model}->objects->[$obj_idx]; + my $model_instance = $model_object->instances->[0]; + + $self->stop_background_process; + + my $rotation = Slic3r::Pointf3->new($angle_x, $angle_y, $angle_z); + foreach my $inst (@{ $model_object->instances }) { + $inst->set_rotations($rotation); + } + Slic3r::GUI::_3DScene::update_gizmos_data($self->{canvas3D}) if ($self->{canvas3D}); + + # update print and start background processing + $self->{print}->add_model_object($model_object, $obj_idx); + + $self->selection_changed; # refresh info (size etc.) + $self->update; + $self->schedule_background_process; + }; + # callback to react to gizmo flatten my $on_gizmo_flatten = sub { my ($angle, $axis_x, $axis_y, $axis_z) = @_; $self->rotate(rad2deg($angle), undef, 'absolute', $axis_x, $axis_y, $axis_z) if $angle != 0; }; + # callback to react to gizmo flatten + my $on_gizmo_flatten_3D = sub { + my ($angle_x, $angle_y, $angle_z) = @_; + + my ($obj_idx, $object) = $self->selected_object; + return if !defined $obj_idx; + + my $model_object = $self->{model}->objects->[$obj_idx]; + my $model_instance = $model_object->instances->[0]; + + $self->stop_background_process; + + my $rotation = Slic3r::Pointf3->new($angle_x, $angle_y, $angle_z); + foreach my $inst (@{ $model_object->instances }) { + $inst->set_rotations($rotation); + } + Slic3r::GUI::_3DScene::update_gizmos_data($self->{canvas3D}) if ($self->{canvas3D}); + + # update print and start background processing + $self->{print}->add_model_object($model_object, $obj_idx); + + $self->selection_changed; # refresh info (size etc.) + $self->update; + $self->schedule_background_process; + }; + # callback to update object's geometry info while using gizmos my $on_update_geometry_info = sub { my ($size_x, $size_y, $size_z, $scale_factor) = @_; @@ -261,7 +313,9 @@ sub new { Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons); Slic3r::GUI::_3DScene::register_on_gizmo_scale_uniformly_callback($self->{canvas3D}, $on_gizmo_scale_uniformly); Slic3r::GUI::_3DScene::register_on_gizmo_rotate_callback($self->{canvas3D}, $on_gizmo_rotate); + Slic3r::GUI::_3DScene::register_on_gizmo_rotate_3D_callback($self->{canvas3D}, $on_gizmo_rotate_3D); Slic3r::GUI::_3DScene::register_on_gizmo_flatten_callback($self->{canvas3D}, $on_gizmo_flatten); + Slic3r::GUI::_3DScene::register_on_gizmo_flatten_3D_callback($self->{canvas3D}, $on_gizmo_flatten_3D); Slic3r::GUI::_3DScene::register_on_update_geometry_info_callback($self->{canvas3D}, $on_update_geometry_info); Slic3r::GUI::_3DScene::register_action_add_callback($self->{canvas3D}, $on_action_add); Slic3r::GUI::_3DScene::register_action_delete_callback($self->{canvas3D}, $on_action_delete); diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp index 43c99f19fd..8a9d6870df 100644 --- a/xs/src/libslic3r/Format/3mf.cpp +++ b/xs/src/libslic3r/Format/3mf.cpp @@ -1283,6 +1283,9 @@ namespace Slic3r { transform(1, 0) * inv_sx, transform(1, 1) * inv_sy, transform(1, 2) * inv_sz, transform(2, 0) * inv_sx, transform(2, 1) * inv_sy, transform(2, 2) * inv_sz; +#if ENABLE_MODELINSTANCE_3D_ROTATION + Vec3d rotation = m3x3.eulerAngles(0, 1, 2); +#else Eigen::AngleAxisd rotation; rotation.fromRotationMatrix(m3x3); @@ -1291,6 +1294,7 @@ namespace Slic3r { return; double angle_z = (rotation.axis() == Vec3d::UnitZ()) ? rotation.angle() : -rotation.angle(); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #if ENABLE_MODELINSTANCE_3D_OFFSET instance.set_offset(offset); @@ -1299,7 +1303,11 @@ namespace Slic3r { instance.offset(1) = offset_y; #endif // ENABLE_MODELINSTANCE_3D_OFFSET instance.scaling_factor = sx; +#if ENABLE_MODELINSTANCE_3D_ROTATION + instance.set_rotation(rotation); +#else instance.rotation = angle_z; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION } bool _3MF_Importer::_handle_start_config(const char** attributes, unsigned int num_attributes) diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp index 458ce79de7..6d3e064079 100644 --- a/xs/src/libslic3r/Format/AMF.cpp +++ b/xs/src/libslic3r/Format/AMF.cpp @@ -30,7 +30,12 @@ // 0 : .amf, .amf.xml and .zip.amf files saved by older slic3r. No version definition in them. // 1 : Introduction of amf versioning. No other change in data saved into amf files. #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION +// 2 : Added z component of offset +// Added x and y components of rotation +#else // 2 : Added z component of offset. +#endif // ENABLE_MODELINSTANCE_3D_ROTATION const unsigned int VERSION_AMF = 2; #else const unsigned int VERSION_AMF = 1; @@ -127,6 +132,10 @@ struct AMFParserContext #if ENABLE_MODELINSTANCE_3D_OFFSET NODE_TYPE_DELTAZ, // amf/constellation/instance/deltaz #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + NODE_TYPE_RX, // amf/constellation/instance/rx + NODE_TYPE_RY, // amf/constellation/instance/ry +#endif // ENABLE_MODELINSTANCE_3D_ROTATION NODE_TYPE_RZ, // amf/constellation/instance/rz NODE_TYPE_SCALE, // amf/constellation/instance/scale NODE_TYPE_METADATA, // anywhere under amf/*/metadata @@ -134,7 +143,11 @@ struct AMFParserContext struct Instance { #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + Instance() : deltax_set(false), deltay_set(false), deltaz_set(false), rx_set(false), ry_set(false), rz_set(false), scale_set(false) {} +#else Instance() : deltax_set(false), deltay_set(false), deltaz_set(false), rz_set(false), scale_set(false) {} +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else Instance() : deltax_set(false), deltay_set(false), rz_set(false), scale_set(false) {} #endif // ENABLE_MODELINSTANCE_3D_OFFSET @@ -149,6 +162,14 @@ struct AMFParserContext float deltaz; bool deltaz_set; #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + // Rotation around the X axis. + float rx; + bool rx_set; + // Rotation around the Y axis. + float ry; + bool ry_set; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION // Rotation around the Z axis. float rz; bool rz_set; @@ -275,6 +296,12 @@ void AMFParserContext::startElement(const char *name, const char **atts) else if (strcmp(name, "deltaz") == 0) node_type_new = NODE_TYPE_DELTAZ; #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + else if (strcmp(name, "rx") == 0) + node_type_new = NODE_TYPE_RX; + else if (strcmp(name, "ry") == 0) + node_type_new = NODE_TYPE_RY; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION else if (strcmp(name, "rz") == 0) node_type_new = NODE_TYPE_RZ; else if (strcmp(name, "scale") == 0) @@ -339,7 +366,11 @@ void AMFParserContext::characters(const XML_Char *s, int len) if (m_path.back() == NODE_TYPE_DELTAX || m_path.back() == NODE_TYPE_DELTAY || m_path.back() == NODE_TYPE_DELTAZ || - m_path.back() == NODE_TYPE_RZ || +#if ENABLE_MODELINSTANCE_3D_ROTATION + m_path.back() == NODE_TYPE_RX || + m_path.back() == NODE_TYPE_RY || +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + m_path.back() == NODE_TYPE_RZ || m_path.back() == NODE_TYPE_SCALE) #else if (m_path.back() == NODE_TYPE_DELTAX || m_path.back() == NODE_TYPE_DELTAY || m_path.back() == NODE_TYPE_RZ || m_path.back() == NODE_TYPE_SCALE) @@ -391,6 +422,20 @@ void AMFParserContext::endElement(const char * /* name */) m_value[0].clear(); break; #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + case NODE_TYPE_RX: + assert(m_instance); + m_instance->rx = float(atof(m_value[0].c_str())); + m_instance->rx_set = true; + m_value[0].clear(); + break; + case NODE_TYPE_RY: + assert(m_instance); + m_instance->ry = float(atof(m_value[0].c_str())); + m_instance->ry_set = true; + m_value[0].clear(); + break; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION case NODE_TYPE_RZ: assert(m_instance); m_instance->rz = float(atof(m_value[0].c_str())); @@ -541,12 +586,16 @@ void AMFParserContext::endDocument() if (instance.deltax_set && instance.deltay_set) { ModelInstance *mi = m_model.objects[object.second.idx]->add_instance(); #if ENABLE_MODELINSTANCE_3D_OFFSET - mi->set_offset(Vec3d((double)instance.deltax, (double)instance.deltay, (double)instance.deltaz)); + mi->set_offset(Vec3d(instance.deltax_set ? (double)instance.deltax : 0.0, instance.deltay_set ? (double)instance.deltay : 0.0, instance.deltaz_set ? (double)instance.deltaz : 0.0)); #else mi->offset(0) = instance.deltax; mi->offset(1) = instance.deltay; #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + mi->set_rotation(Vec3d(instance.rx_set ? (double)instance.rx : 0.0, instance.ry_set ? (double)instance.ry : 0.0, instance.rz_set ? (double)instance.rz : 0.0)); +#else mi->rotation = instance.rz_set ? instance.rz : 0.f; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION mi->scaling_factor = instance.scale_set ? instance.scale : 1.f; } } @@ -850,6 +899,10 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c #if ENABLE_MODELINSTANCE_3D_OFFSET " %lf\n" #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + " %lf\n" + " %lf\n" +#endif // ENABLE_MODELINSTANCE_3D_ROTATION " %lf\n" " %lf\n" " \n", @@ -862,8 +915,15 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c instance->offset(0), instance->offset(1), #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + instance->get_rotation(X), + instance->get_rotation(Y), + instance->get_rotation(Z), +#else instance->rotation, +#endif // ENABLE_MODELINSTANCE_3D_ROTATION instance->scaling_factor); + //FIXME missing instance->scaling_factor instances.append(buf); } diff --git a/xs/src/libslic3r/Format/PRUS.cpp b/xs/src/libslic3r/Format/PRUS.cpp index 45eb56c631..27095acef4 100644 --- a/xs/src/libslic3r/Format/PRUS.cpp +++ b/xs/src/libslic3r/Format/PRUS.cpp @@ -164,7 +164,11 @@ bool load_prus(const char *path, Model *model) const char *zero_tag = ""; const char *zero_xml = strstr(scene_xml_data.data(), zero_tag); float trafo[3][4] = { 0 }; +#if ENABLE_MODELINSTANCE_3D_ROTATION + Vec3d instance_rotation = Vec3d::Zero(); +#else double instance_rotation = 0.; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION double instance_scaling_factor = 1.f; #if ENABLE_MODELINSTANCE_3D_OFFSET Vec3d instance_offset = Vec3d::Zero(); @@ -197,10 +201,14 @@ bool load_prus(const char *path, Model *model) instance_scaling_factor = scale[0]; scale[0] = scale[1] = scale[2] = 1.; } +#if ENABLE_MODELINSTANCE_3D_ROTATION + instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]); +#else if (rotation[0] == 0. && rotation[1] == 0.) { instance_rotation = - rotation[2]; rotation[2] = 0.; } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION Eigen::Matrix3f mat_rot, mat_scale, mat_trafo; mat_rot = Eigen::AngleAxisf(-rotation[2], Eigen::Vector3f::UnitZ()) * Eigen::AngleAxisf(-rotation[1], Eigen::Vector3f::UnitY()) * @@ -366,8 +374,12 @@ bool load_prus(const char *path, Model *model) model_object = model->add_object(name_utf8.data(), path, std::move(mesh)); volume = model_object->volumes.front(); ModelInstance *instance = model_object->add_instance(); - instance->rotation = instance_rotation; - instance->scaling_factor = instance_scaling_factor; +#if ENABLE_MODELINSTANCE_3D_ROTATION + instance->set_rotation(instance_rotation); +#else + instance->rotation = instance_rotation; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + instance->scaling_factor = instance_scaling_factor; #if ENABLE_MODELINSTANCE_3D_OFFSET instance->set_offset(instance_offset); #else diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index ec7447352b..65743da8d8 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -1054,6 +1054,29 @@ size_t ModelVolume::split(unsigned int max_extruders) return idx; } +#if ENABLE_MODELINSTANCE_3D_ROTATION +void ModelInstance::set_rotation(const Vec3d& rotation) +{ + set_rotation(X, rotation(0)); + set_rotation(Y, rotation(1)); + set_rotation(Z, rotation(2)); +} + +void ModelInstance::set_rotation(Axis axis, double rotation) +{ + static const double TWO_PI = 2.0 * (double)PI; + while (rotation < 0.0) + { + rotation += TWO_PI; + } + while (TWO_PI < rotation) + { + rotation -= TWO_PI; + } + m_rotation(axis) = rotation; +} +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const { mesh->transform(world_matrix(dont_translate).cast()); @@ -1098,7 +1121,12 @@ Vec3d ModelInstance::transform_vector(const Vec3d& v, bool dont_translate) const void ModelInstance::transform_polygon(Polygon* polygon) const { +#if ENABLE_MODELINSTANCE_3D_ROTATION + // CHECK_ME -> Is the following correct or it should take in account all three rotations ? + polygon->rotate(this->m_rotation(2)); // rotate around polygon origin +#else polygon->rotate(this->rotation); // rotate around polygon origin +#endif // ENABLE_MODELINSTANCE_3D_ROTATION polygon->scale(this->scaling_factor); // scale around polygon origin } @@ -1114,7 +1142,15 @@ Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, b #endif // ENABLE_MODELINSTANCE_3D_OFFSET if (!dont_rotate) +#if ENABLE_MODELINSTANCE_3D_ROTATION + { + m.rotate(Eigen::AngleAxisd(m_rotation(2), Vec3d::UnitZ())); + m.rotate(Eigen::AngleAxisd(m_rotation(1), Vec3d::UnitY())); + m.rotate(Eigen::AngleAxisd(m_rotation(0), Vec3d::UnitX())); + } +#else m.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ())); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION if (!dont_scale) m.scale(scaling_factor); diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 4c2356429a..ba315e3d60 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -246,11 +246,16 @@ public: #if ENABLE_MODELINSTANCE_3D_OFFSET private: Vec3d m_offset; // in unscaled coordinates +#if ENABLE_MODELINSTANCE_3D_ROTATION + Vec3d m_rotation; // Rotation around the three axes, in radians around mesh center point +#endif // ENABLE_MODELINSTANCE_3D_ROTATION public: #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if !ENABLE_MODELINSTANCE_3D_ROTATION double rotation; // Rotation around the Z axis, in radians around mesh center point +#endif // !ENABLE_MODELINSTANCE_3D_ROTATION double scaling_factor; #if !ENABLE_MODELINSTANCE_3D_OFFSET Vec2d offset; // in unscaled coordinates @@ -269,6 +274,14 @@ public: void set_offset(Axis axis, double offset) { m_offset(axis) = offset; } #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + const Vec3d& get_rotation() const { return m_rotation; } + double get_rotation(Axis axis) const { return m_rotation(axis); } + + void set_rotation(const Vec3d& rotation); + void set_rotation(Axis axis, double rotation); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + // To be called on an external mesh void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; // Calculate a bounding box of a transformed mesh. To be called on an external mesh. @@ -289,9 +302,15 @@ private: ModelObject* object; #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + ModelInstance(ModelObject *object) : m_rotation(Vec3d::Zero()), scaling_factor(1), m_offset(Vec3d::Zero()), object(object), print_volume_state(PVS_Inside) {} + ModelInstance(ModelObject *object, const ModelInstance &other) : + m_rotation(other.m_rotation), scaling_factor(other.scaling_factor), m_offset(other.m_offset), object(object), print_volume_state(PVS_Inside) {} +#else ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), m_offset(Vec3d::Zero()), object(object), print_volume_state(PVS_Inside) {} ModelInstance(ModelObject *object, const ModelInstance &other) : rotation(other.rotation), scaling_factor(other.scaling_factor), m_offset(other.m_offset), object(object), print_volume_state(PVS_Inside) {} +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), offset(Vec2d::Zero()), object(object), print_volume_state(PVS_Inside) {} ModelInstance(ModelObject *object, const ModelInstance &other) : diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index 4b91131ede..9f983ee9f4 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -527,8 +527,13 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { // Invalid geometries would throw exceptions when arranging if(item.vertexCount() > 3) { +#if ENABLE_MODELINSTANCE_3D_ROTATION + // CHECK_ME -> is the following correct or it should take in account all three rotations ? + item.rotation(objinst->get_rotation(Z)); +#else item.rotation(objinst->rotation); - item.translation( { +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + item.translation({ #if ENABLE_MODELINSTANCE_3D_OFFSET ClipperLib::cInt(objinst->get_offset(X)/SCALING_FACTOR), ClipperLib::cInt(objinst->get_offset(Y)/SCALING_FACTOR) @@ -681,7 +686,12 @@ void applyResult( #endif // ENABLE_MODELINSTANCE_3D_OFFSET // write the transformation data into the model instance +#if ENABLE_MODELINSTANCE_3D_ROTATION + // CHECK_ME -> Is the following correct ? + inst_ptr->set_rotation(Vec3d(0.0, 0.0, rot)); +#else inst_ptr->rotation = rot; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #if ENABLE_MODELINSTANCE_3D_OFFSET inst_ptr->set_offset(foff); #else diff --git a/xs/src/libslic3r/Technologies.hpp b/xs/src/libslic3r/Technologies.hpp index 0343bb00c0..25e6b85e77 100644 --- a/xs/src/libslic3r/Technologies.hpp +++ b/xs/src/libslic3r/Technologies.hpp @@ -8,6 +8,8 @@ #define ENABLE_MODELINSTANCE_3D_OFFSET (1 && ENABLE_1_42_0) // Add double click on gizmo grabbers to reset transformation components to their default value #define ENABLE_GIZMOS_RESET (1 && ENABLE_1_42_0) +// Add x and y rotation components to model instances' offset +#define ENABLE_MODELINSTANCE_3D_ROTATION (1 && ENABLE_MODELINSTANCE_3D_OFFSET) #endif // _technologies_h_ diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index e6f0380426..5b830b30db 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -196,7 +196,11 @@ const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f }; GLVolume::GLVolume(float r, float g, float b, float a) : m_offset(Vec3d::Zero()) +#if ENABLE_MODELINSTANCE_3D_ROTATION + , m_rotation(Vec3d::Zero()) +#else , m_rotation(0.0) +#endif // ENABLE_MODELINSTANCE_3D_ROTATION , m_scaling_factor(1.0) , m_world_matrix(Transform3f::Identity()) , m_world_matrix_dirty(true) @@ -255,7 +259,24 @@ void GLVolume::set_render_color() set_render_color(color, 4); } -double GLVolume::get_rotation() +#if ENABLE_MODELINSTANCE_3D_ROTATION +const Vec3d& GLVolume::get_rotation() const +{ + return m_rotation; +} + +void GLVolume::set_rotation(const Vec3d& rotation) +{ + if (m_rotation != rotation) + { + m_rotation = rotation; + m_world_matrix_dirty = true; + m_transformed_bounding_box_dirty = true; + m_transformed_convex_hull_bounding_box_dirty = true; + } +} +#else +double GLVolume::get_rotation() const { return m_rotation; } @@ -270,6 +291,7 @@ void GLVolume::set_rotation(double rotation) m_transformed_convex_hull_bounding_box_dirty = true; } } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION const Vec3d& GLVolume::get_offset() const { @@ -327,7 +349,13 @@ const Transform3f& GLVolume::world_matrix() const { m_world_matrix = Transform3f::Identity(); m_world_matrix.translate(m_offset.cast()); +#if ENABLE_MODELINSTANCE_3D_ROTATION + m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation(2), Vec3f::UnitZ())); + m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation(1), Vec3f::UnitY())); + m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation(0), Vec3f::UnitX())); +#else m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation, Vec3f::UnitZ())); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION m_world_matrix.scale((float)m_scaling_factor); m_world_matrix_dirty = false; } @@ -403,7 +431,13 @@ void GLVolume::render() const ::glCullFace(GL_BACK); ::glPushMatrix(); ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); +#if ENABLE_MODELINSTANCE_3D_ROTATION + ::glRotated(m_rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glRotated(m_rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0); + ::glRotated(m_rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0); +#else ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); if (this->indexed_vertex_array.indexed()) this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); @@ -529,7 +563,13 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c ::glPushMatrix(); ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); +#if ENABLE_MODELINSTANCE_3D_ROTATION + ::glRotated(m_rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glRotated(m_rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0); + ::glRotated(m_rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0); +#else ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); if (n_triangles > 0) @@ -574,7 +614,13 @@ void GLVolume::render_legacy() const ::glPushMatrix(); ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); +#if ENABLE_MODELINSTANCE_3D_ROTATION + ::glRotated(m_rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glRotated(m_rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0); + ::glRotated(m_rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0); +#else ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); if (n_triangles > 0) @@ -698,7 +744,11 @@ std::vector GLVolumeCollection::load_object( #else v.set_offset(Vec3d(instance->offset(0), instance->offset(1), 0.0)); #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + v.set_rotation(instance->get_rotation()); +#else v.set_rotation(instance->rotation); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION v.set_scaling_factor(instance->scaling_factor); } } @@ -2067,12 +2117,30 @@ void _3DScene::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, vo void _3DScene::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback) { +#if !ENABLE_MODELINSTANCE_3D_ROTATION s_canvas_mgr.register_on_gizmo_rotate_callback(canvas, callback); +#endif // !ENABLE_MODELINSTANCE_3D_ROTATION +} + +void _3DScene::register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback) +{ +#if ENABLE_MODELINSTANCE_3D_ROTATION + s_canvas_mgr.register_on_gizmo_rotate_3D_callback(canvas, callback); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION } void _3DScene::register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback) { +#if !ENABLE_MODELINSTANCE_3D_ROTATION s_canvas_mgr.register_on_gizmo_flatten_callback(canvas, callback); +#endif // !ENABLE_MODELINSTANCE_3D_ROTATION +} + +void _3DScene::register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback) +{ +#if ENABLE_MODELINSTANCE_3D_ROTATION + s_canvas_mgr.register_on_gizmo_flatten_3D_callback(canvas, callback); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION } void _3DScene::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback) diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index f2d1c07868..8fc1661fcd 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -256,8 +256,13 @@ public: private: // Offset of the volume to be rendered. Vec3d m_offset; +#if ENABLE_MODELINSTANCE_3D_ROTATION + // Rotation around three axes of the volume to be rendered. + Vec3d m_rotation; +#else // Rotation around Z axis of the volume to be rendered. double m_rotation; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION // Scale factor of the volume to be rendered. double m_scaling_factor; // World matrix of the volume to be rendered. @@ -327,8 +332,13 @@ public: // Sets render color in dependence of current state void set_render_color(); - double get_rotation(); +#if ENABLE_MODELINSTANCE_3D_ROTATION + const Vec3d& get_rotation() const; + void set_rotation(const Vec3d& rotation); +#else + double get_rotation() const; void set_rotation(double rotation); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION const Vec3d& get_offset() const; void set_offset(const Vec3d& offset); @@ -558,7 +568,9 @@ public: static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); static void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); static void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback); + static void register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback); static void register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback); + static void register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback); static void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback); static void register_action_add_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index efb2b2dab1..658e1731aa 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1184,9 +1184,11 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) return false; } +#if !ENABLE_MODELINSTANCE_3D_ROTATION // temporary disable x and y grabbers gizmo->disable_grabber(0); gizmo->disable_grabber(1); +#endif // !ENABLE_MODELINSTANCE_3D_ROTATION m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo)); @@ -1436,6 +1438,35 @@ void GLCanvas3D::Gizmos::set_scale(float scale) reinterpret_cast(it->second)->set_scale(scale); } +#if ENABLE_MODELINSTANCE_3D_ROTATION +Vec3d GLCanvas3D::Gizmos::get_rotation() const +{ + if (!m_enabled) + return Vec3d::Zero(); + + GizmosMap::const_iterator it = m_gizmos.find(Rotate); + return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_rotation() : Vec3d::Zero(); +} + +void GLCanvas3D::Gizmos::set_rotation(const Vec3d& rotation) +{ + if (!m_enabled) + return; + + GizmosMap::const_iterator it = m_gizmos.find(Rotate); + if (it != m_gizmos.end()) + reinterpret_cast(it->second)->set_rotation(rotation); +} + +Vec3d GLCanvas3D::Gizmos::get_flattening_rotation() const +{ + if (!m_enabled) + return Vec3d::Zero(); + + GizmosMap::const_iterator it = m_gizmos.find(Flatten); + return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_rotation() : Vec3d::Zero(); +} +#else float GLCanvas3D::Gizmos::get_angle_z() const { if (!m_enabled) @@ -1463,6 +1494,7 @@ Vec3d GLCanvas3D::Gizmos::get_flattening_normal() const GizmosMap::const_iterator it = m_gizmos.find(Flatten); return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_normal() : Vec3d::Zero(); } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object) { @@ -2400,7 +2432,11 @@ void GLCanvas3D::update_gizmos_data() m_gizmos.set_position(Vec3d(model_instance->offset(0), model_instance->offset(1), 0.0)); #endif // ENABLE_MODELINSTANCE_3D_OFFSET m_gizmos.set_scale(model_instance->scaling_factor); +#if ENABLE_MODELINSTANCE_3D_ROTATION + m_gizmos.set_rotation(model_instance->get_rotation()); +#else m_gizmos.set_angle_z(model_instance->rotation); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION m_gizmos.set_flattening_data(model_object); } } @@ -2409,7 +2445,11 @@ void GLCanvas3D::update_gizmos_data() { m_gizmos.set_position(Vec3d::Zero()); m_gizmos.set_scale(1.0f); +#if ENABLE_MODELINSTANCE_3D_ROTATION + m_gizmos.set_rotation(Vec3d::Zero()); +#else m_gizmos.set_angle_z(0.0f); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION m_gizmos.set_flattening_data(nullptr); } } @@ -2772,6 +2812,19 @@ void GLCanvas3D::register_on_gizmo_scale_uniformly_callback(void* callback) m_on_gizmo_scale_uniformly_callback.register_callback(callback); } +#if ENABLE_MODELINSTANCE_3D_ROTATION +void GLCanvas3D::register_on_gizmo_rotate_3D_callback(void* callback) +{ + if (callback != nullptr) + m_on_gizmo_rotate_3D_callback.register_callback(callback); +} + +void GLCanvas3D::register_on_gizmo_flatten_3D_callback(void* callback) +{ + if (callback != nullptr) + m_on_gizmo_flatten_3D_callback.register_callback(callback); +} +#else void GLCanvas3D::register_on_gizmo_rotate_callback(void* callback) { if (callback != nullptr) @@ -2783,6 +2836,7 @@ void GLCanvas3D::register_on_gizmo_flatten_callback(void* callback) if (callback != nullptr) m_on_gizmo_flatten_callback.register_callback(callback); } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION void GLCanvas3D::register_on_update_geometry_info_callback(void* callback) { @@ -3133,6 +3187,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id(selected_object_idx); if (m_gizmos.get_current_type() == Gizmos::Flatten) { +#if ENABLE_MODELINSTANCE_3D_ROTATION + // Rotate the object so the normal points downward: + const Vec3d& rotation = m_gizmos.get_flattening_rotation(); + m_on_gizmo_flatten_3D_callback.call(rotation(0), rotation(1), rotation(2)); +#else // Rotate the object so the normal points downward: Vec3d normal = m_gizmos.get_flattening_normal(); if (normal(0) != 0.0 || normal(1) != 0.0 || normal(2) != 0.0) { @@ -3140,6 +3199,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) float angle = acos(clamp(-1.0, 1.0, -normal(2))); m_on_gizmo_flatten_callback.call(angle, (float)axis(0), (float)axis(1), (float)axis(2)); } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION } m_dirty = true; @@ -3322,6 +3382,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } case Gizmos::Rotate: { +#if ENABLE_MODELINSTANCE_3D_ROTATION + // Apply new temporary rotation + Vec3d rotation = m_gizmos.get_rotation(); + for (GLVolume* v : volumes) + { + v->set_rotation(rotation); + } + update_rotation_value(rotation); +#else // Apply new temporary angle_z float angle_z = m_gizmos.get_angle_z(); for (GLVolume* v : volumes) @@ -3329,6 +3398,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) v->set_rotation((double)angle_z); } update_rotation_value((double)angle_z, Z); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION break; } default: @@ -3474,14 +3544,19 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } case Gizmos::Rotate: { +#if ENABLE_MODELINSTANCE_3D_ROTATION + const Vec3d& rotation = m_gizmos.get_rotation(); + m_on_gizmo_rotate_3D_callback.call(rotation(0), rotation(1), rotation(2)); +#else m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z()); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION break; } default: break; } m_gizmos.stop_dragging(); - Slic3r::GUI::update_settings_value(); + update_settings_value(); } m_mouse.drag.move_volume_idx = -1; @@ -3921,8 +3996,13 @@ void GLCanvas3D::_deregister_callbacks() m_on_wipe_tower_moved_callback.deregister_callback(); m_on_enable_action_buttons_callback.deregister_callback(); m_on_gizmo_scale_uniformly_callback.deregister_callback(); +#if ENABLE_MODELINSTANCE_3D_ROTATION + m_on_gizmo_rotate_3D_callback.deregister_callback(); + m_on_gizmo_flatten_3D_callback.deregister_callback(); +#else m_on_gizmo_rotate_callback.deregister_callback(); m_on_gizmo_flatten_callback.deregister_callback(); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION m_on_update_geometry_info_callback.deregister_callback(); m_action_add_callback.deregister_callback(); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index b123efccf4..712ffbefcc 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -387,11 +387,20 @@ class GLCanvas3D float get_scale() const; void set_scale(float scale); +#if ENABLE_MODELINSTANCE_3D_ROTATION + Vec3d get_rotation() const; + void set_rotation(const Vec3d& rotation); +#else float get_angle_z() const; void set_angle_z(float angle_z); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION - void set_flattening_data(const ModelObject* model_object); +#if ENABLE_MODELINSTANCE_3D_ROTATION + Vec3d get_flattening_rotation() const; +#else Vec3d get_flattening_normal() const; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + void set_flattening_data(const ModelObject* model_object); void render_current_gizmo(const BoundingBoxf3& box) const; @@ -507,8 +516,13 @@ class GLCanvas3D PerlCallback m_on_wipe_tower_moved_callback; PerlCallback m_on_enable_action_buttons_callback; PerlCallback m_on_gizmo_scale_uniformly_callback; +#if ENABLE_MODELINSTANCE_3D_ROTATION + PerlCallback m_on_gizmo_rotate_3D_callback; + PerlCallback m_on_gizmo_flatten_3D_callback; +#else PerlCallback m_on_gizmo_rotate_callback; PerlCallback m_on_gizmo_flatten_callback; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION PerlCallback m_on_update_geometry_info_callback; PerlCallback m_action_add_callback; @@ -632,8 +646,13 @@ public: void register_on_wipe_tower_moved_callback(void* callback); void register_on_enable_action_buttons_callback(void* callback); void register_on_gizmo_scale_uniformly_callback(void* callback); +#if ENABLE_MODELINSTANCE_3D_ROTATION + void register_on_gizmo_rotate_3D_callback(void* callback); + void register_on_gizmo_flatten_3D_callback(void* callback); +#else void register_on_gizmo_rotate_callback(void* callback); void register_on_gizmo_flatten_callback(void* callback); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION void register_on_update_geometry_info_callback(void* callback); void register_action_add_callback(void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 495f494250..1ac588f6c0 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -699,6 +699,21 @@ void GLCanvas3DManager::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* c it->second->register_on_gizmo_scale_uniformly_callback(callback); } +#if ENABLE_MODELINSTANCE_3D_ROTATION +void GLCanvas3DManager::register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_gizmo_rotate_3D_callback(callback); +} + +void GLCanvas3DManager::register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_gizmo_flatten_3D_callback(callback); +} +#else void GLCanvas3DManager::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -712,6 +727,7 @@ void GLCanvas3DManager::register_on_gizmo_flatten_callback(wxGLCanvas* canvas, v if (it != m_canvases.end()) it->second->register_on_gizmo_flatten_callback(callback); } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION void GLCanvas3DManager::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback) { diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 4922b61714..2331ec3f5e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -163,8 +163,13 @@ public: void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback); void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); +#if ENABLE_MODELINSTANCE_3D_ROTATION + void register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback); + void register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback); +#else void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback); void register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback); void register_action_add_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 1890a7bf81..f9b481a31a 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -1251,15 +1251,28 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f); #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + for (const InstanceData& inst : m_instances) { + Vec3d position = inst.position + dragged_offset; +#else for (Vec3d offset : m_instances_positions) { offset += dragged_offset; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else for (Vec2d offset : m_instances_positions) { offset += to_2d(dragged_offset); #endif // ENABLE_MODELINSTANCE_3D_OFFSET ::glPushMatrix(); #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + ::glTranslated(position(0), position(1), position(2)); + ::glRotated(inst.rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glRotated(inst.rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0); + ::glRotated(inst.rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0); + ::glScaled(inst.scaling_factor, inst.scaling_factor, inst.scaling_factor); +#else ::glTranslated(offset(0), offset(1), offset(2)); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else ::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f); #endif // ENABLE_MODELINSTANCE_3D_OFFSET @@ -1282,13 +1295,25 @@ void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const { ::glColor3f(1.0f, 1.0f, picking_color_component(i)); #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + for (const InstanceData& inst : m_instances) { +#else for (const Vec3d& offset : m_instances_positions) { +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else for (const Vec2d& offset : m_instances_positions) { #endif // ENABLE_MODELINSTANCE_3D_OFFSET ::glPushMatrix(); #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + ::glTranslated(inst.position(0), inst.position(1), inst.position(2)); + ::glRotated(inst.rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glRotated(inst.rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0); + ::glRotated(inst.rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0); + ::glScaled(inst.scaling_factor, inst.scaling_factor, inst.scaling_factor); +#else ::glTranslated(offset(0), offset(1), offset(2)); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else ::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f); #endif // ENABLE_MODELINSTANCE_3D_OFFSET @@ -1307,10 +1332,18 @@ void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) // ...and save the updated positions of the object instances: if (m_model_object && !m_model_object->instances.empty()) { +#if ENABLE_MODELINSTANCE_3D_ROTATION + m_instances.clear(); +#else m_instances_positions.clear(); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION for (const auto* instance : m_model_object->instances) #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + m_instances.emplace_back(instance->get_offset(), instance->get_rotation(), instance->scaling_factor); +#else m_instances_positions.emplace_back(instance->get_offset()); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else m_instances_positions.emplace_back(instance->offset); #endif // ENABLE_MODELINSTANCE_3D_OFFSET @@ -1326,8 +1359,10 @@ void GLGizmoFlatten::update_planes() for (const ModelVolume* vol : m_model_object->volumes) ch.merge(vol->get_convex_hull()); ch = ch.convex_hull_3d(); +#if !ENABLE_MODELINSTANCE_3D_ROTATION ch.scale(m_model_object->instances.front()->scaling_factor); ch.rotate_z(m_model_object->instances.front()->rotation); +#endif // !ENABLE_MODELINSTANCE_3D_ROTATION m_planes.clear(); @@ -1372,8 +1407,8 @@ void GLGizmoFlatten::update_planes() // if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected anyway): if (m_planes.back().vertices.size() == 3 && (m_planes.back().vertices[0] - m_planes.back().vertices[1]).norm() < 1.f - || (m_planes.back().vertices[0] - m_planes.back().vertices[2]).norm() < 1.f) - m_planes.pop_back(); + || (m_planes.back().vertices[0] - m_planes.back().vertices[2]).norm() < 1.f) + m_planes.pop_back(); } // Now we'll go through all the polygons, transform the points into xy plane to process them: @@ -1476,8 +1511,10 @@ void GLGizmoFlatten::update_planes() m_source_data.bounding_boxes.clear(); for (const auto& vol : m_model_object->volumes) m_source_data.bounding_boxes.push_back(vol->get_convex_hull().bounding_box()); +#if !ENABLE_MODELINSTANCE_3D_ROTATION m_source_data.scaling_factor = m_model_object->instances.front()->scaling_factor; m_source_data.rotation = m_model_object->instances.front()->rotation; +#endif // !ENABLE_MODELINSTANCE_3D_ROTATION const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex(); m_source_data.mesh_first_point = Vec3d((double)first_vertex[0], (double)first_vertex[1], (double)first_vertex[2]); } @@ -1489,10 +1526,14 @@ bool GLGizmoFlatten::is_plane_update_necessary() const if (m_state != On || !m_model_object || m_model_object->instances.empty()) return false; +#if ENABLE_MODELINSTANCE_3D_ROTATION + if (m_model_object->volumes.size() != m_source_data.bounding_boxes.size()) +#else if (m_model_object->volumes.size() != m_source_data.bounding_boxes.size() || m_model_object->instances.front()->scaling_factor != m_source_data.scaling_factor || m_model_object->instances.front()->rotation != m_source_data.rotation) - return true; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + return true; // now compare the bounding boxes: for (unsigned int i=0; ivolumes.size(); ++i) @@ -1507,11 +1548,22 @@ bool GLGizmoFlatten::is_plane_update_necessary() const return false; } +#if ENABLE_MODELINSTANCE_3D_ROTATION +Vec3d GLGizmoFlatten::get_flattening_rotation() const +{ + // calculates the rotations in model space + Eigen::Quaterniond q; + Vec3d angles = q.setFromTwoVectors(m_normal, -Vec3d::UnitZ()).toRotationMatrix().eulerAngles(2, 1, 0); + m_normal = Vec3d::Zero(); + return Vec3d(angles(2), angles(1), angles(0)); +} +#else Vec3d GLGizmoFlatten::get_flattening_normal() const { Vec3d normal = m_model_object->instances.front()->world_matrix(true).matrix().block(0, 0, 3, 3).inverse() * m_normal; m_normal = Vec3d::Zero(); return normal.normalized(); } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index bf5838f722..e0f2c73d0b 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -188,6 +188,10 @@ class GLGizmoRotate3D : public GLGizmoBase public: explicit GLGizmoRotate3D(GLCanvas3D& parent); +#if ENABLE_MODELINSTANCE_3D_ROTATION + Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } + void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); } +#else double get_angle_x() const { return m_gizmos[X].get_angle(); } void set_angle_x(double angle) { m_gizmos[X].set_angle(angle); } @@ -196,6 +200,7 @@ public: double get_angle_z() const { return m_gizmos[Z].get_angle(); } void set_angle_z(double angle) { m_gizmos[Z].set_angle(angle); } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION protected: virtual bool on_init(); @@ -340,8 +345,10 @@ private: }; struct SourceDataSummary { std::vector bounding_boxes; // bounding boxes of convex hulls of individual volumes +#if !ENABLE_MODELINSTANCE_3D_ROTATION float scaling_factor; float rotation; +#endif // !ENABLE_MODELINSTANCE_3D_ROTATION Vec3d mesh_first_point; }; @@ -350,7 +357,19 @@ private: std::vector m_planes; #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + struct InstanceData + { + Vec3d position; + Vec3d rotation; + double scaling_factor; + + InstanceData(const Vec3d& position, const Vec3d& rotation, double scaling_factor) : position(position), rotation(rotation), scaling_factor(scaling_factor) {} + }; + std::vector m_instances; +#else Pointf3s m_instances_positions; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else std::vector m_instances_positions; #endif // ENABLE_MODELINSTANCE_3D_OFFSET @@ -364,7 +383,11 @@ public: explicit GLGizmoFlatten(GLCanvas3D& parent); void set_flattening_data(const ModelObject* model_object); +#if ENABLE_MODELINSTANCE_3D_ROTATION + Vec3d get_flattening_rotation() const; +#else Vec3d get_flattening_normal() const; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION protected: virtual bool on_init(); diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp index ae34359ce9..b336a34a95 100644 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp +++ b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp @@ -1802,11 +1802,15 @@ void update_scale_values(double scaling_factor) void update_rotation_values() { +#if ENABLE_MODELINSTANCE_3D_ROTATION + update_rotation_value((*m_objects)[m_selected_object_id]->instances.front()->get_rotation()); +#else auto og = get_optgroup(ogFrequentlyObjectSettings); auto instance = (*m_objects)[m_selected_object_id]->instances.front(); og->set_value("rotation_x", 0); og->set_value("rotation_y", 0); og->set_value("rotation_z", int(Geometry::rad2deg(instance->rotation))); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION } void update_rotation_value(double angle, Axis axis) @@ -1836,6 +1840,16 @@ void update_rotation_value(double angle, Axis axis) og->set_value(axis_str, int(Geometry::rad2deg(angle))); } +#if ENABLE_MODELINSTANCE_3D_ROTATION +void update_rotation_value(const Vec3d& rotation) +{ + auto og = get_optgroup(ogFrequentlyObjectSettings); + og->set_value("rotation_x", int(Geometry::rad2deg(rotation(0)))); + og->set_value("rotation_y", int(Geometry::rad2deg(rotation(1)))); + og->set_value("rotation_z", int(Geometry::rad2deg(rotation(2)))); +} +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + void set_uniform_scaling(const bool uniform_scale) { g_is_uniform_scale = uniform_scale; diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp b/xs/src/slic3r/GUI/GUI_ObjectParts.hpp index e66b4d1db8..fdeb7a629c 100644 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp +++ b/xs/src/slic3r/GUI/GUI_ObjectParts.hpp @@ -124,6 +124,9 @@ void update_scale_values(double scaling_factor); void update_rotation_values(); // update rotation value after "gizmos" void update_rotation_value(double angle, Axis axis); +#if ENABLE_MODELINSTANCE_3D_ROTATION +void update_rotation_value(const Vec3d& rotation); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION void set_uniform_scaling(const bool uniform_scale); void on_begin_drag(wxDataViewEvent &event); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index c3e4ba3b7c..0a3d11ed6a 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -651,6 +651,13 @@ register_on_gizmo_rotate_callback(canvas, callback) CODE: _3DScene::register_on_gizmo_rotate_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); +void +register_on_gizmo_rotate_3D_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_gizmo_rotate_3D_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + void register_on_gizmo_flatten_callback(canvas, callback) SV *canvas; @@ -658,6 +665,13 @@ register_on_gizmo_flatten_callback(canvas, callback) CODE: _3DScene::register_on_gizmo_flatten_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); +void +register_on_gizmo_flatten_3D_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_gizmo_flatten_3D_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + void register_on_update_geometry_info_callback(canvas, callback) SV *canvas; diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 101faf5643..892ecd861d 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -364,8 +364,13 @@ ModelMaterial::attributes() Ref object() %code%{ RETVAL = THIS->get_object(); %}; +#if ENABLE_MODELINSTANCE_3D_ROTATION + double rotation() + %code%{ RETVAL = THIS->get_rotation(Z); %}; +#else double rotation() %code%{ RETVAL = THIS->rotation; %}; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION double scaling_factor() %code%{ RETVAL = THIS->scaling_factor; %}; #if ENABLE_MODELINSTANCE_3D_OFFSET @@ -376,8 +381,17 @@ ModelMaterial::attributes() %code%{ RETVAL = &THIS->offset; %}; #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + void set_rotation(double val) + %code%{ THIS->set_rotation(Z, val); THIS->get_object()->invalidate_bounding_box(); %}; + + void set_rotations(Vec3d *rotation) + %code%{ THIS->set_rotation(*rotation); THIS->get_object()->invalidate_bounding_box(); %}; + +#else void set_rotation(double val) %code%{ THIS->rotation = val; THIS->get_object()->invalidate_bounding_box(); %}; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION void set_scaling_factor(double val) %code%{ THIS->scaling_factor = val; THIS->get_object()->invalidate_bounding_box(); %}; #if ENABLE_MODELINSTANCE_3D_OFFSET From a079f2a34e6efacd03e14fd0ca077ce91f44e3d7 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 21 Sep 2018 10:18:56 +0200 Subject: [PATCH 36/36] Fixed import of rotations from 3mf files --- xs/src/libslic3r/Format/3mf.cpp | 5 +++-- xs/src/libslic3r/Utils.hpp | 14 ++++++++++++++ xs/src/slic3r/GUI/GUI_ObjectParts.cpp | 8 ++++---- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp index 8a9d6870df..ec864d9954 100644 --- a/xs/src/libslic3r/Format/3mf.cpp +++ b/xs/src/libslic3r/Format/3mf.cpp @@ -1278,13 +1278,14 @@ namespace Slic3r { double inv_sy = 1.0 / sy; double inv_sz = 1.0 / sz; - Eigen::Matrix3d m3x3; + Eigen::Matrix m3x3; m3x3 << transform(0, 0) * inv_sx, transform(0, 1) * inv_sy, transform(0, 2) * inv_sz, transform(1, 0) * inv_sx, transform(1, 1) * inv_sy, transform(1, 2) * inv_sz, transform(2, 0) * inv_sx, transform(2, 1) * inv_sy, transform(2, 2) * inv_sz; #if ENABLE_MODELINSTANCE_3D_ROTATION - Vec3d rotation = m3x3.eulerAngles(0, 1, 2); + Vec3d angles = m3x3.eulerAngles(2, 1, 0); + Vec3d rotation(angles(2), angles(1), angles(0)); #else Eigen::AngleAxisd rotation; rotation.fromRotationMatrix(m3x3); diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index c90ad76508..313f93094f 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -74,6 +74,20 @@ inline std::string header_slic3r_generated() { return std::string("generated by // getpid platform wrapper extern unsigned get_current_pid(); +template +Real round_nearest(Real value, unsigned int decimals) +{ + Real res = (Real)0; + if (decimals == 0) + res = ::round(value); + else + { + Real power = ::pow((Real)10, (int)decimals); + res = ::round(value * power + (Real)0.5) / power; + } + return res; +} + // Compute the next highest power of 2 of 32-bit v // http://graphics.stanford.edu/~seander/bithacks.html template diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp index b336a34a95..ed3b69eab1 100644 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp +++ b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp @@ -1837,16 +1837,16 @@ void update_rotation_value(double angle, Axis axis) } } - og->set_value(axis_str, int(Geometry::rad2deg(angle))); + og->set_value(axis_str, round_nearest(int(Geometry::rad2deg(angle)), 0)); } #if ENABLE_MODELINSTANCE_3D_ROTATION void update_rotation_value(const Vec3d& rotation) { auto og = get_optgroup(ogFrequentlyObjectSettings); - og->set_value("rotation_x", int(Geometry::rad2deg(rotation(0)))); - og->set_value("rotation_y", int(Geometry::rad2deg(rotation(1)))); - og->set_value("rotation_z", int(Geometry::rad2deg(rotation(2)))); + og->set_value("rotation_x", int(round_nearest(Geometry::rad2deg(rotation(0)), 0))); + og->set_value("rotation_y", int(round_nearest(Geometry::rad2deg(rotation(1)), 0))); + og->set_value("rotation_z", int(round_nearest(Geometry::rad2deg(rotation(2)), 0))); } #endif // ENABLE_MODELINSTANCE_3D_ROTATION