diff --git a/Build.PL b/Build.PL
index 16e37986a7..4c7fa412af 100644
--- a/Build.PL
+++ b/Build.PL
@@ -21,9 +21,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/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/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/lib/Slic3r.pm b/lib/Slic3r.pm
index 4a178682c7..44100db8cf 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.
@@ -61,22 +49,15 @@ 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;
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,129 +66,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::GCodePreviewData::DESTROY = sub {};
- *Slic3r::GUI::PresetBundle::DESTROY = sub {};
- *Slic3r::GUI::Tab::DESTROY = sub {};
- *Slic3r::GUI::PresetHints::DESTROY = sub {};
- *Slic3r::GUI::TabIface::DESTROY = sub {};
- *Slic3r::GUI::ProgressStatusBar::DESTROY= sub {};
- *Slic3r::OctoPrint::DESTROY = sub {};
- *Slic3r::Duet::DESTROY = sub {};
- *Slic3r::PresetUpdater::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) = @_;
@@ -282,9 +140,4 @@ sub system_info
return $out;
}
-# this package declaration prevents an ugly fatal warning to be emitted when
-# spawning a new thread
-package GLUquadricObjPtr;
-package Wx::Printout;
-
1;
diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm
index 7510d22afb..31f614ba92 100644
--- a/lib/Slic3r/GUI.pm
+++ b/lib/Slic3r/GUI.pm
@@ -6,25 +6,10 @@ 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';
@@ -43,13 +28,11 @@ 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)};
# 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;
@@ -119,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,
@@ -186,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,
@@ -226,16 +205,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 9209299abb..c1975cd5d3 100644
--- a/lib/Slic3r/GUI/MainFrame.pm
+++ b/lib/Slic3r/GUI/MainFrame.pm
@@ -26,13 +26,17 @@ our $appController;
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 inform about a change of object selection
+# 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;
+# 5) To inform about a change of object selection
our $OBJECT_SELECTION_CHANGED_EVENT = Wx::NewEventType;
-# 4) To inform about a change of object settings
+# 6) To inform about a change of object settings
our $OBJECT_SETTINGS_CHANGED_EVENT = Wx::NewEventType;
-# 5) To inform about a remove of object
+# 7) To inform about a remove of object
our $OBJECT_REMOVE_EVENT = Wx::NewEventType;
-# 6) To inform about a update of the scene
+# 8) To inform about a update of the scene
our $UPDATE_SCENE_EVENT = Wx::NewEventType;
sub new {
@@ -53,13 +57,11 @@ 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};
$self->{preferences_event} = $params{preferences_event};
-
+
# initialize tabpanel and menubar
$self->_init_tabpanel;
$self->_init_menubar;
@@ -74,8 +76,8 @@ 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++
-#FIXME Vojtech: Merging error
-# $appController->set_global_progress_indicator($self->{statusbar});
+ Slic3r::GUI::set_progress_status_bar($self->{statusbar});
+ $appController->set_global_progress_indicator($self->{statusbar});
$appController->set_model($self->{plater}->{model});
$appController->set_print($self->{plater}->{print});
@@ -110,6 +112,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();
@@ -146,9 +149,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.
@@ -197,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,12 +237,29 @@ 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");
}
+
+ # 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 {
@@ -287,31 +302,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'),
@@ -465,135 +457,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,
- status_cb => sub {
- my ($percent, $message) = @_;
- $progress_dialog->Update($percent, "$message…");
- },
- );
-
- # 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;
- }
- $sprint->status_cb(undef);
- Slic3r::GUI::warning_catcher($self)->($_) for @warnings;
- }
- $progress_dialog->Destroy;
- undef $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 });
-}
-
sub reslice_now {
my ($self) = @_;
$self->{plater}->reslice if $self->{plater};
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 ac3e445d12..525cc79318 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
@@ -32,19 +31,12 @@ use constant TB_SPLIT => &Wx::NewId;
use constant TB_CUT => &Wx::NewId;
use constant TB_SETTINGS => &Wx::NewId;
use constant TB_LAYER_EDITING => &Wx::NewId;
-use constant TB_SLA_SUPPORTS => &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;
our $appController;
@@ -52,6 +44,7 @@ our $appController;
sub new {
my ($class, $parent, %params) = @_;
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 host_type print_host printhost_apikey printhost_cafile
@@ -72,14 +65,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, [-1,335], wxNB_BOTTOM);
@@ -94,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) = @_;
@@ -160,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) = @_;
@@ -258,7 +300,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') });
@@ -271,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);
@@ -306,43 +350,42 @@ 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 {
+ $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 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});
- 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->{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}); });
+# $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;
}
- # 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})) {
+ 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 ($preview == $self->{preview3D}) {
- $self->{preview3D}->reload_print;
+ } 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)
- Slic3r::GUI::_3DScene::set_as_dirty($self->{preview3D}->canvas);
+ $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})) {
my $selections = $self->collect_selections;
@@ -485,22 +528,13 @@ sub new {
$_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self))
for grep defined($_),
- $self, $self->{canvas3D}, $self->{preview3D}, $self->{list};
+ $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, $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 {
@@ -522,9 +556,10 @@ 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;
{
@@ -1034,16 +1069,15 @@ sub bed_centerf {
}
sub remove {
- my $self = shift;
- my ($obj_idx) = @_;
+ my ($self, $obj_idx) = @_;
$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->{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 no object index is supplied, remove the selected one.
if (! defined $obj_idx) {
($obj_idx, undef) = $self->selected_object;
return if ! defined $obj_idx;
@@ -1058,17 +1092,16 @@ sub remove {
$self->select_object(undef);
$self->update;
- $self->schedule_background_process;
}
sub reset {
- my $self = shift;
+ my ($self) = @_;
$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->{preview_iface}->set_enabled(0) if $self->{preview_iface};
+# $self->{preview3D}->enabled(0) if $self->{preview3D};
@{$self->{objects}} = ();
$self->{model}->clear_objects;
@@ -1088,6 +1121,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}),
@@ -1096,7 +1130,7 @@ sub increase {
);
$self->{print}->objects->[$obj_idx]->add_copy($instance->offset);
}
- # Set conut of object on c++ side
+ # Set count of object on c++ side
Slic3r::GUI::set_object_count($obj_idx, $model_object->instances_count);
# only autoarrange if user has autocentering enabled
@@ -1117,10 +1151,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;
@@ -1129,33 +1162,27 @@ sub decrease {
Slic3r::GUI::set_object_count($obj_idx, $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;
}
$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 = -1;
$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 || $copies == -1) {
# no variation
- $self->resume_background_process;
} elsif ($diff > 0) {
$self->increase($diff);
} elsif ($diff < 0) {
@@ -1253,7 +1280,6 @@ sub rotate {
$self->selection_changed; # refresh info (size etc.)
$self->update;
- $self->schedule_background_process;
}
sub mirror {
@@ -1283,7 +1309,6 @@ sub mirror {
$self->selection_changed; # refresh info (size etc.)
$self->update;
- $self->schedule_background_process;
}
sub changescale {
@@ -1358,14 +1383,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);
@@ -1394,90 +1417,68 @@ 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.
+ # 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 Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D});
-
- # 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};
-
- # We also need to reload 3D scene because of the wipe tower preview box
- if ($self->{config}->wipe_tower) {
- my $selections = $self->collect_selections;
- Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
- Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1) if $self->{canvas3D}
+ 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->{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;
+ Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
+ Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1) if $self->{canvas3D}
+ }
}
}
}
+# 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;
@@ -1486,79 +1487,22 @@ 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};
- $self->statusbar->SetCancelCallback(undef);
- $self->statusbar->StopBusy;
- $self->statusbar->SetStatusText("");
- $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();
- }
+ $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.
sub reslice {
# explicitly cancel a previous thread and start a new one.
my ($self) = @_;
@@ -1603,6 +1547,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;
};
@@ -1644,6 +1589,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
@@ -1655,61 +1602,16 @@ 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;
- $self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
- $self->{preview3D}->reload_print if $self->{preview3D};
+# 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;
Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1);
-
- # 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.
@@ -1724,21 +1626,24 @@ sub on_progress_event {
# 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);
+
+ # 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->ResetCancelCallback();
$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;
- 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");
@@ -1746,14 +1651,13 @@ sub on_export_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;
@@ -1761,14 +1665,20 @@ sub on_export_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);
@@ -1777,8 +1687,8 @@ sub on_export_completed {
$self->object_list_changed;
# refresh preview
- $self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
- $self->{preview3D}->reload_print if $self->{preview3D};
+ $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.
@@ -1833,7 +1743,7 @@ sub print_info_box_show {
=> $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}->m_wipe_tower_number_of_toolchanges)) if ($is_wipe_tower);
+ 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;
@@ -2039,28 +1949,18 @@ sub update {
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->print_info_box_show(0);
-
+ $self->stop_background_process;
+ $self->{print}->reload_model_instances();
+ $self->{canvas}->reload_scene if $self->{canvas};
# $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);
- $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;
}
@@ -2103,7 +2003,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) = @_;
@@ -2135,7 +2035,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};
- 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') {
$update_scheduled = 1;
@@ -2170,13 +2071,15 @@ 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;
}
}
@@ -2213,14 +2116,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
@@ -2248,71 +2143,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_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->pause_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);
+#sub object_cut_dialog {
+# my ($self, $obj_idx) = @_;
+#
+# if (!defined $obj_idx) {
+# ($obj_idx, undef) = $self->selected_object;
# }
-
- # 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};
- 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;
- }
-}
+#
+# 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 changed_object_settings {
my ($self, $obj_idx, $parts_changed, $part_settings_changed) = @_;
@@ -2335,7 +2190,7 @@ sub changed_object_settings {
Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0);
} else {
- $self->resume_background_process;
+ $self->schedule_background_process;
}
}
@@ -2676,11 +2531,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')) {
- Slic3r::GUI::_3DScene::select_view($self->{preview3D}->canvas, $direction);
- Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas);
+ $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});
}
}
@@ -2710,48 +2568,6 @@ package Slic3r::GUI::Plater::Object;
use Moo;
has 'name' => (is => 'rw', required => 1);
-#has 'thumbnail' => (is => 'rw'); # ExPolygon::Collection in scaled model units with no transforms
-#has 'transformed_thumbnail' => (is => 'rw');
-#has 'instance_thumbnails' => (is => 'ro', default => sub { [] }); # array of ExPolygon::Collection objects, each one representing the actual placed thumbnail of each instance in pixel units
has 'selected' => (is => 'rw', default => sub { 0 });
-#sub make_thumbnail {
-# my ($self, $model, $obj_idx) = @_;
-# # make method idempotent
-# $self->thumbnail->clear;
-# # raw_mesh is the non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
-# my $mesh = $model->objects->[$obj_idx]->raw_mesh;
-##FIXME The "correct" variant could be extremely slow.
-## if ($mesh->facets_count <= 5000) {
-## # remove polygons with area <= 1mm
-## my $area_threshold = Slic3r::Geometry::scale 1;
-## $self->thumbnail->append(
-## grep $_->area >= $area_threshold,
-## @{ $mesh->horizontal_projection }, # horizontal_projection returns scaled expolygons
-## );
-## $self->thumbnail->simplify(0.5);
-## } else {
-# my $convex_hull = Slic3r::ExPolygon->new($mesh->convex_hull);
-# $self->thumbnail->append($convex_hull);
-## }
-# return $self->thumbnail;
-#}
-#
-#sub transform_thumbnail {
-# my ($self, $model, $obj_idx) = @_;
-#
-# return unless defined $self->thumbnail;
-#
-# my $model_object = $model->objects->[$obj_idx];
-# my $model_instance = $model_object->instances->[0];
-#
-# # the order of these transformations MUST be the same everywhere, including
-# # in Slic3r::Print->add_model_object()
-# my $t = $self->thumbnail->clone;
-# $t->rotate($model_instance->rotation, Slic3r::Point->new(0,0));
-# $t->scale($model_instance->scaling_factor);
-#
-# $self->transformed_thumbnail($t);
-#}
-
1;
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 382310f245..0000000000
--- a/lib/Slic3r/GUI/Plater/2DToolpaths.pm
+++ /dev/null
@@ -1,916 +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
-));
-
-# make OpenGL::Array thread-safe
-{
- no warnings 'redefine';
- *OpenGL::Array::CLONE_SKIP = sub { 1 };
-}
-
-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 77efbb29b4..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->model_part;
- 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 fd02a030f4..0000000000
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ /dev/null
@@ -1,637 +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;
-use constant ICON_SUPPORT_ENFORCER => 3;
-use constant ICON_SUPPORT_BLOCKER => 4;
-
-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
- $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("support_enforcer.png"), wxBITMAP_TYPE_PNG)); # ICON_SUPPORT_ENFORCER
- $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("support_blocker.png"), wxBITMAP_TYPE_PNG)); # ICON_SUPPORT_BLOCKER
-
- 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 {
- my ($key, $value) = @_;
- wxTheApp->CallAfter(sub {
- $self->set_part_type($value) if ($key eq "part_type");
- $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 :
- $volume->support_enforcer ? ICON_SUPPORT_ENFORCER :
- $volume->support_blocker ? ICON_SUPPORT_BLOCKER :
- 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);
- my $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_OBJECT;
- my $support = 0;
- 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->model_part) {
- $self->{optgroup_movers}->enable;
- if ($volume->support_enforcer || $volume->support_blocker) {
- $support = 1;
- $type = $volume->support_enforcer ?
- Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_ENFORCER :
- Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_BLOCKER;
- } else {
- $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER;
- }
- } else {
- $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_PART;
- $self->{optgroup_movers}->disable;
- }
- $config = $volume->config;
- $self->{staticbox}->SetLabel('Part Settings');
- # get default values
- @opt_keys = $support ? () : @{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
- if (! $support) {
- push @opt_keys, 'extruder';
- $default_config->set('extruder', 0);
- $config->set_ifndef('extruder', 0);
- }
- $self->{settings_panel}->set_type($type);
- $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
- my $fixed_options =
- ($itemData->{type} eq 'object') ? [qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)] :
- $support ? [] : [qw(extruder)];
- $self->{settings_panel}->set_fixed_options($fixed_options);
- $self->{settings_panel}->enable;
- }
-
- Slic3r::GUI::_3DScene::render($self->{canvas}) if $self->{canvas};
-}
-
-sub set_part_type
-{
- my ($self, $part_type) = @_;
- if (my $itemData = $self->get_selection) {
- if ($itemData->{type} eq 'volume') {
- my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ];
- if ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER ||
- $part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_PART) {
- $volume->set_modifier($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER);
- } elsif ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_ENFORCER) {
- $volume->set_support_enforcer;
- } elsif ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_BLOCKER) {
- $volume->set_support_blocker;
- }
- # We want the icon of the selected item to be changed as well.
- $self->reload_tree($itemData->{volume_id});
- }
- }
-}
-
-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}) {
- $object->center_around_origin;
- 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
- my $delta_x = $self->{model_object}->origin_translation->x - $object->origin_translation->x;
- my $delta_y = $self->{model_object}->origin_translation->y - $object->origin_translation->y;
- my $delta_z = $self->{model_object}->origin_translation->z - $object->origin_translation->z;
- $new_volume->mesh->translate($delta_x, $delta_y, $delta_z);
-
- # 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 3ccf1d7f8a..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
- Slic3r::GUI::save_window_size($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;
-
- Slic3r::GUI::restore_window_size($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 b085871f05..0000000000
--- a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
+++ /dev/null
@@ -1,214 +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 :combobox wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxTheApp);
-use Wx::Event qw(EVT_BUTTON EVT_COMBOBOX EVT_LEFT_DOWN EVT_MENU);
-use base 'Wx::ScrolledWindow';
-
-use constant ICON_MATERIAL => 0;
-use constant ICON_SOLIDMESH => 1;
-use constant ICON_MODIFIERMESH => 2;
-
-use constant TYPE_OBJECT => -1;
-use constant TYPE_PART => 0;
-use constant TYPE_MODIFIER => 1;
-use constant TYPE_SUPPORT_ENFORCER => 2;
-use constant TYPE_SUPPORT_BLOCKER => 3;
-
-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->{type} = TYPE_OBJECT;
- $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_type {
- my ($self, $type) = @_;
- $self->{type} = $type;
- if ($type == TYPE_SUPPORT_ENFORCER || $type == TYPE_SUPPORT_BLOCKER) {
- $self->{btn_add}->Hide;
- } else {
- $self->{btn_add}->Show;
- }
-}
-
-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};
-
- if ($self->{type} != TYPE_OBJECT) {
- my $label = Wx::StaticText->new($self, -1, "Type:"),
- my $selection = [ "Part", "Modifier", "Support Enforcer", "Support Blocker" ];
- my $field = Wx::ComboBox->new($self, -1, $selection->[$self->{type}], wxDefaultPosition, Wx::Size->new(160, -1), $selection, wxCB_READONLY);
- my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
- $sizer->Add($label, 1, wxEXPAND | wxALL, 5);
- $sizer->Add($field, 0, wxALL, 5);
- EVT_COMBOBOX($self, $field, sub {
- my $idx = $field->GetSelection; # get index of selected value
- $self->{on_change}->("part_type", $idx) if $self->{on_change};
- });
- $self->{options_sizer}->Add($sizer, 0, wxEXPAND | wxBOTTOM, 0);
- }
-
- my %categories = ();
- if ($self->{type} != TYPE_SUPPORT_ENFORCER && $self->{type} != TYPE_SUPPORT_BLOCKER) {
- 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/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/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
deleted file mode 100644
index 520a1fe34d..0000000000
--- a/lib/Slic3r/Print.pm
+++ /dev/null
@@ -1,300 +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';
-
-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);
- }
-
- # run post-processing scripts
- if (@{$self->config->post_process}) {
- $self->status_cb->(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->status_cb->(($oi + 1)*100/$objnum - 1, "Slicing...");
- }
-
- my $fh = $params{output_file};
- $self->status_cb->(90, "Exporting zipped archive...");
- $self->print_to_png($fh);
- $self->status_cb->(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->status_cb->(($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]);
-
-
-\n";
- close $fh;
- 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 088c0a168e..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 b171e65363..b5b749f12b 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,10 @@ 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;
-}
-
-sub export_svg {
- my ($self) = @_;
-
- $self->_before_export;
-
- $self->_print->export_svg(output_file => $self->output_file);
-
- $self->_after_export;
+ $self->_print->validate;
+ $self->_print->export_gcode($self->output_file // '');
}
sub export_png {
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/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/serial.txt b/serial.txt
new file mode 100644
index 0000000000..7c6816d430
--- /dev/null
+++ b/serial.txt
@@ -0,0 +1,642 @@
+<< start
+<< echo: 3.1.1-RC5-150z
+<< echo: Last Updated: Feb 7 2018 15:28:23 | Author: (none, default config)
+<< Compiled: Feb 7 2018
+<< echo: Free Memory: 1777 PlannerBufferBytes: 1312
+<< echo:Hardcoded Default Settings Loaded
+<< adc_init
+>> N0 M105*39
+<< CrashDetect ENABLED!
+<< tmc2130_init(), mode=NORMAL
+<< PAT9125_init:1
+<< FSensor
+<< ENABLED
+<< echo:SD card ok
+<< echo:busy: processing
+<< Error:Line Number is not Last Line Number+1, Last Line: 0
+<< Resend: 1
+<< ok
+>> N1 M107*36
+<< ok
+>> N2 M115 U3.1.1-RC5*107
+<< ok
+>> N3 M201 X1000 Y1000 Z200 E5000*10
+<< ok
+>> N4 M203 X200 Y200 Z12 E120*8
+<< ok
+>> N5 M204 S1250 T1250*39
+<< ok
+>> N6 M205 X10 Y10 Z0.4 E2.5*63
+<< ok
+>> N7 M205 S0 T0*36
+<< ok
+>> N8 M83*16
+<< ok
+>> N9 M104 S215*106
+<< ok
+>> N10 M140 S60*98
+<< ok
+>> N11 M190 S60*110
+<< T:158.08 E:0 B:57.1
+<< T:157.04 E:0 B:57.1
+<< T:156.77 E:0 B:56.9
+<< T:156.97 E:0 B:57.0
+<< T:158.14 E:0 B:57.0
+<< T:159.62 E:0 B:56.9
+<< T:161.25 E:0 B:56.8
+<< T:163.64 E:0 B:56.8
+<< T:165.94 E:0 B:56.7
+<< T:168.40 E:0 B:56.8
+<< T:170.79 E:0 B:56.7
+<< T:173.68 E:0 B:56.7
+<< T:175.53 E:0 B:56.6
+<< T:178.40 E:0 B:56.6
+<< T:180.94 E:0 B:56.5
+<< T:183.92 E:0 B:56.4
+<< T:186.73 E:0 B:56.4
+<< T:189.20 E:0 B:56.4
+<< T:191.32 E:0 B:56.3
+<< T:193.91 E:0 B:56.3
+<< T:196.38 E:0 B:56.2
+<< T:198.75 E:0 B:56.2
+<< T:201.65 E:0 B:56.3
+<< T:203.57 E:0 B:56.4
+<< T:206.38 E:0 B:56.5
+<< T:208.71 E:0 B:56.6
+<< T:211.04 E:0 B:56.6
+<< T:212.86 E:0 B:56.8
+<< T:214.84 E:0 B:57.0
+<< T:215.52 E:0 B:57.2
+<< T:215.78 E:0 B:57.4
+<< T:216.30 E:0 B:57.6
+<< T:216.51 E:0 B:57.7
+<< T:215.73 E:0 B:58.0
+<< T:215.47 E:0 B:58.2
+<< T:214.95 E:0 B:58.5
+<< T:214.22 E:0 B:58.7
+<< T:213.65 E:0 B:59.0
+<< T:212.24 E:0 B:59.2
+<< T:212.14 E:0 B:59.4
+<< T:212.03 E:0 B:59.7
+<< T:211.51 E:0 B:59.8
+<< ok
+>> N12 M105*20
+<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2
+>> N13 M105*21
+<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2
+>> N14 M105*18
+<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2
+>> N15 M105*19
+<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2
+>> N16 M105*16
+<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2
+>> N17 M105*17
+<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2
+>> N18 M105*30
+<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2
+>> N19 M105*31
+<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2
+>> N20 M105*21
+<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2
+>> N21 M109 S215*93
+<< T:211.3 E:0 W:?
+<< T:211.8 E:0 W:?
+<< T:211.8 E:0 W:?
+<< T:212.1 E:0 W:?
+<< T:212.4 E:0 W:?
+<< T:213.3 E:0 W:?
+<< T:213.3 E:0 W:?
+<< T:213.8 E:0 W:?
+<< T:214.1 E:0 W:2
+<< T:214.1 E:0 W:1
+<< T:214.2 E:0 W:0
+<< ok
+>> N22 M105*23
+<< ok T:214.3 /215.0 B:60.8 /60.0 T0:214.3 /215.0 @:20 B@:7 P:46.4 A:36.0
+>> N23 M105*22
+<< ok T:214.3 /215.0 B:60.8 /60.0 T0:214.3 /215.0 @:20 B@:7 P:46.4 A:36.0
+>> N24 G28 W*82
+<< 0 step=62 mscnt= 993
+<< tmc2130_goto_step 0 0 2 1000
+<< step 61 mscnt = 984
+<< dir=0 steps=-61
+<< dir=1 steps=61
+<< dir=0 steps=3
+<< cnt 2 step 61 mscnt = 986
+<< cnt 1 step 62 mscnt = 1005
+<< cnt 0 step 63 mscnt = 1021
+<< echo:busy: processing
+<< echo:busy: processing
+<< 0 step=34 mscnt= 547
+<< tmc2130_goto_step 1 0 2 1000
+<< step 34 mscnt = 552
+<< dir=1 steps=-34
+<< dir=0 steps=34
+<< dir=1 steps=30
+<< cnt 29 step 34 mscnt = 554
+<< cnt 28 step 35 mscnt = 572
+<< cnt 27 step 36 mscnt = 588
+<< cnt 26 step 37 mscnt = 604
+<< cnt 25 step 38 mscnt = 620
+<< cnt 24 step 39 mscnt = 637
+<< cnt 23 step 40 mscnt = 653
+<< cnt 22 step 41 mscnt = 668
+<< cnt 21 step 42 mscnt = 684
+<< cnt 20 step 43 mscnt = 701
+<< cnt 19 step 44 mscnt = 717
+<< cnt 18 step 45 mscnt = 733
+<< cnt 17 step 46 mscnt = 748
+<< cnt 16 step 47 mscnt = 765
+<< cnt 15 step 48 mscnt = 780
+<< cnt 14 step 49 mscnt = 796
+<< cnt 13 step 50 mscnt = 812
+<< cnt 12 step 51 mscnt = 828
+<< cnt 11 step 52 mscnt = 844
+<< cnt 10 step 53 mscnt = 860
+<< cnt 9 step 54 mscnt = 876
+<< cnt 8 step 55 mscnt = 893
+<< cnt 7 step 56 mscnt = 909
+<< cnt 6 step 57 mscnt = 925
+<< cnt 5 step 58 mscnt = 941
+<< cnt 4 step 59 mscnt = 956
+<< cnt 3 step 60 mscnt = 972
+<< cnt 2 step 61 mscnt = 988
+<< cnt 1 step 62 mscnt = 1005
+<< cnt 0 step 63 mscnt = 1021
+<< echo:busy: processing
+<< ok
+>> N25 M105*16
+<< ok T:213.1 /215.0 B:60.8 /60.0 T0:213.1 /215.0 @:44 B@:35 P:46.5 A:35.6
+>> N26 G80*37
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< ok
+>> N27 M105*18
+<< ok T:214.8 /215.0 B:60.9 /60.0 T0:214.8 /215.0 @:31 B@:25 P:46.3 A:35.7
+>> N28 M105*29
+<< ok T:214.8 /215.0 B:60.9 /60.0 T0:214.8 /215.0 @:31 B@:25 P:46.3 A:35.7
+>> N29 M105*28
+<< ok T:214.8 /215.0 B:60.9 /60.0 T0:214.8 /215.0 @:31 B@:25 P:46.3 A:35.7
+>> N30 M105*20
+<< ok T:214.8 /215.0 B:60.9 /60.0 T0:214.8 /215.0 @:31 B@:25 P:46.3 A:35.7
+>> N31 M105*21
+<< ok T:214.8 /215.0 B:60.9 /60.0 T0:214.8 /215.0 @:31 B@:25 P:46.3 A:35.7
+>> N32 M105*22
+<< ok T:214.8 /215.0 B:60.9 /60.0 T0:214.8 /215.0 @:31 B@:25 P:46.3 A:35.7
+>> N33 G1 Y-3.0 F1000.0*24
+<< ok
+>> N34 G92 E0.0*110
+<< ok
+>> N35 G1 X60.0 E9.0 F1000.0*101
+<< ok
+>> N36 G1 X100.0 E12.5 F1000.0*110
+<< ok
+>> N37 G92 E0.0*109
+<< ok
+>> N38 M221 S95*102
+<< ok
+>> N39 M900 K30*120
+<< Invalid M code.
+<< ok
+>> N40 G21*46
+<< ok
+>> N41 G90*37
+<< ok
+>> N42 M83*46
+<< ok
+>> N43 G1 E-0.80000 F2100.00000*10
+<< ok
+>> N44 G1 Z0.600 F10200.000*1
+<< ok
+>> N45 G1 X112.437 Y93.991 F10200.000*106
+<< ok
+>> N46 G1 Z0.200 F10200.000*7
+<< ok
+>> N47 G1 E0.80000 F2100.00000*35
+<< ok
+>> N48 M204 S1000*107
+<< ok
+>> N49 G1 F1800*122
+<< ok
+>> N50 G1 X112.930 Y93.183 E0.02968*106
+<< ok
+>> N51 G1 X113.335 Y92.806 E0.01733*99
+<< ok
+>> N52 G1 X113.810 Y92.516 E0.01745*97
+<< ok
+>> N53 G1 X114.334 Y92.328 E0.01745*97
+<< ok
+>> N54 G1 X114.885 Y92.248 E0.01745*96
+<< ok
+>> N55 M105*23
+<< ok T:214.9 /215.0 B:60.8 /60.0 T0:214.9 /215.0 @:30 B@:28 P:46.2 A:35.7
+>> N56 G1 X135.004 Y92.246 E0.63084*96
+<< ok
+>> N57 G1 X136.005 Y92.436 E0.03195*101
+<< ok
+>> N58 G1 X136.866 Y92.974 E0.03183*107
+<< ok
+>> N59 G1 X137.473 Y93.788 E0.03183*111
+<< ok
+>> N60 G1 X137.745 Y94.770 E0.03195*100
+<< ok
+>> N61 G1 X137.753 Y115.086 E0.63700*88
+<< ok
+>> N62 G1 X137.563 Y116.009 E0.02955*87
+<< ok
+>> N63 G1 X137.070 Y116.817 E0.02968*88
+<< ok
+>> N64 G1 X136.646 Y117.208 E0.01809*93
+<< ok
+>> N65 G1 X136.149 Y117.503 E0.01809*88
+<< ok
+>> N66 G1 X135.603 Y117.687 E0.01809*94
+<< ok
+>> N67 G1 X135.029 Y117.754 E0.01809*94
+<< ok
+>> N68 G1 X114.914 Y117.753 E0.63071*81
+<< ok
+>> N69 G1 X113.991 Y117.563 E0.02955*83
+<< ok
+>> N70 G1 X113.183 Y117.070 E0.02968*89
+<< ok
+>> N71 G1 X112.792 Y116.646 E0.01809*88
+<< ok
+>> N72 M105*18
+<< ok T:214.0 /215.0 B:60.8 /60.0 T0:214.0 /215.0 @:45 B@:16 P:46.2 A:35.5
+>> N73 G1 X112.497 Y116.149 E0.01809*84
+<< ok
+>> N74 G1 X112.313 Y115.603 E0.01809*82
+<< ok
+>> N75 G1 X112.246 Y115.029 E0.01809*92
+<< ok
+>> N76 G1 X112.247 Y94.914 E0.63071*98
+<< ok
+>> N77 G1 X112.425 Y94.050 E0.02767*111
+<< ok
+>> N78 G1 F8160*126
+<< ok
+>> N79 G1 X112.930 Y93.183 E-0.24526*78
+<< ok
+>> N80 G1 F8160*121
+<< ok
+>> N81 G1 X113.335 Y92.806 E-0.13510*67
+<< ok
+>> N82 G1 F8160*123
+<< ok
+>> N83 G1 X113.810 Y92.516 E-0.13609*74
+<< ok
+>> N84 G1 F8160*125
+<< ok
+>> N85 G1 X114.334 Y92.328 E-0.13609*77
+<< ok
+>> N86 G1 F8160*127
+<< ok
+>> N87 G1 X114.769 Y92.265 E-0.10746*66
+<< ok
+>> N88 G1 E-0.04000 F2100.00000*1
+<< ok
+>> N89 G1 Z0.800 F10200.000*14
+<< ok
+>> N90 G1 X113.989 Y92.849 F10200.000*110
+<< ok
+>> N91 G1 Z0.200 F10200.000*13
+<< ok
+>> N92 G1 E0.80000 F2100.00000*43
+<< ok
+>> N93 G1 F1800*125
+<< ok
+>> N94 G1 X114.911 Y92.625 E0.02977*99
+<< ok
+>> N95 G1 X135.004 Y92.623 E0.62999*108
+<< ok
+>> N96 G1 X135.871 Y92.788 E0.02767*108
+<< ok
+>> N97 G1 X136.617 Y93.258 E0.02767*105
+<< ok
+>> N98 G1 X137.141 Y93.968 E0.02767*107
+<< ok
+>> N99 G1 X137.371 Y94.824 E0.02778*107
+<< ok
+>> N100 G1 X137.376 Y115.065 E0.63464*97
+<< ok
+>> N101 G1 X137.209 Y115.878 E0.02602*104
+<< ok
+>> N102 G1 X136.773 Y116.584 E0.02602*111
+<< ok
+>> N103 G1 X136.407 Y116.916 E0.01550*110
+<< ok
+>> N104 G1 X135.980 Y117.166 E0.01550*102
+<< ok
+>> N105 G1 X135.511 Y117.321 E0.01550*98
+<< ok
+>> N106 G1 X135.020 Y117.377 E0.01550*101
+<< ok
+>> N107 G1 X114.935 Y117.376 E0.62975*101
+<< ok
+>> N108 G1 X114.122 Y117.209 E0.02602*100
+<< ok
+>> N109 G1 X113.416 Y116.773 E0.02602*105
+<< ok
+>> N110 G1 X113.084 Y116.407 E0.01550*105
+<< ok
+>> N111 G1 X112.834 Y115.980 E0.01550*107
+<< ok
+>> N112 G1 X112.679 Y115.511 E0.01550*107
+<< ok
+>> N113 G1 X112.623 Y115.020 E0.01550*98
+<< ok
+>> N114 G1 X112.624 Y94.935 E0.62975*89
+<< ok
+>> N115 G1 X112.791 Y94.122 E0.02602*80
+<< ok
+>> N116 G1 X113.227 Y93.416 E0.02602*95
+<< ok
+>> N117 G1 X113.940 Y92.885 E0.02789*81
+<< ok
+>> N118 G1 F8160*73
+<< ok
+>> N119 G1 X114.911 Y92.625 E-0.24574*113
+<< ok
+>> N120 G1 F8160*66
+<< ok
+>> N121 G1 X117.015 Y92.624 E-0.51426*113
+<< ok
+>> N122 G1 E-0.04000 F2100.00000*48
+<< ok
+>> N123 G1 Z0.800 F10200.000*63
+<< ok
+>> N124 G1 X115.587 Y95.587 F10200.000*92
+<< ok
+>> N125 M105*33
+<< ok T:214.2 /215.0 B:60.7 /60.0 T0:214.2 /215.0 @:41 B@:24 P:46.2 A:36.0
+>> N126 G1 Z0.200 F10200.000*48
+<< ok
+>> N127 G1 E0.80000 F2100.00000*20
+<< ok
+>> N128 G1 F1800*76
+<< ok
+>> N129 G1 X134.413 Y95.587 E0.59027*87
+<< ok
+>> N130 G1 X134.413 Y114.413 E0.59027*107
+<< ok
+>> N131 G1 X115.587 Y114.413 E0.59027*101
+<< ok
+>> N132 G1 X115.587 Y95.647 E0.58839*91
+<< ok
+>> N133 G1 X115.210 Y95.210 F10200.000*90
+<< ok
+>> N134 G1 F1800*65
+<< ok
+>> N135 G1 X134.790 Y95.210 E0.61392*93
+<< ok
+>> N136 G1 X134.790 Y114.790 E0.61392*107
+<< ok
+>> N137 G1 X115.210 Y114.790 E0.61392*100
+<< ok
+>> N138 G1 X115.210 Y95.270 E0.61204*86
+<< ok
+>> N139 G1 X115.596 Y95.314 F10200.000*92
+<< ok
+>> N140 G1 F8160*68
+<< ok
+>> N141 G1 X118.319 Y95.260 E-0.76000*113
+<< ok
+>> N142 G1 E-0.04000 F2100.00000*54
+<< ok
+>> N143 G1 Z0.800 F10200.000*57
+<< ok
+>> N144 G1 X115.700 Y113.527 F10200.000*98
+<< ok
+>> N145 G1 Z0.200 F10200.000*53
+<< ok
+>> N146 G1 E0.80000 F2100.00000*19
+<< ok
+>> N147 G1 F1800*69
+<< ok
+>> N148 G1 X116.303 Y114.130 E0.02708*98
+<< ok
+>> N149 G1 X116.843 Y114.130 E0.01716*96
+<< ok
+>> N150 G1 X115.870 Y113.157 E0.04372*110
+<< ok
+>> N151 G1 X115.870 Y112.617 E0.01716*110
+<< ok
+>> N152 G1 X117.383 Y114.130 E0.06799*108
+<< ok
+>> N153 G1 X117.924 Y114.130 E0.01716*106
+<< ok
+>> N154 M105*39
+<< ok T:215.1 /215.0 B:60.7 /60.0 T0:215.1 /215.0 @:29 B@:12 P:46.2 A:35.7
+>> N155 G1 X115.870 Y112.076 E0.09225*102
+<< ok
+>> N156 G1 X115.870 Y111.536 E0.01716*106
+<< ok
+>> N157 G1 X118.464 Y114.130 E0.11652*104
+<< ok
+>> N158 G1 X119.004 Y114.130 E0.01716*100
+<< ok
+>> N159 G1 X115.870 Y110.996 E0.14079*104
+<< ok
+>> N160 G1 X115.870 Y110.456 E0.01716*105
+<< ok
+>> N161 G1 X119.544 Y114.130 E0.16505*105
+<< ok
+>> N162 G1 X120.085 Y114.130 E0.01716*110
+<< ok
+>> N163 G1 X115.870 Y109.915 E0.18932*104
+<< ok
+>> N164 G1 X115.870 Y109.375 E0.01716*99
+<< ok
+>> N165 G1 X120.625 Y114.130 E0.21358*105
+<< ok
+>> N166 G1 X121.165 Y114.130 E0.01716*100
+<< ok
+>> N167 G1 X115.870 Y108.835 E0.23785*100
+<< ok
+>> N168 G1 X115.870 Y108.295 E0.01716*97
+<< ok
+>> N169 G1 X121.705 Y114.130 E0.26212*111
+<< ok
+>> N170 G1 X122.245 Y114.130 E0.01716*97
+<< ok
+>> N171 G1 X115.870 Y107.755 E0.28638*105
+<< ok
+>> N172 G1 X115.870 Y107.214 E0.01716*108
+<< ok
+>> N173 G1 X122.786 Y114.130 E0.31065*104
+<< ok
+>> N174 G1 X123.326 Y114.130 E0.01716*96
+<< ok
+>> N175 G1 X115.870 Y106.674 E0.33491*101
+<< ok
+>> N176 G1 X115.870 Y106.134 E0.01716*104
+<< ok
+>> N177 G1 X123.866 Y114.130 E0.35918*107
+<< ok
+>> N178 G1 X124.406 Y114.130 E0.01716*110
+<< ok
+>> N179 G1 X115.870 Y105.594 E0.38344*99
+<< ok
+>> N180 G1 X115.870 Y105.054 E0.01716*101
+<< ok
+>> N181 G1 X124.946 Y114.130 E0.40771*101
+<< ok
+>> N182 G1 X125.487 Y114.130 E0.01716*99
+<< ok
+>> N183 G1 X115.870 Y104.513 E0.43198*103
+<< ok
+>> N184 G1 X115.870 Y103.973 E0.01716*107
+<< ok
+>> N185 G1 X126.027 Y114.130 E0.45624*105
+<< ok
+>> N186 G1 X126.567 Y114.130 E0.01716*107
+<< ok
+>> N187 G1 X115.870 Y103.433 E0.48051*104
+<< ok
+>> N188 G1 X115.870 Y102.893 E0.01716*105
+<< ok
+>> N189 G1 X127.107 Y114.130 E0.50477*103
+<< ok
+>> N190 M105*47
+<< ok T:215.3 /215.0 B:60.7 /60.0 T0:215.3 /215.0 @:28 B@:18 P:46.3 A:35.5
+>> N191 G1 X127.648 Y114.130 E0.01716*98
+<< ok
+>> N192 G1 X115.870 Y102.352 E0.52904*111
+<< ok
+>> N193 G1 X115.870 Y101.812 E0.01716*105
+<< ok
+>> N194 G1 X128.188 Y114.130 E0.55330*98
+<< ok
+>> N195 G1 X128.728 Y114.130 E0.01716*110
+<< ok
+>> N196 G1 X115.870 Y101.272 E0.57757*102
+<< ok
+>> N197 G1 X115.870 Y100.732 E0.01716*97
+<< ok
+>> N198 G1 X129.268 Y114.130 E0.60184*105
+<< ok
+>> N199 G1 X129.808 Y114.130 E0.01716*110
+<< ok
+>> N200 G1 X115.870 Y100.192 E0.62610*98
+<< ok
+>> N201 G1 X115.870 Y99.651 E0.01716*88
+<< ok
+>> N202 G1 X130.349 Y114.130 E0.65037*111
+<< ok
+>> N203 G1 X130.889 Y114.130 E0.01716*111
+<< ok
+>> N204 G1 X115.870 Y99.111 E0.67463*95
+<< ok
+>> N205 G1 X115.870 Y98.571 E0.01716*92
+<< ok
+>> N206 G1 X131.429 Y114.130 E0.69890*98
+<< ok
+>> N207 G1 X131.969 Y114.130 E0.01716*101
+<< ok
+>> N208 M105*45
+<< ok T:214.7 /215.0 B:60.6 /60.0 T0:214.7 /215.0 @:38 B@:12 P:46.2 A:35.9
+>> N209 G1 X115.870 Y98.031 E0.72316*81
+<< ok
+>> N210 G1 X115.870 Y97.491 E0.01716*88
+<< ok
+>> N211 G1 X132.509 Y114.130 E0.74743*105
+<< ok
+>> N212 G1 X133.050 Y114.130 E0.01716*96
+<< ok
+>> N213 M105*39
+<< ok T:215.1 /215.0 B:60.4 /60.0 T0:215.1 /215.0 @:32 B@:39 P:46.3 A:35.8
+>> N214 M105*32
+<< ok T:214.6 /215.0 B:60.5 /60.0 T0:214.6 /215.0 @:39 B@:14 P:46.3 A:36.3
+>> N215 G28*21
+<< echo:busy: processing
+<< 0 step=61 mscnt= 989
+<< tmc2130_goto_step 0 0 2 1000
+<< step 61 mscnt = 984
+<< dir=0 steps=-61
+<< dir=1 steps=61
+<< dir=0 steps=3
+<< cnt 2 step 61 mscnt = 986
+<< cnt 1 step 62 mscnt = 1004
+<< cnt 0 step 63 mscnt = 1021
+<< echo:busy: processing
+<< echo:busy: processing
+<< 0 step=34 mscnt= 547
+<< tmc2130_goto_step 1 0 2 1000
+<< step 34 mscnt = 552
+<< dir=1 steps=-34
+<< dir=0 steps=34
+<< dir=1 steps=30
+<< cnt 29 step 34 mscnt = 554
+<< cnt 28 step 35 mscnt = 573
+<< cnt 27 step 36 mscnt = 589
+<< cnt 26 step 37 mscnt = 604
+<< cnt 25 step 38 mscnt = 621
+<< cnt 24 step 39 mscnt = 636
+<< cnt 23 step 40 mscnt = 652
+<< cnt 22 step 41 mscnt = 668
+<< cnt 21 step 42 mscnt = 684
+<< cnt 20 step 43 mscnt = 701
+<< cnt 19 step 44 mscnt = 717
+<< cnt 18 step 45 mscnt = 733
+<< cnt 17 step 46 mscnt = 749
+<< cnt 16 step 47 mscnt = 765
+<< cnt 15 step 48 mscnt = 781
+<< cnt 14 step 49 mscnt = 796
+<< cnt 13 step 50 mscnt = 812
+<< cnt 12 step 51 mscnt = 829
+<< cnt 11 step 52 mscnt = 844
+<< cnt 10 step 53 mscnt = 860
+<< cnt 9 step 54 mscnt = 876
+<< cnt 8 step 55 mscnt = 893
+<< cnt 7 step 56 mscnt = 909
+<< cnt 6 step 57 mscnt = 925
+<< cnt 5 step 58 mscnt = 941
+<< cnt 4 step 59 mscnt = 957
+<< cnt 3 step 60 mscnt = 973
+<< cnt 2 step 61 mscnt = 989
+<< cnt 1 step 62 mscnt = 1005
+<< cnt 0 step 63 mscnt = 1021
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< echo:busy: processing
+<< ok
+>> N216 M105*34
+<< ok T:214.8 /215.0 B:60.2 /60.0 T0:214.8 /215.0 @:33 B@:27 P:46.0 A:36.1
+>> N217 M105*35
+<< ok T:214.8 /215.0 B:60.2 /60.0 T0:214.8 /215.0 @:33 B@:27 P:46.0 A:36.1
+>> N218 M105*44
+<< ok T:214.9 /215.0 B:60.1 /60.0 T0:214.9 /215.0 @:31 B@:33 P:45.9 A:36.1
+>> N219 M105*45
+<< ok T:214.9 /215.0 B:60.1 /60.0 T0:214.9 /215.0 @:31 B@:33 P:45.9 A:36.1
+>> N220 M105*39
+<< ok T:214.9 /215.0 B:60.1 /60.0 T0:214.9 /215.0 @:31 B@:33 P:45.9 A:36.1
+>> N221 M105*38
+<< ok T:214.9 /215.0 B:60.1 /60.0 T0:214.9 /215.0 @:31 B@:33 P:45.9 A:36.1
+>> N222 M105*37
+<< ok T:214.9 /215.0 B:60.1 /60.0 T0:214.9 /215.0 @:31 B@:33 P:45.9 A:36.1
+>> N223 M105*36
+<< ok T:214.5 /215.0 B:60.1 /60.0 T0:214.5 /215.0 @:38 B@:31 P:46.0 A:36.3
+DISCONNECTED
diff --git a/slic3r.pl b/slic3r.pl
index 0ddd9662fa..95427443b3 100755
--- a/slic3r.pl
+++ b/slic3r.pl
@@ -38,11 +38,9 @@ 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},
- 'export-svg' => \$opt{export_svg},
'export-png' => \$opt{export_png},
'merge|m' => \$opt{merge},
'repair' => \$opt{repair},
@@ -108,10 +106,10 @@ 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};
}
+ Slic3r::GUI::set_gui_appctl();
$gui = Slic3r::GUI->new;
#setlocale(LC_NUMERIC, 'C');
$gui->{mainframe}->load_config_file($_) for @{$opt{load}};
@@ -124,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}) {
@@ -203,10 +204,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},
);
@@ -216,10 +213,11 @@ 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}) {
- $sprint->export_png;
+ if ($opt{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,
@@ -284,7 +282,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/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/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__
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/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/CMakeLists.txt b/xs/CMakeLists.txt
index 83bedd7543..7d4c324f4e 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -2,16 +2,12 @@
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/)
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()
@@ -20,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)
@@ -105,6 +107,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
@@ -192,6 +196,8 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/GUI/AboutDialog.hpp
${LIBDIR}/slic3r/GUI/AppConfig.cpp
${LIBDIR}/slic3r/GUI/AppConfig.hpp
+ ${LIBDIR}/slic3r/GUI/BackgroundSlicingProcess.cpp
+ ${LIBDIR}/slic3r/GUI/BackgroundSlicingProcess.hpp
${LIBDIR}/slic3r/GUI/BitmapCache.cpp
${LIBDIR}/slic3r/GUI/BitmapCache.hpp
${LIBDIR}/slic3r/GUI/ConfigSnapshotDialog.cpp
@@ -222,6 +228,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
@@ -262,6 +272,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
@@ -284,7 +295,6 @@ 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
@@ -459,9 +469,11 @@ 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
+ ${XSP_DIR}/GUI_Preview.xsp
${XSP_DIR}/Layer.xsp
${XSP_DIR}/Line.xsp
${XSP_DIR}/Model.xsp
@@ -513,6 +525,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
@@ -600,12 +614,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/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm
index 391b06dac6..3073d068ee 100644
--- a/xs/lib/Slic3r/XS.pm
+++ b/xs/lib/Slic3r/XS.pm
@@ -231,23 +231,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/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/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp
index d5777349db..cce3020f88 100644
--- a/xs/src/libslic3r/ExtrusionEntity.hpp
+++ b/xs/src/libslic3r/ExtrusionEntity.hpp
@@ -120,8 +120,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/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/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/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 e92674a171..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.
@@ -111,23 +111,23 @@ 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),
// bridge_flow_ratio
0.f);
}
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)),
// bridge_flow_ratio
0.f);
}
@@ -137,10 +137,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),
// bridge_flow_ratio
0.f);
}
diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp
index 43c99f19fd..ec864d9954 100644
--- a/xs/src/libslic3r/Format/3mf.cpp
+++ b/xs/src/libslic3r/Format/3mf.cpp
@@ -1278,11 +1278,15 @@ 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 angles = m3x3.eulerAngles(2, 1, 0);
+ Vec3d rotation(angles(2), angles(1), angles(0));
+#else
Eigen::AngleAxisd rotation;
rotation.fromRotationMatrix(m3x3);
@@ -1291,6 +1295,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 +1304,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 803d9ee54f..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;
}
}
@@ -800,7 +849,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);
@@ -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/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index eb80a588c5..d10705c183 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
#include
#include
@@ -337,15 +338,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;
@@ -417,6 +418,12 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
{
PROFILE_CLEAR();
+ // 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);
+
BOOST_LOG_TRIVIAL(info) << "Exporting G-code...";
// Remove the old g-code if it exists.
@@ -429,27 +436,34 @@ 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 {
+ 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 (print->config.remaining_times.value)
- {
+ if (print->config().remaining_times.value) {
m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
if (m_silent_time_estimator_enabled)
m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
}
- 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";
@@ -459,12 +473,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();
@@ -477,66 +492,66 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// resets time estimators
m_normal_time_estimator.reset();
- m_normal_time_estimator.set_dialect(print.config.gcode_flavor);
- m_silent_time_estimator_enabled = (print.config.gcode_flavor == gcfMarlin) && print.config.silent_mode;
+ m_normal_time_estimator.set_dialect(print.config().gcode_flavor);
+ m_silent_time_estimator_enabled = (print.config().gcode_flavor == gcfMarlin) && print.config().silent_mode;
// Until we have a UI support for the other firmwares than the Marlin, use the hardcoded default values
// and let the user to enter the G-code limits into the start G-code.
// If the following block is enabled for other firmwares than the Marlin, then the function
// this->print_machine_envelope(file, print);
// shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor.
- if (print.config.gcode_flavor.value == gcfMarlin) {
- m_normal_time_estimator.set_max_acceleration(print.config.machine_max_acceleration_extruding.values[0]);
- m_normal_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[0]);
- m_normal_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[0]);
- m_normal_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[0]);
- m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[0]);
- m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[0]);
- m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[0]);
- m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[0]);
- m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[0]);
- m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[0]);
- m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[0]);
- m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[0]);
- m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[0]);
- m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[0]);
- m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[0]);
- m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[0]);
+ if (print.config().gcode_flavor.value == gcfMarlin) {
+ m_normal_time_estimator.set_max_acceleration(print.config().machine_max_acceleration_extruding.values[0]);
+ m_normal_time_estimator.set_retract_acceleration(print.config().machine_max_acceleration_retracting.values[0]);
+ m_normal_time_estimator.set_minimum_feedrate(print.config().machine_min_extruding_rate.values[0]);
+ m_normal_time_estimator.set_minimum_travel_feedrate(print.config().machine_min_travel_rate.values[0]);
+ m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config().machine_max_acceleration_x.values[0]);
+ m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config().machine_max_acceleration_y.values[0]);
+ m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config().machine_max_acceleration_z.values[0]);
+ m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config().machine_max_acceleration_e.values[0]);
+ m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config().machine_max_feedrate_x.values[0]);
+ m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config().machine_max_feedrate_y.values[0]);
+ m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config().machine_max_feedrate_z.values[0]);
+ m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config().machine_max_feedrate_e.values[0]);
+ m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config().machine_max_jerk_x.values[0]);
+ m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config().machine_max_jerk_y.values[0]);
+ m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config().machine_max_jerk_z.values[0]);
+ m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config().machine_max_jerk_e.values[0]);
if (m_silent_time_estimator_enabled)
{
m_silent_time_estimator.reset();
- m_silent_time_estimator.set_dialect(print.config.gcode_flavor);
- m_silent_time_estimator.set_max_acceleration(print.config.machine_max_acceleration_extruding.values[1]);
- m_silent_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[1]);
- m_silent_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[1]);
- m_silent_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[1]);
- m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[1]);
- m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[1]);
- m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[1]);
- m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[1]);
- m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[1]);
- m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[1]);
- m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[1]);
- m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[1]);
- m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[1]);
- m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[1]);
- m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[1]);
- m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[1]);
- if (print.config.single_extruder_multi_material) {
+ m_silent_time_estimator.set_dialect(print.config().gcode_flavor);
+ m_silent_time_estimator.set_max_acceleration(print.config().machine_max_acceleration_extruding.values[1]);
+ m_silent_time_estimator.set_retract_acceleration(print.config().machine_max_acceleration_retracting.values[1]);
+ m_silent_time_estimator.set_minimum_feedrate(print.config().machine_min_extruding_rate.values[1]);
+ m_silent_time_estimator.set_minimum_travel_feedrate(print.config().machine_min_travel_rate.values[1]);
+ m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config().machine_max_acceleration_x.values[1]);
+ m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config().machine_max_acceleration_y.values[1]);
+ m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config().machine_max_acceleration_z.values[1]);
+ m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config().machine_max_acceleration_e.values[1]);
+ m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config().machine_max_feedrate_x.values[1]);
+ m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config().machine_max_feedrate_y.values[1]);
+ m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config().machine_max_feedrate_z.values[1]);
+ m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config().machine_max_feedrate_e.values[1]);
+ m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config().machine_max_jerk_x.values[1]);
+ m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config().machine_max_jerk_y.values[1]);
+ m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config().machine_max_jerk_z.values[1]);
+ m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config().machine_max_jerk_e.values[1]);
+ if (print.config().single_extruder_multi_material) {
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
// are considered to be active for the single extruder multi-material printers only.
- m_silent_time_estimator.set_filament_load_times(print.config.filament_load_time.values);
- m_silent_time_estimator.set_filament_unload_times(print.config.filament_unload_time.values);
+ m_silent_time_estimator.set_filament_load_times(print.config().filament_load_time.values);
+ m_silent_time_estimator.set_filament_unload_times(print.config().filament_unload_time.values);
}
}
}
// Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful.
- if (print.config.single_extruder_multi_material) {
+ if (print.config().single_extruder_multi_material) {
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
// are considered to be active for the single extruder multi-material printers only.
- m_normal_time_estimator.set_filament_load_times(print.config.filament_load_time.values);
- m_normal_time_estimator.set_filament_unload_times(print.config.filament_unload_time.values);
+ m_normal_time_estimator.set_filament_load_times(print.config().filament_load_time.values);
+ m_normal_time_estimator.set_filament_unload_times(print.config().filament_unload_time.values);
}
// resets analyzer
@@ -552,14 +567,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// change_layer() in turn increments the progress bar status.
m_layer_count = 0;
PrintObjectPtrs printable_objects = print.get_printable_objects();
- if (print.config.complete_objects.value) {
+ if (print.config().complete_objects.value) {
// Add each of the object's layers separately.
for (auto object : printable_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()));
@@ -568,18 +583,19 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Print all objects with the same print_z together.
std::vector zs;
for (auto object : printable_objects) {
- 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.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)(std::unique(zs.begin(), zs.end()) - zs.begin());
}
+ 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.
@@ -587,27 +603,28 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// get the minimum cross-section used in the print
std::vector mm3_per_mm;
for (auto object : printable_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 (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();
// 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()) {
@@ -616,19 +633,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.
@@ -636,7 +654,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')
@@ -646,12 +664,14 @@ 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 = printable_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 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);
@@ -659,13 +679,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();
// adds tags for time estimators
- if (print.config.remaining_times.value)
+ if (print.config().remaining_times.value)
{
_writeln(file, GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag);
if (m_silent_time_estimator_enabled)
@@ -673,7 +694,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
}
// 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.
@@ -683,7 +704,7 @@ 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 < printable_objects.size(); ++initial_print_object_id) {
tool_ordering = ToolOrdering(*printable_objects[initial_print_object_id], initial_extruder_id);
@@ -693,11 +714,11 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
} 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;
has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
- initial_extruder_id = (has_wipe_tower && ! print.config.single_extruder_multi_material_priming) ?
+ initial_extruder_id = (has_wipe_tower && ! print.config().single_extruder_multi_material_priming) ?
// The priming towers will be skipped.
tool_ordering.all_extruders().back() :
// Don't skip the priming towers.
@@ -711,6 +732,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);
@@ -718,7 +740,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
this->print_machine_envelope(file, print);
// 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.
@@ -729,8 +751,8 @@ 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);
- m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config.single_extruder_multi_material_priming);
- std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config.start_gcode.value, initial_extruder_id);
+ m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming);
+ 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);
// Set extruder(s) temperature before and after start G-code.
@@ -747,49 +769,51 @@ 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();
+
// Set other general things.
_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 : printable_objects)
- for (const Layer *layer : object->layers)
+ for (const Layer *layer : object->layers())
for (const ExPolygon &expoly : layer->slices.expolygons)
- for (const Point © : object->_shifted_copies) {
+ for (const Point © : object->copies()) {
islands.emplace_back(expoly.contour);
islands.back().translate(- copy);
}
//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
- 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 Vec2d &extruder_offset = print.config.extruder_offset.get_at(extruder_id);
+ const Vec2d &extruder_offset = print.config().extruder_offset.get_at(extruder_id);
Polygon s(outer_skirt);
s.translate(Point::new_scale(- extruder_offset(0), - extruder_offset(1)));
skirts.emplace_back(std::move(s));
@@ -807,16 +831,17 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
);
#endif
}
+ print.throw_if_canceled();
}
- if (! (has_wipe_tower && print.config.single_extruder_multi_material_priming)) {
+ if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming)) {
// Set initial extruder only after custom start G-code.
// Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
_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(printable_objects);
@@ -824,9 +849,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
size_t finished_objects = 0;
for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) {
const PrintObject &object = *objects[object_id];
- for (const Point © : object._shifted_copies) {
+ for (const Point © : object.copies()) {
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
- if (object_id != initial_print_object_id || © != object._shifted_copies.data()) {
+ if (object_id != initial_print_object_id || © != object.copies().data()) {
// Don't initialize for the first object and first copy.
tool_ordering = ToolOrdering(object, final_extruder_id);
unsigned int new_extruder_id = tool_ordering.first_extruder();
@@ -837,6 +862,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));
if (finished_objects > 0) {
// Move to the origin position for the copy we're going to print.
@@ -852,7 +878,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);
@@ -866,7 +892,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
for (const LayerToPrint <p : layers_to_print) {
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());
+ this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), © - object.copies().data());
+ print.throw_if_canceled();
}
if (m_pressure_equalizer)
_write(file, m_pressure_equalizer->process("", true));
@@ -882,16 +909,16 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
Points object_reference_points;
PrintObjectPtrs printable_objects = print.get_printable_objects();
for (PrintObject *object : printable_objects)
- object_reference_points.push_back(object->_shifted_copies.front());
+ object_reference_points.push_back(object->copies().front());
Slic3r::Geometry::chained_path(object_reference_points, object_indices);
// Sort layers by Z.
// All extrusion moves with the same top layer height are extruded uninterrupted.
std::vector>> layers_to_print = collect_layers_to_print(print);
// Prusa Multi-Material wipe tower.
if (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"));
- if (print.config.single_extruder_multi_material_priming) {
+ if (print.config().single_extruder_multi_material_priming) {
_write(file, m_wipe_tower->prime(*this));
// Verify, whether the print overaps the priming extrusions.
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
@@ -915,6 +942,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
_write(file, "M1 S10\n");
}
}
+ print.throw_if_canceled();
}
// Extrude the layers.
for (auto &layer : layers_to_print) {
@@ -922,6 +950,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));
@@ -947,17 +976,18 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value));
- 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(), &config));
+ _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id(), &config));
} 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()), &config));
+ 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()), &config));
}
- _writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id(), &config));
+ _writeln(file, this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config));
}
_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_normal_time_estimator.calculate_time(false);
@@ -965,37 +995,30 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
m_silent_time_estimator.calculate_time(false);
// 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.total_wipe_tower_cost = 0.;
- print.total_wipe_tower_filament = 0.;
- print.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
- print.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
+ print.m_print_statistics.clear();
+ 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() + (has_wipe_tower ? print.m_wipe_tower_used_filament[extruder.id()] : 0.f);
- double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.m_wipe_tower_used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
+ 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.filament_stats.insert(std::pair(extruder.id(), (float)used_filament));
+ print.m_print_statistics.filament_stats.insert(std::pair(extruder.id(), (float)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 += used_filament;
- print.total_extruded_volume += extruded_volume;
- print.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.;
- print.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.;
+ 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.total_cost);
+ _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());
if (m_silent_time_estimator_enabled)
_write_format(file, "; estimated printing time (silent mode) = %s\n", m_silent_time_estimator.get_time_dhms().c_str());
@@ -1008,6 +1031,7 @@ 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)
@@ -1020,7 +1044,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" +
@@ -1087,29 +1111,29 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
// Do not process this piece of G-code by the time estimator, it already knows the values through another sources.
void GCode::print_machine_envelope(FILE *file, Print &print)
{
- if (print.config.gcode_flavor.value == gcfMarlin) {
+ if (print.config().gcode_flavor.value == gcfMarlin) {
fprintf(file, "M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n",
- int(print.config.machine_max_acceleration_x.values.front() + 0.5),
- int(print.config.machine_max_acceleration_y.values.front() + 0.5),
- int(print.config.machine_max_acceleration_z.values.front() + 0.5),
- int(print.config.machine_max_acceleration_e.values.front() + 0.5));
+ int(print.config().machine_max_acceleration_x.values.front() + 0.5),
+ int(print.config().machine_max_acceleration_y.values.front() + 0.5),
+ int(print.config().machine_max_acceleration_z.values.front() + 0.5),
+ int(print.config().machine_max_acceleration_e.values.front() + 0.5));
fprintf(file, "M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n",
- int(print.config.machine_max_feedrate_x.values.front() + 0.5),
- int(print.config.machine_max_feedrate_y.values.front() + 0.5),
- int(print.config.machine_max_feedrate_z.values.front() + 0.5),
- int(print.config.machine_max_feedrate_e.values.front() + 0.5));
+ int(print.config().machine_max_feedrate_x.values.front() + 0.5),
+ int(print.config().machine_max_feedrate_y.values.front() + 0.5),
+ int(print.config().machine_max_feedrate_z.values.front() + 0.5),
+ int(print.config().machine_max_feedrate_e.values.front() + 0.5));
fprintf(file, "M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
- int(print.config.machine_max_acceleration_extruding.values.front() + 0.5),
- int(print.config.machine_max_acceleration_retracting.values.front() + 0.5),
- int(print.config.machine_max_acceleration_extruding.values.front() + 0.5));
+ int(print.config().machine_max_acceleration_extruding.values.front() + 0.5),
+ int(print.config().machine_max_acceleration_retracting.values.front() + 0.5),
+ int(print.config().machine_max_acceleration_extruding.values.front() + 0.5));
fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
- print.config.machine_max_jerk_x.values.front(),
- print.config.machine_max_jerk_y.values.front(),
- print.config.machine_max_jerk_z.values.front(),
- print.config.machine_max_jerk_e.values.front());
+ print.config().machine_max_jerk_x.values.front(),
+ print.config().machine_max_jerk_y.values.front(),
+ print.config().machine_max_jerk_z.values.front(),
+ print.config().machine_max_jerk_e.values.front());
fprintf(file, "M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n",
- int(print.config.machine_min_extruding_rate.values.front() + 0.5),
- int(print.config.machine_min_travel_rate.values.front() + 0.5));
+ int(print.config().machine_min_extruding_rate.values.front() + 0.5),
+ int(print.config().machine_min_travel_rate.values.front() + 0.5));
}
}
@@ -1120,7 +1144,7 @@ void GCode::print_machine_envelope(FILE *file, Print &print)
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);
@@ -1143,23 +1167,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));
}
@@ -1232,15 +1256,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;
@@ -1255,22 +1279,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";
}
@@ -1278,14 +1302,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;
}
@@ -1293,9 +1317,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.
@@ -1317,7 +1341,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);
@@ -1336,7 +1360,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.
@@ -1350,22 +1374,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;
}
@@ -1412,11 +1436,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];
// Now we must process perimeters and infills and create islands of extrusions in by_region std::map.
@@ -1433,11 +1457,14 @@ void GCode::process_layer(
continue;
// This extrusion is part of certain Region, which tells us which extruder should be used for it:
- int correct_extruder_id = Print::get_extruder(*fill, region); entity_type=="infills" ? std::max(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
- std::max(region.config.perimeter_extruder.value - 1, 0);
+ int correct_extruder_id = Print::get_extruder(*fill, region);
+ //FIXME what is this?
+ entity_type=="infills" ?
+ std::max(0, (is_solid_infill(fill->entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1) :
+ std::max(region.config().perimeter_extruder.value - 1, 0);
// Let's recover vector of extruder overrides:
- const ExtruderPerCopy* entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size());
+ const ExtruderPerCopy* entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->copies().size());
// Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
for (unsigned int extruder : layer_tools.extruders)
@@ -1459,8 +1486,8 @@ 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[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->_shifted_copies.size());
+ islands[i].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region());
+ islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size());
break;
}
}
@@ -1494,7 +1521,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();
@@ -1515,7 +1542,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;
@@ -1527,7 +1554,6 @@ void GCode::process_layer(
auto objects_by_extruder_it = by_extruder.find(extruder_id);
if (objects_by_extruder_it == by_extruder.end())
continue;
-
// We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
for (int print_wipe_extrusions=const_cast(layer_tools).wiping_extrusions().is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) {
if (print_wipe_extrusions == 0)
@@ -1540,15 +1566,15 @@ 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));
Points copies;
if (single_object_idx == size_t(-1))
- copies = print_object->_shifted_copies;
+ copies = print_object->copies();
else
- copies.push_back(print_object->_shifted_copies[single_object_idx]);
+ copies.push_back(print_object->copies()[single_object_idx]);
// Sort the copies by the closest point starting with the current print position.
unsigned int copy_id = 0;
@@ -1569,7 +1595,7 @@ void GCode::process_layer(
for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
const auto& by_region_specific = const_cast(layer_tools).wiping_extrusions().is_anything_overridden() ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
- if (print.config.infill_first) {
+ if (print.config().infill_first) {
gcode += this->extrude_infill(print, by_region_specific);
gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
} else {
@@ -1612,14 +1638,14 @@ void GCode::apply_print_config(const PrintConfig &print_config)
void GCode::append_full_config(const Print& print, std::string& str)
{
- const StaticPrintConfig *configs[] = { static_cast(&print.config), &print.default_object_config, &print.default_region_config };
+ const StaticPrintConfig *configs[] = { static_cast(&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())
if (key != "compatible_printers")
str += "; " + key + " = " + cfg->serialize(key) + "\n";
}
- const DynamicConfig &full_config = print.placeholder_parser.config();
+ const DynamicConfig &full_config = print.placeholder_parser().config();
for (const char *key : {
"print_settings_id", "filament_settings_id", "printer_settings_id",
"printer_model", "printer_variant", "default_print_profile", "default_filament_profile",
@@ -2178,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 "";
}
}
@@ -2202,7 +2228,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);
}
@@ -2214,7 +2240,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);
@@ -2363,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())
@@ -2689,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.hpp b/xs/src/libslic3r/GCode.hpp
index e16c3213dc..45f17a68ae 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -150,7 +150,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.
@@ -164,6 +165,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/PostProcessor.cpp b/xs/src/libslic3r/GCode/PostProcessor.cpp
new file mode 100644
index 0000000000..c04aeae3cb
--- /dev/null
+++ b/xs/src/libslic3r/GCode/PostProcessor.cpp
@@ -0,0 +1,60 @@
+#include "PostProcessor.hpp"
+
+#if 1
+//#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())
+ 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.";
+ }
+}
+
+} // namespace Slic3r
+
+#endif
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/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/GCode/PrintExtents.cpp b/xs/src/libslic3r/GCode/PrintExtents.cpp
index 2ed6077696..92a58fdf06 100644
--- a/xs/src/libslic3r/GCode/PrintExtents.cpp
+++ b/xs/src/libslic3r/GCode/PrintExtents.cpp
@@ -93,25 +93,25 @@ 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();
}
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.
@@ -121,7 +121,7 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
if (support_layer)
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
bbox_this.merge(extrusionentity_extents(extrusion_entity));
- for (const Point &offset : print_object._shifted_copies) {
+ for (const Point &offset : print_object.copies()) {
BoundingBoxf bbox_translated(bbox_this);
bbox_translated.translate(unscale(offset));
bbox.merge(bbox_translated);
@@ -137,11 +137,11 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
// Wipe tower extrusions are saved as if the tower was at the origin with no rotation
// We need to get position and angle of the wipe tower to transform them to actual position.
Transform2d trafo =
- Eigen::Translation2d(print.config.wipe_tower_x.value, print.config.wipe_tower_y.value) *
- Eigen::Rotation2Dd(print.config.wipe_tower_rotation_angle.value);
+ Eigen::Translation2d(print.config().wipe_tower_x.value, print.config().wipe_tower_y.value) *
+ Eigen::Rotation2Dd(print.config().wipe_tower_rotation_angle.value);
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) {
@@ -164,8 +164,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 189a94d496..175b69447f 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -34,19 +34,19 @@ bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
// 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);
}
@@ -57,16 +57,16 @@ 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)
{
- m_print_config_ptr = &print.config;
+ m_print_config_ptr = &print.config();
PrintObjectPtrs objects = print.get_printable_objects();
// Initialize the print layers for all objects and all layers.
@@ -74,13 +74,13 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
{
std::vector zs;
for (auto object : objects) {
- 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);
- 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);
}
@@ -92,7 +92,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
// 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);
}
@@ -133,13 +133,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)
@@ -148,14 +148,14 @@ 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()) {
bool something_nonoverriddable = true;
@@ -170,7 +170,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
}
if (something_nonoverriddable)
- 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;
}
@@ -197,9 +197,9 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
if (something_nonoverriddable || !m_print_config_ptr)
{
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;
@@ -430,10 +430,10 @@ bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, con
if (print_config.filament_soluble.get_at(Print::get_extruder(eec, region)))
return false;
- if (object.config.wipe_into_objects)
+ if (object.config().wipe_into_objects)
return true;
- if (!region.config.wipe_into_infill || eec.role() != erInternalInfill)
+ if (!region.config().wipe_into_infill || eec.role() != erInternalInfill)
return false;
return true;
@@ -447,12 +447,12 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
const LayerTools& lt = *m_layer_tools;
const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
- if (print.config.filament_soluble.get_at(old_extruder) || print.config.filament_soluble.get_at(new_extruder))
+ if (print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder))
return volume_to_wipe; // Soluble filament cannot be wiped in a random infill, neither the filament after it
// we will sort objects so that dedicated for wiping are at the beginning:
PrintObjectPtrs object_list = print.get_printable_objects();
- std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; });
+ std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config().wipe_into_objects; });
// We will now iterate through
// - first the dedicated objects to mark perimeters or infills (depending on infill_first)
@@ -462,7 +462,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
bool perimeters_done = false;
for (int i=0 ; i<(int)object_list.size() + (perimeters_done ? 0 : 1); ++i) {
- if (!perimeters_done && (i==(int)object_list.size() || !object_list[i]->config.wipe_into_objects)) { // we passed the last dedicated object in list
+ if (!perimeters_done && (i==(int)object_list.size() || !object_list[i]->config().wipe_into_objects)) { // we passed the last dedicated object in list
perimeters_done = true;
i=-1; // let's go from the start again
continue;
@@ -471,26 +471,26 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
const auto& object = object_list[i];
// Finds this layer:
- auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)layers.end())
+ auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)layers().end())
continue;
const Layer* this_layer = *this_layer_it;
- unsigned int num_of_copies = object->_shifted_copies.size();
+ unsigned int num_of_copies = object->copies().size();
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
- for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
- const auto& region = *object->print()->regions[region_id];
+ for (size_t region_id = 0; region_id < object->print()->regions().size(); ++ region_id) {
+ const auto& region = *object->print()->regions()[region_id];
- if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
+ if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
continue;
- if ((!print.config.infill_first ? perimeters_done : !perimeters_done) || (!object->config.wipe_into_objects && region.config.wipe_into_infill)) {
- for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) { // iterate through all infill Collections
+ if ((!print.config().infill_first ? perimeters_done : !perimeters_done) || (!object->config().wipe_into_objects && region.config().wipe_into_infill)) {
+ for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections
auto* fill = dynamic_cast(ee);
- if (!is_overriddable(*fill, print.config, *object, region))
+ if (!is_overriddable(*fill, print.config(), *object, region))
continue;
// What extruder would this normally be printed with?
@@ -499,10 +499,10 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
if (volume_to_wipe<=0)
continue;
- if (!object->config.wipe_into_objects && !print.config.infill_first && region.config.wipe_into_infill)
+ if (!object->config().wipe_into_objects && !print.config().infill_first && region.config().wipe_into_infill)
// In this case we must check that the original extruder is used on this layer before the one we are overridding
// (and the perimeters will be finished before the infill is printed):
- if (!lt.is_extruder_order(region.config.perimeter_extruder - 1, new_extruder))
+ if (!lt.is_extruder_order(region.config().perimeter_extruder - 1, new_extruder))
continue;
if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder
@@ -513,11 +513,11 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
}
// Now the same for perimeters - see comments above for explanation:
- if (object->config.wipe_into_objects && (print.config.infill_first ? perimeters_done : !perimeters_done))
+ if (object->config().wipe_into_objects && (print.config().infill_first ? perimeters_done : !perimeters_done))
{
- for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) {
+ for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) {
auto* fill = dynamic_cast(ee);
- if (!is_overriddable(*fill, print.config, *object, region))
+ if (!is_overriddable(*fill, print.config(), *object, region))
continue;
if (volume_to_wipe<=0)
@@ -544,29 +544,29 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
{
const LayerTools& lt = *m_layer_tools;
- unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config);
- unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config);
+ unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config());
+ unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config());
PrintObjectPtrs printable_objects = print.get_printable_objects();
for (const PrintObject* object : printable_objects) {
// Finds this layer:
- auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)layers.end())
+ auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)layers().end())
continue;
const Layer* this_layer = *this_layer_it;
- unsigned int num_of_copies = object->_shifted_copies.size();
+ unsigned int num_of_copies = object->copies().size();
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
- for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
- const auto& region = *object->print()->regions[region_id];
+ for (size_t region_id = 0; region_id < object->print()->regions().size(); ++ region_id) {
+ const auto& region = *object->print()->regions()[region_id];
- if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
+ if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
continue;
- for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) { // iterate through all infill Collections
+ for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections
auto* fill = dynamic_cast(ee);
- if (!is_overriddable(*fill, print.config, *object, region)
+ if (!is_overriddable(*fill, print.config(), *object, region)
|| is_entity_overridden(fill, copy) )
continue;
@@ -574,12 +574,12 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
// printed before its perimeter, or not be printed at all (in case its original extruder has
// not been added to LayerTools
// Either way, we will now force-override it with something suitable:
- if (print.config.infill_first
- || object->config.wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely
- || lt.is_extruder_order(region.config.perimeter_extruder - 1, last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints
- || std::find(lt.extruders.begin(), lt.extruders.end(), region.config.infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME)
+ if (print.config().infill_first
+ || object->config().wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely
+ || lt.is_extruder_order(region.config().perimeter_extruder - 1, last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints
+ || std::find(lt.extruders.begin(), lt.extruders.end(), region.config().infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME)
)
- set_extruder_override(fill, copy, (print.config.infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
+ set_extruder_override(fill, copy, (print.config().infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
else {
// In this case we can (and should) leave it to be printed normally.
// Force overriding would mean it gets printed before its perimeter.
@@ -587,13 +587,13 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
}
// Now the same for perimeters - see comments above for explanation:
- for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) { // iterate through all perimeter Collections
+ for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { // iterate through all perimeter Collections
auto* fill = dynamic_cast(ee);
- if (!is_overriddable(*fill, print.config, *object, region)
+ if (!is_overriddable(*fill, print.config(), *object, region)
|| is_entity_overridden(fill, copy) )
continue;
- set_extruder_override(fill, copy, (print.config.infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
+ set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
}
}
}
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 61d8df0358..54bdfdfd66 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -1166,7 +1166,6 @@ void WipeTowerPrusaMM::save_on_last_wipe()
}
}
-
// Processes vector m_plan and calls respective functions to generate G-code for the wipe tower
// Resulting ToolChangeResults are appended into vector "result"
void WipeTowerPrusaMM::generate(std::vector> &result)
@@ -1256,6 +1255,4 @@ void WipeTowerPrusaMM::make_wipe_tower_square()
lay.extra_spacing = lay.depth / lay.toolchanges_depth();
}
-
-
}; // namespace Slic3r
diff --git a/xs/src/libslic3r/GCodeReader.cpp b/xs/src/libslic3r/GCodeReader.cpp
index 79b6ed9707..51853e9fa2 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/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/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/Layer.cpp b/xs/src/libslic3r/Layer.cpp
index 20e8f8ae18..6c2bd0da90 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,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 : m_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
}
}
@@ -164,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(0), bbox.max(1));
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
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();
}
@@ -190,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(0), bbox.max(1));
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
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 6205831285..8dbe850ccc 100644
--- a/xs/src/libslic3r/Layer.hpp
+++ b/xs/src/libslic3r/Layer.hpp
@@ -15,87 +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.
+ // collection of surfaces generated by slicing the original geometry
+ // divided by type top/bottom/internal
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;
+
// Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature")
// and for re-starting of infills.
ExPolygons fill_expolygons;
// collection of surfaces for infill generation
SurfaceCollection fill_surfaces;
- // 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;
- // Collection of expolygons representing the bridged areas (thus not needing support material).
- //FIXME Not used as of now.
+ // 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;
+
+ // collection of expolygons representing the bridged areas (thus not
+ // needing support material)
Polygons bridged;
// collection of polylines representing the unsupported bridge edges
PolylineCollection unsupported_bridge_edges;
- // Ordered collection of extrusion paths/loops to build all perimeters.
- // This collection contains only ExtrusionEntityCollection objects.
+ // ordered collection of extrusion paths/loops to build all perimeters
+ // (this collection contains only ExtrusionEntityCollection objects)
ExtrusionEntityCollection perimeters;
- // Ordered collection of extrusion paths to fill surfaces.
- // This collection contains only ExtrusionEntityCollection objects.
+
+ // ordered collection of extrusion paths to fill surfaces
+ // (this collection contains only ExtrusionEntityCollection objects)
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)
@@ -103,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 06e7fcceeb..e0f97703cf 100644
--- a/xs/src/libslic3r/LayerRegion.cpp
+++ b/xs/src/libslic3r/LayerRegion.cpp
@@ -17,13 +17,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()
);
}
@@ -60,9 +60,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,
@@ -115,7 +115,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.
@@ -258,9 +258,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());
}
@@ -350,13 +350,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;
@@ -364,9 +364,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/Model.cpp b/xs/src/libslic3r/Model.cpp
index 9f66efaef7..65743da8d8 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;
@@ -885,6 +886,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)
@@ -989,8 +991,6 @@ const TriangleMesh& ModelVolume::get_convex_hull() const
ModelVolume::Type ModelVolume::type_from_string(const std::string &s)
{
// Legacy support
- if (s == "0")
- return MODEL_PART;
if (s == "1")
return PARAMETER_MODIFIER;
// New type (supporting the support enforcers & blockers)
@@ -1002,6 +1002,9 @@ ModelVolume::Type ModelVolume::type_from_string(const std::string &s)
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)
@@ -1051,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());
@@ -1095,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
}
@@ -1111,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 bc78e6b7ff..ea7f3fee25 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -133,6 +133,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.
@@ -249,11 +250,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
@@ -272,6 +278,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.
@@ -292,9 +306,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 372689c39d..9f983ee9f4 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);
}
};
@@ -527,14 +527,19 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
// Invalid geometries would throw exceptions when arranging
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)
+#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
- ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR),
- ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR)
+ item.rotation(objinst->rotation);
+#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)
+#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);
@@ -668,18 +673,25 @@ 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
+#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
@@ -695,7 +707,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 +753,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/libslic3r/PerimeterGenerator.hpp b/xs/src/libslic3r/PerimeterGenerator.hpp
index c0f449908d..44af8c8bef 100644
--- a/xs/src/libslic3r/PerimeterGenerator.hpp
+++ b/xs/src/libslic3r/PerimeterGenerator.hpp
@@ -37,30 +37,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,
@@ -78,15 +78,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 1cfd511417..cc6f1c75e4 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/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 a582a2b624..cdc12d2d15 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -6,6 +6,7 @@
#include "Geometry.hpp"
#include "I18N.hpp"
#include "SupportMaterial.hpp"
+#include "GCode.hpp"
#include "GCode/WipeTowerPrusaMM.hpp"
#include
#include
@@ -13,7 +14,6 @@
#include
#include
-#include "slic3r/ProgressIndicator.hpp"
#include "PrintExport.hpp"
//! macro used to mark string used at localization,
@@ -27,38 +27,50 @@ template class PrintState;
void Print::clear_objects()
{
- for (int i = int(this->objects.size())-1; i >= 0; --i)
- this->delete_object(i);
- for (PrintRegion *region : this->regions)
+ tbb::mutex::scoped_lock lock(m_mutex);
+ for (PrintObject *object : m_objects)
+ delete object;
+ m_objects.clear();
+ for (PrintRegion *region : m_regions)
delete region;
- this->regions.clear();
+ m_regions.clear();
+ this->invalidate_all_steps();
}
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 */)
{
- /* 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(this->objects.size());
- for (PrintObject *object : this->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);
}
@@ -69,23 +81,30 @@ 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 : this->objects)
+ for (PrintObject *object : m_objects)
invalidated |= object->reload_model_instances();
return invalidated;
}
PrintObjectPtrs Print::get_printable_objects() const
{
- PrintObjectPtrs printable_objects(this->objects);
+ PrintObjectPtrs printable_objects(m_objects);
printable_objects.erase(std::remove_if(printable_objects.begin(), printable_objects.end(), [](PrintObject* o) { return !o->is_printable(); }), printable_objects.end());
return printable_objects;
}
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().
@@ -97,7 +116,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",
@@ -176,14 +195,19 @@ 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"
@@ -250,29 +274,29 @@ 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 = this->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 |= this->state.invalidate(psBrim);
+ invalidated |= m_state.invalidate(psBrim, m_mutex, m_cancel_callback);
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())
+ if (m_objects.empty())
return false;
- for (const PrintObject *object : this->objects)
- if (!object->state.is_done(step))
+ for (const PrintObject *object : m_objects)
+ if (!object->m_state.is_done(step))
return false;
return true;
}
@@ -282,15 +306,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);
@@ -303,16 +327,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);
}
}
@@ -335,10 +359,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);
}
}
@@ -348,7 +372,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;
}
@@ -356,14 +380,16 @@ 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) {
- 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.
+ //FIXME lock mutex!
this->invalidate_all_steps();
size_t volume_id = 0;
@@ -374,15 +400,15 @@ void Print::add_model_object(ModelObject* model_object, int idx)
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 < 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 = m_regions.size();
+ this->add_region(config);
}
// Assign volume to a region.
object->add_region_volume(region_id, volume_id);
@@ -390,22 +416,27 @@ void Print::add_model_object(ModelObject* model_object, int idx)
}
// 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
@@ -419,23 +450,25 @@ 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();
// 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.
@@ -447,13 +480,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.
@@ -462,11 +495,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];
@@ -496,10 +529,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);
}
@@ -514,8 +547,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)
@@ -524,7 +557,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();
@@ -533,38 +566,42 @@ 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(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_(config.max_print_height)));
+ BoundingBox bed_box_2D = get_extents(Polygon::new_scale(m_config.bed_shape.values));
+ 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 : this->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.");
- 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;
{
@@ -578,9 +615,9 @@ 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) {
+ for (const Point © : object->m_copies) {
Polygon p = convex_hull;
p.translate(copy);
if (! intersection(convex_hulls_other, p).empty())
@@ -592,54 +629,54 @@ 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(2));
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 L("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 L("The Spiral Vase option can only be used when printing a single object.");
- if (this->regions.size() > 1)
+ if (m_regions.size() > 1)
return L("The Spiral Vase option can only be used when printing single material objects.");
}
- if (this->config.single_extruder_multi_material) {
- for (size_t i=1; iconfig.nozzle_diameter.values.size(); ++i)
- if (this->config.nozzle_diameter.values[i] != this->config.nozzle_diameter.values[i-1])
+ if (m_config.single_extruder_multi_material) {
+ for (size_t i=1; ihas_wipe_tower() && ! this->objects.empty()) {
- if (this->config.gcode_flavor != gcfRepRap && this->config.gcode_flavor != gcfMarlin)
+ if (this->has_wipe_tower() && ! m_objects.empty()) {
+ if (m_config.gcode_flavor != gcfRepRap && m_config.gcode_flavor != gcfMarlin)
return L("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 L("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();
+ SlicingParameters slicing_params0 = m_objects.front()->slicing_parameters();
- const PrintObject* tallest_object = this->objects.front(); // let's find the tallest object
- for (const auto* object : objects)
+ const PrintObject* tallest_object = m_objects.front(); // let's find the tallest object
+ for (const auto* object : m_objects)
if (*(object->layer_height_profile.end()-2) > *(tallest_object->layer_height_profile.end()-2) )
tallest_object = object;
- for (PrintObject *object : this->objects) {
+ 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 L("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 L("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 L("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 L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
@@ -647,7 +684,7 @@ std::string Print::validate() const
object->update_layer_height_profile();
object->layer_height_profile_valid = was_layer_height_profile_valid;
- if ( this->config.variable_layer_height ) { // comparing layer height profiles
+ if ( m_config.variable_layer_height ) { // comparing layer height profiles
bool failed = false;
if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size() ) {
int i = 0;
@@ -679,17 +716,16 @@ 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());
-
- unsigned int total_extruders_count = this->config.nozzle_diameter.size();
+ unsigned int total_extruders_count = m_config.nozzle_diameter.size();
for (const auto& extruder_idx : extruders)
if ( extruder_idx >= total_extruders_count )
return L("One or more object were assigned an extruder that the printer does not have.");
- 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.
@@ -700,16 +736,16 @@ std::string Print::validate() const
}
// validate first_layer_height
- double first_layer_height = object->config.get_abs_value(L("first_layer_height"));
+ double first_layer_height = object->config().get_abs_value(L("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;
@@ -718,7 +754,7 @@ std::string Print::validate() const
return L("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 L("Layer height can't be greater than nozzle diameter");
}
}
@@ -731,8 +767,8 @@ std::string Print::validate() const
BoundingBox Print::bounding_box() const
{
BoundingBox bb;
- for (const PrintObject *object : this->objects)
- for (Point copy : object->_shifted_copies) {
+ for (const PrintObject *object : m_objects)
+ for (Point copy : object->m_copies) {
bb.merge(copy);
copy += to_2d(object->size);
bb.merge(copy);
@@ -749,7 +785,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
@@ -758,18 +794,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
);
@@ -783,17 +819,18 @@ 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())
+ 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");
}
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
@@ -803,7 +840,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
);
@@ -811,11 +848,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
@@ -825,7 +862,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
);
@@ -833,7 +870,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())
@@ -843,7 +880,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;
@@ -857,7 +894,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.
@@ -866,6 +903,76 @@ 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 : m_objects)
+ obj->make_perimeters();
+ this->throw_if_canceled();
+ this->set_status(70, "Infilling layers");
+ for (PrintObject *obj : m_objects)
+ obj->infill();
+ this->throw_if_canceled();
+ for (PrintObject *obj : m_objects)
+ obj->generate_support_material();
+ this->throw_if_canceled();
+ if (! m_state.is_done(psSkirt)) {
+ this->set_started(psSkirt);
+ m_skirt.clear();
+ if (this->has_skirt()) {
+ this->set_status(88, "Generating skirt");
+ this->_make_skirt();
+ }
+ this->set_done(psSkirt);
+ }
+ this->throw_if_canceled();
+ if (! m_state.is_done(psBrim)) {
+ this->set_started(psBrim);
+ m_brim.clear();
+ if (m_config.brim_width > 0) {
+ this->set_status(88, "Generating brim");
+ this->_make_brim();
+ }
+ this->set_done(psBrim);
+ }
+ this->throw_if_canceled();
+ if (! m_state.is_done(psWipeTower)) {
+ this->set_started(psWipeTower);
+ m_wipe_tower_data.clear();
+ if (this->has_wipe_tower()) {
+ //this->set_status(95, "Generating wipe tower");
+ this->_make_wipe_tower();
+ }
+ this->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.
@@ -883,8 +990,8 @@ void Print::_make_skirt()
for (const PrintObject *object : printable_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.
@@ -892,7 +999,7 @@ void Print::_make_skirt()
for (const PrintObject *object : printable_objects) {
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)
@@ -900,14 +1007,14 @@ 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)
append(object_points, extrusion_entity->as_polyline().points);
}
// Repeat points for each object copy.
- for (const Point &shift : object->_shifted_copies) {
+ for (const Point &shift : object->m_copies) {
Points copy_points = object_points;
for (Point &pt : copy_points)
pt += shift;
@@ -919,6 +1026,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,
@@ -937,24 +1045,25 @@ 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.
Flow brim_flow = this->brim_flow();
- double actual_brim_width = brim_flow.spacing() * floor(this->config.brim_width.value / brim_flow.spacing());
- coord_t distance = scale_(std::max(this->config.skirt_distance.value, actual_brim_width) - spacing/2.);
+ double actual_brim_width = brim_flow.spacing() * floor(m_config.brim_width.value / brim_flow.spacing());
+ coord_t distance = scale_(std::max(m_config.skirt_distance.value, actual_brim_width) - spacing/2.);
// 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.);
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.
@@ -974,16 +1083,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())
@@ -994,7 +1103,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()
@@ -1005,20 +1114,21 @@ void Print::_make_brim()
PrintObjectPtrs printable_objects = get_printable_objects();
for (PrintObject *object : printable_objects) {
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));
- islands.reserve(islands.size() + object_islands.size() * object->_shifted_copies.size());
- for (const Point &pt : object->_shifted_copies)
+ 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->m_copies.size());
+ for (const Point &pt : object->m_copies)
for (Polygon &poly : object_islands) {
islands.push_back(poly);
islands.back().translate(pt);
}
}
Polygons loops;
- size_t num_loops = size_t(floor(this->config.brim_width.value / flow.spacing()));
+ size_t num_loops = size_t(floor(m_config.brim_width.value / flow.spacing()));
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);
@@ -1032,37 +1142,27 @@ 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()
{
- this->_clear_wipe_tower();
+ m_wipe_tower_data.clear();
if (! this->has_wipe_tower())
return;
- m_wipe_tower_depth = 0.f;
-
// Get wiping matrix to get number of extruders and convert vector to vector:
- std::vector wiping_matrix(cast(this->config.wiping_volumes_matrix.values));
+ std::vector wiping_matrix(cast(m_config.wiping_volumes_matrix.values));
// Extract purging volumes for each extruder pair:
std::vector> wipe_volumes;
const unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+EPSILON);
@@ -1070,8 +1170,8 @@ void Print::_make_wipe_tower()
wipe_volumes.push_back(std::vector(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders));
// 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;
@@ -1081,46 +1181,45 @@ 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 LayerTools < = m_tool_ordering.layer_tools()[i];
+ const 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) {
- LayerTools < = const_cast(m_tool_ordering.layer_tools()[i]);
+ 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;
}
}
}
+ this->throw_if_canceled();
// 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_rotation_angle.value), float(this->config.cooling_tube_retraction.value),
- float(this->config.cooling_tube_length.value), float(this->config.parking_pos_retraction.value),
- float(this->config.extra_loading_move.value), float(this->config.wipe_tower_bridging), wipe_volumes,
- 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_rotation_angle.value), float(m_config.cooling_tube_retraction.value),
+ float(m_config.cooling_tube_length.value), float(m_config.parking_pos_retraction.value),
+ float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging), wipe_volumes,
+ m_wipe_tower_data.tool_ordering.first_extruder());
//wipe_tower.set_retract();
//wipe_tower.set_zhop();
@@ -1129,100 +1228,101 @@ void Print::_make_wipe_tower()
for (size_t i = 0; i < number_of_extruders; ++ 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),
- this->config.filament_loading_speed.get_at(i),
- this->config.filament_loading_speed_start.get_at(i),
- this->config.filament_unloading_speed.get_at(i),
- this->config.filament_unloading_speed_start.get_at(i),
- this->config.filament_toolchange_delay.get_at(i),
- this->config.filament_cooling_moves.get_at(i),
- this->config.filament_cooling_initial_speed.get_at(i),
- this->config.filament_cooling_final_speed.get_at(i),
- this->config.filament_ramming_parameters.get_at(i),
- this->config.nozzle_diameter.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),
+ m_config.filament_loading_speed.get_at(i),
+ m_config.filament_loading_speed_start.get_at(i),
+ m_config.filament_unloading_speed.get_at(i),
+ m_config.filament_unloading_speed_start.get_at(i),
+ m_config.filament_toolchange_delay.get_at(i),
+ m_config.filament_cooling_moves.get_at(i),
+ m_config.filament_cooling_initial_speed.get_at(i),
+ m_config.filament_cooling_final_speed.get_at(i),
+ m_config.filament_ramming_parameters.get_at(i),
+ m_config.nozzle_diameter.get_at(i));
- m_wipe_tower_priming = Slic3r::make_unique(
- wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), false));
+ m_wipe_tower_data.priming = Slic3r::make_unique(
+ wipe_tower.prime(this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
// Lets go through the wipe tower layers and determine pairs of extruder changes for each
// to pass to wipe_tower (so that it can use it for planning the layout of the tower)
{
- unsigned int current_extruder_id = m_tool_ordering.all_extruders().back();
- for (auto &layer_tools : m_tool_ordering.layer_tools()) { // for all layers
+ unsigned int current_extruder_id = m_wipe_tower_data.tool_ordering.all_extruders().back();
+ for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
if (!layer_tools.has_wipe_tower) continue;
- bool first_layer = &layer_tools == &m_tool_ordering.front();
+ bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false);
for (const auto extruder_id : layer_tools.extruders) {
- if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
+ if ((first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
// Not all of that can be used for infill purging:
- volume_to_wipe -= config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
+ volume_to_wipe -= m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
// try to assign some infills/objects for the wiping:
volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, volume_to_wipe);
// add back the minimal amount toforce on the wipe tower:
- volume_to_wipe += config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
+ volume_to_wipe += m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
// request a toolchange at the wipe tower with at least volume_to_wipe purging amount
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id,
- first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe);
+ first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back(), volume_to_wipe);
current_extruder_id = extruder_id;
}
}
layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
- if (&layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
+ if (&layer_tools == &m_wipe_tower_data.tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
break;
}
}
// Generate the wipe tower layers.
- m_wipe_tower_tool_changes.reserve(m_tool_ordering.layer_tools().size());
- wipe_tower.generate(m_wipe_tower_tool_changes);
- m_wipe_tower_depth = wipe_tower.get_depth();
+ m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size());
+ wipe_tower.generate(m_wipe_tower_data.tool_changes);
+ m_wipe_tower_data.depth = wipe_tower.get_depth();
// 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));
- m_wipe_tower_used_filament = wipe_tower.get_used_filament();
- m_wipe_tower_number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
+ 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()
+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(L("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;
@@ -1239,31 +1339,32 @@ std::string Print::output_filepath(const std::string &path)
return path;
}
-void Print::set_status(int percent, const std::string &message)
+void Print::export_png(const std::string &dirpath)
{
- if(progressindicator) progressindicator->update(unsigned(percent), message);
- else {
- printf("Print::status %d => %s\n", percent, message.c_str());
- std::cout.flush();
- }
-}
-
-void Print::print_to_png(std::string dirpath) {
- print_to(*this,
- dirpath,
- float(this->config.bed_size_x.value),
- float(this->config.bed_size_y.value),
- int(this->config.pixel_width.value),
- int(this->config.pixel_height.value),
- float(this->config.exp_time.value),
- float(this->config.exp_time_first.value));
+// 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
int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion)
{
- return is_infill(fill.role()) ? std::max(0, (is_solid_infill(fill.entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
- std::max(region.config.perimeter_extruder.value - 1, 0);
+ return is_infill(fill.role()) ? std::max(0, (is_solid_infill(fill.entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1) :
+ std::max(region.config().perimeter_extruder.value - 1, 0);
}
-}
+} // namespace Slic3r
+
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index 7c2638c968..7f88110bc3 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -2,9 +2,11 @@
#define slic3r_Print_hpp_
#include "libslic3r.h"
+#include
#include
#include
#include
+#include
#include "BoundingBox.hpp"
#include "Flow.hpp"
#include "PrintConfig.hpp"
@@ -17,56 +19,110 @@
#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].store(INVALID, std::memory_order_relaxed); }
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 invalidate(StepType step) {
- bool invalidated = this->state[step] != INVALID;
- this->state[step] = INVALID;
+ // With full memory barrier.
+ bool is_done(StepType step) const { return m_state[step] == DONE; }
+
+ // 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].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) {
+#if 0
+ if (mtx.state != mtx.HELD) {
+ printf("Not held!\n");
+ }
+#endif
+ 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 (this->state[i] != INVALID) {
- invalidated = true;
- 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);
}
- memset(state, 0, sizeof(state));
return invalidated;
}
+
+private:
+ std::atomic m_state[COUNT];
};
// A PrintRegion object represents a group of volumes to print
@@ -75,20 +131,27 @@ class PrintRegion
{
friend class Print;
+// Methods NOT modifying the PrintRegion's state:
public:
- PrintRegionConfig config;
-
- Print* print() { return this->_print; }
- Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
+ 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;
+ 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:
+ 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() {}
};
@@ -104,43 +167,35 @@ 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;
+ t_layer_height_ranges layer_height_ranges;
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
// The pairs of are packed into a 1D array to simplify handling by the Perl XS.
// layer_height_profile must not be set by the background thread.
- std::vector layer_height_profile;
+ std::vector layer_height_profile;
// There is a layer_height_profile at both PrintObject and ModelObject. The layer_height_profile at the ModelObject
// is used for interactive editing and for loading / storing into a project file (AMF file as of today).
// This flag indicates that the layer_height_profile at the UI has been updated, therefore the backend needs to get it.
// This flag is necessary as we cannot safely clear the layer_height_profile if the background calculation is running.
- bool layer_height_profile_valid;
+ bool layer_height_profile_valid;
// this is set to true when LayerRegion->slices is split in top/internal/bottom
// so that next call to make_perimeters() performs a union() before computing loops
- bool typed_slices;
+ bool typed_slices;
- Vec3crd size; // XYZ in scaled coordinates
+ Vec3crd size; // XYZ in scaled coordinates
- // scaled coordinates to add to copies (to compensate for the alignment
- // operated when creating the object but still preserving a coherent API
- // for external callers)
- Point _copies_shift;
+ 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; }
- // Slic3r::Point objects in scaled G-code coordinates in our coordinates
- Points _shifted_copies;
-
- LayerPtrs layers;
- SupportLayerPtrs support_layers;
- PrintState state;
-
- 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 Vec2d &point);
bool delete_last_copy();
bool delete_all_copies() { return this->set_copies(Points()); }
@@ -159,24 +214,26 @@ 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 this->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
// to initialize the height profile with the height ranges.
@@ -196,168 +253,282 @@ public:
// (layer height, first layer height, raft settings, print nozzle diameter etc).
SlicingParameters slicing_parameters() const;
- 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();
-
- bool is_printable() const { return !this->_shifted_copies.empty(); }
+ // 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:
- Print* _print;
- ModelObject* _model_object;
- Points _copies; // Slic3r::Point objects in scaled G-code coordinates
+ 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 _make_perimeters();
+ bool has_support_material() const;
+ void detect_surfaces_type();
+ void process_external_surfaces();
+ void discover_vertical_shells();
+ void bridge_over_infill();
+ void clip_fill_surfaces();
+ void discover_horizontal_shells();
+ void combine_infill();
+ void _generate_support_material();
+
+ bool is_printable() const { return ! m_copies.empty(); }
+
+ Print *m_print;
+ ModelObject *m_model_object;
+ PrintObjectConfig m_config;
+ // Slic3r::Point objects in scaled G-code coordinates
+ Points m_copies;
+ // scaled coordinates to add to copies (to compensate for the alignment
+ // operated when creating the object but still preserving a coherent API
+ // for external callers)
+ Point m_copies_shift;
+
+ LayerPtrs m_layers;
+ SupportLayerPtrs m_support_layers;
+
+ PrintState m_state;
// 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);
+ 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
+{
+ // 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;
+ std::vector used_filament;
+ int number_of_toolchanges;
+
+ // Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
+ float depth;
+
+ void clear() {
+ tool_ordering.clear();
+ priming.reset(nullptr);
+ tool_changes.clear();
+ final_purge.reset(nullptr);
+ used_filament.clear();
+ number_of_toolchanges = -1;
+ depth = 0.f;
+ }
+};
+
+struct PrintStatistics
+{
+ PrintStatistics() { clear(); }
+ std::string estimated_normal_print_time;
+ std::string estimated_silent_print_time;
+ double total_used_filament;
+ 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() {
+ estimated_normal_print_time.clear();
+ estimated_silent_print_time.clear();
+ total_used_filament = 0.;
+ total_extruded_volume = 0.;
+ total_cost = 0.;
+ total_weight = 0.;
+ total_wipe_tower_cost = 0.;
+ total_wipe_tower_filament = 0.;
+ filament_stats.clear();
+ }
+};
+
typedef std::vector PrintObjectPtrs;
typedef std::vector PrintRegionPtrs;
-class ProgressIndicator;
-using ProgressIndicatorPtr = std::shared_ptr;
-
// The complete print tray with possibly multiple objects.
class Print
{
public:
- PrintConfig config;
- PrintObjectConfig default_object_config;
- PrintRegionConfig default_region_config;
- PrintObjectPtrs objects;
- PrintRegionPtrs regions;
- PlaceholderParser placeholder_parser;
-
- // TODO: status_cb
- ProgressIndicatorPtr progressindicator;
-
- std::string estimated_normal_print_time;
- std::string estimated_silent_print_time;
- double total_used_filament, total_extruded_volume, total_cost, total_weight, total_wipe_tower_cost, total_wipe_tower_filament;
- std::map filament_stats;
- PrintState state;
-
- // 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); }
- void delete_object(size_t idx);
- void reload_object(size_t idx);
- bool reload_model_instances();
+ // 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);
+ // SLA export, temporary.
+ void export_png(const std::string &dirpath);
- PrintObjectPtrs get_printable_objects() const;
-
- // 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 this->state.invalidate_all(); }
- bool step_done(PrintObjectStep step) const;
-
- void add_model_object(ModelObject* model_object, int idx = -1);
- bool apply_config(DynamicPrintConfig config);
- float get_wipe_tower_depth() const { return m_wipe_tower_depth; }
- bool has_infinite_skirt() const;
- bool has_skirt() const;
+ bool is_step_done(PrintStep step) const { return m_state.is_done(step); }
+ bool is_step_done(PrintObjectStep step) const;
+
+ bool has_infinite_skirt() const;
+ bool has_skirt() const;
+ PrintObjectPtrs get_printable_objects() const;
+ float get_wipe_tower_depth() const { return m_wipe_tower_data.depth; }
+
// 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;
+
+ 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 PrintObject* get_object(int idx) const { return m_objects[idx]; }
+ const PrintRegionPtrs& regions() const { return m_regions; }
+ const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; }
// Returns extruder this eec should be printed with, according to PrintRegion config:
static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion);
- void _make_skirt();
- void _make_brim();
+ 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;
- 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;
- // 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;
- std::vector m_wipe_tower_used_filament;
- int m_wipe_tower_number_of_toolchanges = -1;
+ 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;
- // Calls a registered callback to update the status.
- void set_status(int percent, const std::string &message);
- // 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 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, 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; }
+ // 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());
+ }
+
+ 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; }
+ 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; }
- void print_to_png(std::string dirpath);
+ // 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); 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); }
+
+ // 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);
+ // 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() const { if (m_canceled) throw CanceledException(); }
- // Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
- float m_wipe_tower_depth = 0.f;
+ 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:
+ // The mutex will be used to guard the worker thread against entering a stage
+ // while the data influencing the stage is modified.
+ mutable tbb::mutex m_mutex;
// Has the calculation been canceled?
- tbb::atomic m_canceled;
+ tbb::atomic m_canceled;
+ // 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/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 94b0ad5aa7..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);
@@ -1989,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/PrintExport.hpp b/xs/src/libslic3r/PrintExport.hpp
index caaec26b21..917a39e081 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"
@@ -32,14 +28,14 @@ enum class FilePrinterFormat {
* different implementations of this class template for each supported format.
*
*/
-template
+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);
@@ -51,32 +47,57 @@ 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; };
+
+// This has to be explicitly implemented in the gui layer or a default zlib
+// based implementation is needed.
+template class LayerWriter {
+public:
+
+ LayerWriter(const std::string& /*zipfile_path*/) {
+ static_assert(VeryFalse::value,
+ "No layer writer implementation provided!");
+ }
+
+ void next_entry(const std::string& /*fname*/) {}
+
+ std::string get_name() { return ""; }
+
+ bool is_ok() { return false; }
+
+ template LayerWriter& 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;
@@ -91,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);
@@ -134,92 +155,84 @@ 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 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();
}
}
inline void save(const std::string& path) {
+ try {
+ LayerWriter writer(path);
- wxFileName filepath(path);
+ std::string project = writer.get_name();
- wxFFileOutputStream zipfile(path);
+ writer.next_entry("config.ini");
+ writer << createIniContent(project);
- std::string project = filepath.GetName().ToStdString();
+ 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 << m_layers_rst[i].second.rdbuf();
+ m_layers_rst[i].second.str("");
+ }
+ }
- if(!zipfile.IsOk()) {
- 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;
}
-
- 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) {
+ 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);
@@ -227,23 +240,23 @@ 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); }
-template
+template
void print_to(Print& print,
std::string dirpath,
double width_mm,
@@ -258,16 +271,18 @@ void print_to(Print& print,
// rasterized to the same image.
std::map layers;
- auto& objects = print.objects;
+ auto& objects = print.objects();
// Merge the sliced layers with the support layers
- std::for_each(objects.begin(), objects.end(), [&layers](PrintObject *o) {
- for(auto l : o->layers) {
+ 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);
}
- for(auto l : o->support_layers) {
+ for(const auto l : o->support_layers()) {
auto& lyrs = layers[static_cast(scale_(l->print_z))];
lyrs.push_back(l);
}
@@ -288,10 +303,10 @@ 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);
+ printer.print_config(print);
printer.layers(layers.size()); // Allocate space for all the layers
@@ -303,18 +318,16 @@ void print_to(Print& print,
keys.reserve(layers.size());
for(auto& e : layers) keys.push_back(e.first);
- 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]];
- 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;
@@ -329,21 +342,14 @@ void print_to(Print& print,
});
// Draw all the polygons in the slice to the actual layer.
- std::for_each(l.object()->_shifted_copies.begin(),
- l.object()->_shifted_copies.end(),
- [&] (Point d)
- {
- std::for_each(slices.expolygons.begin(),
- slices.expolygons.end(),
- [&] (ExPolygon slice)
- {
+ for (const Point &d : l.object()->copies())
+ for (ExPolygon slice : slices.expolygons) {
slice.translate(px(d), py(d));
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) {
BOOST_LOG_TRIVIAL(warning) << "support material for layer "
@@ -355,12 +361,12 @@ 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();
if( st - st_prev > 10) {
- print.set_status(initstatus + st, jobdesc);
+ print.set_status(st, jobdesc);
st_prev = st;
}
m.unlock();
@@ -379,9 +385,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/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index 65da492587..ef2364dc41 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -36,8 +36,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),
size(Vec3crd::Zero()),
layer_height_profile_valid(false)
{
@@ -49,7 +49,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Bounding
// don't assume it's already aligned and we don't alter the original position in model.
// We store the XY translation so that we can place copies correctly in the output G-code
// (copies are expressed in G-code coordinates and this translation is not publicly exposed).
- this->_copies_shift = Point::new_scale(modobj_bbox.min(0), modobj_bbox.min(1));
+ m_copies_shift = Point::new_scale(modobj_bbox.min(0), modobj_bbox.min(1));
// Scale the object size and store it
this->size = (modobj_bbox.size() * (1. / SCALING_FACTOR)).cast();
}
@@ -59,48 +59,59 @@ 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)
{
- Points points = this->_copies;
+ 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);
}
bool PrintObject::delete_last_copy()
{
- Points points = this->_copies;
+ tbb::mutex::scoped_lock lock(m_print->m_mutex);
+ Points points = m_copies;
points.pop_back();
return this->set_copies(points);
}
bool PrintObject::set_copies(const Points &points)
{
- bool copies_num_changed = this->_copies.size() != points.size();
- this->_copies = points;
+ bool copies_num_changed = m_copies.size() != points.size();
// order copies with a nearest neighbor search and translate them by _copies_shift
- this->_shifted_copies.clear();
- this->_shifted_copies.reserve(points.size());
+ m_copies.clear();
+ m_copies.reserve(points.size());
// order copies with a nearest-neighbor search
std::vector ordered_copies;
Slic3r::Geometry::chained_path(points, ordered_copies);
for (size_t point_idx : ordered_copies)
- this->_shifted_copies.push_back(points[point_idx] + this->_copies_shift);
+ m_copies.push_back(points[point_idx] + m_copies_shift);
- 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);
if (copies_num_changed)
- invalidated |= this->_print->invalidate_step(psWipeTower);
+ invalidated |= m_print->invalidate_step(psWipeTower);
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)
{
#if ENABLE_MODELINSTANCE_3D_OFFSET
if (mi->is_printable())
@@ -116,30 +127,342 @@ 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->is_step_done(posSlice))
+ return;
+ this->set_started(posSlice);
+ m_print->set_status(10, "Processing triangulated mesh");
+ this->_slice();
+ 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();
+ m_print->throw_if_canceled();
+ if (! warning.empty())
+ BOOST_LOG_TRIVIAL(info) << warning;
+ // Simplify slices if required.
+ 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");
+ this->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->is_step_done(posPerimeters))
+ return;
+
+ this->set_started(posPerimeters);
+ 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();
+ m_print->throw_if_canceled();
+ }
+ this->typed_slices = false;
+ }
+
+ // 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
+ 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 || 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, 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) {
+ 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} ];
+ 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
+ }
+ }
+ });
+ 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, m_layers.size()),
+ [this](const tbb::blocked_range& range) {
+ for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
+ m_print->throw_if_canceled();
+ m_layers[layer_idx]->make_perimeters();
+ }
+ }
+ );
+ m_print->throw_if_canceled();
+ 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->set_done(posPerimeters);
+}
+
+void PrintObject::prepare_infill()
+{
+ if (this->is_step_done(posPrepareInfill))
+ return;
+
+ this->set_started(posPrepareInfill);
+ 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();
+ 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 : m_layers)
+ for (auto *region : layer->m_regions) {
+ region->prepare_fill_surfaces();
+ m_print->throw_if_canceled();
+ }
+
+ // 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();
+ m_print->throw_if_canceled();
+
+ // Add solid fills to ensure the shell vertical thickness.
+ this->discover_vertical_shells();
+ 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 : 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
+ } // 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();
+ 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 : 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
+ } // 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();
+ 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 : 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
+ } // 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();
+ m_print->throw_if_canceled();
+
+ // combine fill surfaces to honor the "infill every N layers" option
+ this->combine_infill();
+ 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 : 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 : 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 */
+
+ this->set_done(posPrepareInfill);
+}
+
+void PrintObject::infill()
+{
+ if (! this->is_printable())
+ return;
+
+ // prerequisites
+ this->prepare_infill();
+
+ if (! this->is_step_done(posInfill)) {
+ this->set_started(posInfill);
+ BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start";
+ tbb::parallel_for(
+ 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) {
+ m_print->throw_if_canceled();
+ m_layers[layer_idx]->make_fills();
+ }
+ }
+ );
+ 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};
+ */
+ this->set_done(posInfill);
+ }
+}
+
+void PrintObject::generate_support_material()
+{
+ if (! this->is_step_done(posSupportMaterial)) {
+ this->set_started(posSupportMaterial);
+ this->clear_support_layers();
+ 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();
+ m_print->throw_if_canceled();
+ }
+ 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().
@@ -251,8 +574,8 @@ bool PrintObject::invalidate_state_by_config_options(const std::vectorinvalidate_all_steps();
this->reset_layer_height_profile();
- this->invalidate_all_steps();
invalidated = true;
}
}
@@ -265,141 +588,44 @@ bool PrintObject::invalidate_state_by_config_options(const std::vectorstate.invalidate(step);
+ bool invalidated = m_state.invalidate(step, m_print->m_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.
// It also decides about what the wipe_into_infill / wipe_into_object features will do,
// and that too depends on many of the settings.
- 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;
-}
-
-void PrintObject::_prepare_infill()
-{
- if (!this->is_printable())
- return;
-
- // 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 */
+ 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).
@@ -420,41 +646,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) {
+ 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;
@@ -572,26 +799,29 @@ void PrintObject::detect_surfaces_type()
}
}
); // for each layer of a region
+ 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) {
- 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
});
+ 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
@@ -603,19 +833,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) {
- // 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]);
}
}
);
+ m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - end";
}
}
@@ -633,17 +865,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;
}
}
@@ -652,14 +884,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) {
- 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;
@@ -669,7 +902,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));
@@ -682,7 +915,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);
@@ -713,36 +946,38 @@ void PrintObject::discover_vertical_shells()
cache.holes = union_(cache.holes, false);
}
});
+ 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) {
- 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];
@@ -753,30 +988,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));
}
}
});
+ 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);
-
+ 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");
@@ -799,9 +1035,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]);
@@ -819,8 +1055,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;
@@ -982,12 +1218,13 @@ void PrintObject::discover_vertical_shells()
layerm->fill_surfaces.append(new_internal_void, stInternalVoid);
layerm->fill_surfaces.append(new_internal_solid, stInternalSolid);
} // for each layer
- });
+ });
+ 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");
}
@@ -1005,14 +1242,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
@@ -1023,10 +1260,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;
@@ -1041,8 +1278,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;
@@ -1122,6 +1359,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 */
+ m_print->throw_if_canceled();
}
}
}
@@ -1129,7 +1367,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(2)), this->print()->object_extruders());
}
@@ -1220,36 +1458,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