Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortamasmeszaros <meszaros.q@gmail.com>2019-07-17 18:25:31 +0300
committertamasmeszaros <meszaros.q@gmail.com>2019-07-17 18:25:31 +0300
commita695dec51a83250535458c0b3457dedbdd23d352 (patch)
treefa50bc0a5e004111080f2ce965ae8453a60dc660
parentc82fb9c84fa9a72b41e8cad8445f65b4b4038b06 (diff)
parent2b9d285a16302fd3555a3ead6293baa3d3b4c769 (diff)
Merge branch 'master' into tm_arrange_selection
-rw-r--r--.clang-format2
-rw-r--r--CMakeLists.txt19
-rw-r--r--deps/CMakeLists.txt4
-rw-r--r--deps/deps-unix-common.cmake10
-rw-r--r--deps/deps-windows.cmake14
-rw-r--r--doc/How to build - Mac OS.md3
-rw-r--r--lib/Slic3r.pm1
-rw-r--r--lib/Slic3r/Print/Simple.pm104
-rw-r--r--lib/Slic3r/Test.pm82
-rw-r--r--resources/icons/drop_to_bed.pngbin0 -> 528 bytes
-rw-r--r--resources/icons/redo.svg12
-rw-r--r--resources/icons/redo_toolbar.svg12
-rw-r--r--resources/icons/row.pngbin0 -> 1923 bytes
-rw-r--r--resources/icons/table.pngbin0 -> 465 bytes
-rw-r--r--resources/icons/undo_toolbar.svg12
-rw-r--r--sandboxes/slabasebed/slabasebed.cpp32
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/admesh/connect.cpp2
-rw-r--r--src/avrdude/arduino.c13
-rw-r--r--src/avrdude/avr.c24
-rw-r--r--src/avrdude/avr910.c2
-rw-r--r--src/avrdude/avrdude-slic3r.conf.h6
-rw-r--r--src/avrdude/avrdude-slic3r.cpp6
-rw-r--r--src/avrdude/avrdude.h2
-rw-r--r--src/avrdude/avrpart.c4
-rw-r--r--src/avrdude/buspirate.c4
-rw-r--r--src/avrdude/butterfly.c2
-rw-r--r--src/avrdude/conf-generate.cpp6
-rw-r--r--src/avrdude/config.c6
-rw-r--r--src/avrdude/config_gram.c2
-rw-r--r--src/avrdude/config_gram.y2
-rw-r--r--src/avrdude/fileio.c43
-rw-r--r--src/avrdude/lists.c12
-rw-r--r--src/avrdude/main.c6
-rw-r--r--src/avrdude/pindefs.c2
-rw-r--r--src/avrdude/ser_win32.c12
-rw-r--r--src/avrdude/serbb_win32.c8
-rw-r--r--src/avrdude/stk500.c12
-rw-r--r--src/avrdude/stk500v2.c205
-rw-r--r--src/avrdude/term.c26
-rw-r--r--src/libslic3r/BoundingBox.hpp8
-rw-r--r--src/libslic3r/CMakeLists.txt6
-rw-r--r--src/libslic3r/Config.cpp162
-rw-r--r--src/libslic3r/Config.hpp194
-rw-r--r--src/libslic3r/EdgeGrid.cpp24
-rw-r--r--src/libslic3r/EdgeGrid.hpp4
-rw-r--r--src/libslic3r/ExtrusionSimulator.cpp16
-rw-r--r--src/libslic3r/Fill/Fill.cpp12
-rw-r--r--src/libslic3r/Fill/FillPlanePath.cpp6
-rw-r--r--src/libslic3r/Fill/FillRectilinear2.cpp13
-rw-r--r--src/libslic3r/Fill/FillRectilinear3.cpp6
-rw-r--r--src/libslic3r/Format/3mf.cpp174
-rw-r--r--src/libslic3r/Format/AMF.cpp79
-rw-r--r--src/libslic3r/Format/objparser.cpp4
-rw-r--r--src/libslic3r/GCode.cpp307
-rw-r--r--src/libslic3r/GCode.hpp8
-rw-r--r--src/libslic3r/GCode/Analyzer.cpp40
-rw-r--r--src/libslic3r/GCode/Analyzer.hpp7
-rw-r--r--src/libslic3r/GCode/CoolingBuffer.cpp2
-rw-r--r--src/libslic3r/GCode/PreviewData.cpp15
-rw-r--r--src/libslic3r/GCode/PrintExtents.cpp29
-rw-r--r--src/libslic3r/GCode/SpiralVase.cpp2
-rw-r--r--src/libslic3r/GCode/ToolOrdering.cpp6
-rw-r--r--src/libslic3r/GCode/WipeTower.cpp (renamed from src/libslic3r/GCode/WipeTowerPrusaMM.cpp)641
-rw-r--r--src/libslic3r/GCode/WipeTower.hpp462
-rw-r--r--src/libslic3r/GCode/WipeTowerPrusaMM.hpp388
-rw-r--r--src/libslic3r/GCodeTimeEstimator.cpp323
-rw-r--r--src/libslic3r/GCodeTimeEstimator.hpp35
-rw-r--r--src/libslic3r/Geometry.cpp2
-rw-r--r--src/libslic3r/Geometry.hpp14
-rw-r--r--src/libslic3r/Layer.cpp2
-rw-r--r--src/libslic3r/LayerRegion.cpp4
-rw-r--r--src/libslic3r/Line.cpp1
-rw-r--r--src/libslic3r/MTUtils.hpp62
-rw-r--r--src/libslic3r/Model.cpp107
-rw-r--r--src/libslic3r/Model.hpp395
-rw-r--r--src/libslic3r/MotionPlanner.cpp2
-rw-r--r--src/libslic3r/ObjectID.cpp22
-rw-r--r--src/libslic3r/ObjectID.hpp93
-rw-r--r--src/libslic3r/PerimeterGenerator.cpp4
-rw-r--r--src/libslic3r/PlaceholderParser.cpp6
-rw-r--r--src/libslic3r/Point.hpp21
-rw-r--r--src/libslic3r/PolylineCollection.cpp2
-rw-r--r--src/libslic3r/Print.cpp523
-rw-r--r--src/libslic3r/Print.hpp36
-rw-r--r--src/libslic3r/PrintBase.hpp2
-rw-r--r--src/libslic3r/PrintConfig.cpp131
-rw-r--r--src/libslic3r/PrintConfig.hpp66
-rw-r--r--src/libslic3r/PrintObject.cpp349
-rw-r--r--src/libslic3r/SLA/SLAAutoSupports.cpp9
-rw-r--r--src/libslic3r/SLA/SLABasePool.cpp362
-rw-r--r--src/libslic3r/SLA/SLABasePool.hpp37
-rw-r--r--src/libslic3r/SLA/SLACommon.hpp14
-rw-r--r--src/libslic3r/SLA/SLASpatIndex.hpp65
-rw-r--r--src/libslic3r/SLA/SLASupportTree.cpp517
-rw-r--r--src/libslic3r/SLA/SLASupportTree.hpp22
-rw-r--r--src/libslic3r/SLA/SLASupportTreeIGL.cpp136
-rw-r--r--src/libslic3r/SLAPrint.cpp247
-rw-r--r--src/libslic3r/SLAPrint.hpp4
-rw-r--r--src/libslic3r/Slicing.cpp25
-rw-r--r--src/libslic3r/Slicing.hpp15
-rw-r--r--src/libslic3r/SupportMaterial.cpp24
-rw-r--r--src/libslic3r/Technologies.hpp11
-rw-r--r--src/libslic3r/TriangleMesh.cpp4
-rw-r--r--src/libslic3r/TriangleMesh.hpp20
-rw-r--r--src/libslic3r/Utils.hpp4
-rw-r--r--src/libslic3r/pchheader.hpp5
-rw-r--r--src/libslic3r/utils.cpp4
-rw-r--r--src/semver/semver.c17
-rw-r--r--src/slic3r/CMakeLists.txt6
-rw-r--r--src/slic3r/GUI/3DBed.cpp29
-rw-r--r--src/slic3r/GUI/3DBed.hpp12
-rw-r--r--src/slic3r/GUI/3DScene.cpp701
-rw-r--r--src/slic3r/GUI/3DScene.hpp165
-rw-r--r--src/slic3r/GUI/AppConfig.cpp27
-rw-r--r--src/slic3r/GUI/AppConfig.hpp3
-rw-r--r--src/slic3r/GUI/BedShapeDialog.cpp36
-rw-r--r--src/slic3r/GUI/BonjourDialog.cpp2
-rw-r--r--src/slic3r/GUI/ConfigWizard.cpp6
-rw-r--r--src/slic3r/GUI/Field.cpp14
-rw-r--r--src/slic3r/GUI/FirmwareDialog.cpp3
-rw-r--r--src/slic3r/GUI/GLCanvas3D.cpp651
-rw-r--r--src/slic3r/GUI/GLCanvas3D.hpp34
-rw-r--r--src/slic3r/GUI/GLCanvas3DManager.cpp5
-rw-r--r--src/slic3r/GUI/GLCanvas3DManager.hpp1
-rw-r--r--src/slic3r/GUI/GLToolbar.cpp319
-rw-r--r--src/slic3r/GUI/GLToolbar.hpp110
-rw-r--r--src/slic3r/GUI/GUI_App.cpp17
-rw-r--r--src/slic3r/GUI/GUI_App.hpp2
-rw-r--r--src/slic3r/GUI/GUI_ObjectLayers.cpp341
-rw-r--r--src/slic3r/GUI/GUI_ObjectLayers.hpp88
-rw-r--r--src/slic3r/GUI/GUI_ObjectList.cpp935
-rw-r--r--src/slic3r/GUI/GUI_ObjectList.hpp72
-rw-r--r--src/slic3r/GUI/GUI_ObjectManipulation.cpp87
-rw-r--r--src/slic3r/GUI/GUI_ObjectManipulation.hpp1
-rw-r--r--src/slic3r/GUI/GUI_ObjectSettings.cpp138
-rw-r--r--src/slic3r/GUI/GUI_ObjectSettings.hpp2
-rw-r--r--src/slic3r/GUI/GUI_Preview.cpp51
-rw-r--r--src/slic3r/GUI/GUI_Preview.hpp2
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoBase.cpp15
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoBase.hpp44
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoCut.cpp74
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoCut.hpp18
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp40
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp20
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoMove.cpp27
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoMove.hpp16
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp47
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp30
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoScale.cpp39
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoScale.hpp18
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp184
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp31
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmosManager.cpp348
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmosManager.hpp98
-rw-r--r--src/slic3r/GUI/ImGuiWrapper.cpp34
-rw-r--r--src/slic3r/GUI/ImGuiWrapper.hpp3
-rw-r--r--src/slic3r/GUI/KBShortcutsDialog.cpp3
-rw-r--r--src/slic3r/GUI/MainFrame.cpp60
-rw-r--r--src/slic3r/GUI/MainFrame.hpp5
-rw-r--r--src/slic3r/GUI/OptionsGroup.cpp15
-rw-r--r--src/slic3r/GUI/OptionsGroup.hpp8
-rw-r--r--src/slic3r/GUI/Plater.cpp350
-rw-r--r--src/slic3r/GUI/Plater.hpp17
-rw-r--r--src/slic3r/GUI/Preset.cpp25
-rw-r--r--src/slic3r/GUI/PresetBundle.cpp2
-rw-r--r--src/slic3r/GUI/Selection.cpp217
-rw-r--r--src/slic3r/GUI/Selection.hpp12
-rw-r--r--src/slic3r/GUI/Tab.cpp155
-rw-r--r--src/slic3r/GUI/Tab.hpp6
-rw-r--r--src/slic3r/GUI/WipeTowerDialog.cpp2
-rw-r--r--src/slic3r/GUI/wxExtensions.cpp312
-rw-r--r--src/slic3r/GUI/wxExtensions.hpp38
-rw-r--r--src/slic3r/Utils/PresetUpdater.cpp22
-rw-r--r--src/slic3r/Utils/UndoRedo.cpp819
-rw-r--r--src/slic3r/Utils/UndoRedo.hpp95
-rw-r--r--t/combineinfill.t2
-rw-r--r--t/multi.t4
-rw-r--r--t/print.t23
-rw-r--r--t/skirt_brim.t4
-rw-r--r--xs/t/15_config.t29
-rw-r--r--xs/t/19_model.t8
-rw-r--r--xs/xsp/Config.xsp6
-rw-r--r--xs/xsp/Model.xsp6
-rw-r--r--xs/xsp/Print.xsp8
185 files changed, 9231 insertions, 5043 deletions
diff --git a/.clang-format b/.clang-format
index e0a41be5f..6b68254ec 100644
--- a/.clang-format
+++ b/.clang-format
@@ -46,7 +46,7 @@ BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
-ColumnLimit: 75
+ColumnLimit: 78
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: true
ConstructorInitializerAllOnOneLineOrOnePerLine: true
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ba264c8c3..49717b68d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -169,8 +169,19 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STRE
# On GCC and Clang, no return from a non-void function is a warning only. Here, we make it an error.
add_compile_options(-Werror=return-type)
- #removes LOTS of extraneous Eigen warnings
- # add_compile_options(-Wno-ignored-attributes) # Tamas: Eigen include dirs are marked as SYSTEM
+ #removes LOTS of extraneous Eigen warnings (GCC only supports it since 6.1)
+ #if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6.1)
+ # add_compile_options(-Wno-ignored-attributes) # Tamas: Eigen include dirs are marked as SYSTEM
+ #endif()
+
+ #GCC generates loads of -Wunknown-pragmas when compiling igl. The fix is not easy due to a bug in gcc, see
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66943 or
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431
+ # We will turn the warning of for GCC for now:
+ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+ add_compile_options(-Wno-unknown-pragmas)
+ endif()
+
if (SLIC3R_ASAN)
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
@@ -344,6 +355,10 @@ if (NOT GLEW_FOUND)
endif ()
include_directories(${GLEW_INCLUDE_DIRS})
+# Find the Cereal serialization library
+add_library(cereal INTERFACE)
+target_include_directories(cereal INTERFACE include)
+
# l10n
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization")
add_custom_target(pot
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index d8e72370b..f6a80f3ca 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -89,6 +89,7 @@ if (MSVC)
dep_libcurl
dep_wxwidgets
dep_gtest
+ dep_cereal
dep_nlopt
# dep_qhull # Experimental
dep_zlib # on Windows we still need zlib
@@ -103,9 +104,10 @@ else()
dep_libcurl
dep_wxwidgets
dep_gtest
+ dep_cereal
dep_nlopt
dep_qhull
- dep_libigl
+ # dep_libigl # Not working, static build has different Eigen
)
endif()
diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake
index 3614e9444..e323460a6 100644
--- a/deps/deps-unix-common.cmake
+++ b/deps/deps-unix-common.cmake
@@ -19,6 +19,16 @@ ExternalProject_Add(dep_gtest
CMAKE_ARGS -DBUILD_GMOCK=OFF ${DEP_CMAKE_OPTS} -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
)
+ExternalProject_Add(dep_cereal
+ EXCLUDE_FROM_ALL 1
+ URL "https://github.com/USCiLab/cereal/archive/v1.2.2.tar.gz"
+# URL_HASH SHA256=c6dd7a5701fff8ad5ebb45a3dc8e757e61d52658de3918e38bab233e7fd3b4ae
+ CMAKE_ARGS
+ -DJUST_INSTALL_CEREAL=on
+ -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
+ ${DEP_CMAKE_OPTS}
+)
+
ExternalProject_Add(dep_nlopt
EXCLUDE_FROM_ALL 1
URL "https://github.com/stevengj/nlopt/archive/v2.5.0.tar.gz"
diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake
index 0b3fcb13c..9092f330b 100644
--- a/deps/deps-windows.cmake
+++ b/deps/deps-windows.cmake
@@ -115,6 +115,20 @@ if (${DEP_DEBUG})
endif ()
+ExternalProject_Add(dep_cereal
+ EXCLUDE_FROM_ALL 1
+ URL "https://github.com/USCiLab/cereal/archive/v1.2.2.tar.gz"
+# URL_HASH SHA256=c6dd7a5701fff8ad5ebb45a3dc8e757e61d52658de3918e38bab233e7fd3b4ae
+ CMAKE_GENERATOR "${DEP_MSVC_GEN}"
+ CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}"
+ CMAKE_ARGS
+ -DJUST_INSTALL_CEREAL=on
+ "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
+ BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
+ INSTALL_COMMAND ""
+)
+
+
ExternalProject_Add(dep_nlopt
EXCLUDE_FROM_ALL 1
URL "https://github.com/stevengj/nlopt/archive/v2.5.0.tar.gz"
diff --git a/doc/How to build - Mac OS.md b/doc/How to build - Mac OS.md
index 42a71e10d..b4196909d 100644
--- a/doc/How to build - Mac OS.md
+++ b/doc/How to build - Mac OS.md
@@ -20,6 +20,9 @@ You can also customize the bundle output path using the `-DDESTDIR=<some path>`
**Warning**: Once the dependency bundle is installed in a destdir, the destdir cannot be moved elsewhere.
(This is because wxWidgets hardcodes the installation path.)
+FIXME The Cereal serialization library needs a tiny patch on some old OSX clang installations
+https://github.com/USCiLab/cereal/issues/339#issuecomment-246166717
+
### Building PrusaSlicer
diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm
index 44100db8c..94f0b5658 100644
--- a/lib/Slic3r.pm
+++ b/lib/Slic3r.pm
@@ -50,7 +50,6 @@ use Slic3r::Point;
use Slic3r::Polygon;
use Slic3r::Polyline;
use Slic3r::Print::Object;
-use Slic3r::Print::Simple;
use Slic3r::Surface;
our $build = eval "use Slic3r::Build; 1";
diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm
deleted file mode 100644
index 2ab68f4d3..000000000
--- a/lib/Slic3r/Print/Simple.pm
+++ /dev/null
@@ -1,104 +0,0 @@
-# A simple wrapper to quickly print a single model without a GUI.
-# Used by the command line slic3r.pl, by command line utilities pdf-slic3s.pl and view-toolpaths.pl,
-# and by the quick slice menu of the Slic3r GUI.
-#
-# It creates and owns an instance of Slic3r::Print to perform the slicing
-# and it accepts an instance of Slic3r::Model from the outside.
-
-package Slic3r::Print::Simple;
-use Moo;
-
-use Slic3r::Geometry qw(X Y);
-
-has '_print' => (
- is => 'ro',
- default => sub { Slic3r::Print->new },
- handles => [qw(apply_config_perl_tests_only extruders output_filepath
- total_used_filament total_extruded_volume
- placeholder_parser process)],
-);
-
-has 'duplicate' => (
- is => 'rw',
- default => sub { 1 },
-);
-
-has 'scale' => (
- is => 'rw',
- default => sub { 1 },
-);
-
-has 'rotate' => (
- is => 'rw',
- default => sub { 0 },
-);
-
-has 'duplicate_grid' => (
- is => 'rw',
- default => sub { [1,1] },
-);
-
-has 'print_center' => (
- is => 'rw',
- default => sub { Slic3r::Pointf->new(100,100) },
-);
-
-has 'dont_arrange' => (
- is => 'rw',
- default => sub { 0 },
-);
-
-has 'output_file' => (
- is => 'rw',
-);
-
-sub set_model {
- # $model is of type Slic3r::Model
- my ($self, $model) = @_;
-
- # make method idempotent so that the object is reusable
- $self->_print->clear_objects;
-
- # make sure all objects have at least one defined instance
- my $need_arrange = $model->add_default_instances && ! $self->dont_arrange;
-
- # apply scaling and rotation supplied from command line if any
- foreach my $instance (map @{$_->instances}, @{$model->objects}) {
- $instance->set_scaling_factor($instance->scaling_factor * $self->scale);
- $instance->set_rotation($instance->rotation + $self->rotate);
- }
-
- if ($self->duplicate_grid->[X] > 1 || $self->duplicate_grid->[Y] > 1) {
- $model->duplicate_objects_grid($self->duplicate_grid->[X], $self->duplicate_grid->[Y], $self->_print->config->duplicate_distance);
- } elsif ($need_arrange) {
- $model->duplicate_objects($self->duplicate, $self->_print->config->min_object_distance);
- } elsif ($self->duplicate > 1) {
- # if all input objects have defined position(s) apply duplication to the whole model
- $model->duplicate($self->duplicate, $self->_print->config->min_object_distance);
- }
- $_->translate(0,0,-$_->bounding_box->z_min) for @{$model->objects};
- $model->center_instances_around_point($self->print_center) if (! $self->dont_arrange);
-
- foreach my $model_object (@{$model->objects}) {
- $self->_print->auto_assign_extruders($model_object);
- $self->_print->add_model_object($model_object);
- }
-}
-
-sub export_gcode {
- my ($self) = @_;
- $self->_print->validate;
- $self->_print->export_gcode($self->output_file // '');
-}
-
-sub export_png {
- my ($self) = @_;
-
- $self->_before_export;
-
- $self->_print->export_png(output_file => $self->output_file);
-
- $self->_after_export;
-}
-
-1;
diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm
index d1b99e48c..570bca41b 100644
--- a/lib/Slic3r/Test.pm
+++ b/lib/Slic3r/Test.pm
@@ -146,60 +146,66 @@ sub mesh {
}
sub model {
- my ($model_name, %params) = @_;
+ my ($model_names, %params) = @_;
+ $model_names = [ $model_names ] if ! ref($model_names);
- my $input_file = "${model_name}.stl";
- my $mesh = mesh($model_name, %params);
-# $mesh->write_ascii("out/$input_file");
-
my $model = Slic3r::Model->new;
- my $object = $model->add_object(input_file => $input_file);
- $model->set_material($model_name);
- $object->add_volume(mesh => $mesh, material_id => $model_name);
- $object->add_instance(
- offset => Slic3r::Pointf->new(0,0),
- # 3D full transform
- rotation => Slic3r::Pointf3->new(0, 0, $params{rotation} // 0),
- scaling_factor => Slic3r::Pointf3->new($params{scale} // 1, $params{scale} // 1, $params{scale} // 1),
- # old transform
-# rotation => $params{rotation} // 0,
-# scaling_factor => $params{scale} // 1,
- );
+
+ for my $model_name (@$model_names) {
+ my $input_file = "${model_name}.stl";
+ my $mesh = mesh($model_name, %params);
+ # $mesh->write_ascii("out/$input_file");
+
+ my $object = $model->add_object(input_file => $input_file);
+ $model->set_material($model_name);
+ $object->add_volume(mesh => $mesh, material_id => $model_name);
+ $object->add_instance(
+ offset => Slic3r::Pointf->new(0,0),
+ # 3D full transform
+ rotation => Slic3r::Pointf3->new(0, 0, $params{rotation} // 0),
+ scaling_factor => Slic3r::Pointf3->new($params{scale} // 1, $params{scale} // 1, $params{scale} // 1),
+ # old transform
+ # rotation => $params{rotation} // 0,
+ # scaling_factor => $params{scale} // 1,
+ );
+ }
return $model;
}
sub init_print {
my ($models, %params) = @_;
+ my $model;
+ if (ref($models) eq 'ARRAY') {
+ $model = model($models, %params);
+ } elsif (ref($models)) {
+ $model = $models;
+ } else {
+ $model = model([$models], %params);
+ }
my $config = Slic3r::Config->new;
$config->apply($params{config}) if $params{config};
$config->set('gcode_comments', 1) if $ENV{SLIC3R_TESTS_GCODE};
my $print = Slic3r::Print->new;
- $print->apply_config_perl_tests_only($config);
-
- $models = [$models] if ref($models) ne 'ARRAY';
- $models = [ map { ref($_) ? $_ : model($_, %params) } @$models ];
- for my $model (@$models) {
- die "Unknown model in test" if !defined $model;
- if (defined $params{duplicate} && $params{duplicate} > 1) {
- $model->duplicate($params{duplicate} // 1, $print->config->min_object_distance);
- }
- $model->arrange_objects($print->config->min_object_distance);
- $model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100));
- foreach my $model_object (@{$model->objects}) {
- $print->auto_assign_extruders($model_object);
- $print->add_model_object($model_object);
- }
+ die "Unknown model in test" if !defined $model;
+ if (defined $params{duplicate} && $params{duplicate} > 1) {
+ $model->duplicate($params{duplicate} // 1, $config->min_object_distance);
}
- # Call apply_config_perl_tests_only one more time, so that the layer height profiles are updated over all PrintObjects.
- $print->apply_config_perl_tests_only($config);
+ $model->arrange_objects($config->min_object_distance);
+ $model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100));
+ foreach my $model_object (@{$model->objects}) {
+ $model_object->ensure_on_bed;
+ $print->auto_assign_extruders($model_object);
+ }
+
+ $print->apply($model, $config);
$print->validate;
# We return a proxy object in order to keep $models alive as required by the Print API.
return Slic3r::Test::Print->new(
- print => $print,
- models => $models,
+ print => $print,
+ model => $model,
);
}
@@ -250,7 +256,7 @@ sub add_facet {
package Slic3r::Test::Print;
use Moo;
-has 'print' => (is => 'ro', required => 1, handles => [qw(process apply_config_perl_tests_only)]);
-has 'models' => (is => 'ro', required => 1);
+has 'print' => (is => 'ro', required => 1, handles => [qw(process apply)]);
+has 'model' => (is => 'ro', required => 1);
1;
diff --git a/resources/icons/drop_to_bed.png b/resources/icons/drop_to_bed.png
new file mode 100644
index 000000000..b60e1b137
--- /dev/null
+++ b/resources/icons/drop_to_bed.png
Binary files differ
diff --git a/resources/icons/redo.svg b/resources/icons/redo.svg
new file mode 100644
index 000000000..9109779bb
--- /dev/null
+++ b/resources/icons/redo.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<g id="redo">
+ <path fill="none" stroke="#ED6B21" stroke-width="2" stroke-linecap="round" stroke-miterlimit="10" d="M13.39,11
+ c-0.91,1.78-2.76,3-4.89,3C5.46,14,3,11.54,3,8.5C3,5.46,5.46,3,8.5,3C8.67,3,8.84,3.01,9,3.03"/>
+
+ <polygon fill="#ED6B21" stroke="#ED6B21" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
+ 9,1 9,5 12,3 "/>
+</g>
+</svg>
diff --git a/resources/icons/redo_toolbar.svg b/resources/icons/redo_toolbar.svg
new file mode 100644
index 000000000..ad073244f
--- /dev/null
+++ b/resources/icons/redo_toolbar.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<g id="redo">
+ <path fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" d="M13.39,11
+ c-0.91,1.78-2.76,3-4.89,3C5.46,14,3,11.54,3,8.5C3,5.46,5.46,3,8.5,3C8.67,3,8.84,3.01,9,3.03"/>
+
+ <polygon fill="#ED6B21" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
+ 9,1 9,5 12,3 "/>
+</g>
+</svg>
diff --git a/resources/icons/row.png b/resources/icons/row.png
new file mode 100644
index 000000000..18a6034fd
--- /dev/null
+++ b/resources/icons/row.png
Binary files differ
diff --git a/resources/icons/table.png b/resources/icons/table.png
new file mode 100644
index 000000000..3bc0bd32f
--- /dev/null
+++ b/resources/icons/table.png
Binary files differ
diff --git a/resources/icons/undo_toolbar.svg b/resources/icons/undo_toolbar.svg
new file mode 100644
index 000000000..699ccd807
--- /dev/null
+++ b/resources/icons/undo_toolbar.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<g id="undo">
+ <path fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" d="M3,11
+ c0.91,1.78,2.76,3,4.89,3c3.04,0,5.5-2.46,5.5-5.5c0-3.04-2.46-5.5-5.5-5.5c-0.17,0-0.34,0.01-0.5,0.03"/>
+
+ <polygon fill="#ED6B21" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
+ 7.39,1 7.39,5 4.39,3 "/>
+</g>
+</svg>
diff --git a/sandboxes/slabasebed/slabasebed.cpp b/sandboxes/slabasebed/slabasebed.cpp
index 0c34eb9f5..5393f61fd 100644
--- a/sandboxes/slabasebed/slabasebed.cpp
+++ b/sandboxes/slabasebed/slabasebed.cpp
@@ -15,7 +15,8 @@ const std::string USAGE_STR = {
namespace Slic3r { namespace sla {
-Contour3D create_base_pool(const ExPolygons &ground_layer,
+Contour3D create_base_pool(const Polygons &ground_layer,
+ const Polygons &holes = {},
const PoolConfig& cfg = PoolConfig());
Contour3D walls(const Polygon& floor_plate, const Polygon& ceiling,
@@ -42,37 +43,28 @@ int main(const int argc, const char *argv[]) {
model.ReadSTLFile(argv[1]);
model.align_to_origin();
- ExPolygons ground_slice;
- sla::Contour3D mesh;
-// TriangleMesh basepool;
-
+ Polygons ground_slice;
sla::base_plate(model, ground_slice, 0.1f);
-
if(ground_slice.empty()) return EXIT_FAILURE;
-// ExPolygon bottom_plate = ground_slice.front();
-// ExPolygon top_plate = bottom_plate;
-// sla::offset(top_plate, coord_t(3.0/SCALING_FACTOR));
-// sla::offset(bottom_plate, coord_t(1.0/SCALING_FACTOR));
+ Polygon gndfirst; gndfirst = ground_slice.front();
+ sla::offset_with_breakstick_holes(gndfirst, 0.5, 10, 0.3);
+
+ sla::Contour3D mesh;
+
bench.start();
-// TriangleMesh pool;
sla::PoolConfig cfg;
cfg.min_wall_height_mm = 0;
- cfg.edge_radius_mm = 0.2;
- mesh = sla::create_base_pool(ground_slice, cfg);
-
-// mesh.merge(triangulate_expolygon_3d(top_plate, 3.0, false));
-// mesh.merge(triangulate_expolygon_3d(bottom_plate, 0.0, true));
-// mesh = sla::walls(bottom_plate.contour, top_plate.contour, 0, 3, 2.0, [](){});
-
+ cfg.edge_radius_mm = 0;
+ mesh = sla::create_base_pool(ground_slice, {}, cfg);
+
bench.stop();
cout << "Base pool creation time: " << std::setprecision(10)
<< bench.getElapsedSec() << " seconds." << endl;
-
-// auto point = []()
+
for(auto& trind : mesh.indices) {
Vec3d p0 = mesh.points[size_t(trind[0])];
Vec3d p1 = mesh.points[size_t(trind[1])];
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 61faa0571..3ee46289a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -75,7 +75,7 @@ if (NOT MSVC)
set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
endif ()
-target_link_libraries(PrusaSlicer libslic3r)
+target_link_libraries(PrusaSlicer libslic3r cereal)
if (APPLE)
# add_compile_options(-stdlib=libc++)
# add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE)
diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp
index e729c8922..b86ec5055 100644
--- a/src/admesh/connect.cpp
+++ b/src/admesh/connect.cpp
@@ -132,7 +132,7 @@ struct HashTableEdges {
~HashTableEdges() {
#ifndef NDEBUG
for (int i = 0; i < this->M; ++ i)
- for (HashEdge *temp = this->heads[i]; this->heads[i] != this->tail; temp = this->heads[i])
+ for (HashEdge *temp = this->heads[i]; temp != this->tail; temp = temp->next)
++ this->freed;
this->tail = nullptr;
#endif /* NDEBUG */
diff --git a/src/avrdude/arduino.c b/src/avrdude/arduino.c
index 53e5ed822..e6008adeb 100644
--- a/src/avrdude/arduino.c
+++ b/src/avrdude/arduino.c
@@ -41,6 +41,7 @@
static int arduino_read_sig_bytes(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m)
{
unsigned char buf[32];
+ (void)p;
/* Signature byte reads are always 3 bytes. */
@@ -83,9 +84,9 @@ static int arduino_read_sig_bytes(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m)
static int prusa_init_external_flash(PROGRAMMER * pgm)
{
// Note: send/receive as in _the firmare_ send & receives
- const char entry_magic_send [] = "start\n";
- const char entry_magic_receive[] = "w25x20cl_enter\n";
- const char entry_magic_cfm [] = "w25x20cl_cfm\n";
+ const char entry_magic_send[] = "start\n";
+ const unsigned char entry_magic_receive[] = "w25x20cl_enter\n";
+ const char entry_magic_cfm[] = "w25x20cl_cfm\n";
const size_t buffer_len = 32; // Should be large enough for the above messages
int res;
@@ -94,7 +95,7 @@ static int prusa_init_external_flash(PROGRAMMER * pgm)
// 1. receive the "start" command
recv_size = sizeof(entry_magic_send) - 1;
- res = serial_recv(&pgm->fd, buffer, recv_size);
+ res = serial_recv(&pgm->fd, (unsigned char *)buffer, recv_size);
if (res < 0) {
avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname);
return -1;
@@ -111,7 +112,7 @@ static int prusa_init_external_flash(PROGRAMMER * pgm)
// 3. Receive the entry confirmation command
recv_size = sizeof(entry_magic_cfm) - 1;
- res = serial_recv(&pgm->fd, buffer, recv_size);
+ res = serial_recv(&pgm->fd, (unsigned char *)buffer, recv_size);
if (res < 0) {
avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname);
return -1;
@@ -142,7 +143,7 @@ static int arduino_open(PROGRAMMER * pgm, char * port)
// Sometimes there may be line noise generating input on the printer's USB-to-serial IC
// Here we try to clean its input buffer with a sequence of newlines (a minimum of 9 is needed):
- const char cleanup_newlines[] = "\n\n\n\n\n\n\n\n\n\n";
+ const unsigned char cleanup_newlines[] = "\n\n\n\n\n\n\n\n\n\n";
if (serial_send(&pgm->fd, cleanup_newlines, sizeof(cleanup_newlines) - 1) < 0) {
return -1;
}
diff --git a/src/avrdude/avr.c b/src/avrdude/avr.c
index 73dcaf4ff..defae75d5 100644
--- a/src/avrdude/avr.c
+++ b/src/avrdude/avr.c
@@ -341,7 +341,7 @@ int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype,
avr_tpi_setup_rw(pgm, mem, 0, TPI_NVMCMD_NO_OPERATION);
/* load bytes */
- for (lastaddr = i = 0; i < mem->size; i++) {
+ for (lastaddr = i = 0; i < (unsigned)mem->size; i++) {
RETURN_IF_CANCEL();
if (vmem == NULL ||
(vmem->tags[i] & TAG_ALLOCATED) != 0)
@@ -374,7 +374,7 @@ int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype,
/* quickly scan number of pages to be written to first */
for (pageaddr = 0, npages = 0;
- pageaddr < mem->size;
+ pageaddr < (unsigned)mem->size;
pageaddr += mem->page_size) {
/* check whether this page must be read */
for (i = pageaddr;
@@ -391,7 +391,7 @@ int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype,
}
for (pageaddr = 0, failure = 0, nread = 0;
- !failure && pageaddr < mem->size;
+ !failure && pageaddr < (unsigned)mem->size;
pageaddr += mem->page_size) {
RETURN_IF_CANCEL();
/* check whether this page must be read */
@@ -437,7 +437,7 @@ int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype,
}
}
- for (i=0; i < mem->size; i++) {
+ for (i = 0; i < (unsigned)mem->size; i++) {
RETURN_IF_CANCEL();
if (vmem == NULL ||
(vmem->tags[i] & TAG_ALLOCATED) != 0)
@@ -634,18 +634,18 @@ int avr_write_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
writeop = mem->op[AVR_OP_WRITE_HI];
else
writeop = mem->op[AVR_OP_WRITE_LO];
- caddr = addr / 2;
+ caddr = (unsigned short)(addr / 2);
}
else if (mem->paged && mem->op[AVR_OP_LOADPAGE_LO]) {
if (addr & 0x01)
writeop = mem->op[AVR_OP_LOADPAGE_HI];
else
writeop = mem->op[AVR_OP_LOADPAGE_LO];
- caddr = addr / 2;
+ caddr = (unsigned short)(addr / 2);
}
else {
writeop = mem->op[AVR_OP_WRITE];
- caddr = addr;
+ caddr = (unsigned short)addr;
}
if (writeop == NULL) {
@@ -723,7 +723,7 @@ int avr_write_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
gettimeofday (&tv, NULL);
prog_time = (tv.tv_sec * 1000000) + tv.tv_usec;
} while ((r != data) &&
- ((prog_time-start_time) < mem->max_write_delay));
+ ((prog_time - start_time) < (unsigned long)mem->max_write_delay));
}
/*
@@ -878,7 +878,7 @@ int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size,
}
/* write words, low byte first */
- for (lastaddr = i = 0; i < wsize; i += 2) {
+ for (lastaddr = i = 0; i < (unsigned)wsize; i += 2) {
RETURN_IF_CANCEL();
if ((m->tags[i] & TAG_ALLOCATED) != 0 ||
(m->tags[i + 1] & TAG_ALLOCATED) != 0) {
@@ -915,7 +915,7 @@ int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size,
/* quickly scan number of pages to be written to first */
for (pageaddr = 0, npages = 0;
- pageaddr < wsize;
+ pageaddr < (unsigned)wsize;
pageaddr += m->page_size) {
/* check whether this page must be written to */
for (i = pageaddr;
@@ -928,7 +928,7 @@ int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size,
}
for (pageaddr = 0, failure = 0, nwritten = 0;
- !failure && pageaddr < wsize;
+ !failure && pageaddr < (unsigned)wsize;
pageaddr += m->page_size) {
RETURN_IF_CANCEL();
/* check whether this page must be written to */
@@ -968,7 +968,7 @@ int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size,
page_tainted = 0;
flush_page = 0;
- for (i=0; i<wsize; i++) {
+ for (i = 0; i < (unsigned)wsize; i++) {
RETURN_IF_CANCEL();
data = m->buf[i];
report_progress(i, wsize, NULL);
diff --git a/src/avrdude/avr910.c b/src/avrdude/avr910.c
index aa5cc07a9..17a4ab69f 100644
--- a/src/avrdude/avr910.c
+++ b/src/avrdude/avr910.c
@@ -676,7 +676,7 @@ static int avr910_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
avr910_set_addr(pgm, addr / rd_size);
while (addr < max_addr) {
- if ((max_addr - addr) < blocksize) {
+ if ((max_addr - addr) < (unsigned)blocksize) {
blocksize = max_addr - addr;
}
cmd[1] = (blocksize >> 8) & 0xff;
diff --git a/src/avrdude/avrdude-slic3r.conf.h b/src/avrdude/avrdude-slic3r.conf.h
index 7cc901336..905b14ee8 100644
--- a/src/avrdude/avrdude-slic3r.conf.h
+++ b/src/avrdude/avrdude-slic3r.conf.h
@@ -1,5 +1,5 @@
/* WARN: This file is auto-generated from `avrdude-slic3r.conf` */
-unsigned char avrdude_slic3r_conf[] = {
+const unsigned char avrdude_slic3r_conf[] = {
0x0a, 0x23, 0x0a, 0x23, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73,
0x20, 0x61, 0x20, 0x62, 0x61, 0x73, 0x69, 0x63, 0x20, 0x6d, 0x69, 0x6e,
0x69, 0x6d, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20,
@@ -1184,5 +1184,5 @@ unsigned char avrdude_slic3r_conf[] = {
0x20, 0x20, 0x3b, 0x0a, 0x0a, 0x0a,
0, 0
};
-size_t avrdude_slic3r_conf_size = 14178;
-size_t avrdude_slic3r_conf_size_yy = 14180;
+const size_t avrdude_slic3r_conf_size = 14178;
+const size_t avrdude_slic3r_conf_size_yy = 14180;
diff --git a/src/avrdude/avrdude-slic3r.cpp b/src/avrdude/avrdude-slic3r.cpp
index 0140d93ed..7eff436e2 100644
--- a/src/avrdude/avrdude-slic3r.cpp
+++ b/src/avrdude/avrdude-slic3r.cpp
@@ -93,7 +93,7 @@ void AvrDude::priv::unset_handlers()
int AvrDude::priv::run_one(const std::vector<std::string> &args) {
- std::vector<char*> c_args {{ const_cast<char*>(PACKAGE) }};
+ std::vector<char*> c_args { const_cast<char*>(PACKAGE) };
std::string command_line { PACKAGE };
for (const auto &arg : args) {
@@ -105,7 +105,7 @@ int AvrDude::priv::run_one(const std::vector<std::string> &args) {
HandlerGuard guard(*this);
- message_fn(command_line.c_str(), command_line.size());
+ message_fn(command_line.c_str(), (unsigned)command_line.size());
const auto res = ::avrdude_main(static_cast<int>(c_args.size()), c_args.data());
@@ -200,7 +200,7 @@ AvrDude::Ptr AvrDude::run()
auto &message_fn = self->p->message_fn;
if (message_fn) {
message_fn(msg, sizeof(msg));
- message_fn(what, std::strlen(what));
+ message_fn(what, (unsigned)std::strlen(what));
message_fn("\n", 1);
}
diff --git a/src/avrdude/avrdude.h b/src/avrdude/avrdude.h
index ff464f46f..bc784cec6 100644
--- a/src/avrdude/avrdude.h
+++ b/src/avrdude/avrdude.h
@@ -64,6 +64,8 @@ int avrdude_main(int argc, char * argv []);
#include <windows.h>
#include <unistd.h>
+#define strdup _strdup
+
#ifdef UNICODE
#error "UNICODE should not be defined for avrdude bits on Windows"
#endif
diff --git a/src/avrdude/avrpart.c b/src/avrdude/avrpart.c
index d0bb951ee..1c7d6af00 100644
--- a/src/avrdude/avrpart.c
+++ b/src/avrdude/avrpart.c
@@ -358,7 +358,7 @@ AVRMEM * avr_locate_mem(AVRPART * p, char * desc)
int matches;
int l;
- l = strlen(desc);
+ l = (int)strlen(desc);
matches = 0;
match = NULL;
for (ln=lfirst(p->mem); ln; ln=lnext(ln)) {
@@ -662,7 +662,7 @@ void avr_display(FILE * f, AVRPART * p, const char * prefix, int verbose)
prefix);
px = prefix;
- i = strlen(prefix) + 5;
+ i = (int)strlen(prefix) + 5;
buf = (char *)malloc(i);
if (buf == NULL) {
/* ugh, this is not important enough to bail, just ignore it */
diff --git a/src/avrdude/buspirate.c b/src/avrdude/buspirate.c
index 5875d4283..dc8c68fbe 100644
--- a/src/avrdude/buspirate.c
+++ b/src/avrdude/buspirate.c
@@ -128,7 +128,7 @@ static int buspirate_recv_bin(struct programmer_t *pgm, unsigned char *buf, size
avrdude_message(MSG_DEBUG, "%s: buspirate_recv_bin():\n", progname);
dump_mem(MSG_DEBUG, buf, len);
- return len;
+ return (int)len;
}
static int buspirate_expect_bin(struct programmer_t *pgm,
@@ -249,7 +249,7 @@ static int buspirate_send(struct programmer_t *pgm, const char *str)
static int buspirate_is_prompt(const char *str)
{
- int strlen_str = strlen(str);
+ int strlen_str = (int)strlen(str);
/* Prompt ends with '>' or '> '
* all other input probably ends with '\n' */
return (str[strlen_str - 1] == '>' || str[strlen_str - 2] == '>');
diff --git a/src/avrdude/butterfly.c b/src/avrdude/butterfly.c
index beb5e04de..8f582e72a 100644
--- a/src/avrdude/butterfly.c
+++ b/src/avrdude/butterfly.c
@@ -675,7 +675,7 @@ static int butterfly_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
butterfly_set_addr(pgm, addr / rd_size);
}
while (addr < max_addr) {
- if ((max_addr - addr) < blocksize) {
+ if ((max_addr - addr) < (unsigned)blocksize) {
blocksize = max_addr - addr;
};
cmd[1] = (blocksize >> 8) & 0xff;
diff --git a/src/avrdude/conf-generate.cpp b/src/avrdude/conf-generate.cpp
index f2761ba22..4aa80ae0a 100644
--- a/src/avrdude/conf-generate.cpp
+++ b/src/avrdude/conf-generate.cpp
@@ -21,7 +21,7 @@ int main(int argc, char const *argv[])
}
std::cout << "/* WARN: This file is auto-generated from `" << filename << "` */" << std::endl;
- std::cout << "unsigned char " << symbol << "[] = {";
+ std::cout << "const unsigned char " << symbol << "[] = {";
char c;
std::cout << std::hex;
@@ -34,8 +34,8 @@ int main(int argc, char const *argv[])
std::cout << "\n 0, 0\n};\n";
std::cout << std::dec;
- std::cout << "size_t " << symbol << "_size = " << size << ";" << std::endl;
- std::cout << "size_t " << symbol << "_size_yy = " << size + 2 << ";" << std::endl;
+ std::cout << "const size_t " << symbol << "_size = " << size << ";" << std::endl;
+ std::cout << "const size_t " << symbol << "_size_yy = " << size + 2 << ";" << std::endl;
return 0;
}
diff --git a/src/avrdude/config.c b/src/avrdude/config.c
index 1c0ff5525..b82fb29cb 100644
--- a/src/avrdude/config.c
+++ b/src/avrdude/config.c
@@ -240,7 +240,7 @@ TOKEN * string(char * text)
return NULL; /* yyerror already called */
}
- len = strlen(text);
+ len = (int)strlen(text);
tkn->value.type = V_STR;
tkn->value.string = (char *) malloc(len+1);
@@ -351,7 +351,7 @@ int read_config(const char * file)
}
typedef struct yy_buffer_state *YY_BUFFER_STATE;
-extern YY_BUFFER_STATE yy_scan_bytes(char *base, size_t size);
+extern YY_BUFFER_STATE yy_scan_bytes(const char *base, size_t size);
extern void yy_delete_buffer(YY_BUFFER_STATE b);
int read_config_builtin()
@@ -363,7 +363,7 @@ int read_config_builtin()
// Note: Can't use yy_scan_buffer, it's buggy (?), leads to fread from a null FILE*
// and so unfortunatelly we have to use the copying variant here
- YY_BUFFER_STATE buffer = yy_scan_bytes(avrdude_slic3r_conf, avrdude_slic3r_conf_size);
+ YY_BUFFER_STATE buffer = yy_scan_bytes((const char *)avrdude_slic3r_conf, avrdude_slic3r_conf_size);
if (buffer == NULL) {
avrdude_message(MSG_INFO, "%s: read_config_builtin: Failed to initialize parsing buffer\n", progname);
return -1;
diff --git a/src/avrdude/config_gram.c b/src/avrdude/config_gram.c
index c1a65b13e..2d32fe739 100644
--- a/src/avrdude/config_gram.c
+++ b/src/avrdude/config_gram.c
@@ -3640,7 +3640,7 @@ static int parse_cmdbits(OPCODE * op)
break;
}
- len = strlen(s);
+ len = (int)strlen(s);
if (len == 0) {
yyerror("invalid bit specifier \"\"");
diff --git a/src/avrdude/config_gram.y b/src/avrdude/config_gram.y
index 0aa95a8e8..6a062352b 100644
--- a/src/avrdude/config_gram.y
+++ b/src/avrdude/config_gram.y
@@ -1493,7 +1493,7 @@ static int parse_cmdbits(OPCODE * op)
break;
}
- len = strlen(s);
+ len = (int)strlen(s);
if (len == 0) {
yyerror("invalid bit specifier \"\"");
diff --git a/src/avrdude/fileio.c b/src/avrdude/fileio.c
index 7803497a0..7f4c8edee 100644
--- a/src/avrdude/fileio.c
+++ b/src/avrdude/fileio.c
@@ -264,7 +264,7 @@ static int ihex_readrec(struct ihexrec * ihex, char * rec)
unsigned char cksum;
int rc;
- len = strlen(rec);
+ len = (int)strlen(rec);
offset = 1;
cksum = 0;
@@ -274,7 +274,7 @@ static int ihex_readrec(struct ihexrec * ihex, char * rec)
for (i=0; i<2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
- ihex->reclen = strtoul(buf, &e, 16);
+ ihex->reclen = (unsigned char)strtoul(buf, &e, 16);
if (e == buf || *e != 0)
return -1;
@@ -294,7 +294,7 @@ static int ihex_readrec(struct ihexrec * ihex, char * rec)
for (i=0; i<2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
- ihex->rectyp = strtoul(buf, &e, 16);
+ ihex->rectyp = (unsigned char)strtoul(buf, &e, 16);
if (e == buf || *e != 0)
return -1;
@@ -308,7 +308,7 @@ static int ihex_readrec(struct ihexrec * ihex, char * rec)
for (i=0; i<2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
- ihex->data[j] = strtoul(buf, &e, 16);
+ ihex->data[j] = (char)strtoul(buf, &e, 16);
if (e == buf || *e != 0)
return -1;
cksum += ihex->data[j];
@@ -320,7 +320,7 @@ static int ihex_readrec(struct ihexrec * ihex, char * rec)
for (i=0; i<2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
- ihex->cksum = strtoul(buf, &e, 16);
+ ihex->cksum = (char)strtoul(buf, &e, 16);
if (e == buf || *e != 0)
return -1;
@@ -361,7 +361,7 @@ static int ihex2b(char * infile, FILE * inf,
while (fgets((char *)buffer,MAX_LINE_LEN,inf)!=NULL) {
lineno++;
- len = strlen(buffer);
+ len = (int)strlen(buffer);
if (buffer[len-1] == '\n')
buffer[--len] = 0;
if (buffer[0] != ':')
@@ -388,7 +388,7 @@ static int ihex2b(char * infile, FILE * inf,
return -1;
}
nextaddr = ihex.loadofs + baseaddr - fileoffset;
- if (nextaddr + ihex.reclen > bufsize) {
+ if (nextaddr + ihex.reclen > (unsigned)bufsize) {
avrdude_message(MSG_INFO, "%s: ERROR: address 0x%04x out of range at line %d of %s\n",
progname, nextaddr+ihex.reclen, lineno, infile);
return -1;
@@ -502,10 +502,11 @@ static int b2srec(unsigned char * inbuf, int bufsize,
cksum += n + addr_width + 1;
- for (i=addr_width; i>0; i--)
+ for (i = addr_width; i>0; i--) {
cksum += (nextaddr >> (i-1) * 8) & 0xff;
+ }
- for (i=nextaddr; i<nextaddr + n; i++) {
+ for (unsigned i = nextaddr; i < nextaddr + n; i++) {
fprintf(outf, "%02X", buf[i]);
cksum += buf[i];
}
@@ -562,7 +563,7 @@ static int srec_readrec(struct ihexrec * srec, char * rec)
unsigned char cksum;
int rc;
- len = strlen(rec);
+ len = (int)strlen(rec);
offset = 1;
cksum = 0;
addr_width = 2;
@@ -582,7 +583,7 @@ static int srec_readrec(struct ihexrec * srec, char * rec)
for (i=0; i<2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
- srec->reclen = strtoul(buf, &e, 16);
+ srec->reclen = (char)strtoul(buf, &e, 16);
cksum += srec->reclen;
srec->reclen -= (addr_width+1);
if (e == buf || *e != 0)
@@ -594,7 +595,7 @@ static int srec_readrec(struct ihexrec * srec, char * rec)
for (i=0; i<addr_width*2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
- srec->loadofs = strtoull(buf, &e, 16);
+ srec->loadofs = strtoul(buf, &e, 16);
if (e == buf || *e != 0)
return -1;
@@ -608,7 +609,7 @@ static int srec_readrec(struct ihexrec * srec, char * rec)
for (i=0; i<2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
- srec->data[j] = strtoul(buf, &e, 16);
+ srec->data[j] = (char)strtoul(buf, &e, 16);
if (e == buf || *e != 0)
return -1;
cksum += srec->data[j];
@@ -620,7 +621,7 @@ static int srec_readrec(struct ihexrec * srec, char * rec)
for (i=0; i<2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
- srec->cksum = strtoul(buf, &e, 16);
+ srec->cksum = (char)strtoul(buf, &e, 16);
if (e == buf || *e != 0)
return -1;
@@ -650,7 +651,7 @@ static int srec2b(char * infile, FILE * inf,
while (fgets((char *)buffer,MAX_LINE_LEN,inf)!=NULL) {
lineno++;
- len = strlen(buffer);
+ len = (int)strlen(buffer);
if (buffer[len-1] == '\n')
buffer[--len] = 0;
if (buffer[0] != 0x53)
@@ -729,7 +730,7 @@ static int srec2b(char * infile, FILE * inf,
return -1;
}
nextaddr -= fileoffset;
- if (nextaddr + srec.reclen > bufsize) {
+ if (nextaddr + srec.reclen > (unsigned)bufsize) {
avrdude_message(MSG_INFO, msg, progname, nextaddr+srec.reclen, "",
lineno, infile);
return -1;
@@ -740,7 +741,7 @@ static int srec2b(char * infile, FILE * inf,
}
if (nextaddr+srec.reclen > maxaddr)
maxaddr = nextaddr+srec.reclen;
- reccount++;
+ reccount++;
}
}
@@ -1143,12 +1144,12 @@ static int fileio_rbin(struct fioparms * fio,
switch (fio->op) {
case FIO_READ:
- rc = fread(buf, 1, size, f);
+ rc = (int)fread(buf, 1, size, f);
if (rc > 0)
memset(mem->tags, TAG_ALLOCATED, rc);
break;
case FIO_WRITE:
- rc = fwrite(buf, 1, size, f);
+ rc = (int)fwrite(buf, 1, size, f);
break;
default:
avrdude_message(MSG_INFO, "%s: fileio: invalid operation=%d\n",
@@ -1190,7 +1191,7 @@ static int fileio_imm(struct fioparms * fio,
progname, p);
return -1;
}
- mem->buf[loc] = b;
+ mem->buf[loc] = (char)b;
mem->tags[loc++] = TAG_ALLOCATED;
p = strtok(NULL, " ,");
rc = loc;
@@ -1452,7 +1453,7 @@ static int fmt_autodetect(char * fname, unsigned section)
}
buf[MAX_LINE_LEN-1] = 0;
- len = strlen((char *)buf);
+ len = (int)strlen((char *)buf);
if (buf[len-1] == '\n')
buf[--len] = 0;
diff --git a/src/avrdude/lists.c b/src/avrdude/lists.c
index cab88364e..063507ed3 100644
--- a/src/avrdude/lists.c
+++ b/src/avrdude/lists.c
@@ -444,7 +444,7 @@ lcreat ( void * liststruct, int elements )
l->poolsize = DEFAULT_POOLSIZE;
}
else {
- l->poolsize = elements*sizeof(LISTNODE)+sizeof(NODEPOOL);
+ l->poolsize = (short)(elements*sizeof(LISTNODE)+sizeof(NODEPOOL));
}
l->n_ln_pool = (l->poolsize-sizeof(NODEPOOL))/sizeof(LISTNODE);
@@ -803,7 +803,7 @@ lget_n ( LISTID lid, unsigned int n )
CKLMAGIC(l);
- if ((n<1)||(n>lsize(l))) {
+ if ((n < 1) || (n > (unsigned)lsize(l))) {
return NULL;
}
@@ -844,7 +844,7 @@ lget_ln ( LISTID lid, unsigned int n )
CKLMAGIC(l);
- if ((n<1)||(n>lsize(l))) {
+ if ((n < 1) || (n > (unsigned)lsize(l))) {
return NULL;
}
@@ -941,7 +941,7 @@ insert_ln ( LIST * l, LISTNODE * ln, void * data_ptr )
|
| Insert data before the nth item in the list.
-----------------------------------------------------------------*/
-int
+int
lins_n ( LISTID lid, void * data_ptr, unsigned int n )
{
int i;
@@ -952,7 +952,7 @@ lins_n ( LISTID lid, void * data_ptr, unsigned int n )
CKLMAGIC(l);
- if ((n<1)||(n>(l->num+1))) {
+ if ((n < 1) || (n > (unsigned)(l->num+1))) {
return -1;
}
@@ -1193,7 +1193,7 @@ lrmv_n ( LISTID lid, unsigned int n )
CKLMAGIC(l);
- if ((n<1)||(n>l->num)) {
+ if ((n < 1) || (n > (unsigned)l->num)) {
return NULL;
}
diff --git a/src/avrdude/main.c b/src/avrdude/main.c
index 8f9040349..60be7ec3a 100644
--- a/src/avrdude/main.c
+++ b/src/avrdude/main.c
@@ -107,7 +107,7 @@ int avrdude_message(const int msglvl, const char *format, ...)
if (rc > 0 && rc < MSGBUFFER_SIZE) {
avrdude_message_handler(msgbuffer, rc, avrdude_message_handler_user_p);
} else {
- avrdude_message_handler(format_error, strlen(format_error), avrdude_message_handler_user_p);
+ avrdude_message_handler(format_error, (unsigned)strlen(format_error), avrdude_message_handler_user_p);
}
}
@@ -567,7 +567,7 @@ int avrdude_main(int argc, char * argv [])
// #endif
- len = strlen(progname) + 2;
+ len = (int)strlen(progname) + 2;
for (i=0; i<len; i++)
progbuf[i] = ' ';
progbuf[i] = 0;
@@ -601,7 +601,7 @@ int avrdude_main(int argc, char * argv [])
bitclock = strtod(optarg, &e);
if (*e != 0) {
/* trailing unit of measure present */
- int suffixlen = strlen(e);
+ size_t suffixlen = strlen(e);
switch (suffixlen) {
case 2:
if ((e[0] != 'h' && e[0] != 'H') || e[1] != 'z')
diff --git a/src/avrdude/pindefs.c b/src/avrdude/pindefs.c
index 5753fc67b..d89f4828b 100644
--- a/src/avrdude/pindefs.c
+++ b/src/avrdude/pindefs.c
@@ -217,7 +217,7 @@ const char * pinmask_to_str(const pinmask_t * const pinmask) {
* @param[in] size the number of entries in checklist
* @returns 0 if all pin definitions are valid, -1 otherwise
*/
-int pins_check(const struct programmer_t * const pgm, const struct pin_checklist_t * const checklist, const int size, bool output) {
+int pins_check(const struct programmer_t *const pgm, const struct pin_checklist_t *const checklist, const int size, const bool output) {
static const struct pindef_t no_valid_pins = {{0}, {0}}; // default value if check list does not contain anything else
int rv = 0; // return value
int pinname; // loop counter through pinnames
diff --git a/src/avrdude/ser_win32.c b/src/avrdude/ser_win32.c
index 8efca0397..6b8a18f8f 100644
--- a/src/avrdude/ser_win32.c
+++ b/src/avrdude/ser_win32.c
@@ -292,7 +292,7 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
if (hComPort == INVALID_HANDLE_VALUE) {
const char *error = last_error_string(0);
avrdude_message(MSG_INFO, "%s: ser_open(): can't open device \"%s\": %s\n", progname, port, error);
- free(error);
+ free((char *)error);
return -1;
}
@@ -460,10 +460,10 @@ static int ser_send(union filedescriptor *fd, const unsigned char * buf, size_t
serial_w32SetTimeOut(hComPort,500);
- if (!WriteFile(hComPort, buf, buflen, &written, NULL)) {
+ if (!WriteFile(hComPort, buf, (DWORD)buflen, &written, NULL)) {
const char *error = last_error_string(0);
avrdude_message(MSG_INFO, "%s: ser_send(): write error: %s\n", progname, error);
- free(error);
+ free((char *)error);
return -1;
}
@@ -576,10 +576,10 @@ static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen
serial_w32SetTimeOut(hComPort, serial_recv_timeout);
- if (!ReadFile(hComPort, buf, buflen, &read, NULL)) {
+ if (!ReadFile(hComPort, buf, (DWORD)buflen, &read, NULL)) {
const char *error = last_error_string(0);
avrdude_message(MSG_INFO, "%s: ser_recv(): read error: %s\n", progname, error);
- free(error);
+ free((char *)error);
return -1;
}
@@ -642,7 +642,7 @@ static int ser_drain(union filedescriptor *fd, int display)
if (!readres) {
const char *error = last_error_string(0);
avrdude_message(MSG_INFO, "%s: ser_drain(): read error: %s\n", progname, error);
- free(error);
+ free((char *)error);
return -1;
}
diff --git a/src/avrdude/serbb_win32.c b/src/avrdude/serbb_win32.c
index 4f1069c85..76db6ce3f 100644
--- a/src/avrdude/serbb_win32.c
+++ b/src/avrdude/serbb_win32.c
@@ -308,8 +308,8 @@ static int serbb_open(PROGRAMMER *pgm, char *port)
progname, port);
return -1;
}
- avrdude_message(MSG_DEBUG, "%s: ser_open(): opened comm port \"%s\", handle 0x%x\n",
- progname, port, (int)hComPort);
+ avrdude_message(MSG_DEBUG, "%s: ser_open(): opened comm port \"%s\", handle %p\n",
+ progname, port, (void *)hComPort);
pgm->fd.pfd = (void *)hComPort;
@@ -326,8 +326,8 @@ static void serbb_close(PROGRAMMER *pgm)
pgm->setpin(pgm, PIN_AVR_RESET, 1);
CloseHandle (hComPort);
}
- avrdude_message(MSG_DEBUG, "%s: ser_close(): closed comm port handle 0x%x\n",
- progname, (int)hComPort);
+ avrdude_message(MSG_DEBUG, "%s: ser_close(): closed comm port handle %p\n",
+ progname, (void *)hComPort);
hComPort = INVALID_HANDLE_VALUE;
}
diff --git a/src/avrdude/stk500.c b/src/avrdude/stk500.c
index efb7078bc..256076cc6 100644
--- a/src/avrdude/stk500.c
+++ b/src/avrdude/stk500.c
@@ -504,7 +504,7 @@ static int stk500_initialize(PROGRAMMER * pgm, AVRPART * p)
}
else {
buf[9] = 0xff;
- buf[10] = 0xff;
+ buf[10] = 0xff;
buf[13] = 0;
buf[14] = 0;
buf[17] = 0;
@@ -821,7 +821,7 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
break;
}
- for (prusa3d_semicolon_workaround_round = 0; prusa3d_semicolon_workaround_round < (has_semicolon ? 2 : 1); ++ prusa3d_semicolon_workaround_round) {
+ for (prusa3d_semicolon_workaround_round = 0; prusa3d_semicolon_workaround_round < (has_semicolon ? 2u : 1u); prusa3d_semicolon_workaround_round++) {
/* build command block and avoid multiple send commands as it leads to a crash
of the silabs usb serial driver on mac os x */
i = 0;
@@ -834,7 +834,7 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
buf[i++] = block_size & 0x0f;
buf[i++] = memtype;
if (has_semicolon) {
- for (j = 0; j < block_size; ++i, ++ j) {
+ for (j = 0; j < (unsigned)block_size; ++i, ++ j) {
buf[i] = m->buf[addr + j];
if (buf[i] == ';')
buf[i] |= (prusa3d_semicolon_workaround_round ? 0xf0 : 0x0f);
@@ -1088,8 +1088,8 @@ static int stk500_set_sck_period(PROGRAMMER * pgm, double v)
min = 8.0 / STK500_XTAL;
max = 255 * min;
- dur = v / min + 0.5;
-
+ dur = (int)(v / min + 0.5);
+
if (v < min) {
dur = 1;
avrdude_message(MSG_INFO, "%s: stk500_set_sck_period(): p = %.1f us too small, using %.1f us\n",
@@ -1099,7 +1099,7 @@ static int stk500_set_sck_period(PROGRAMMER * pgm, double v)
avrdude_message(MSG_INFO, "%s: stk500_set_sck_period(): p = %.1f us too large, using %.1f us\n",
progname, v / 1e-6, dur * min / 1e-6);
}
-
+
return stk500_setparm(pgm, Parm_STK_SCK_DURATION, dur);
}
diff --git a/src/avrdude/stk500v2.c b/src/avrdude/stk500v2.c
index 9bc629ba4..33bdf8eea 100644
--- a/src/avrdude/stk500v2.c
+++ b/src/avrdude/stk500v2.c
@@ -130,58 +130,58 @@ struct jtagispentry
#define SZ_SPI_MULTI (USHRT_MAX - 1)
};
-static const struct jtagispentry jtagispcmds[] = {
- /* generic */
- { CMD_SET_PARAMETER, 2 },
- { CMD_GET_PARAMETER, 3 },
- { CMD_OSCCAL, 2 },
- { CMD_LOAD_ADDRESS, 2 },
- /* ISP mode */
- { CMD_ENTER_PROGMODE_ISP, 2 },
- { CMD_LEAVE_PROGMODE_ISP, 2 },
- { CMD_CHIP_ERASE_ISP, 2 },
- { CMD_PROGRAM_FLASH_ISP, 2 },
- { CMD_READ_FLASH_ISP, SZ_READ_FLASH_EE },
- { CMD_PROGRAM_EEPROM_ISP, 2 },
- { CMD_READ_EEPROM_ISP, SZ_READ_FLASH_EE },
- { CMD_PROGRAM_FUSE_ISP, 3 },
- { CMD_READ_FUSE_ISP, 4 },
- { CMD_PROGRAM_LOCK_ISP, 3 },
- { CMD_READ_LOCK_ISP, 4 },
- { CMD_READ_SIGNATURE_ISP, 4 },
- { CMD_READ_OSCCAL_ISP, 4 },
- { CMD_SPI_MULTI, SZ_SPI_MULTI },
- /* all HV modes */
- { CMD_SET_CONTROL_STACK, 2 },
- /* HVSP mode */
- { CMD_ENTER_PROGMODE_HVSP, 2 },
- { CMD_LEAVE_PROGMODE_HVSP, 2 },
- { CMD_CHIP_ERASE_HVSP, 2 },
- { CMD_PROGRAM_FLASH_HVSP, 2 },
- { CMD_READ_FLASH_HVSP, SZ_READ_FLASH_EE },
- { CMD_PROGRAM_EEPROM_HVSP, 2 },
- { CMD_READ_EEPROM_HVSP, SZ_READ_FLASH_EE },
- { CMD_PROGRAM_FUSE_HVSP, 2 },
- { CMD_READ_FUSE_HVSP, 3 },
- { CMD_PROGRAM_LOCK_HVSP, 2 },
- { CMD_READ_LOCK_HVSP, 3 },
- { CMD_READ_SIGNATURE_HVSP, 3 },
- { CMD_READ_OSCCAL_HVSP, 3 },
- /* PP mode */
- { CMD_ENTER_PROGMODE_PP, 2 },
- { CMD_LEAVE_PROGMODE_PP, 2 },
- { CMD_CHIP_ERASE_PP, 2 },
- { CMD_PROGRAM_FLASH_PP, 2 },
- { CMD_READ_FLASH_PP, SZ_READ_FLASH_EE },
- { CMD_PROGRAM_EEPROM_PP, 2 },
- { CMD_READ_EEPROM_PP, SZ_READ_FLASH_EE },
- { CMD_PROGRAM_FUSE_PP, 2 },
- { CMD_READ_FUSE_PP, 3 },
- { CMD_PROGRAM_LOCK_PP, 2 },
- { CMD_READ_LOCK_PP, 3 },
- { CMD_READ_SIGNATURE_PP, 3 },
- { CMD_READ_OSCCAL_PP, 3 },
-};
+// static const struct jtagispentry jtagispcmds[] = {
+// /* generic */
+// { CMD_SET_PARAMETER, 2 },
+// { CMD_GET_PARAMETER, 3 },
+// { CMD_OSCCAL, 2 },
+// { CMD_LOAD_ADDRESS, 2 },
+// /* ISP mode */
+// { CMD_ENTER_PROGMODE_ISP, 2 },
+// { CMD_LEAVE_PROGMODE_ISP, 2 },
+// { CMD_CHIP_ERASE_ISP, 2 },
+// { CMD_PROGRAM_FLASH_ISP, 2 },
+// { CMD_READ_FLASH_ISP, SZ_READ_FLASH_EE },
+// { CMD_PROGRAM_EEPROM_ISP, 2 },
+// { CMD_READ_EEPROM_ISP, SZ_READ_FLASH_EE },
+// { CMD_PROGRAM_FUSE_ISP, 3 },
+// { CMD_READ_FUSE_ISP, 4 },
+// { CMD_PROGRAM_LOCK_ISP, 3 },
+// { CMD_READ_LOCK_ISP, 4 },
+// { CMD_READ_SIGNATURE_ISP, 4 },
+// { CMD_READ_OSCCAL_ISP, 4 },
+// { CMD_SPI_MULTI, SZ_SPI_MULTI },
+// /* all HV modes */
+// { CMD_SET_CONTROL_STACK, 2 },
+// /* HVSP mode */
+// { CMD_ENTER_PROGMODE_HVSP, 2 },
+// { CMD_LEAVE_PROGMODE_HVSP, 2 },
+// { CMD_CHIP_ERASE_HVSP, 2 },
+// { CMD_PROGRAM_FLASH_HVSP, 2 },
+// { CMD_READ_FLASH_HVSP, SZ_READ_FLASH_EE },
+// { CMD_PROGRAM_EEPROM_HVSP, 2 },
+// { CMD_READ_EEPROM_HVSP, SZ_READ_FLASH_EE },
+// { CMD_PROGRAM_FUSE_HVSP, 2 },
+// { CMD_READ_FUSE_HVSP, 3 },
+// { CMD_PROGRAM_LOCK_HVSP, 2 },
+// { CMD_READ_LOCK_HVSP, 3 },
+// { CMD_READ_SIGNATURE_HVSP, 3 },
+// { CMD_READ_OSCCAL_HVSP, 3 },
+// /* PP mode */
+// { CMD_ENTER_PROGMODE_PP, 2 },
+// { CMD_LEAVE_PROGMODE_PP, 2 },
+// { CMD_CHIP_ERASE_PP, 2 },
+// { CMD_PROGRAM_FLASH_PP, 2 },
+// { CMD_READ_FLASH_PP, SZ_READ_FLASH_EE },
+// { CMD_PROGRAM_EEPROM_PP, 2 },
+// { CMD_READ_EEPROM_PP, SZ_READ_FLASH_EE },
+// { CMD_PROGRAM_FUSE_PP, 2 },
+// { CMD_READ_FUSE_PP, 3 },
+// { CMD_PROGRAM_LOCK_PP, 2 },
+// { CMD_READ_LOCK_PP, 3 },
+// { CMD_READ_SIGNATURE_PP, 3 },
+// { CMD_READ_OSCCAL_PP, 3 },
+// };
/*
* From XML file:
@@ -379,15 +379,15 @@ static void stk500v2_jtag3_teardown(PROGRAMMER * pgm)
}
-static unsigned short
-b2_to_u16(unsigned char *b)
-{
- unsigned short l;
- l = b[0];
- l += (unsigned)b[1] << 8;
+// static unsigned short
+// b2_to_u16(unsigned char *b)
+// {
+// unsigned short l;
+// l = b[0];
+// l += (unsigned)b[1] << 8;
- return l;
-}
+// return l;
+// }
static int stk500v2_send_mk2(PROGRAMMER * pgm, unsigned char * data, size_t len)
{
@@ -399,16 +399,16 @@ static int stk500v2_send_mk2(PROGRAMMER * pgm, unsigned char * data, size_t len)
return 0;
}
-static unsigned short get_jtagisp_return_size(unsigned char cmd)
-{
- int i;
+// static unsigned short get_jtagisp_return_size(unsigned char cmd)
+// {
+// int i;
- for (i = 0; i < sizeof jtagispcmds / sizeof jtagispcmds[0]; i++)
- if (jtagispcmds[i].cmd == cmd)
- return jtagispcmds[i].size;
+// for (i = 0; i < sizeof jtagispcmds / sizeof jtagispcmds[0]; i++)
+// if (jtagispcmds[i].cmd == cmd)
+// return jtagispcmds[i].size;
- return 0;
-}
+// return 0;
+// }
/*
* Send the data as a JTAG ICE mkII encapsulated ISP packet.
@@ -504,7 +504,7 @@ static int stk500v2_send(PROGRAMMER * pgm, unsigned char * data, size_t len)
buf[0] = MESSAGE_START;
buf[1] = PDATA(pgm)->command_sequence;
- buf[2] = len / 256;
+ buf[2] = (char)(len / 256);
buf[3] = len % 256;
buf[4] = TOKEN;
memcpy(buf+5, data, len);
@@ -1128,7 +1128,8 @@ static int stk500v2_program_enable(PROGRAMMER * pgm, AVRPART * p)
{
unsigned char buf[16];
char msg[100]; /* see remarks above about size needed */
- int rv, tries;
+ int rv;
+ // int tries;
PDATA(pgm)->lastpart = p;
@@ -1143,7 +1144,7 @@ static int stk500v2_program_enable(PROGRAMMER * pgm, AVRPART * p)
/* Activate AVR-style (low active) RESET */
stk500v2_setparm_real(pgm, PARAM_RESET_POLARITY, 0x01);
- tries = 0;
+ // tries = 0;
// retry:
buf[0] = CMD_ENTER_PROGMODE_ISP;
buf[1] = p->timeout;
@@ -1882,7 +1883,7 @@ static int stk500hv_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
if (stk500v2_loadaddr(pgm, use_ext_addr | (paddr >> addrshift)) < 0)
return -1;
} else {
- buf[1] = addr;
+ buf[1] = (char)addr;
}
avrdude_message(MSG_NOTICE2, "%s: stk500hv_read_byte(): Sending read memory command: ",
@@ -2137,7 +2138,7 @@ static int stk500hv_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
if (stk500v2_loadaddr(pgm, use_ext_addr | (paddr >> addrshift)) < 0)
return -1;
} else {
- buf[1] = addr;
+ buf[1] = (char)addr;
buf[2] = data;
if (mode == PPMODE) {
buf[3] = pulsewidth;
@@ -2298,7 +2299,7 @@ static int stk500v2_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
unsigned int page_size,
unsigned int addr, unsigned int n_bytes)
{
-static int page = 0;
+// static int page = 0;
unsigned int block_size, last_addr, addrshift, use_ext_addr;
unsigned int maxaddr = addr + n_bytes;
unsigned char commandbuf[10];
@@ -2833,10 +2834,10 @@ static int stk500v2_set_fosc(PROGRAMMER * pgm, double v)
progname, v, unit, STK500V2_XTAL / 2e6);
fosc = STK500V2_XTAL / 2;
} else
- fosc = (unsigned)v;
+ fosc = (int)v;
for (idx = 0; idx < sizeof(ps) / sizeof(ps[0]); idx++) {
- if (fosc >= STK500V2_XTAL / (256 * ps[idx] * 2)) {
+ if (fosc >= (int)(STK500V2_XTAL / (256 * ps[idx] * 2))) {
/* this prescaler value can handle our frequency */
prescale = idx + 1;
cmatch = (unsigned)(STK500V2_XTAL / (2 * fosc * ps[idx])) - 1;
@@ -3065,8 +3066,8 @@ static int stk600_set_fosc(PROGRAMMER * pgm, double v)
{
unsigned int oct, dac;
- oct = 1.443 * log(v / 1039.0);
- dac = 2048 - (2078.0 * pow(2, (double)(10 + oct))) / v;
+ oct = (unsigned)(1.443 * log(v / 1039.0));
+ dac = (unsigned)(2048.0 - (2078.0 * pow(2, (double)(10 + oct))) / v);
return stk500v2_setparm2(pgm, PARAM2_CLOCK_CONF, (oct << 12) | (dac << 2));
}
@@ -3075,7 +3076,7 @@ static int stk600_set_sck_period(PROGRAMMER * pgm, double v)
{
unsigned int sck;
- sck = ceil((16e6 / (2 * 1.0 / v)) - 1);
+ sck = (unsigned)ceil((16e6 / (2 * 1.0 / v)) - 1);
if (sck >= 4096)
sck = 4095;
@@ -3093,7 +3094,7 @@ static int stk500v2_jtag3_set_sck_period(PROGRAMMER * pgm, double v)
else if (v > 1E-3)
sck = 1;
else
- sck = 1.0 / (1000.0 * v);
+ sck = (unsigned)(1.0 / (1000.0 * v));
value[0] = CMD_SET_SCK;
value[1] = sck & 0xff;
@@ -3143,7 +3144,7 @@ static int stk500v2_setparm_real(PROGRAMMER * pgm, unsigned char parm, unsigned
static int stk500v2_setparm(PROGRAMMER * pgm, unsigned char parm, unsigned char value)
{
- unsigned char current_value;
+ unsigned char current_value = 0;
int res;
res = stk500v2_getparm(pgm, parm, &current_value);
@@ -3214,8 +3215,15 @@ static const char *stk600_get_cardname(const struct carddata *table,
static void stk500v2_display(PROGRAMMER * pgm, const char * p)
{
- unsigned char maj, min, hdw, topcard, maj_s1, min_s1, maj_s2, min_s2;
- unsigned int rev;
+ unsigned char maj = 0;
+ unsigned char min = 0;
+ unsigned char hdw = 0;
+ unsigned char topcard = 0;
+ unsigned char maj_s1 = 0;
+ unsigned char min_s1 = 0;
+ unsigned char maj_s2 = 0;
+ unsigned char min_s2 = 0;
+ unsigned int rev = 0;
const char *topcard_name, *pgmname;
switch (PDATA(pgm)->pgmtype) {
@@ -3294,13 +3302,20 @@ f_to_kHz_MHz(double f, const char **unit)
static void stk500v2_print_parms1(PROGRAMMER * pgm, const char * p)
{
- unsigned char vtarget, vadjust, osc_pscale, osc_cmatch, sck_duration =0; //XXX 0 is not correct, check caller
- unsigned int sck_stk600, clock_conf, dac, oct, varef;
- unsigned char vtarget_jtag[4];
+ unsigned char vtarget = 0;
+ unsigned char vadjust = 0;
+ unsigned char sck_duration = 0;
+ unsigned char osc_pscale = 0;
+ unsigned char osc_cmatch = 0;
+ unsigned varef = 0;
+ unsigned sck_stk600 = 0;
+ unsigned clock_conf = 0;
+ unsigned dac, oct;
+ // unsigned char vtarget_jtag[4];
int prescale;
double f;
const char *unit;
- void *mycookie;
+ // void *mycookie;
if (PDATA(pgm)->pgmtype == PGMTYPE_JTAGICE_MKII) {
return;
@@ -3963,10 +3978,10 @@ static int stk600_xprog_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
b[0] = XPRG_CMD_WRITE_MEM;
b[1] = memcode;
b[2] = 0; /* pagemode: non-paged write */
- b[3] = addr >> 24;
- b[4] = addr >> 16;
- b[5] = addr >> 8;
- b[6] = addr;
+ b[3] = (char)(addr >> 24);
+ b[4] = (char)(addr >> 16);
+ b[5] = (char)(addr >> 8);
+ b[6] = (char)addr;
b[7] = 0;
b[8] = write_size;
b[9] = data;
@@ -4011,10 +4026,10 @@ static int stk600_xprog_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
addr += mem->offset;
b[0] = XPRG_CMD_READ_MEM;
- b[2] = addr >> 24;
- b[3] = addr >> 16;
- b[4] = addr >> 8;
- b[5] = addr;
+ b[2] = (char)(addr >> 24);
+ b[3] = (char)(addr >> 16);
+ b[4] = (char)(addr >> 8);
+ b[5] = (char)addr;
b[6] = 0;
b[7] = 1;
if (stk600_xprog_command(pgm, b, 8, 3) < 0) {
diff --git a/src/avrdude/term.c b/src/avrdude/term.c
index 012f6f1a5..182367cf2 100644
--- a/src/avrdude/term.c
+++ b/src/avrdude/term.c
@@ -281,7 +281,7 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p,
maxsize = mem->size;
- if (addr >= maxsize) {
+ if (addr >= (unsigned long)maxsize) {
if (argc == 2) {
/* wrap around */
addr = 0;
@@ -294,7 +294,7 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p,
}
/* trim len if nessary to not read past the end of memory */
- if ((addr + len) > maxsize)
+ if ((addr + len) > (unsigned long)maxsize)
len = maxsize - addr;
buf = malloc(len);
@@ -303,7 +303,7 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p,
return -1;
}
- for (i=0; i<len; i++) {
+ for (i = 0; i < (unsigned long)len; i++) {
rc = pgm->read_byte(pgm, p, mem, addr+i, &buf[i]);
if (rc != 0) {
avrdude_message(MSG_INFO, "error reading %s address 0x%05lx of part %s\n",
@@ -364,7 +364,7 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
return -1;
}
- if (addr > maxsize) {
+ if (addr > (unsigned long)maxsize) {
avrdude_message(MSG_INFO, "%s (write): address 0x%05lx is out of range for %s memory\n",
progname, addr, memtype);
return -1;
@@ -373,7 +373,7 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
/* number of bytes to write at the specified address */
len = argc - 3;
- if ((addr + len) > maxsize) {
+ if ((addr + len) > (unsigned long)maxsize) {
avrdude_message(MSG_INFO, "%s (write): selected address and # bytes exceed "
"range for %s memory\n",
progname, memtype);
@@ -386,8 +386,8 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
return -1;
}
- for (i=3; i<argc; i++) {
- buf[i-3] = strtoul(argv[i], &e, 0);
+ for (i = 3; i < (unsigned long)argc; i++) {
+ buf[i-3] = (char)strtoul(argv[i], &e, 0);
if (*e || (e == argv[i])) {
avrdude_message(MSG_INFO, "%s (write): can't parse byte \"%s\"\n",
progname, argv[i]);
@@ -397,7 +397,7 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
}
pgm->err_led(pgm, OFF);
- for (werror=0, i=0; i<len; i++) {
+ for (werror = 0, i = 0; i < (unsigned long)len; i++) {
rc = avr_write_byte(pgm, p, mem, addr+i, buf[i]);
if (rc) {
@@ -462,7 +462,7 @@ static int cmd_send(PROGRAMMER * pgm, struct avrpart * p,
/* load command bytes */
for (i=1; i<argc; i++) {
- cmd[i-1] = strtoul(argv[i], &e, 0);
+ cmd[i-1] = (char)strtoul(argv[i], &e, 0);
if (*e || (e == argv[i])) {
avrdude_message(MSG_INFO, "%s (send): can't parse byte \"%s\"\n",
progname, argv[i]);
@@ -789,7 +789,7 @@ static int tokenize(char * s, char *** argv)
char * nbuf;
char ** av;
- slen = strlen(s);
+ slen = (int)strlen(s);
/*
* initialize allow for 20 arguments, use realloc to grow this if
@@ -812,7 +812,7 @@ static int tokenize(char * s, char *** argv)
nexttok(r, &q, &r);
strcpy(nbuf, q);
bufv[n] = nbuf;
- len = strlen(q);
+ len = (int)strlen(q);
l += len + 1;
nbuf += len + 1;
nbuf[0] = 0;
@@ -841,7 +841,7 @@ static int tokenize(char * s, char *** argv)
q = (char *)&av[n+1];
memcpy(q, buf, l);
for (i=0; i<n; i++) {
- offset = bufv[i] - buf;
+ offset = (int)(bufv[i] - buf);
av[i] = q + offset;
}
av[i] = NULL;
@@ -862,7 +862,7 @@ static int do_cmd(PROGRAMMER * pgm, struct avrpart * p,
int hold;
int len;
- len = strlen(argv[0]);
+ len = (int)strlen(argv[0]);
hold = -1;
for (i=0; i<NCMDS; i++) {
if (strcasecmp(argv[0], cmd[i].name) == 0) {
diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp
index 26302f702..b1ebdcfbc 100644
--- a/src/libslic3r/BoundingBox.hpp
+++ b/src/libslic3r/BoundingBox.hpp
@@ -161,4 +161,12 @@ inline bool empty(const BoundingBox3Base<VT> &bb)
} // namespace Slic3r
+// Serialization through the Cereal library
+namespace cereal {
+ template<class Archive> void serialize(Archive& archive, Slic3r::BoundingBox &bb) { archive(bb.min, bb.max, bb.defined); }
+ template<class Archive> void serialize(Archive& archive, Slic3r::BoundingBox3 &bb) { archive(bb.min, bb.max, bb.defined); }
+ template<class Archive> void serialize(Archive& archive, Slic3r::BoundingBoxf &bb) { archive(bb.min, bb.max, bb.defined); }
+ template<class Archive> void serialize(Archive& archive, Slic3r::BoundingBoxf3 &bb) { archive(bb.min, bb.max, bb.defined); }
+}
+
#endif
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 736290489..ac40e99bc 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -81,9 +81,8 @@ add_library(libslic3r STATIC
GCode/SpiralVase.hpp
GCode/ToolOrdering.cpp
GCode/ToolOrdering.hpp
+ GCode/WipeTower.cpp
GCode/WipeTower.hpp
- GCode/WipeTowerPrusaMM.cpp
- GCode/WipeTowerPrusaMM.hpp
GCode.cpp
GCode.hpp
GCodeReader.cpp
@@ -114,6 +113,8 @@ add_library(libslic3r STATIC
MultiPoint.cpp
MultiPoint.hpp
MutablePriorityQueue.hpp
+ ObjectID.cpp
+ ObjectID.hpp
PerimeterGenerator.cpp
PerimeterGenerator.hpp
PlaceholderParser.cpp
@@ -189,6 +190,7 @@ target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNE
target_link_libraries(libslic3r
libnest2d
admesh
+ cereal
libigl
miniz
boost_libs
diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp
index 0738b77c6..9d0649a1f 100644
--- a/src/libslic3r/Config.cpp
+++ b/src/libslic3r/Config.cpp
@@ -209,6 +209,51 @@ std::vector<std::string> ConfigOptionDef::cli_args(const std::string &key) const
return args;
}
+ConfigOption* ConfigOptionDef::create_empty_option() const
+{
+ switch (this->type) {
+ case coFloat: return new ConfigOptionFloat();
+ case coFloats: return new ConfigOptionFloats();
+ case coInt: return new ConfigOptionInt();
+ case coInts: return new ConfigOptionInts();
+ case coString: return new ConfigOptionString();
+ case coStrings: return new ConfigOptionStrings();
+ case coPercent: return new ConfigOptionPercent();
+ case coPercents: return new ConfigOptionPercents();
+ case coFloatOrPercent: return new ConfigOptionFloatOrPercent();
+ case coPoint: return new ConfigOptionPoint();
+ case coPoints: return new ConfigOptionPoints();
+ case coPoint3: return new ConfigOptionPoint3();
+// case coPoint3s: return new ConfigOptionPoint3s();
+ case coBool: return new ConfigOptionBool();
+ case coBools: return new ConfigOptionBools();
+ case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map);
+ default: throw std::runtime_error(std::string("Unknown option type for option ") + this->label);
+ }
+}
+
+ConfigOption* ConfigOptionDef::create_default_option() const
+{
+ if (this->default_value)
+ return (this->default_value->type() == coEnum) ?
+ // Special case: For a DynamicConfig, convert a templated enum to a generic enum.
+ new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt()) :
+ this->default_value->clone();
+ return this->create_empty_option();
+}
+
+// Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread!
+ConfigOptionDef* ConfigDef::add(const t_config_option_key &opt_key, ConfigOptionType type)
+{
+ static size_t serialization_key_ordinal_last = 0;
+ ConfigOptionDef *opt = &this->options[opt_key];
+ opt->opt_key = opt_key;
+ opt->type = type;
+ opt->serialization_key_ordinal = ++ serialization_key_ordinal_last;
+ this->by_serialization_key_ordinal[opt->serialization_key_ordinal] = opt;
+ return opt;
+}
+
std::string ConfigOptionDef::nocli = "~~~noCLI";
std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function<bool(const ConfigOptionDef &)> filter) const
@@ -358,7 +403,7 @@ t_config_option_keys ConfigBase::equal(const ConfigBase &other) const
return equal;
}
-std::string ConfigBase::serialize(const t_config_option_key &opt_key) const
+std::string ConfigBase::opt_serialize(const t_config_option_key &opt_key) const
{
const ConfigOption* opt = this->option(opt_key);
assert(opt != nullptr);
@@ -469,7 +514,7 @@ void ConfigBase::setenv_() const
for (size_t i = 0; i < envname.size(); ++i)
envname[i] = (envname[i] <= 'z' && envname[i] >= 'a') ? envname[i]-('a'-'A') : envname[i];
- boost::nowide::setenv(envname.c_str(), this->serialize(*it).c_str(), 1);
+ boost::nowide::setenv(envname.c_str(), this->opt_serialize(*it).c_str(), 1);
}
}
@@ -593,16 +638,16 @@ void ConfigBase::save(const std::string &file) const
c.open(file, std::ios::out | std::ios::trunc);
c << "# " << Slic3r::header_slic3r_generated() << std::endl;
for (const std::string &opt_key : this->keys())
- c << opt_key << " = " << this->serialize(opt_key) << std::endl;
+ c << opt_key << " = " << this->opt_serialize(opt_key) << std::endl;
c.close();
}
bool DynamicConfig::operator==(const DynamicConfig &rhs) const
{
- t_options_map::const_iterator it1 = this->options.begin();
- t_options_map::const_iterator it1_end = this->options.end();
- t_options_map::const_iterator it2 = rhs.options.begin();
- t_options_map::const_iterator it2_end = rhs.options.end();
+ auto it1 = this->options.begin();
+ auto it1_end = this->options.end();
+ auto it2 = rhs.options.begin();
+ auto it2_end = rhs.options.end();
for (; it1 != it1_end && it2 != it2_end; ++ it1, ++ it2)
if (it1->first != it2->first || *it1->second != *it2->second)
// key or value differ
@@ -612,10 +657,10 @@ bool DynamicConfig::operator==(const DynamicConfig &rhs) const
ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool create)
{
- t_options_map::iterator it = options.find(opt_key);
+ auto it = options.find(opt_key);
if (it != options.end())
// Option was found.
- return it->second;
+ return it->second.get();
if (! create)
// Option was not found and a new option shall not be created.
return nullptr;
@@ -628,34 +673,8 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
// throw std::runtime_error(std::string("Invalid option name: ") + opt_key);
// Let the parent decide what to do if the opt_key is not defined by this->def().
return nullptr;
- ConfigOption *opt = nullptr;
- if (optdef->default_value) {
- opt = (optdef->default_value->type() == coEnum) ?
- // Special case: For a DynamicConfig, convert a templated enum to a generic enum.
- new ConfigOptionEnumGeneric(optdef->enum_keys_map, optdef->default_value->getInt()) :
- optdef->default_value->clone();
- } else {
- switch (optdef->type) {
- case coFloat: opt = new ConfigOptionFloat(); break;
- case coFloats: opt = new ConfigOptionFloats(); break;
- case coInt: opt = new ConfigOptionInt(); break;
- case coInts: opt = new ConfigOptionInts(); break;
- case coString: opt = new ConfigOptionString(); break;
- case coStrings: opt = new ConfigOptionStrings(); break;
- case coPercent: opt = new ConfigOptionPercent(); break;
- case coPercents: opt = new ConfigOptionPercents(); break;
- case coFloatOrPercent: opt = new ConfigOptionFloatOrPercent(); break;
- case coPoint: opt = new ConfigOptionPoint(); break;
- case coPoints: opt = new ConfigOptionPoints(); break;
- case coPoint3: opt = new ConfigOptionPoint3(); break;
- // case coPoint3s: opt = new ConfigOptionPoint3s(); break;
- case coBool: opt = new ConfigOptionBool(); break;
- case coBools: opt = new ConfigOptionBools(); break;
- case coEnum: opt = new ConfigOptionEnumGeneric(optdef->enum_keys_map); break;
- default: throw std::runtime_error(std::string("Unknown option type for option ") + opt_key);
- }
- }
- this->options[opt_key] = opt;
+ ConfigOption *opt = optdef->create_default_option();
+ this->options.emplace_hint(it, opt_key, std::unique_ptr<ConfigOption>(opt));
return opt;
}
@@ -732,18 +751,18 @@ bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra,
}
// Store the option value.
const bool existing = this->has(opt_key);
- if (keys != nullptr && !existing) {
+ if (keys != nullptr && ! existing) {
// Save the order of detected keys.
keys->push_back(opt_key);
}
ConfigOption *opt_base = this->option(opt_key, true);
ConfigOptionVectorBase *opt_vector = opt_base->is_vector() ? static_cast<ConfigOptionVectorBase*>(opt_base) : nullptr;
if (opt_vector) {
+ if (! existing)
+ // remove the default values
+ opt_vector->clear();
// Vector values will be chained. Repeated use of a parameter will append the parameter or parameters
// to the end of the value.
- if (!existing)
- // remove the default values
- opt_vector->deserialize("", true);
if (opt_base->type() == coBools)
static_cast<ConfigOptionBools*>(opt_base)->values.push_back(!no);
else
@@ -802,3 +821,64 @@ t_config_option_keys StaticConfig::keys() const
}
}
+
+#include <cereal/types/polymorphic.hpp>
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOption)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<double>)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<int>)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<std::string>)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<Slic3r::Vec2d>)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<Slic3r::Vec3d>)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<bool>)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVectorBase)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<double>)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<int>)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<std::string>)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<Slic3r::Vec2d>)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<unsigned char>)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloat)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloats)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionInt)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionInts)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionString)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionStrings)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercent)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercents)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatOrPercent)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoints)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint3)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionBool)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionBools)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionEnumGeneric)
+CEREAL_REGISTER_TYPE(Slic3r::ConfigBase)
+CEREAL_REGISTER_TYPE(Slic3r::DynamicConfig)
+
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<double>)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<int>)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<std::string>)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<Slic3r::Vec2d>)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<Slic3r::Vec3d>)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<bool>)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionVectorBase)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<double>)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<int>)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<std::string>)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<Slic3r::Vec2d>)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<unsigned char>)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<double>, Slic3r::ConfigOptionFloat)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<double>, Slic3r::ConfigOptionFloats)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<int>, Slic3r::ConfigOptionInt)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<int>, Slic3r::ConfigOptionInts)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<std::string>, Slic3r::ConfigOptionString)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<std::string>, Slic3r::ConfigOptionStrings)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloat, Slic3r::ConfigOptionPercent)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloats, Slic3r::ConfigOptionPercents)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionPercent, Slic3r::ConfigOptionFloatOrPercent)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<Slic3r::Vec2d>, Slic3r::ConfigOptionPoint)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<Slic3r::Vec2d>, Slic3r::ConfigOptionPoints)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<Slic3r::Vec3d>, Slic3r::ConfigOptionPoint3)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<bool>, Slic3r::ConfigOptionBool)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<unsigned char>, Slic3r::ConfigOptionBools)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionInt, Slic3r::ConfigOptionEnumGeneric)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigBase, Slic3r::DynamicConfig)
diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp
index ee4bc4e46..844287efb 100644
--- a/src/libslic3r/Config.hpp
+++ b/src/libslic3r/Config.hpp
@@ -18,6 +18,9 @@
#include <boost/format.hpp>
#include <boost/property_tree/ptree.hpp>
+#include <cereal/access.hpp>
+#include <cereal/types/base_class.hpp>
+
namespace Slic3r {
// Name of the configuration option.
@@ -152,6 +155,10 @@ public:
bool operator==(const T &rhs) const { return this->value == rhs; }
bool operator!=(const T &rhs) const { return this->value != rhs; }
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive & ar) { ar(this->value); }
};
// Value of a vector valued option (bools, ints, floats, strings, points)
@@ -167,8 +174,10 @@ public:
// Set a single vector item from either a scalar option or the first value of a vector option.vector of ConfigOptions.
// This function is useful to split values from multiple extrder / filament settings into separate configurations.
virtual void set_at(const ConfigOption *rhs, size_t i, size_t j) = 0;
-
+ // Resize the vector of values, copy the newly added values from opt_default if provided.
virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0;
+ // Clear the values vector.
+ virtual void clear() = 0;
// Get size of this vector.
virtual size_t size() const = 0;
@@ -277,6 +286,8 @@ public:
}
}
+ // Clear the values vector.
+ void clear() override { this->values.clear(); }
size_t size() const override { return this->values.size(); }
bool empty() const override { return this->values.empty(); }
@@ -290,6 +301,10 @@ public:
bool operator==(const std::vector<T> &rhs) const { return this->values == rhs; }
bool operator!=(const std::vector<T> &rhs) const { return this->values != rhs; }
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive & ar) { ar(this->values); }
};
class ConfigOptionFloat : public ConfigOptionSingle<double>
@@ -324,6 +339,10 @@ public:
this->set(opt);
return *this;
}
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<double>>(this)); }
};
class ConfigOptionFloats : public ConfigOptionVector<double>
@@ -382,6 +401,10 @@ public:
this->set(opt);
return *this;
}
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<double>>(this)); }
};
class ConfigOptionInt : public ConfigOptionSingle<int>
@@ -418,6 +441,10 @@ public:
this->set(opt);
return *this;
}
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<int>>(this)); }
};
class ConfigOptionInts : public ConfigOptionVector<int>
@@ -468,6 +495,10 @@ public:
}
return true;
}
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<int>>(this)); }
};
class ConfigOptionString : public ConfigOptionSingle<std::string>
@@ -492,6 +523,10 @@ public:
UNUSED(append);
return unescape_string_cstyle(str, this->value);
}
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<std::string>>(this)); }
};
// semicolon-separated strings
@@ -526,6 +561,10 @@ public:
this->values.clear();
return unescape_strings_cstyle(str, this->values);
}
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<std::string>>(this)); }
};
class ConfigOptionPercent : public ConfigOptionFloat
@@ -558,6 +597,10 @@ public:
iss >> this->value;
return !iss.fail();
}
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionFloat>(this)); }
};
class ConfigOptionPercents : public ConfigOptionFloats
@@ -612,6 +655,10 @@ public:
}
return true;
}
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionFloats>(this)); }
};
class ConfigOptionFloatOrPercent : public ConfigOptionPercent
@@ -661,6 +708,10 @@ public:
iss >> this->value;
return !iss.fail();
}
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionPercent>(this), percent); }
};
class ConfigOptionPoint : public ConfigOptionSingle<Vec2d>
@@ -691,6 +742,10 @@ public:
return sscanf(str.data(), " %lf , %lf %c", &this->value(0), &this->value(1), &dummy) == 2 ||
sscanf(str.data(), " %lf x %lf %c", &this->value(0), &this->value(1), &dummy) == 2;
}
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<Vec2d>>(this)); }
};
class ConfigOptionPoints : public ConfigOptionVector<Vec2d>
@@ -750,8 +805,21 @@ public:
}
return true;
}
-};
+private:
+ friend class cereal::access;
+ template<class Archive> void save(Archive& archive) const {
+ size_t cnt = this->values.size();
+ archive(cnt);
+ archive.saveBinary((const char*)this->values.data(), sizeof(Vec2d) * cnt);
+ }
+ template<class Archive> void load(Archive& archive) {
+ size_t cnt;
+ archive(cnt);
+ this->values.assign(cnt, Vec2d());
+ archive.loadBinary((char*)this->values.data(), sizeof(Vec2d) * cnt);
+ }
+};
class ConfigOptionPoint3 : public ConfigOptionSingle<Vec3d>
{
@@ -783,6 +851,10 @@ public:
return sscanf(str.data(), " %lf , %lf , %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2 ||
sscanf(str.data(), " %lf x %lf x %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2;
}
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<Vec3d>>(this)); }
};
class ConfigOptionBool : public ConfigOptionSingle<bool>
@@ -809,6 +881,10 @@ public:
this->value = (str.compare("1") == 0);
return true;
}
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<bool>>(this)); }
};
class ConfigOptionBools : public ConfigOptionVector<unsigned char>
@@ -864,6 +940,10 @@ public:
}
return true;
}
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<unsigned char>>(this)); }
};
// Map from an enum integer value to an enum name.
@@ -1002,19 +1082,73 @@ public:
this->value = it->second;
return true;
}
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive& ar) { ar(cereal::base_class<ConfigOptionInt>(this)); }
};
// Definition of a configuration value for the purpose of GUI presentation, editing, value mapping and config file handling.
class ConfigOptionDef
{
public:
+ // Identifier of this option. It is stored here so that it is accessible through the by_serialization_key_ordinal map.
+ t_config_option_key opt_key;
// What type? bool, int, string etc.
ConfigOptionType type = coNone;
// Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor.
Slic3r::clonable_ptr<const ConfigOption> default_value;
- void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr<const ConfigOption>(ptr); }
- template<typename T>
- const T* get_default_value() const { return static_cast<const T*>(this->default_value.get()); }
+ void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr<const ConfigOption>(ptr); }
+ template<typename T> const T* get_default_value() const { return static_cast<const T*>(this->default_value.get()); }
+
+ // Create an empty option to be used as a base for deserialization of DynamicConfig.
+ ConfigOption* create_empty_option() const;
+ // Create a default option to be inserted into a DynamicConfig.
+ ConfigOption* create_default_option() const;
+
+ template<class Archive> ConfigOption* load_option_from_archive(Archive &archive) const {
+ switch (this->type) {
+ case coFloat: { auto opt = new ConfigOptionFloat(); archive(*opt); return opt; }
+ case coFloats: { auto opt = new ConfigOptionFloats(); archive(*opt); return opt; }
+ case coInt: { auto opt = new ConfigOptionInt(); archive(*opt); return opt; }
+ case coInts: { auto opt = new ConfigOptionInts(); archive(*opt); return opt; }
+ case coString: { auto opt = new ConfigOptionString(); archive(*opt); return opt; }
+ case coStrings: { auto opt = new ConfigOptionStrings(); archive(*opt); return opt; }
+ case coPercent: { auto opt = new ConfigOptionPercent(); archive(*opt); return opt; }
+ case coPercents: { auto opt = new ConfigOptionPercents(); archive(*opt); return opt; }
+ case coFloatOrPercent: { auto opt = new ConfigOptionFloatOrPercent(); archive(*opt); return opt; }
+ case coPoint: { auto opt = new ConfigOptionPoint(); archive(*opt); return opt; }
+ case coPoints: { auto opt = new ConfigOptionPoints(); archive(*opt); return opt; }
+ case coPoint3: { auto opt = new ConfigOptionPoint3(); archive(*opt); return opt; }
+ case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; }
+ case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; }
+ case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; }
+ default: throw std::runtime_error(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key);
+ }
+ }
+
+ template<class Archive> ConfigOption* save_option_to_archive(Archive &archive, const ConfigOption *opt) const {
+ switch (this->type) {
+ case coFloat: archive(*static_cast<const ConfigOptionFloat*>(opt)); break;
+ case coFloats: archive(*static_cast<const ConfigOptionFloats*>(opt)); break;
+ case coInt: archive(*static_cast<const ConfigOptionInt*>(opt)); break;
+ case coInts: archive(*static_cast<const ConfigOptionInts*>(opt)); break;
+ case coString: archive(*static_cast<const ConfigOptionString*>(opt)); break;
+ case coStrings: archive(*static_cast<const ConfigOptionStrings*>(opt)); break;
+ case coPercent: archive(*static_cast<const ConfigOptionPercent*>(opt)); break;
+ case coPercents: archive(*static_cast<const ConfigOptionPercents*>(opt)); break;
+ case coFloatOrPercent: archive(*static_cast<const ConfigOptionFloatOrPercent*>(opt)); break;
+ case coPoint: archive(*static_cast<const ConfigOptionPoint*>(opt)); break;
+ case coPoints: archive(*static_cast<const ConfigOptionPoints*>(opt)); break;
+ case coPoint3: archive(*static_cast<const ConfigOptionPoint3*>(opt)); break;
+ case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break;
+ case coBools: archive(*static_cast<const ConfigOptionBools*>(opt)); break;
+ case coEnum: archive(*static_cast<const ConfigOptionEnumGeneric*>(opt)); break;
+ default: throw std::runtime_error(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key);
+ }
+ // Make the compiler happy, shut up the warnings.
+ return nullptr;
+ }
// Usually empty.
// Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
@@ -1084,6 +1218,9 @@ public:
return false;
}
+ // 0 is an invalid key.
+ size_t serialization_key_ordinal = 0;
+
// Returns the alternative CLI arguments for the given option.
// If there are no cli arguments defined, use the key and replace underscores with dashes.
std::vector<std::string> cli_args(const std::string &key) const;
@@ -1103,7 +1240,8 @@ typedef std::map<t_config_option_key, ConfigOptionDef> t_optiondef_map;
class ConfigDef
{
public:
- t_optiondef_map options;
+ t_optiondef_map options;
+ std::map<size_t, const ConfigOptionDef*> by_serialization_key_ordinal;
bool has(const t_config_option_key &opt_key) const { return this->options.count(opt_key) > 0; }
const ConfigOptionDef* get(const t_config_option_key &opt_key) const {
@@ -1124,11 +1262,7 @@ public:
std::function<bool(const ConfigOptionDef &)> filter = [](const ConfigOptionDef &){ return true; }) const;
protected:
- ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type) {
- ConfigOptionDef* opt = &this->options[opt_key];
- opt->type = type;
- return opt;
- }
+ ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type);
};
// An abstract configuration store.
@@ -1197,7 +1331,7 @@ public:
bool equals(const ConfigBase &other) const { return this->diff(other).empty(); }
t_config_option_keys diff(const ConfigBase &other) const;
t_config_option_keys equal(const ConfigBase &other) const;
- std::string serialize(const t_config_option_key &opt_key) const;
+ std::string opt_serialize(const t_config_option_key &opt_key) const;
// Set a configuration value from a string, it will call an overridable handle_legacy()
// to resolve renamed and removed configuration keys.
bool set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false);
@@ -1235,7 +1369,7 @@ public:
assert(this->def() == nullptr || this->def() == rhs.def());
this->clear();
for (const auto &kvp : rhs.options)
- this->options[kvp.first] = kvp.second->clone();
+ this->options[kvp.first].reset(kvp.second->clone());
return *this;
}
@@ -1258,15 +1392,13 @@ public:
for (const auto &kvp : rhs.options) {
auto it = this->options.find(kvp.first);
if (it == this->options.end())
- this->options[kvp.first] = kvp.second->clone();
+ this->options[kvp.first].reset(kvp.second->clone());
else {
assert(it->second->type() == kvp.second->type());
if (it->second->type() == kvp.second->type())
*it->second = *kvp.second;
- else {
- delete it->second;
- it->second = kvp.second->clone();
- }
+ else
+ it->second.reset(kvp.second->clone());
}
}
return *this;
@@ -1277,14 +1409,13 @@ public:
DynamicConfig& operator+=(DynamicConfig &&rhs)
{
assert(this->def() == nullptr || this->def() == rhs.def());
- for (const auto &kvp : rhs.options) {
+ for (auto &kvp : rhs.options) {
auto it = this->options.find(kvp.first);
if (it == this->options.end()) {
- this->options[kvp.first] = kvp.second;
+ this->options.insert(std::make_pair(kvp.first, std::move(kvp.second)));
} else {
assert(it->second->type() == kvp.second->type());
- delete it->second;
- it->second = kvp.second;
+ it->second = std::move(kvp.second);
}
}
rhs.options.clear();
@@ -1301,8 +1432,6 @@ public:
void clear()
{
- for (auto &opt : this->options)
- delete opt.second;
this->options.clear();
}
@@ -1311,7 +1440,6 @@ public:
auto it = this->options.find(opt_key);
if (it == this->options.end())
return false;
- delete it->second;
this->options.erase(it);
return true;
}
@@ -1336,11 +1464,10 @@ public:
{
auto it = this->options.find(opt_key);
if (it == this->options.end()) {
- this->options[opt_key] = opt;
+ this->options[opt_key].reset(opt);
return true;
} else {
- delete it->second;
- it->second = opt;
+ it->second.reset(opt);
return false;
}
}
@@ -1370,12 +1497,15 @@ public:
void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
bool read_cli(int argc, char** argv, t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
- typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
- t_options_map::const_iterator cbegin() const { return options.cbegin(); }
- t_options_map::const_iterator cend() const { return options.cend(); }
+ std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cbegin() const { return options.cbegin(); }
+ std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cend() const { return options.cend(); }
+ size_t size() const { return options.size(); }
private:
- t_options_map options;
+ std::map<t_config_option_key, std::unique_ptr<ConfigOption>> options;
+
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(options); }
};
/// Configuration store with a static definition of configuration values.
diff --git a/src/libslic3r/EdgeGrid.cpp b/src/libslic3r/EdgeGrid.cpp
index c34aca8f2..9d02ef09b 100644
--- a/src/libslic3r/EdgeGrid.cpp
+++ b/src/libslic3r/EdgeGrid.cpp
@@ -146,10 +146,10 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
coord_t iy = p1(1) / m_resolution;
coord_t ixb = p2(0) / m_resolution;
coord_t iyb = p2(1) / m_resolution;
- assert(ix >= 0 && ix < m_cols);
- assert(iy >= 0 && iy < m_rows);
- assert(ixb >= 0 && ixb < m_cols);
- assert(iyb >= 0 && iyb < m_rows);
+ assert(ix >= 0 && size_t(ix) < m_cols);
+ assert(iy >= 0 && size_t(iy) < m_rows);
+ assert(ixb >= 0 && size_t(ixb) < m_cols);
+ assert(iyb >= 0 && size_t(iyb) < m_rows);
// Account for the end points.
++ m_cells[iy*m_cols+ix].end;
if (ix == ixb && iy == iyb)
@@ -290,10 +290,10 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
coord_t iy = p1(1) / m_resolution;
coord_t ixb = p2(0) / m_resolution;
coord_t iyb = p2(1) / m_resolution;
- assert(ix >= 0 && ix < m_cols);
- assert(iy >= 0 && iy < m_rows);
- assert(ixb >= 0 && ixb < m_cols);
- assert(iyb >= 0 && iyb < m_rows);
+ assert(ix >= 0 && size_t(ix) < m_cols);
+ assert(iy >= 0 && size_t(iy) < m_rows);
+ assert(ixb >= 0 && size_t(ixb) < m_cols);
+ assert(iyb >= 0 && size_t(iyb) < m_rows);
// Account for the end points.
m_cell_data[m_cells[iy*m_cols + ix].end++] = std::pair<size_t, size_t>(i, j);
if (ix == ixb && iy == iyb)
@@ -775,11 +775,11 @@ void EdgeGrid::Grid::calculate_sdf()
// For each corner of this cell and its 1 ring neighbours:
for (int corner_y = -1; corner_y < 3; ++ corner_y) {
coord_t corner_r = r + corner_y;
- if (corner_r < 0 || corner_r >= nrows)
+ if (corner_r < 0 || (size_t)corner_r >= nrows)
continue;
for (int corner_x = -1; corner_x < 3; ++ corner_x) {
coord_t corner_c = c + corner_x;
- if (corner_c < 0 || corner_c >= ncols)
+ if (corner_c < 0 || (size_t)corner_c >= ncols)
continue;
float &d_min = m_signed_distance_field[corner_r * ncols + corner_c];
Slic3r::Point pt(m_bbox.min(0) + corner_c * m_resolution, m_bbox.min(1) + corner_r * m_resolution);
@@ -1137,9 +1137,9 @@ bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radiu
return false;
bbox.max(0) /= m_resolution;
bbox.max(1) /= m_resolution;
- if (bbox.max(0) >= m_cols)
+ if ((size_t)bbox.max(0) >= m_cols)
bbox.max(0) = m_cols - 1;
- if (bbox.max(1) >= m_rows)
+ if ((size_t)bbox.max(1) >= m_rows)
bbox.max(1) = m_rows - 1;
// Lower boundary, round to grid and test validity.
bbox.min(0) -= search_radius;
diff --git a/src/libslic3r/EdgeGrid.hpp b/src/libslic3r/EdgeGrid.hpp
index f99ab30c4..7faafdb3e 100644
--- a/src/libslic3r/EdgeGrid.hpp
+++ b/src/libslic3r/EdgeGrid.hpp
@@ -78,8 +78,8 @@ protected:
#endif
bool cell_inside_or_crossing(int r, int c) const
{
- if (r < 0 || r >= m_rows ||
- c < 0 || c >= m_cols)
+ if (r < 0 || (size_t)r >= m_rows ||
+ c < 0 || (size_t)c >= m_cols)
// The cell is outside the domain. Hoping that the contours were correctly oriented, so
// there is a CCW outmost contour so the out of domain cells are outside.
return false;
diff --git a/src/libslic3r/ExtrusionSimulator.cpp b/src/libslic3r/ExtrusionSimulator.cpp
index fcb2fe825..8c2ab037f 100644
--- a/src/libslic3r/ExtrusionSimulator.cpp
+++ b/src/libslic3r/ExtrusionSimulator.cpp
@@ -660,7 +660,7 @@ void gcode_spread_points(
for (ExtrusionPoints::const_iterator it = points.begin(); it != points.end(); ++ it) {
const V2f &center = it->center;
const float radius = it->radius;
- const float radius2 = radius * radius;
+ //const float radius2 = radius * radius;
const float height_target = it->height;
B2f bbox(center - V2f(radius, radius), center + V2f(radius, radius));
B2i bboxi(
@@ -774,8 +774,8 @@ void gcode_spread_points(
}
}
#endif
- float area_circle_total2 = float(M_PI) * sqr(radius);
- float area_err = fabs(area_circle_total2 - area_circle_total) / area_circle_total2;
+// float area_circle_total2 = float(M_PI) * sqr(radius);
+// float area_err = fabs(area_circle_total2 - area_circle_total) / area_circle_total2;
// printf("area_circle_total: %f, %f, %f\n", area_circle_total, area_circle_total2, area_err);
float volume_full = float(M_PI) * sqr(radius) * height_target;
// if (true) {
@@ -905,8 +905,8 @@ void ExtrusionSimulator::set_image_size(const Point &image_size)
// printf("Allocating image data, allocated\n");
//FIXME fill the image with red vertical lines.
- for (size_t r = 0; r < image_size.y(); ++ r) {
- for (size_t c = 0; c < image_size.x(); c += 2) {
+ for (size_t r = 0; r < size_t(image_size.y()); ++ r) {
+ for (size_t c = 0; c < size_t(image_size.x()); c += 2) {
// Color red
pimpl->image_data[r * image_size.x() * 4 + c * 4] = 255;
// Opacity full
@@ -958,7 +958,7 @@ void ExtrusionSimulator::extrude_to_accumulator(const ExtrusionPath &path, const
float scalex = float(viewport.size().x()) / float(bbox.size().x());
float scaley = float(viewport.size().y()) / float(bbox.size().y());
float w = scale_(path.width) * scalex;
- float h = scale_(path.height) * scalex;
+ //float h = scale_(path.height) * scalex;
w = scale_(path.mm3_per_mm / path.height) * scalex;
// printf("scalex: %f, scaley: %f\n", scalex, scaley);
// printf("bbox: %d,%d %d,%d\n", bbox.min.x(), bbox.min.y, bbox.max.x(), bbox.max.y);
@@ -993,8 +993,8 @@ void ExtrusionSimulator::evaluate_accumulator(ExtrusionSimulationType simulation
for (int r = 0; r < sz.y(); ++r) {
for (int c = 0; c < sz.x(); ++c) {
float p = 0;
- for (int j = 0; j < pimpl->bitmap_oversampled; ++ j) {
- for (int i = 0; i < pimpl->bitmap_oversampled; ++ i) {
+ for (unsigned int j = 0; j < pimpl->bitmap_oversampled; ++ j) {
+ for (unsigned int i = 0; i < pimpl->bitmap_oversampled; ++ i) {
if (pimpl->bitmap[r * pimpl->bitmap_oversampled + j][c * pimpl->bitmap_oversampled + i])
p += 1.f;
}
diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp
index 75952e4c2..fbdef29b9 100644
--- a/src/libslic3r/Fill/Fill.cpp
+++ b/src/libslic3r/Fill/Fill.cpp
@@ -126,7 +126,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
Polygons surfaces_polygons = to_polygons(surfaces);
Polygons collapsed = diff(
surfaces_polygons,
- offset2(surfaces_polygons, -distance_between_surfaces/2, +distance_between_surfaces/2),
+ offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2),
true);
Polygons to_subtract;
to_subtract.reserve(collapsed.size() + number_polygons(surfaces));
@@ -137,7 +137,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
surfaces_append(
surfaces,
intersection_ex(
- offset(collapsed, distance_between_surfaces),
+ offset(collapsed, (float)distance_between_surfaces),
to_subtract,
true),
stInternalSolid);
@@ -219,14 +219,14 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
f->z = layerm.layer()->print_z;
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);
+ f->link_max_length = (coord_t)scale_(link_max_length);
// Used by the concentric infill pattern to clip the loops to create extrusion paths.
- f->loop_clipping = scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER;
+ f->loop_clipping = coord_t(scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
// f->layer_height = h;
// apply half spacing using this flow's own spacing and generate infill
FillParams params;
- params.density = 0.01 * density;
+ params.density = float(0.01 * density);
// params.dont_adjust = true;
params.dont_adjust = false;
Polylines polylines = f->fill_surface(&surface, params);
@@ -240,7 +240,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// so we can safely ignore the slight variation that might have
// been applied to $f->flow_spacing
} else {
- flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow());
+ flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, (float)h, is_bridge || f->use_bridge_flow());
}
// Save into layer.
diff --git a/src/libslic3r/Fill/FillPlanePath.cpp b/src/libslic3r/Fill/FillPlanePath.cpp
index c52353b02..3b9266a0f 100644
--- a/src/libslic3r/Fill/FillPlanePath.cpp
+++ b/src/libslic3r/Fill/FillPlanePath.cpp
@@ -130,14 +130,14 @@ static inline Point hilbert_n_to_xy(const size_t n)
}
}
int state = (ndigits & 1) ? 4 : 0;
- int dirstate = (ndigits & 1) ? 0 : 4;
+// int dirstate = (ndigits & 1) ? 0 : 4;
coord_t x = 0;
coord_t y = 0;
for (int i = (int)ndigits - 1; i >= 0; -- i) {
int digit = (n >> (i * 2)) & 3;
state += digit;
- if (digit != 3)
- dirstate = state; // lowest non-3 digit
+// if (digit != 3)
+// dirstate = state; // lowest non-3 digit
x |= digit_to_x[state] << i;
y |= digit_to_y[state] << i;
state = next_state[state];
diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp
index 65440d0ef..8aea75886 100644
--- a/src/libslic3r/Fill/FillRectilinear2.cpp
+++ b/src/libslic3r/Fill/FillRectilinear2.cpp
@@ -287,7 +287,8 @@ public:
assert(aoffset1 < 0);
assert(aoffset2 < 0);
assert(aoffset2 < aoffset1);
- bool sticks_removed = remove_sticks(polygons_src);
+// bool sticks_removed =
+ remove_sticks(polygons_src);
// if (sticks_removed) printf("Sticks removed!\n");
polygons_outer = offset(polygons_src, aoffset1,
ClipperLib::jtMiter,
@@ -481,7 +482,7 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical
{
// This routine will propose a connecting line even if the connecting perimeter segment intersects
// iVertical line multiple times before reaching iIntersectionOther.
- if (iIntersectionOther == -1)
+ if (iIntersectionOther == size_t(-1))
return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED;
assert(dir_is_next ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0));
const SegmentedIntersectionLine &il_this = segs[iVerticalLine];
@@ -858,8 +859,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
if (il > ir)
// No vertical line intersects this segment.
continue;
- assert(il >= 0 && il < segs.size());
- assert(ir >= 0 && ir < segs.size());
+ assert(il >= 0 && size_t(il) < segs.size());
+ assert(ir >= 0 && size_t(ir) < segs.size());
for (int i = il; i <= ir; ++ i) {
coord_t this_x = segs[i].pos;
assert(this_x == i * line_spacing + x0);
@@ -1159,8 +1160,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
int iSegAbove = -1;
int iSegBelow = -1;
{
- SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ?
- SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW;
+// SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ?
+// SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW;
// Does the perimeter intersect the current vertical line above intrsctn?
for (size_t i = i_intersection + 1; i + 1 < seg.intersections.size(); ++ i)
// if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) {
diff --git a/src/libslic3r/Fill/FillRectilinear3.cpp b/src/libslic3r/Fill/FillRectilinear3.cpp
index dab584298..078feeae9 100644
--- a/src/libslic3r/Fill/FillRectilinear3.cpp
+++ b/src/libslic3r/Fill/FillRectilinear3.cpp
@@ -849,7 +849,7 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical
{
// This routine will propose a connecting line even if the connecting perimeter segment intersects
// iVertical line multiple times before reaching iIntersectionOther.
- if (iIntersectionOther == -1)
+ if (iIntersectionOther == size_t(-1))
return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED;
assert(dir_is_next ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0));
const SegmentedIntersectionLine &il_this = segs[iVerticalLine];
@@ -1284,8 +1284,8 @@ static bool fill_hatching_segments_legacy(
int iSegAbove = -1;
int iSegBelow = -1;
{
- SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ?
- SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW;
+// SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ?
+// SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW;
// Does the perimeter intersect the current vertical line above intrsctn?
for (size_t i = i_intersection + 1; i + 1 < seg.intersections.size(); ++ i)
// if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) {
diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index 4793586e3..0cc0a9d58 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -11,10 +11,16 @@
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/nowide/cstdio.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/xml_parser.hpp>
+#include <boost/foreach.hpp>
+namespace pt = boost::property_tree;
+
#include <expat.h>
#include <Eigen/Dense>
#include "miniz_extension.hpp"
@@ -33,6 +39,7 @@ const std::string RELATIONSHIPS_FILE = "_rels/.rels";
const std::string PRINT_CONFIG_FILE = "Metadata/Slic3r_PE.config";
const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config";
const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt";
+const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config_ranges.xml";
const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
const char* MODEL_TAG = "model";
@@ -331,6 +338,7 @@ namespace Slic3r {
typedef std::map<int, ObjectMetadata> IdToMetadataMap;
typedef std::map<int, Geometry> IdToGeometryMap;
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
+ typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap;
typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
// Version of the 3mf file
@@ -347,6 +355,7 @@ namespace Slic3r {
CurrentConfig m_curr_config;
IdToMetadataMap m_objects_metadata;
IdToLayerHeightsProfileMap m_layer_heights_profiles;
+ IdToLayerConfigRangesMap m_layer_config_ranges;
IdToSlaSupportPointsMap m_sla_support_points;
std::string m_curr_metadata_name;
std::string m_curr_characters;
@@ -365,6 +374,7 @@ namespace Slic3r {
bool _load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config);
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
+ void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, const std::string& archive_filename);
@@ -476,6 +486,7 @@ namespace Slic3r {
m_curr_config.volume_id = -1;
m_objects_metadata.clear();
m_layer_heights_profiles.clear();
+ m_layer_config_ranges.clear();
m_sla_support_points.clear();
m_curr_metadata_name.clear();
m_curr_characters.clear();
@@ -546,9 +557,14 @@ namespace Slic3r {
if (boost::algorithm::iequals(name, LAYER_HEIGHTS_PROFILE_FILE))
{
- // extract slic3r lazer heights profile file
+ // extract slic3r layer heights profile file
_extract_layer_heights_profile_config_from_archive(archive, stat);
}
+ if (boost::algorithm::iequals(name, LAYER_CONFIG_RANGES_FILE))
+ {
+ // extract slic3r layer config ranges file
+ _extract_layer_config_ranges_from_archive(archive, stat);
+ }
else if (boost::algorithm::iequals(name, SLA_SUPPORT_POINTS_FILE))
{
// extract sla support points file
@@ -592,6 +608,11 @@ namespace Slic3r {
if (obj_layer_heights_profile != m_layer_heights_profiles.end())
model_object->layer_height_profile = obj_layer_heights_profile->second;
+ // m_layer_config_ranges are indexed by a 1 based model object index.
+ IdToLayerConfigRangesMap::iterator obj_layer_config_ranges = m_layer_config_ranges.find(object.second + 1);
+ if (obj_layer_config_ranges != m_layer_config_ranges.end())
+ model_object->layer_config_ranges = obj_layer_config_ranges->second;
+
// m_sla_support_points are indexed by a 1 based model object index.
IdToSlaSupportPointsMap::iterator obj_sla_support_points = m_sla_support_points.find(object.second + 1);
if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty()) {
@@ -675,7 +696,7 @@ namespace Slic3r {
if (!XML_ParseBuffer(m_xml_parser, (int)stat.m_uncomp_size, 1))
{
char error_buf[1024];
- ::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), XML_GetCurrentLineNumber(m_xml_parser));
+ ::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), (int)XML_GetCurrentLineNumber(m_xml_parser));
add_error(error_buf);
return false;
}
@@ -769,6 +790,66 @@ namespace Slic3r {
}
}
+ void _3MF_Importer::_extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
+ {
+ if (stat.m_uncomp_size > 0)
+ {
+ std::string buffer((size_t)stat.m_uncomp_size, 0);
+ mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
+ if (res == 0) {
+ add_error("Error while reading layer config ranges data to buffer");
+ return;
+ }
+
+ std::istringstream iss(buffer); // wrap returned xml to istringstream
+ pt::ptree objects_tree;
+ pt::read_xml(iss, objects_tree);
+
+ for (const auto& object : objects_tree.get_child("objects"))
+ {
+ pt::ptree object_tree = object.second;
+ int obj_idx = object_tree.get<int>("<xmlattr>.id", -1);
+ if (obj_idx <= 0) {
+ add_error("Found invalid object id");
+ continue;
+ }
+
+ IdToLayerConfigRangesMap::iterator object_item = m_layer_config_ranges.find(obj_idx);
+ if (object_item != m_layer_config_ranges.end()) {
+ add_error("Found duplicated layer config range");
+ continue;
+ }
+
+ t_layer_config_ranges config_ranges;
+
+ for (const auto& range : object_tree)
+ {
+ if (range.first != "range")
+ continue;
+ pt::ptree range_tree = range.second;
+ double min_z = range_tree.get<double>("<xmlattr>.min_z");
+ double max_z = range_tree.get<double>("<xmlattr>.max_z");
+
+ // get Z range information
+ DynamicPrintConfig& config = config_ranges[{ min_z, max_z }];
+
+ for (const auto& option : range_tree)
+ {
+ if (option.first != "option")
+ continue;
+ std::string opt_key = option.second.get<std::string>("<xmlattr>.opt_key");
+ std::string value = option.second.data();
+
+ config.set_deserialize(opt_key, value);
+ }
+ }
+
+ if (!config_ranges.empty())
+ m_layer_config_ranges.insert(IdToLayerConfigRangesMap::value_type(obj_idx, config_ranges));
+ }
+ }
+ }
+
void _3MF_Importer::_extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
{
if (stat.m_uncomp_size > 0)
@@ -895,7 +976,7 @@ namespace Slic3r {
if (!XML_ParseBuffer(m_xml_parser, (int)stat.m_uncomp_size, 1))
{
char error_buf[1024];
- ::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), XML_GetCurrentLineNumber(m_xml_parser));
+ ::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), (int)XML_GetCurrentLineNumber(m_xml_parser));
add_error(error_buf);
return false;
}
@@ -1452,7 +1533,7 @@ namespace Slic3r {
object->second.metadata.emplace_back(key, value);
else if (type == VOLUME_TYPE)
{
- if (m_curr_config.volume_id < object->second.volumes.size())
+ if (size_t(m_curr_config.volume_id) < object->second.volumes.size())
object->second.volumes[m_curr_config.volume_id].metadata.emplace_back(key, value);
}
else
@@ -1624,6 +1705,7 @@ namespace Slic3r {
bool _add_mesh_to_object_stream(std::stringstream& stream, ModelObject& object, VolumeToOffsetsMap& volumes_offsets);
bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items);
bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
+ bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config);
bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data);
@@ -1684,6 +1766,16 @@ namespace Slic3r {
return false;
}
+ // Adds layer config ranges file ("Metadata/Slic3r_PE_layer_config_ranges.txt").
+ // All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
+ // The index differes from the index of an object ID of an object instance of a 3MF file!
+ if (!_add_layer_config_ranges_file_to_archive(archive, model))
+ {
+ close_zip_writer(&archive);
+ boost::filesystem::remove(filename);
+ return false;
+ }
+
// Adds sla support points file ("Metadata/Slic3r_PE_sla_support_points.txt").
// All sla support points of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
// The index differes from the index of an object ID of an object instance of a 3MF file!
@@ -1895,7 +1987,7 @@ namespace Slic3r {
return false;
}
- vertices_count += its.vertices.size();
+ vertices_count += (int)its.vertices.size();
const Transform3d& matrix = volume->get_matrix();
@@ -1925,7 +2017,7 @@ namespace Slic3r {
// updates triangle offsets
volume_it->second.first_triangle_id = triangles_count;
- triangles_count += its.indices.size();
+ triangles_count += (int)its.indices.size();
volume_it->second.last_triangle_id = triangles_count - 1;
for (size_t i = 0; i < its.indices.size(); ++ i)
@@ -2013,6 +2105,70 @@ namespace Slic3r {
return true;
}
+ bool _3MF_Exporter::_add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model)
+ {
+ std::string out = "";
+ pt::ptree tree;
+
+ unsigned int object_cnt = 0;
+ for (const ModelObject* object : model.objects)
+ {
+ object_cnt++;
+ const t_layer_config_ranges& ranges = object->layer_config_ranges;
+ if (!ranges.empty())
+ {
+ pt::ptree& obj_tree = tree.add("objects.object","");
+
+ obj_tree.put("<xmlattr>.id", object_cnt);
+
+ // Store the layer config ranges.
+ for (const auto& range : ranges)
+ {
+ pt::ptree& range_tree = obj_tree.add("range", "");
+
+ // store minX and maxZ
+ range_tree.put("<xmlattr>.min_z", range.first.first);
+ range_tree.put("<xmlattr>.max_z", range.first.second);
+
+ // store range configuration
+ const DynamicPrintConfig& config = range.second;
+ for (const std::string& opt_key : config.keys())
+ {
+ pt::ptree& opt_tree = range_tree.add("option", config.opt_serialize(opt_key));
+ opt_tree.put("<xmlattr>.opt_key", opt_key);
+ }
+ }
+ }
+ }
+
+ if (!tree.empty())
+ {
+ std::ostringstream oss;
+ boost::property_tree::write_xml(oss, tree);
+ out = oss.str();
+
+ // Post processing("beautification") of the output string for a better preview
+ boost::replace_all(out, "><object", ">\n <object");
+ boost::replace_all(out, "><range", ">\n <range");
+ boost::replace_all(out, "><option", ">\n <option");
+ boost::replace_all(out, "></range>", ">\n </range>");
+ boost::replace_all(out, "></object>", ">\n </object>");
+ // OR just
+ boost::replace_all(out, "><", ">\n<");
+ }
+
+ if (!out.empty())
+ {
+ if (!mz_zip_writer_add_mem(&archive, LAYER_CONFIG_RANGES_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
+ {
+ add_error("Unable to add layer heights profile file to archive");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
bool _3MF_Exporter::_add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model)
{
std::string out = "";
@@ -2060,7 +2216,7 @@ namespace Slic3r {
for (const std::string &key : config.keys())
if (key != "compatible_printers")
- out += "; " + key + " = " + config.serialize(key) + "\n";
+ out += "; " + key + " = " + config.opt_serialize(key) + "\n";
if (!out.empty())
{
@@ -2094,7 +2250,7 @@ namespace Slic3r {
// stores object's config data
for (const std::string& key : obj->config.keys())
{
- stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.serialize(key) << "\"/>\n";
+ stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.opt_serialize(key) << "\"/>\n";
}
for (const ModelVolume* volume : obj_metadata.second.object->volumes)
@@ -2124,7 +2280,7 @@ namespace Slic3r {
// stores volume's config data
for (const std::string& key : volume->config.keys())
{
- stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.serialize(key) << "\"/>\n";
+ stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n";
}
stream << " </" << VOLUME_TAG << ">\n";
diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp
index a33d21c9f..4370a2e1c 100644
--- a/src/libslic3r/Format/AMF.cpp
+++ b/src/libslic3r/Format/AMF.cpp
@@ -106,6 +106,9 @@ struct AMFParserContext
// amf/material/metadata
NODE_TYPE_OBJECT, // amf/object
// amf/object/metadata
+ NODE_TYPE_LAYER_CONFIG, // amf/object/layer_config_ranges
+ NODE_TYPE_RANGE, // amf/object/layer_config_ranges/range
+ // amf/object/layer_config_ranges/range/metadata
NODE_TYPE_MESH, // amf/object/mesh
NODE_TYPE_VERTICES, // amf/object/mesh/vertices
NODE_TYPE_VERTEX, // amf/object/mesh/vertices/vertex
@@ -189,7 +192,7 @@ struct AMFParserContext
};
// Version of the amf file
- unsigned int m_version;
+ unsigned int m_version;
// Current Expat XML parser instance.
XML_Parser m_parser;
// Model to receive objects extracted from an AMF file.
@@ -260,7 +263,9 @@ void AMFParserContext::startElement(const char *name, const char **atts)
m_value[0] = get_attribute(atts, "type");
node_type_new = NODE_TYPE_METADATA;
}
- } else if (strcmp(name, "mesh") == 0) {
+ } else if (strcmp(name, "layer_config_ranges") == 0 && m_path[1] == NODE_TYPE_OBJECT)
+ node_type_new = NODE_TYPE_LAYER_CONFIG;
+ else if (strcmp(name, "mesh") == 0) {
if (m_path[1] == NODE_TYPE_OBJECT)
node_type_new = NODE_TYPE_MESH;
} else if (strcmp(name, "instance") == 0) {
@@ -317,6 +322,10 @@ void AMFParserContext::startElement(const char *name, const char **atts)
else if (strcmp(name, "mirrorz") == 0)
node_type_new = NODE_TYPE_MIRRORZ;
}
+ else if (m_path[2] == NODE_TYPE_LAYER_CONFIG && strcmp(name, "range") == 0) {
+ assert(m_object);
+ node_type_new = NODE_TYPE_RANGE;
+ }
break;
case 4:
if (m_path[3] == NODE_TYPE_VERTICES) {
@@ -334,6 +343,10 @@ void AMFParserContext::startElement(const char *name, const char **atts)
} else if (strcmp(name, "triangle") == 0)
node_type_new = NODE_TYPE_TRIANGLE;
}
+ else if (m_path[3] == NODE_TYPE_RANGE && strcmp(name, "metadata") == 0) {
+ m_value[0] = get_attribute(atts, "type");
+ node_type_new = NODE_TYPE_METADATA;
+ }
break;
case 5:
if (strcmp(name, "coordinates") == 0) {
@@ -571,8 +584,13 @@ void AMFParserContext::endElement(const char * /* name */)
config = &m_material->config;
else if (m_path[1] == NODE_TYPE_OBJECT && m_object)
config = &m_object->config;
- } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume)
+ }
+ else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume)
config = &m_volume->config;
+ else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_RANGE && m_object && !m_object->layer_config_ranges.empty()) {
+ auto it = --m_object->layer_config_ranges.end();
+ config = &it->second;
+ }
if (config)
config->set_deserialize(opt_key, m_value[1]);
} else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "layer_height_profile") == 0) {
@@ -598,7 +616,7 @@ void AMFParserContext::endElement(const char * /* name */)
if (end != nullptr)
*end = 0;
- point(coord_idx) = atof(p);
+ point(coord_idx) = float(atof(p));
if (++coord_idx == 5) {
m_object->sla_support_points.push_back(sla::SupportPoint(point));
coord_idx = 0;
@@ -609,6 +627,16 @@ void AMFParserContext::endElement(const char * /* name */)
}
m_object->sla_points_status = sla::PointsStatus::UserModified;
}
+ else if (m_path.size() == 5 && m_path[1] == NODE_TYPE_OBJECT && m_path[3] == NODE_TYPE_RANGE &&
+ m_object && strcmp(opt_key, "layer_height_range") == 0) {
+ // Parse object's layer_height_range, a semicolon separated doubles.
+ char* p = const_cast<char*>(m_value[1].c_str());
+ char* end = strchr(p, ';');
+ *end = 0;
+
+ const t_layer_height_range range = {double(atof(p)), double(atof(end + 1))};
+ m_object->layer_config_ranges[range];
+ }
else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) {
if (strcmp(opt_key, "modifier") == 0) {
// Is this volume a modifier volume?
@@ -694,8 +722,8 @@ bool load_amf_file(const char *path, DynamicPrintConfig *config, Model *model)
}
int done = feof(pFile);
if (XML_Parse(parser, buff, len, done) == XML_STATUS_ERROR) {
- printf("AMF parser: Parse error at line %ul:\n%s\n",
- XML_GetCurrentLineNumber(parser),
+ printf("AMF parser: Parse error at line %d:\n%s\n",
+ (int)XML_GetCurrentLineNumber(parser),
XML_ErrorString(XML_GetErrorCode(parser)));
break;
}
@@ -753,7 +781,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi
if (!XML_ParseBuffer(parser, (int)stat.m_uncomp_size, 1))
{
- printf("Error (%s) while parsing xml file at line %d\n", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser));
+ printf("Error (%s) while parsing xml file at line %d\n", XML_ErrorString(XML_GetErrorCode(parser)), (int)XML_GetCurrentLineNumber(parser));
close_zip_reader(&archive);
return false;
}
@@ -873,7 +901,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
std::string str_config = "\n";
for (const std::string &key : config->keys())
if (key != "compatible_printers")
- str_config += "; " + key + " = " + config->serialize(key) + "\n";
+ str_config += "; " + key + " = " + config->opt_serialize(key) + "\n";
stream << "<metadata type=\"" << SLIC3R_CONFIG_TYPE << "\">" << xml_escape(str_config) << "</metadata>\n";
}
@@ -885,7 +913,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
for (const auto &attr : material.second->attributes)
stream << " <metadata type=\"" << attr.first << "\">" << attr.second << "</metadata>\n";
for (const std::string &key : material.second->config.keys())
- stream << " <metadata type=\"slic3r." << key << "\">" << material.second->config.serialize(key) << "</metadata>\n";
+ stream << " <metadata type=\"slic3r." << key << "\">" << material.second->config.opt_serialize(key) << "</metadata>\n";
stream << " </material>\n";
}
std::string instances;
@@ -893,7 +921,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
ModelObject *object = model->objects[object_id];
stream << " <object id=\"" << object_id << "\">\n";
for (const std::string &key : object->config.keys())
- stream << " <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
+ stream << " <metadata type=\"slic3r." << key << "\">" << object->config.opt_serialize(key) << "</metadata>\n";
if (!object->name.empty())
stream << " <metadata type=\"name\">" << xml_escape(object->name) << "</metadata>\n";
const std::vector<double> &layer_height_profile = object->layer_height_profile;
@@ -905,7 +933,30 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << ";" << layer_height_profile[i];
stream << "\n </metadata>\n";
}
- //FIXME Store the layer height ranges (ModelObject::layer_height_ranges)
+
+ // Export layer height ranges including the layer range specific config overrides.
+ const t_layer_config_ranges& config_ranges = object->layer_config_ranges;
+ if (!config_ranges.empty())
+ {
+ // Store the layer config range as a single semicolon separated list.
+ stream << " <layer_config_ranges>\n";
+ size_t layer_counter = 0;
+ for (auto range : config_ranges) {
+ stream << " <range id=\"" << layer_counter << "\">\n";
+
+ stream << " <metadata type=\"slic3r.layer_height_range\">";
+ stream << range.first.first << ";" << range.first.second << "</metadata>\n";
+
+ for (const std::string& key : range.second.keys())
+ stream << " <metadata type=\"slic3r." << key << "\">" << range.second.opt_serialize(key) << "</metadata>\n";
+
+ stream << " </range>\n";
+ layer_counter++;
+ }
+
+ stream << " </layer_config_ranges>\n";
+ }
+
const std::vector<sla::SupportPoint>& sla_support_points = object->sla_support_points;
if (!sla_support_points.empty()) {
@@ -941,7 +992,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << " </coordinates>\n";
stream << " </vertex>\n";
}
- num_vertices += its.vertices.size();
+ num_vertices += (int)its.vertices.size();
}
stream << " </vertices>\n";
for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) {
@@ -952,14 +1003,14 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
else
stream << " <volume materialid=\"" << volume->material_id() << "\">\n";
for (const std::string &key : volume->config.keys())
- stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
+ stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.opt_serialize(key) << "</metadata>\n";
if (!volume->name.empty())
stream << " <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
if (volume->is_modifier())
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
const indexed_triangle_set &its = volume->mesh().its;
- for (size_t i = 0; i < (int)its.indices.size(); ++i) {
+ for (size_t i = 0; i < its.indices.size(); ++i) {
stream << " <triangle>\n";
for (int j = 0; j < 3; ++j)
stream << " <v" << j + 1 << ">" << its.indices[i][j] + vertices_offset << "</v" << j + 1 << ">\n";
diff --git a/src/libslic3r/Format/objparser.cpp b/src/libslic3r/Format/objparser.cpp
index 88dfae695..62bcd306e 100644
--- a/src/libslic3r/Format/objparser.cpp
+++ b/src/libslic3r/Format/objparser.cpp
@@ -176,8 +176,7 @@ static bool obj_parseline(const char *line, ObjData &data)
EATWS();
if (*line == 0)
return false;
- // number of vertices of this face
- int n = 0;
+
// current vertex to be parsed
ObjVertex vertex;
char *endptr = 0;
@@ -266,7 +265,6 @@ static bool obj_parseline(const char *line, ObjData &data)
{
// o [object name]
EATWS();
- const char *name = line;
while (*line != ' ' && *line != '\t' && *line != 0)
++ line;
// copy name to line.
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index c42669de0..37a14d42c 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -4,7 +4,7 @@
#include "EdgeGrid.hpp"
#include "Geometry.hpp"
#include "GCode/PrintExtents.hpp"
-#include "GCode/WipeTowerPrusaMM.hpp"
+#include "GCode/WipeTower.hpp"
#include "Utils.hpp"
#include <algorithm>
@@ -162,9 +162,9 @@ std::string Wipe::wipe(GCode &gcodegen, bool toolchange)
return gcode;
}
-static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const WipeTower::xy &wipe_tower_pt)
+static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const Vec2f &wipe_tower_pt)
{
- return Point(scale_(wipe_tower_pt.x - gcodegen.origin()(0)), scale_(wipe_tower_pt.y - gcodegen.origin()(1)));
+ return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1)));
}
std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const
@@ -174,47 +174,97 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
// Toolchangeresult.gcode assumes the wipe tower corner is at the origin
// We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
float alpha = m_wipe_tower_rotation/180.f * float(M_PI);
- WipeTower::xy start_pos = tcr.start_pos;
- WipeTower::xy end_pos = tcr.end_pos;
- start_pos.rotate(alpha);
- start_pos.translate(m_wipe_tower_pos);
- end_pos.rotate(alpha);
- end_pos.translate(m_wipe_tower_pos);
- std::string tcr_rotated_gcode = rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha);
+ Vec2f start_pos = tcr.start_pos;
+ Vec2f end_pos = tcr.end_pos;
+ if (!tcr.priming) {
+ start_pos = Eigen::Rotation2Df(alpha) * start_pos;
+ start_pos += m_wipe_tower_pos;
+ end_pos = Eigen::Rotation2Df(alpha) * end_pos;
+ end_pos += m_wipe_tower_pos;
+ }
+ std::string tcr_rotated_gcode = tcr.priming ? tcr.gcode : rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha);
// Disable linear advance for the wipe tower operations.
gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
- // Move over the wipe tower.
- // Retract for a tool change, using the toolchange retract value and setting the priming extra length.
- gcode += gcodegen.retract(true);
- gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
- gcode += gcodegen.travel_to(
- wipe_tower_point_to_object_point(gcodegen, start_pos),
- erMixed,
- "Travel to a Wipe Tower");
- gcode += gcodegen.unretract();
-
- // Let the tool change be executed by the wipe tower class.
- // Inform the G-code writer about the changes done behind its back.
- gcode += tcr_rotated_gcode;
- // Let the m_writer know the current extruder_id, but ignore the generated G-code.
- if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))
- gcodegen.writer().toolchange(new_extruder_id);
+
+ if (!tcr.priming) {
+ // Move over the wipe tower.
+ // Retract for a tool change, using the toolchange retract value and setting the priming extra length.
+ gcode += gcodegen.retract(true);
+ gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
+ gcode += gcodegen.travel_to(
+ wipe_tower_point_to_object_point(gcodegen, start_pos),
+ erMixed,
+ "Travel to a Wipe Tower");
+ gcode += gcodegen.unretract();
+ }
+
+
+ // Process the end filament gcode.
+ std::string end_filament_gcode_str;
+ if (gcodegen.writer().extruder() != nullptr) {
+ // Process the custom end_filament_gcode in case of single_extruder_multi_material.
+ unsigned int old_extruder_id = gcodegen.writer().extruder()->id();
+ const std::string &end_filament_gcode = gcodegen.config().end_filament_gcode.get_at(old_extruder_id);
+ if (gcodegen.writer().extruder() != nullptr && ! end_filament_gcode.empty()) {
+ end_filament_gcode_str = gcodegen.placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id);
+ check_add_eol(end_filament_gcode_str);
+ }
+ }
+
+ // Process the custom toolchange_gcode. If it is empty, provide a simple Tn command to change the filament.
+ // Otherwise, leave control to the user completely.
+ std::string toolchange_gcode_str;
+ if (true /*gcodegen.writer().extruder() != nullptr*/) {
+ const std::string& toolchange_gcode = gcodegen.config().toolchange_gcode.value;
+ if (!toolchange_gcode.empty()) {
+ DynamicConfig config;
+ int previous_extruder_id = gcodegen.writer().extruder() ? (int)gcodegen.writer().extruder()->id() : -1;
+ config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id));
+ config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id));
+ config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index));
+ config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z));
+ toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config);
+ check_add_eol(toolchange_gcode_str);
+ }
+
+ std::string toolchange_command;
+ if (tcr.priming || (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)))
+ toolchange_command = gcodegen.writer().toolchange(new_extruder_id);
+ if (toolchange_gcode.empty())
+ toolchange_gcode_str = toolchange_command;
+ else {
+ // We have informed the m_writer about the current extruder_id, we can ignore the generated G-code.
+ }
+ }
+
gcodegen.placeholder_parser().set("current_extruder", new_extruder_id);
- // Always append the filament start G-code even if the extruder did not switch,
- // because the wipe tower resets the linear advance and we want it to be re-enabled.
+ // Process the start filament gcode.
+ std::string start_filament_gcode_str;
const std::string &start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(new_extruder_id);
if (! start_filament_gcode.empty()) {
// Process the start_filament_gcode for the active filament only.
DynamicConfig config;
config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id));
- gcode += gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config);
- check_add_eol(gcode);
+ start_filament_gcode_str = gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config);
+ check_add_eol(start_filament_gcode_str);
}
+
+ // Insert the end filament, toolchange, and start filament gcode into the generated gcode.
+ DynamicConfig config;
+ config.set_key_value("end_filament_gcode", new ConfigOptionString(end_filament_gcode_str));
+ config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str));
+ config.set_key_value("start_filament_gcode", new ConfigOptionString(start_filament_gcode_str));
+ std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config);
+ unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode);
+ gcode += tcr_gcode;
+ check_add_eol(toolchange_gcode_str);
+
+
// A phony move to the end position at the wipe tower.
- gcodegen.writer().travel_to_xy(Vec2d(end_pos.x, end_pos.y));
+ gcodegen.writer().travel_to_xy(end_pos.cast<double>());
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos));
// Prepare a future wipe.
@@ -224,8 +274,8 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos));
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
- WipeTower::xy((std::abs(m_left - end_pos.x) < std::abs(m_right - end_pos.x)) ? m_right : m_left,
- end_pos.y)));
+ Vec2f((std::abs(m_left - end_pos.x()) < std::abs(m_right - end_pos.x())) ? m_right : m_left,
+ end_pos.y())));
}
// Let the planner know we are traveling between objects.
@@ -235,14 +285,14 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
// This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode
// Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate)
-std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const
+std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const Vec2f& start_pos, const Vec2f& translation, float angle) const
{
std::istringstream gcode_str(gcode_original);
std::string gcode_out;
std::string line;
- WipeTower::xy pos = start_pos;
- WipeTower::xy transformed_pos;
- WipeTower::xy old_pos(-1000.1f, -1000.1f);
+ Vec2f pos = start_pos;
+ Vec2f transformed_pos;
+ Vec2f old_pos(-1000.1f, -1000.1f);
while (gcode_str) {
std::getline(gcode_str, line); // we read the gcode line by line
@@ -253,25 +303,25 @@ std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gco
char ch = 0;
while (line_str >> ch) {
if (ch == 'X')
- line_str >> pos.x;
+ line_str >> pos.x();
else
if (ch == 'Y')
- line_str >> pos.y;
+ line_str >> pos.y();
else
line_out << ch;
}
transformed_pos = pos;
- transformed_pos.rotate(angle);
- transformed_pos.translate(translation);
+ transformed_pos = Eigen::Rotation2Df(angle) * transformed_pos;
+ transformed_pos += translation;
if (transformed_pos != old_pos) {
line = line_out.str();
char buf[2048] = "G1";
- if (transformed_pos.x != old_pos.x)
- sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x);
- if (transformed_pos.y != old_pos.y)
- sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y);
+ if (transformed_pos.x() != old_pos.x())
+ sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x());
+ if (transformed_pos.y() != old_pos.y())
+ sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y());
line.replace(line.find("G1 "), 3, buf);
old_pos = transformed_pos;
@@ -288,27 +338,36 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen)
assert(m_layer_idx == 0);
std::string gcode;
- if (&m_priming != nullptr && ! m_priming.extrusions.empty()) {
+ if (&m_priming != nullptr) {
// Disable linear advance for the wipe tower operations.
- gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
- // Let the tool change be executed by the wipe tower class.
- // Inform the G-code writer about the changes done behind its back.
- gcode += m_priming.gcode;
- // Let the m_writer know the current extruder_id, but ignore the generated G-code.
- unsigned int current_extruder_id = m_priming.extrusions.back().tool;
- gcodegen.writer().toolchange(current_extruder_id);
- gcodegen.placeholder_parser().set("current_extruder", current_extruder_id);
+ //gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
+
+ for (const WipeTower::ToolChangeResult& tcr : m_priming) {
+ if (!tcr.extrusions.empty())
+ gcode += append_tcr(gcodegen, tcr, tcr.new_tool);
+
+
+ // Let the tool change be executed by the wipe tower class.
+ // Inform the G-code writer about the changes done behind its back.
+ //gcode += tcr.gcode;
+ // Let the m_writer know the current extruder_id, but ignore the generated G-code.
+ // unsigned int current_extruder_id = tcr.extrusions.back().tool;
+ // gcodegen.writer().toolchange(current_extruder_id);
+ // gcodegen.placeholder_parser().set("current_extruder", current_extruder_id);
+
+ }
+
// A phony move to the end position at the wipe tower.
- gcodegen.writer().travel_to_xy(Vec2d(m_priming.end_pos.x, m_priming.end_pos.y));
- gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos));
+ /* gcodegen.writer().travel_to_xy(Vec2d(m_priming.back().end_pos.x, m_priming.back().end_pos.y));
+ gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.back().end_pos));
// Prepare a future wipe.
gcodegen.m_wipe.path.points.clear();
// Start the wipe at the current position.
- gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos));
+ gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, m_priming.back().end_pos));
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
- gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
- WipeTower::xy((std::abs(m_left - m_priming.end_pos.x) < std::abs(m_right - m_priming.end_pos.x)) ? m_right : m_left,
- m_priming.end_pos.y)));
+ gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
+ WipeTower::xy((std::abs(m_left - m_priming.back().end_pos.x) < std::abs(m_right - m_priming.back().end_pos.x)) ? m_right : m_left,
+ m_priming.back().end_pos.y)));*/
}
return gcode;
}
@@ -316,10 +375,10 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen)
std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer)
{
std::string gcode;
- assert(m_layer_idx >= 0 && m_layer_idx <= m_tool_changes.size());
+ assert(m_layer_idx >= 0 && size_t(m_layer_idx) <= m_tool_changes.size());
if (! m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) {
- if (m_layer_idx < m_tool_changes.size()) {
- assert(m_tool_change_idx < m_tool_changes[m_layer_idx].size());
+ if (m_layer_idx < (int)m_tool_changes.size()) {
+ assert(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size());
gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id);
}
m_brim_done = true;
@@ -586,6 +645,9 @@ void GCode::_do_export(Print &print, FILE *file)
}
m_analyzer.set_extruder_offsets(extruder_offsets);
+ // tell analyzer about the gcode flavor
+ m_analyzer.set_gcode_flavor(print.config().gcode_flavor);
+
// resets analyzer's tracking data
m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm;
m_last_width = GCodeAnalyzer::Default_Width;
@@ -804,24 +866,16 @@ void GCode::_do_export(Print &print, FILE *file)
// 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 (has_wipe_tower) {
- // Wipe tower will control the extruder switching, it will call the start_filament_gcode.
- } else {
- // Only initialize the initial extruder.
+
+ // Process filament-specific gcode.
+ /* if (has_wipe_tower) {
+ // Wipe tower will control the extruder switching, it will call the start_filament_gcode.
+ } else {
DynamicConfig config;
config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id)));
- _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
- }
- } else {
- DynamicConfig config;
- for (const std::string &start_gcode : print.config().start_filament_gcode.values) {
- int extruder_id = (unsigned int)(&start_gcode - &print.config().start_filament_gcode.values.front());
- config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id));
- _writeln(file, this->placeholder_parser_process("start_filament_gcode", start_gcode, extruder_id, &config));
- }
+ _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
}
+*/
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
print.throw_if_canceled();
@@ -1057,6 +1111,10 @@ void GCode::_do_export(Print &print, FILE *file)
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";
+ print.m_print_statistics.estimated_normal_color_print_times = m_normal_time_estimator.get_color_times_dhms();
+ if (m_silent_time_estimator_enabled)
+ print.m_print_statistics.estimated_silent_color_print_times = m_silent_time_estimator.get_color_times_dhms();
+
std::vector<Extruder> extruders = m_writer.extruders();
if (! extruders.empty()) {
std::pair<std::string, unsigned int> out_filament_used_mm ("; filament used [mm] = ", 0);
@@ -1253,7 +1311,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
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);
+ m_writer.set_temperature(temp, 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) {
@@ -1344,11 +1402,11 @@ void GCode::process_layer(
// 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() >= (size_t)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() ||
- layer_region->perimeters.items_count() > 1 ||
+ if (size_t(layer_region->region()->config().bottom_solid_layers.value) > layer.id() ||
+ layer_region->perimeters.items_count() > 1u ||
layer_region->fills.items_count() > 0) {
enable = false;
break;
@@ -1405,7 +1463,9 @@ void GCode::process_layer(
m_colorprint_heights.erase(m_colorprint_heights.begin());
colorprint_change = true;
}
- if (colorprint_change && print.extruders().size()==1)
+
+ // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count
+ if (colorprint_change && print./*extruders()*/config().nozzle_diameter.size()==1)
gcode += "M600\n";
@@ -1414,11 +1474,11 @@ void GCode::process_layer(
bool extrude_skirt =
! 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() < (size_t)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.
- (first_layer || object_layer != nullptr || support_layer->id() < m_config.raft_layers.value);
+ (first_layer || object_layer != nullptr || support_layer->id() < (size_t)m_config.raft_layers.value);
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder;
coordf_t skirt_height = 0.;
if (extrude_skirt) {
@@ -1749,7 +1809,7 @@ void GCode::append_full_config(const Print& print, std::string& str)
const StaticPrintConfig *cfg = configs[i];
for (const std::string &key : cfg->keys())
if (key != "compatible_printers")
- str += "; " + key + " = " + cfg->serialize(key) + "\n";
+ str += "; " + key + " = " + cfg->opt_serialize(key) + "\n";
}
const DynamicConfig &full_config = print.placeholder_parser().config();
for (const char *key : {
@@ -2067,19 +2127,18 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// Retrieve the last start position for this object.
float last_pos_weight = 1.f;
- switch (seam_position) {
- case spAligned:
+
+ if (seam_position == spAligned) {
// Seam is aligned to the seam at the preceding layer.
if (m_layer != NULL && m_seam_position.count(m_layer->object()) > 0) {
last_pos = m_seam_position[m_layer->object()];
last_pos_weight = 1.f;
}
- break;
- case spRear:
+ }
+ else if (seam_position == spRear) {
last_pos = m_layer->object()->bounding_box().center();
last_pos(1) += coord_t(3. * m_layer->object()->bounding_box().radius());
last_pos_weight = 5.f;
- break;
}
// Insert a projection of last_pos into the polygon.
@@ -2088,7 +2147,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
Points::iterator it = project_point_to_polygon_and_insert(polygon, last_pos, 0.1 * nozzle_r);
last_pos_proj_idx = it - polygon.points.begin();
}
- Point last_pos_proj = polygon.points[last_pos_proj_idx];
+
// Parametrize the polygon by its length.
std::vector<float> lengths = polygon_parameter_by_length(polygon);
@@ -2098,7 +2157,6 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// No penalty for reflex points, slight penalty for convex points, high penalty for flat surfaces.
const float penaltyConvexVertex = 1.f;
const float penaltyFlatSurface = 5.f;
- const float penaltySeam = 1.3f;
const float penaltyOverhangHalf = 10.f;
// Penalty for visible seams.
for (size_t i = 0; i < polygon.points.size(); ++ i) {
@@ -2143,10 +2201,14 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// Signed distance is positive outside the object, negative inside the object.
// The point is considered at an overhang, if it is more than nozzle radius
// outside of the lower layer contour.
- bool found = (*lower_layer_edge_grid)->signed_distance(p, search_r, dist);
+ #ifdef NDEBUG // to suppress unused variable warning in release mode
+ (*lower_layer_edge_grid)->signed_distance(p, search_r, dist);
+ #else
+ bool found = (*lower_layer_edge_grid)->signed_distance(p, search_r, dist);
+ #endif
// If the approximate Signed Distance Field was initialized over lower_layer_edge_grid,
// then the signed distnace shall always be known.
- assert(found);
+ assert(found);
penalties[i] += extrudate_overlap_penalty(float(nozzle_r), penaltyOverhangHalf, float(dist));
}
}
@@ -2554,7 +2616,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
gcode += buf;
}
- if (m_last_mm3_per_mm != path.mm3_per_mm)
+ if (last_was_wipe_tower || (m_last_mm3_per_mm != path.mm3_per_mm))
{
m_last_mm3_per_mm = path.mm3_per_mm;
@@ -2732,38 +2794,57 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
m_wipe.reset_path();
if (m_writer.extruder() != nullptr) {
- // Process the custom end_filament_gcode in case of single_extruder_multi_material.
+ // Process the custom end_filament_gcode. set_extruder() is only called if there is no wipe tower
+ // so it should not be injected twice.
unsigned int old_extruder_id = m_writer.extruder()->id();
const std::string &end_filament_gcode = m_config.end_filament_gcode.get_at(old_extruder_id);
- if (m_config.single_extruder_multi_material && ! end_filament_gcode.empty()) {
+ if (! end_filament_gcode.empty()) {
gcode += placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id);
check_add_eol(gcode);
}
}
- m_placeholder_parser.set("current_extruder", extruder_id);
+
+ // If ooze prevention is enabled, park current extruder in the nearest
+ // standby point and set it to the standby temperature.
+ if (m_ooze_prevention.enable && m_writer.extruder() != nullptr)
+ gcode += m_ooze_prevention.pre_toolchange(*this);
- if (m_writer.extruder() != nullptr && ! m_config.toolchange_gcode.value.empty()) {
- // Process the custom toolchange_gcode.
+ const std::string& toolchange_gcode = m_config.toolchange_gcode.value;
+
+ // Process the custom toolchange_gcode. If it is empty, insert just a Tn command.
+ if (!toolchange_gcode.empty()) {
DynamicConfig config;
- config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id()));
+ config.set_key_value("previous_extruder", new ConfigOptionInt((int)(m_writer.extruder() != nullptr ? m_writer.extruder()->id() : -1 )));
config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id));
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
- gcode += placeholder_parser_process("toolchange_gcode", m_config.toolchange_gcode.value, extruder_id, &config);
+ gcode += placeholder_parser_process("toolchange_gcode", toolchange_gcode, extruder_id, &config);
check_add_eol(gcode);
}
-
- // If ooze prevention is enabled, park current extruder in the nearest
- // standby point and set it to the standby temperature.
- if (m_ooze_prevention.enable && m_writer.extruder() != nullptr)
- gcode += m_ooze_prevention.pre_toolchange(*this);
- // Append the toolchange command.
- gcode += m_writer.toolchange(extruder_id);
- // Append the filament start G-code for single_extruder_multi_material.
+
+ // We inform the writer about what is happening, but we may not use the resulting gcode.
+ std::string toolchange_command = m_writer.toolchange(extruder_id);
+ if (toolchange_gcode.empty())
+ gcode += toolchange_command;
+ else {
+ // user provided his own toolchange gcode, no need to do anything
+ }
+
+ // Set the temperature if the wipe tower didn't (not needed for non-single extruder MM)
+ if (m_config.single_extruder_multi_material && !m_config.wipe_tower) {
+ int temp = (m_layer_index == 0 ? m_config.first_layer_temperature.get_at(extruder_id) :
+ m_config.temperature.get_at(extruder_id));
+
+ gcode += m_writer.set_temperature(temp, false);
+ }
+
+ m_placeholder_parser.set("current_extruder", extruder_id);
+
+ // Append the filament start G-code.
const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id);
- if (m_config.single_extruder_multi_material && ! start_filament_gcode.empty()) {
- // Process the start_filament_gcode for the active filament only.
+ if (! start_filament_gcode.empty()) {
+ // Process the start_filament_gcode for the new filament.
gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id);
check_add_eol(gcode);
}
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index 21957d32c..4b81b42aa 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -83,7 +83,7 @@ class WipeTowerIntegration {
public:
WipeTowerIntegration(
const PrintConfig &print_config,
- const WipeTower::ToolChangeResult &priming,
+ const std::vector<WipeTower::ToolChangeResult> &priming,
const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes,
const WipeTower::ToolChangeResult &final_purge) :
m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f),
@@ -108,15 +108,15 @@ private:
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const;
// Postprocesses gcode: rotates and moves all G1 extrusions and returns result
- std::string rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const;
+ std::string rotate_wipe_tower_moves(const std::string& gcode_original, const Vec2f& start_pos, const Vec2f& translation, float angle) const;
// Left / right edges of the wipe tower, for the planning of wipe moves.
const float m_left;
const float m_right;
- const WipeTower::xy m_wipe_tower_pos;
+ const Vec2f m_wipe_tower_pos;
const float m_wipe_tower_rotation;
// Reference to cached values at the Printer class.
- const WipeTower::ToolChangeResult &m_priming;
+ const std::vector<WipeTower::ToolChangeResult> &m_priming;
const std::vector<std::vector<WipeTower::ToolChangeResult>> &m_tool_changes;
const WipeTower::ToolChangeResult &m_final_purge;
// Current layer index.
diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp
index 321d9a342..6cf118cc2 100644
--- a/src/libslic3r/GCode/Analyzer.cpp
+++ b/src/libslic3r/GCode/Analyzer.cpp
@@ -106,6 +106,11 @@ void GCodeAnalyzer::set_extruder_offsets(const GCodeAnalyzer::ExtruderOffsetsMap
m_extruder_offsets = extruder_offsets;
}
+void GCodeAnalyzer::set_gcode_flavor(const GCodeFlavor& flavor)
+{
+ m_gcode_flavor = flavor;
+}
+
void GCodeAnalyzer::reset()
{
_set_units(Millimeters);
@@ -249,6 +254,14 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi
_processM83(line);
break;
}
+ case 108:
+ case 135:
+ {
+ // these are used by MakerWare and Sailfish firmwares
+ // for tool changing - we can process it in one place
+ _processM108orM135(line);
+ break;
+ }
}
break;
@@ -426,9 +439,27 @@ void GCodeAnalyzer::_processM600(const GCodeReader::GCodeLine& line)
_set_cp_color_id(m_state.cur_cp_color_id);
}
-void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line)
+void GCodeAnalyzer::_processM108orM135(const GCodeReader::GCodeLine& line)
+{
+ // These M-codes are used by MakerWare and Sailfish to change active tool.
+ // They have to be processed otherwise toolchanges will be unrecognised
+ // by the analyzer - see https://github.com/prusa3d/PrusaSlicer/issues/2566
+
+ size_t code = ::atoi(&(line.cmd()[1]));
+ if ((code == 108 && m_gcode_flavor == gcfSailfish)
+ || (code == 135 && m_gcode_flavor == gcfMakerWare)) {
+
+ std::string cmd = line.raw();
+ size_t T_pos = cmd.find("T");
+ if (T_pos != std::string::npos) {
+ cmd = cmd.substr(T_pos);
+ _processT(cmd);
+ }
+ }
+}
+
+void GCodeAnalyzer::_processT(const std::string& cmd)
{
- std::string cmd = line.cmd();
if (cmd.length() > 1)
{
unsigned int id = (unsigned int)::strtol(cmd.substr(1).c_str(), nullptr, 10);
@@ -442,6 +473,11 @@ void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line)
}
}
+void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line)
+{
+ _processT(line.cmd());
+}
+
bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line)
{
std::string comment = line.comment();
diff --git a/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp
index 4c201c640..ab8bade71 100644
--- a/src/libslic3r/GCode/Analyzer.hpp
+++ b/src/libslic3r/GCode/Analyzer.hpp
@@ -106,6 +106,7 @@ private:
GCodeReader m_parser;
TypeToMovesMap m_moves_map;
ExtruderOffsetsMap m_extruder_offsets;
+ GCodeFlavor m_gcode_flavor;
// The output of process_layer()
std::string m_process_output;
@@ -115,6 +116,8 @@ public:
void set_extruder_offsets(const ExtruderOffsetsMap& extruder_offsets);
+ void set_gcode_flavor(const GCodeFlavor& flavor);
+
// Reinitialize the analyzer
void reset();
@@ -164,10 +167,14 @@ private:
// Set extruder to relative mode
void _processM83(const GCodeReader::GCodeLine& line);
+ // Set tool (MakerWare and Sailfish flavor)
+ void _processM108orM135(const GCodeReader::GCodeLine& line);
+
// Set color change
void _processM600(const GCodeReader::GCodeLine& line);
// Processes T line (Select Tool)
+ void _processT(const std::string& command);
void _processT(const GCodeReader::GCodeLine& line);
// Processes the tags
diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp
index 552fbf88c..1aa15ef33 100644
--- a/src/libslic3r/GCode/CoolingBuffer.cpp
+++ b/src/libslic3r/GCode/CoolingBuffer.cpp
@@ -672,7 +672,7 @@ std::string CoolingBuffer::apply_layer_cooldown(
#define EXTRUDER_CONFIG(OPT) config.OPT.get_at(m_current_extruder)
int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed);
int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0;
- if (layer_id >= EXTRUDER_CONFIG(disable_fan_first_layers)) {
+ if (layer_id >= (size_t)EXTRUDER_CONFIG(disable_fan_first_layers)) {
int max_fan_speed = EXTRUDER_CONFIG(max_fan_speed);
float slowdown_below_layer_time = float(EXTRUDER_CONFIG(slowdown_below_layer_time));
float fan_below_layer_time = float(EXTRUDER_CONFIG(fan_below_layer_time));
diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp
index 8eba6801e..55f07998b 100644
--- a/src/libslic3r/GCode/PreviewData.cpp
+++ b/src/libslic3r/GCode/PreviewData.cpp
@@ -404,6 +404,8 @@ std::string GCodePreviewData::get_legend_title() const
return L("Tool");
case Extrusion::ColorPrint:
return L("Color Print");
+ case Extrusion::Num_View_Types:
+ break; // just to supress warning about non-handled value
}
return "";
@@ -466,7 +468,7 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
}
case Extrusion::Tool:
{
- unsigned int tools_colors_count = tool_colors.size() / 4;
+ unsigned int tools_colors_count = (unsigned int)tool_colors.size() / 4;
items.reserve(tools_colors_count);
for (unsigned int i = 0; i < tools_colors_count; ++i)
{
@@ -491,20 +493,25 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
items.emplace_back(Slic3r::I18N::translate(L("Default print color")), color);
break;
}
+
+ std::string id_str = std::to_string(i + 1) + ": ";
+
if (i == 0) {
- items.emplace_back((boost::format(Slic3r::I18N::translate(L("up to %.2f mm"))) % cp_values[0].first).str(), color);
+ items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("up to %.2f mm"))) % cp_values[0].first).str(), color);
break;
}
if (i == color_print_cnt) {
- items.emplace_back((boost::format(Slic3r::I18N::translate(L("above %.2f mm"))) % cp_values[i-1].second).str(), color);
+ items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("above %.2f mm"))) % cp_values[i - 1].second).str(), color);
continue;
}
// items.emplace_back((boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i-1] % cp_values[i]).str(), color);
- items.emplace_back((boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i-1].second % cp_values[i].first).str(), color);
+ items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i - 1].second% cp_values[i].first).str(), color);
}
break;
}
+ case Extrusion::Num_View_Types:
+ break; // just to supress warning about non-handled value
}
return items;
diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp
index 92a58fdf0..07a71a0ea 100644
--- a/src/libslic3r/GCode/PrintExtents.cpp
+++ b/src/libslic3r/GCode/PrintExtents.cpp
@@ -149,8 +149,8 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
const WipeTower::Extrusion &e = tcr.extrusions[i];
if (e.width > 0) {
Vec2d delta = 0.5 * Vec2d(e.width, e.width);
- Vec2d p1 = trafo * Vec2d((&e - 1)->pos.x, (&e - 1)->pos.y);
- Vec2d p2 = trafo * Vec2d(e.pos.x, e.pos.y);
+ Vec2d p1 = trafo * (&e - 1)->pos.cast<double>();
+ Vec2d p2 = trafo * e.pos.cast<double>();
bbox.merge(p1.cwiseMin(p2) - delta);
bbox.merge(p1.cwiseMax(p2) + delta);
}
@@ -165,18 +165,19 @@ BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print)
{
BoundingBoxf bbox;
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) {
- Vec2d p1((&e - 1)->pos.x, (&e - 1)->pos.y);
- Vec2d p2(e.pos.x, e.pos.y);
- bbox.merge(p1);
- coordf_t radius = 0.5 * e.width;
- bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius);
- bbox.min(1) = std::min(bbox.min(1), std::min(p1(1), p2(1)) - radius);
- bbox.max(0) = std::max(bbox.max(0), std::max(p1(0), p2(0)) + radius);
- bbox.max(1) = std::max(bbox.max(1), std::max(p1(1), p2(1)) + radius);
+ for (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) {
+ const Vec2d& p1 = (&e - 1)->pos.cast<double>();
+ const Vec2d& p2 = e.pos.cast<double>();
+ bbox.merge(p1);
+ coordf_t radius = 0.5 * e.width;
+ bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius);
+ bbox.min(1) = std::min(bbox.min(1), std::min(p1(1), p2(1)) - radius);
+ bbox.max(0) = std::max(bbox.max(0), std::max(p1(0), p2(0)) + radius);
+ bbox.max(1) = std::max(bbox.max(1), std::max(p1(1), p2(1)) + radius);
+ }
}
}
}
diff --git a/src/libslic3r/GCode/SpiralVase.cpp b/src/libslic3r/GCode/SpiralVase.cpp
index 8e8ae3075..a4ae42b31 100644
--- a/src/libslic3r/GCode/SpiralVase.cpp
+++ b/src/libslic3r/GCode/SpiralVase.cpp
@@ -24,7 +24,7 @@ std::string SpiralVase::process_layer(const std::string &gcode)
// Get total XY length for this layer by summing all extrusion moves.
float total_layer_length = 0;
float layer_height = 0;
- float z;
+ float z = 0.f;
bool set_z = false;
{
diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp
index e25ad91fe..f10b45723 100644
--- a/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/src/libslic3r/GCode/ToolOrdering.cpp
@@ -324,9 +324,8 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_
m_layer_tools[j].has_wipe_tower = true;
} else {
LayerTools &lt_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new);
- LayerTools &lt_prev = m_layer_tools[j - 1];
LayerTools &lt_next = m_layer_tools[j + 1];
- assert(! lt_prev.extruders.empty() && ! lt_next.extruders.empty());
+ assert(! m_layer_tools[j - 1].extruders.empty() && ! lt_next.extruders.empty());
// FIXME: Following assert tripped when running combine_infill.t. I decided to comment it out for now.
// If it is a bug, it's likely not critical, because this code is unchanged for a long time. It might
// still be worth looking into it more and decide if it is a bug or an obsolete assert.
@@ -495,9 +494,6 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
if (!is_overriddable(*fill, print.config(), *object, region))
continue;
- // What extruder would this normally be printed with?
- unsigned int correct_extruder = Print::get_extruder(*fill, region);
-
if (volume_to_wipe<=0)
continue;
diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTower.cpp
index edfe475b5..37e4040d1 100644
--- a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/src/libslic3r/GCode/WipeTower.cpp
@@ -13,7 +13,7 @@ TODO LIST
*/
-#include "WipeTowerPrusaMM.hpp"
+#include "WipeTower.hpp"
#include <assert.h>
#include <math.h>
@@ -35,12 +35,23 @@ TODO LIST
namespace Slic3r
{
-namespace PrusaMultiMaterial {
+// Rotate the point around center of the wipe tower about given angle (in degrees)
+static Vec2f rotate(const Vec2f& pt, float width, float depth, float angle)
+{
+ Vec2f out(0,0);
+ float temp_x = pt(0) - width / 2.f;
+ float temp_y = pt(1) - depth / 2.f;
+ angle *= float(M_PI/180.);
+ out.x() += temp_x * cos(angle) - temp_y * sin(angle) + width / 2.f;
+ out.y() += temp_x * sin(angle) + temp_y * cos(angle) + depth / 2.f;
+ return out;
+}
+
-class Writer
+class WipeTowerWriter
{
public:
- Writer(float layer_height, float line_width, GCodeFlavor flavor) :
+ WipeTowerWriter(float layer_height, float line_width, GCodeFlavor flavor, const std::vector<WipeTower::FilamentParameters>& filament_parameters) :
m_current_pos(std::numeric_limits<float>::max(), std::numeric_limits<float>::max()),
m_current_z(0.f),
m_current_feedrate(0.f),
@@ -49,7 +60,8 @@ public:
m_preview_suppressed(false),
m_elapsed_time(0.f),
m_default_analyzer_line_width(line_width),
- m_gcode_flavor(flavor)
+ m_gcode_flavor(flavor),
+ m_filpar(filament_parameters)
{
// adds tag for analyzer:
char buf[64];
@@ -60,7 +72,7 @@ public:
change_analyzer_line_width(line_width);
}
- Writer& change_analyzer_line_width(float line_width) {
+ WipeTowerWriter& change_analyzer_line_width(float line_width) {
// adds tag for analyzer:
char buf[64];
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width);
@@ -68,25 +80,35 @@ public:
return *this;
}
- Writer& set_initial_position(const WipeTower::xy &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) {
+ WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) {
+ static const float area = M_PI * 1.75f * 1.75f / 4.f;
+ float mm3_per_mm = (len == 0.f ? 0.f : area * e / len);
+ // adds tag for analyzer:
+ char buf[64];
+ sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm);
+ m_gcode += buf;
+ return *this;
+ }
+
+ WipeTowerWriter& set_initial_position(const Vec2f &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) {
m_wipe_tower_width = width;
m_wipe_tower_depth = depth;
m_internal_angle = internal_angle;
- m_start_pos = WipeTower::xy(pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle);
+ m_start_pos = rotate(pos + Vec2f(0.f,m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle);
m_current_pos = pos;
return *this;
}
- Writer& set_initial_tool(const unsigned int tool) { m_current_tool = tool; return *this; }
+ WipeTowerWriter& set_initial_tool(const unsigned int tool) { m_current_tool = tool; return *this; }
- Writer& set_z(float z)
+ WipeTowerWriter& set_z(float z)
{ m_current_z = z; return *this; }
- Writer& set_extrusion_flow(float flow)
+ WipeTowerWriter& set_extrusion_flow(float flow)
{ m_extrusion_flow = flow; return *this; }
- Writer& set_y_shift(float shift) {
- m_current_pos.y -= shift-m_y_shift;
+ WipeTowerWriter& set_y_shift(float shift) {
+ m_current_pos.y() -= shift-m_y_shift;
m_y_shift = shift;
return (*this);
}
@@ -94,10 +116,10 @@ public:
// Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various
// filament loading and cooling moves from normal extrusion moves. Therefore the writer
// is asked to suppres output of some lines, which look like extrusions.
- Writer& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; }
- Writer& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; }
+ WipeTowerWriter& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; }
+ WipeTowerWriter& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; }
- Writer& feedrate(float f)
+ WipeTowerWriter& feedrate(float f)
{
if (f != m_current_feedrate)
m_gcode += "G1" + set_format_F(f) + "\n";
@@ -106,33 +128,33 @@ public:
const std::string& gcode() const { return m_gcode; }
const std::vector<WipeTower::Extrusion>& extrusions() const { return m_extrusions; }
- float x() const { return m_current_pos.x; }
- float y() const { return m_current_pos.y; }
- const WipeTower::xy& pos() const { return m_current_pos; }
- const WipeTower::xy start_pos_rotated() const { return m_start_pos; }
- const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); }
+ float x() const { return m_current_pos.x(); }
+ float y() const { return m_current_pos.y(); }
+ const Vec2f& pos() const { return m_current_pos; }
+ const Vec2f start_pos_rotated() const { return m_start_pos; }
+ const Vec2f pos_rotated() const { return rotate(m_current_pos + Vec2f(0.f, m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); }
float elapsed_time() const { return m_elapsed_time; }
float get_and_reset_used_filament_length() { float temp = m_used_filament_length; m_used_filament_length = 0.f; return temp; }
// Extrude with an explicitely provided amount of extrusion.
- Writer& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false)
+ WipeTowerWriter& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false, bool limit_volumetric_flow = true)
{
- if (x == m_current_pos.x && y == m_current_pos.y && e == 0.f && (f == 0.f || f == m_current_feedrate))
+ if (x == m_current_pos.x() && y == m_current_pos.y() && e == 0.f && (f == 0.f || f == m_current_feedrate))
// Neither extrusion nor a travel move.
return *this;
- float dx = x - m_current_pos.x;
- float dy = y - m_current_pos.y;
+ float dx = x - m_current_pos.x();
+ float dy = y - m_current_pos.y();
double len = sqrt(dx*dx+dy*dy);
if (record_length)
m_used_filament_length += e;
-
// Now do the "internal rotation" with respect to the wipe tower center
- WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we are
- WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we want to go
+ Vec2f rotated_current_pos(rotate(m_current_pos + Vec2f(0.f,m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we are
+ Vec2f rot(rotate(Vec2f(x,y+m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we want to go
if (! m_preview_suppressed && e > 0.f && len > 0.) {
+ change_analyzer_mm3_per_mm(len, e);
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
// This is left zero if it is a travel move.
float width = float(double(e) * /*Filament_Area*/2.40528 / (len * m_layer_height));
@@ -140,25 +162,30 @@ public:
width += m_layer_height * float(1. - M_PI / 4.);
if (m_extrusions.empty() || m_extrusions.back().pos != rotated_current_pos)
m_extrusions.emplace_back(WipeTower::Extrusion(rotated_current_pos, 0, m_current_tool));
- m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(rot.x, rot.y), width, m_current_tool));
+ m_extrusions.emplace_back(WipeTower::Extrusion(rot, width, m_current_tool));
}
m_gcode += "G1";
- if (std::abs(rot.x - rotated_current_pos.x) > EPSILON)
- m_gcode += set_format_X(rot.x);
+ if (std::abs(rot.x() - rotated_current_pos.x()) > EPSILON)
+ m_gcode += set_format_X(rot.x());
- if (std::abs(rot.y - rotated_current_pos.y) > EPSILON)
- m_gcode += set_format_Y(rot.y);
+ if (std::abs(rot.y() - rotated_current_pos.y()) > EPSILON)
+ m_gcode += set_format_Y(rot.y());
if (e != 0.f)
m_gcode += set_format_E(e);
- if (f != 0.f && f != m_current_feedrate)
+ if (f != 0.f && f != m_current_feedrate) {
+ if (limit_volumetric_flow) {
+ float e_speed = e / (((len == 0) ? std::abs(e) : len) / f * 60.f);
+ f /= std::max(1.f, e_speed / m_filpar[m_current_tool].max_e_speed);
+ }
m_gcode += set_format_F(f);
+ }
- m_current_pos.x = x;
- m_current_pos.y = y;
+ m_current_pos.x() = x;
+ m_current_pos.y() = y;
// Update the elapsed time with a rough estimate.
m_elapsed_time += ((len == 0) ? std::abs(e) : len) / m_current_feedrate * 60.f;
@@ -166,42 +193,42 @@ public:
return *this;
}
- Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f, bool record_length = false)
- { return extrude_explicit(dest.x, dest.y, e, f, record_length); }
+ WipeTowerWriter& extrude_explicit(const Vec2f &dest, float e, float f = 0.f, bool record_length = false, bool limit_volumetric_flow = true)
+ { return extrude_explicit(dest.x(), dest.y(), e, f, record_length); }
// Travel to a new XY position. f=0 means use the current value.
- Writer& travel(float x, float y, float f = 0.f)
+ WipeTowerWriter& travel(float x, float y, float f = 0.f)
{ return extrude_explicit(x, y, 0.f, f); }
- Writer& travel(const WipeTower::xy &dest, float f = 0.f)
- { return extrude_explicit(dest.x, dest.y, 0.f, f); }
+ WipeTowerWriter& travel(const Vec2f &dest, float f = 0.f)
+ { return extrude_explicit(dest.x(), dest.y(), 0.f, f); }
// Extrude a line from current position to x, y with the extrusion amount given by m_extrusion_flow.
- Writer& extrude(float x, float y, float f = 0.f)
+ WipeTowerWriter& extrude(float x, float y, float f = 0.f)
{
- float dx = x - m_current_pos.x;
- float dy = y - m_current_pos.y;
+ float dx = x - m_current_pos.x();
+ float dy = y - m_current_pos.y();
return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true);
}
- Writer& extrude(const WipeTower::xy &dest, const float f = 0.f)
- { return extrude(dest.x, dest.y, f); }
+ WipeTowerWriter& extrude(const Vec2f &dest, const float f = 0.f)
+ { return extrude(dest.x(), dest.y(), f); }
- Writer& rectangle(const WipeTower::xy& ld,float width,float height,const float f = 0.f)
+ WipeTowerWriter& rectangle(const Vec2f& ld,float width,float height,const float f = 0.f)
{
- WipeTower::xy corners[4];
+ Vec2f corners[4];
corners[0] = ld;
- corners[1] = WipeTower::xy(ld,width,0.f);
- corners[2] = WipeTower::xy(ld,width,height);
- corners[3] = WipeTower::xy(ld,0.f,height);
+ corners[1] = ld + Vec2f(width,0.f);
+ corners[2] = ld + Vec2f(width,height);
+ corners[3] = ld + Vec2f(0.f,height);
int index_of_closest = 0;
- if (x()-ld.x > ld.x+width-x()) // closer to the right
+ if (x()-ld.x() > ld.x()+width-x()) // closer to the right
index_of_closest = 1;
- if (y()-ld.y > ld.y+height-y()) // closer to the top
+ if (y()-ld.y() > ld.y()+height-y()) // closer to the top
index_of_closest = (index_of_closest==0 ? 3 : 2);
- travel(corners[index_of_closest].x, y()); // travel to the closest corner
- travel(x(),corners[index_of_closest].y);
+ travel(corners[index_of_closest].x(), y()); // travel to the closest corner
+ travel(x(),corners[index_of_closest].y());
int i = index_of_closest;
do {
@@ -212,7 +239,7 @@ public:
return (*this);
}
- Writer& load(float e, float f = 0.f)
+ WipeTowerWriter& load(float e, float f = 0.f)
{
if (e == 0.f && (f == 0.f || f == m_current_feedrate))
return *this;
@@ -224,29 +251,29 @@ public:
m_gcode += "\n";
return *this;
}
-
- // Derectract while moving in the X direction.
- // If |x| > 0, the feed rate relates to the x distance,
- // otherwise the feed rate relates to the e distance.
- Writer& load_move_x(float x, float e, float f = 0.f)
- { return extrude_explicit(x, m_current_pos.y, e, f); }
-
- Writer& retract(float e, float f = 0.f)
+
+ WipeTowerWriter& retract(float e, float f = 0.f)
{ return load(-e, f); }
// Loads filament while also moving towards given points in x-axis (x feedrate is limited by cutting the distance short if necessary)
- Writer& load_move_x_advanced(float farthest_x, float loading_dist, float loading_speed, float max_x_speed = 50.f)
+ WipeTowerWriter& load_move_x_advanced(float farthest_x, float loading_dist, float loading_speed, float max_x_speed = 50.f)
{
- float time = std::abs(loading_dist / loading_speed);
- float x_speed = std::min(max_x_speed, std::abs(farthest_x - x()) / time);
- float feedrate = 60.f * std::hypot(x_speed, loading_speed);
+ float time = std::abs(loading_dist / loading_speed); // time that the move must take
+ float x_distance = std::abs(farthest_x - x()); // max x-distance that we can travel
+ float x_speed = x_distance / time; // x-speed to do it in that time
+
+ if (x_speed > max_x_speed) {
+ // Necessary x_speed is too high - we must shorten the distance to achieve max_x_speed and still respect the time.
+ x_distance = max_x_speed * time;
+ x_speed = max_x_speed;
+ }
- float end_point = x() + (farthest_x > x() ? 1.f : -1.f) * x_speed * time;
- return extrude_explicit(end_point, y(), loading_dist, feedrate);
+ float end_point = x() + (farthest_x > x() ? 1.f : -1.f) * x_distance;
+ return extrude_explicit(end_point, y(), loading_dist, x_speed * 60.f, false, false);
}
// Elevate the extruder head above the current print_z position.
- Writer& z_hop(float hop, float f = 0.f)
+ WipeTowerWriter& z_hop(float hop, float f = 0.f)
{
m_gcode += std::string("G1") + set_format_Z(m_current_z + hop);
if (f != 0 && f != m_current_feedrate)
@@ -256,39 +283,36 @@ public:
}
// Lower the extruder head back to the current print_z position.
- Writer& z_hop_reset(float f = 0.f)
+ WipeTowerWriter& z_hop_reset(float f = 0.f)
{ return z_hop(0, f); }
// Move to x1, +y_increment,
// extrude quickly amount e to x2 with feed f.
- Writer& ram(float x1, float x2, float dy, float e0, float e, float f)
+ WipeTowerWriter& ram(float x1, float x2, float dy, float e0, float e, float f)
{
- extrude_explicit(x1, m_current_pos.y + dy, e0, f, true);
- extrude_explicit(x2, m_current_pos.y, e, 0.f, true);
+ extrude_explicit(x1, m_current_pos.y() + dy, e0, f, true, false);
+ extrude_explicit(x2, m_current_pos.y(), e, 0.f, true, false);
return *this;
}
// Let the end of the pulled out filament cool down in the cooling tube
// by moving up and down and moving the print head left / right
// at the current Y position to spread the leaking material.
- Writer& cool(float x1, float x2, float e1, float e2, float f)
+ WipeTowerWriter& cool(float x1, float x2, float e1, float e2, float f)
{
- extrude_explicit(x1, m_current_pos.y, e1, f);
- extrude_explicit(x2, m_current_pos.y, e2);
+ extrude_explicit(x1, m_current_pos.y(), e1, f, false, false);
+ extrude_explicit(x2, m_current_pos.y(), e2, false, false);
return *this;
}
- Writer& set_tool(int tool)
+ WipeTowerWriter& set_tool(int tool)
{
- char buf[64];
- sprintf(buf, "T%d\n", tool);
- m_gcode += buf;
m_current_tool = tool;
return *this;
}
// Set extruder temperature, don't wait by default.
- Writer& set_extruder_temp(int temperature, bool wait = false)
+ WipeTowerWriter& set_extruder_temp(int temperature, bool wait = false)
{
char buf[128];
sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature);
@@ -297,7 +321,7 @@ public:
};
// Wait for a period of time (seconds).
- Writer& wait(float time)
+ WipeTowerWriter& wait(float time)
{
if (time==0)
return *this;
@@ -308,7 +332,7 @@ public:
};
// Set speed factor override percentage.
- Writer& speed_override(int speed)
+ WipeTowerWriter& speed_override(int speed)
{
char buf[128];
sprintf(buf, "M220 S%d\n", speed);
@@ -317,21 +341,21 @@ public:
};
// Let the firmware back up the active speed override value.
- Writer& speed_override_backup()
+ WipeTowerWriter& speed_override_backup()
{
m_gcode += "M220 B\n";
return *this;
};
// Let the firmware restore the active speed override value.
- Writer& speed_override_restore()
+ WipeTowerWriter& speed_override_restore()
{
m_gcode += "M220 R\n";
return *this;
};
// Set digital trimpot motor
- Writer& set_extruder_trimpot(int current)
+ WipeTowerWriter& set_extruder_trimpot(int current)
{
char buf[128];
if (m_gcode_flavor == gcfRepRap)
@@ -342,20 +366,20 @@ public:
return *this;
};
- Writer& flush_planner_queue()
+ WipeTowerWriter& flush_planner_queue()
{
m_gcode += "G4 S0\n";
return *this;
}
// Reset internal extruder counter.
- Writer& reset_extruder()
+ WipeTowerWriter& reset_extruder()
{
m_gcode += "G92 E0\n";
return *this;
}
- Writer& comment_with_value(const char *comment, int value)
+ WipeTowerWriter& comment_with_value(const char *comment, int value)
{
char strvalue[64];
sprintf(strvalue, "%d", value);
@@ -364,7 +388,7 @@ public:
};
- Writer& set_fan(unsigned int speed)
+ WipeTowerWriter& set_fan(unsigned int speed)
{
if (speed == m_last_fan_speed)
return *this;
@@ -382,18 +406,11 @@ public:
return *this;
}
- Writer& comment_material(WipeTowerPrusaMM::material_type material)
- {
- m_gcode += "; material : ";
- m_gcode += WipeTowerPrusaMM::to_string(material) + "\n";
- return *this;
- };
-
- Writer& append(const char *text) { m_gcode += text; return *this; }
+ WipeTowerWriter& append(const std::string& text) { m_gcode += text; return *this; }
private:
- WipeTower::xy m_start_pos;
- WipeTower::xy m_current_pos;
+ Vec2f m_start_pos;
+ Vec2f m_current_pos;
float m_current_z;
float m_current_feedrate;
unsigned int m_current_tool;
@@ -412,19 +429,20 @@ private:
const float m_default_analyzer_line_width;
float m_used_filament_length = 0.f;
GCodeFlavor m_gcode_flavor;
+ const std::vector<WipeTower::FilamentParameters>& m_filpar;
std::string set_format_X(float x)
{
char buf[64];
sprintf(buf, " X%.3f", x);
- m_current_pos.x = x;
+ m_current_pos.x() = x;
return buf;
}
std::string set_format_Y(float y) {
char buf[64];
sprintf(buf, " Y%.3f", y);
- m_current_pos.y = y;
+ m_current_pos.y() = y;
return buf;
}
@@ -447,58 +465,13 @@ private:
return buf;
}
- Writer& operator=(const Writer &rhs);
-}; // class Writer
-
-}; // namespace PrusaMultiMaterial
+ WipeTowerWriter& operator=(const WipeTowerWriter &rhs);
+}; // class WipeTowerWriter
-WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *name)
-{
- if (strcasecmp(name, "PLA") == 0)
- return PLA;
- if (strcasecmp(name, "ABS") == 0)
- return ABS;
- if (strcasecmp(name, "PET") == 0)
- return PET;
- if (strcasecmp(name, "HIPS") == 0)
- return HIPS;
- if (strcasecmp(name, "FLEX") == 0)
- return FLEX;
- if (strcasecmp(name, "SCAFF") == 0)
- return SCAFF;
- if (strcasecmp(name, "EDGE") == 0)
- return EDGE;
- if (strcasecmp(name, "NGEN") == 0)
- return NGEN;
- if (strcasecmp(name, "PVA") == 0)
- return PVA;
- if (strcasecmp(name, "PC") == 0)
- return PC;
- return INVALID;
-}
-
-std::string WipeTowerPrusaMM::to_string(material_type material)
-{
- switch (material) {
- case PLA: return "PLA";
- case ABS: return "ABS";
- case PET: return "PET";
- case HIPS: return "HIPS";
- case FLEX: return "FLEX";
- case SCAFF: return "SCAFF";
- case EDGE: return "EDGE";
- case NGEN: return "NGEN";
- case PVA: return "PVA";
- case PC: return "PC";
- case INVALID:
- default: return "INVALID";
- }
-}
-
// Returns gcode to prime the nozzles at the front edge of the print bed.
-WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
+std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
// print_z of the first layer.
float first_layer_height,
// Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object.
@@ -516,25 +489,36 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
// box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area);
const float prime_section_width = std::min(240.f / tools.size(), 60.f);
- box_coordinates cleaning_box(xy(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f);
+ box_coordinates cleaning_box(Vec2f(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f);
- PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor);
- writer.set_extrusion_flow(m_extrusion_flow)
- .set_z(m_z_pos)
- .set_initial_tool(m_current_tool)
- .append(";--------------------\n"
- "; CP PRIMING START\n")
- .append(";--------------------\n");
- if (m_retain_speed_override)
- writer.speed_override_backup();
- writer.speed_override(100);
- writer.set_initial_position(xy(0.f, 0.f)) // Always move to the starting position
- .travel(cleaning_box.ld, 7200);
- if (m_set_extruder_trimpot)
- writer.set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming.
+ std::vector<ToolChangeResult> results;
+ // Iterate over all priming toolchanges and push respective ToolChangeResults into results vector.
for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) {
+ int old_tool = m_current_tool;
+
+ WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar);
+ writer.set_extrusion_flow(m_extrusion_flow)
+ .set_z(m_z_pos)
+ .set_initial_tool(m_current_tool);
+
+ // This is the first toolchange - initiate priming
+ if (idx_tool == 0) {
+ writer.append(";--------------------\n"
+ "; CP PRIMING START\n")
+ .append(";--------------------\n")
+ .speed_override_backup()
+ .speed_override(100)
+ .set_initial_position(Vec2f::Zero()) // Always move to the starting position
+ .travel(cleaning_box.ld, 7200);
+ if (m_set_extruder_trimpot)
+ writer.set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming.
+ }
+ else
+ writer.set_initial_position(results.back().end_pos);
+
+
unsigned int tool = tools[idx_tool];
m_left_to_right = true;
toolchange_Change(writer, tool, m_filpar[tool].material); // Select the tool, set a speed override for soluble and flex materials.
@@ -547,53 +531,63 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
//writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200);
toolchange_Wipe(writer, cleaning_box , 20.f);
box_coordinates box = cleaning_box;
- box.translate(0.f, writer.y() - cleaning_box.ld.y + m_perimeter_width);
+ box.translate(0.f, writer.y() - cleaning_box.ld.y() + m_perimeter_width);
toolchange_Unload(writer, box , m_filpar[m_current_tool].material, m_filpar[tools[idx_tool + 1]].first_layer_temperature);
cleaning_box.translate(prime_section_width, 0.f);
writer.travel(cleaning_box.ld, 7200);
}
++ m_num_tool_changes;
+
+
+ // Ask our writer about how much material was consumed:
+ if (m_current_tool < m_used_filament_length.size())
+ m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
+ ToolChangeResult result;
+ result.priming = true;
+ result.initial_tool = old_tool;
+ result.new_tool = m_current_tool;
+ result.print_z = this->m_z_pos;
+ result.layer_height = this->m_layer_height;
+ result.gcode = writer.gcode();
+ result.elapsed_time = writer.elapsed_time();
+ result.extrusions = writer.extrusions();
+ result.start_pos = writer.start_pos_rotated();
+ result.end_pos = writer.pos_rotated();
+
+ results.push_back(std::move(result));
+
+ // This is the last priming toolchange - finish priming
+ if (idx_tool+1 == tools.size()) {
+ // Reset the extruder current to a normal value.
+ if (m_set_extruder_trimpot)
+ writer.set_extruder_trimpot(550);
+ writer.speed_override_restore()
+ .feedrate(6000)
+ .flush_planner_queue()
+ .reset_extruder()
+ .append("; CP PRIMING END\n"
+ ";------------------\n"
+ "\n\n");
+ }
}
m_old_temperature = -1; // If the priming is turned off in config, the temperature changing commands will not actually appear
// in the output gcode - we should not remember emitting them (we will output them twice in the worst case)
- // Reset the extruder current to a normal value.
- if (m_set_extruder_trimpot)
- writer.set_extruder_trimpot(550);
- if (m_retain_speed_override)
- writer.speed_override_restore();
- writer.feedrate(6000)
- .flush_planner_queue()
- .reset_extruder()
- .append("; CP PRIMING END\n"
- ";------------------\n"
- "\n\n");
-
// so that tool_change() will know to extrude the wipe tower brim:
m_print_brim = true;
- // Ask our writer about how much material was consumed:
- if (m_current_tool < m_used_filament_length.size())
- m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
-
- ToolChangeResult result;
- result.priming = true;
- result.print_z = this->m_z_pos;
- result.layer_height = this->m_layer_height;
- result.gcode = writer.gcode();
- result.elapsed_time = writer.elapsed_time();
- result.extrusions = writer.extrusions();
- result.start_pos = writer.start_pos_rotated();
- result.end_pos = writer.pos_rotated();
- return result;
+ return results;
}
-WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, bool last_in_layer)
+WipeTower::ToolChangeResult WipeTower::tool_change(unsigned int tool, bool last_in_layer)
{
if ( m_print_brim )
return toolchange_Brim();
+ int old_tool = m_current_tool;
+
float wipe_area = 0.f;
bool last_change_in_layer = false;
float wipe_volume = 0.f;
@@ -615,26 +609,28 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
}
box_coordinates cleaning_box(
- xy(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
+ Vec2f(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
m_wipe_tower_width - m_perimeter_width,
(tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5*m_perimeter_width
: m_wipe_tower_depth-m_perimeter_width));
- PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor);
+ WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar);
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
.set_initial_tool(m_current_tool)
.set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f))
.append(";--------------------\n"
"; CP TOOLCHANGE START\n")
- .comment_with_value(" toolchange #", m_num_tool_changes + 1) // the number is zero-based
- .comment_material(m_filpar[m_current_tool].material)
- .append(";--------------------\n");
- if (m_retain_speed_override)
- writer.speed_override_backup();
+ .comment_with_value(" toolchange #", m_num_tool_changes + 1); // the number is zero-based
+
+ if (tool != (unsigned)(-1))
+ writer.append(std::string("; material : " + (m_current_tool < m_filpar.size() ? m_filpar[m_current_tool].material : "(NONE)") + " -> " + m_filpar[tool].material + "\n").c_str())
+ .append(";--------------------\n");
+
+ writer.speed_override_backup();
writer.speed_override(100);
- xy initial_position = cleaning_box.ld + WipeTower::xy(0.f,m_depth_traversed);
+ Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed);
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
// Increase the extruder driver current to allow fast ramming.
@@ -658,9 +654,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
if (last_change_in_layer) {// draw perimeter line
writer.set_y_shift(m_y_shift);
if (m_peters_wipe_tower)
- writer.rectangle(WipeTower::xy(0.f, 0.f),m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth);
+ writer.rectangle(Vec2f::Zero(), m_layer_info->depth + 3*m_perimeter_width, m_wipe_tower_depth);
else {
- writer.rectangle(WipeTower::xy(0.f, 0.f),m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
+ writer.rectangle(Vec2f::Zero(), m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
if (layer_finished()) { // no finish_layer will be called, we must wipe the nozzle
writer.travel(writer.x()> m_wipe_tower_width / 2.f ? 0.f : m_wipe_tower_width, writer.y());
}
@@ -669,8 +665,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
if (m_set_extruder_trimpot)
writer.set_extruder_trimpot(550); // Reset the extruder current to a normal value.
- if (m_retain_speed_override)
- writer.speed_override_restore();
+ writer.speed_override_restore();
writer.feedrate(6000)
.flush_planner_queue()
.reset_extruder()
@@ -684,6 +679,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
ToolChangeResult result;
result.priming = false;
+ result.initial_tool = old_tool;
+ result.new_tool = m_current_tool;
result.print_z = this->m_z_pos;
result.layer_height = this->m_layer_height;
result.gcode = writer.gcode();
@@ -694,25 +691,27 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
return result;
}
-WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, float y_offset)
+WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_offset)
{
+ int old_tool = m_current_tool;
+
const box_coordinates wipeTower_box(
- WipeTower::xy(0.f, 0.f),
+ Vec2f::Zero(),
m_wipe_tower_width,
m_wipe_tower_depth);
- PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor);
+ WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar);
writer.set_extrusion_flow(m_extrusion_flow * 1.1f)
.set_z(m_z_pos) // Let the writer know the current Z position as a base for Z-hop.
.set_initial_tool(m_current_tool)
.append(";-------------------------------------\n"
"; CP WIPE TOWER FIRST LAYER BRIM START\n");
- xy initial_position = wipeTower_box.lu - xy(m_perimeter_width * 6.f, 0);
+ Vec2f initial_position = wipeTower_box.lu - Vec2f(m_perimeter_width * 6.f, 0);
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
- writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower.
- 1.5f * m_extrusion_flow * (wipeTower_box.lu.y - wipeTower_box.ld.y), 2400);
+ writer.extrude_explicit(wipeTower_box.ld - Vec2f(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower.
+ 1.5f * m_extrusion_flow * (wipeTower_box.lu.y() - wipeTower_box.ld.y()), 2400);
// The tool is supposed to be active and primed at the time when the wipe tower brim is extruded.
// Extrude 4 rounds of a brim around the future wipe tower.
@@ -738,6 +737,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
ToolChangeResult result;
result.priming = false;
+ result.initial_tool = old_tool;
+ result.new_tool = m_current_tool;
result.print_z = this->m_z_pos;
result.layer_height = this->m_layer_height;
result.gcode = writer.gcode();
@@ -751,14 +752,14 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
-void WipeTowerPrusaMM::toolchange_Unload(
- PrusaMultiMaterial::Writer &writer,
+void WipeTower::toolchange_Unload(
+ WipeTowerWriter &writer,
const box_coordinates &cleaning_box,
- const material_type current_material,
+ const std::string& current_material,
const int new_temperature)
{
- float xl = cleaning_box.ld.x + 1.f * m_perimeter_width;
- float xr = cleaning_box.rd.x - 1.f * m_perimeter_width;
+ float xl = cleaning_box.ld.x() + 1.f * m_perimeter_width;
+ float xr = cleaning_box.rd.x() - 1.f * m_perimeter_width;
const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator; // desired ramming line thickness
const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm
@@ -771,7 +772,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
float remaining = xr - xl ; // keeps track of distance to the next turnaround
float e_done = 0; // measures E move done from each segment
- writer.travel(xl, cleaning_box.ld.y + m_depth_traversed + y_step/2.f ); // move to starting position
+ writer.travel(xl, cleaning_box.ld.y() + m_depth_traversed + y_step/2.f ); // move to starting position
// if the ending point of the ram would end up in mid air, align it with the end of the wipe tower:
if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion )) {
@@ -824,7 +825,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
const float e = m_filpar[m_current_tool].ramming_speed[i] * 0.25f / Filament_Area; // transform volume per sec to E move;
const float dist = std::min(x - e_done, remaining); // distance to travel for either the next 0.25s, or to the next turnaround
const float actual_time = dist/x * 0.25;
- writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), std::hypot(dist, e * (dist / x)) / (actual_time / 60.));
+ writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), dist / (actual_time / 60.));
remaining -= dist;
if (remaining < WT_EPSILON) { // we reached a turning point
@@ -838,25 +839,27 @@ void WipeTowerPrusaMM::toolchange_Unload(
e_done = 0;
}
}
- WipeTower::xy end_of_ramming(writer.x(),writer.y());
+ Vec2f end_of_ramming(writer.x(),writer.y());
writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier
// Retraction:
float old_x = writer.x();
float turning_point = (!m_left_to_right ? xl : xr );
- float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming
- writer.suppress_preview()
- .retract(15.f, m_filpar[m_current_tool].unloading_speed_start * 60.f) // feedrate 5000mm/min = 83mm/s
- .retract(0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed * 60.f)
- .retract(0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed * 60.f)
- .retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f)
-
- /*.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
- .load_move_x_advanced(old_x, -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed)
- .load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed)
- .load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
- .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/
- .resume_preview();
+ if (m_semm && (m_cooling_tube_retraction != 0 || m_cooling_tube_length != 0)) {
+ float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming
+ writer.suppress_preview()
+ .retract(15.f, m_filpar[m_current_tool].unloading_speed_start * 60.f) // feedrate 5000mm/min = 83mm/s
+ .retract(0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed * 60.f)
+ .retract(0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed * 60.f)
+ .retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f)
+
+ /*.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
+ .load_move_x_advanced(old_x, -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed)
+ .load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed)
+ .load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
+ .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/
+ .resume_preview();
+ }
if (new_temperature != 0 && (new_temperature != m_old_temperature || m_is_first_layer) ) { // Set the extruder temperature, but don't wait.
// If the required temperature is the same as last time, don't emit the M104 again (if user adjusted the value, it would be reset)
// However, always change temperatures on the first layer (this is to avoid issues with priming lines turned off).
@@ -891,73 +894,72 @@ void WipeTowerPrusaMM::toolchange_Unload(
// this is to align ramming and future wiping extrusions, so the future y-steps can be uniform from the start:
// the perimeter_width will later be subtracted, it is there to not load while moving over just extruded material
- writer.travel(end_of_ramming.x, end_of_ramming.y + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width, 2400.f);
+ writer.travel(end_of_ramming.x(), end_of_ramming.y() + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width, 2400.f);
writer.resume_preview()
.flush_planner_queue();
}
// Change the tool, set a speed override for soluble and flex materials.
-void WipeTowerPrusaMM::toolchange_Change(
- PrusaMultiMaterial::Writer &writer,
+void WipeTower::toolchange_Change(
+ WipeTowerWriter &writer,
const unsigned int new_tool,
- material_type new_material)
+ const std::string& new_material)
{
// Ask the writer about how much of the old filament we consumed:
if (m_current_tool < m_used_filament_length.size())
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
- // Speed override for the material. Go slow for flex and soluble materials.
- int speed_override;
- switch (new_material) {
- case PVA: speed_override = (m_z_pos < 0.80f) ? 60 : 80; break;
- case SCAFF: speed_override = 35; break;
- case FLEX: speed_override = 35; break;
- default: speed_override = 100;
- }
- writer.set_tool(new_tool);
- if (m_retain_speed_override)
- assert(speed_override == 100);
- else
- writer.speed_override(speed_override);
+ // This is where we want to place the custom gcodes. We will use placeholders for this.
+ // These will be substituted by the actual gcodes when the gcode is generated.
+ writer.append("[end_filament_gcode]\n");
+ writer.append("[toolchange_gcode]\n");
+
+ // The toolchange Tn command will be inserted later, only in case that the user does
+ // not provide a custom toolchange gcode.
+ writer.set_tool(new_tool); // This outputs nothing, the writer just needs to know the tool has changed.
+ writer.append("[start_filament_gcode]\n");
+
writer.flush_planner_queue();
m_current_tool = new_tool;
}
-void WipeTowerPrusaMM::toolchange_Load(
- PrusaMultiMaterial::Writer &writer,
+void WipeTower::toolchange_Load(
+ WipeTowerWriter &writer,
const box_coordinates &cleaning_box)
-{
- float xl = cleaning_box.ld.x + m_perimeter_width * 0.75f;
- float xr = cleaning_box.rd.x - m_perimeter_width * 0.75f;
- float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position
-
- // Load the filament while moving left / right, so the excess material will not create a blob at a single position.
- float turning_point = ( oldx-xl < xr-oldx ? xr : xl );
- float edist = m_parking_pos_retraction+m_extra_loading_move;
-
- writer.append("; CP TOOLCHANGE LOAD\n")
- .suppress_preview()
- /*.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration
- .load_move_x_advanced(oldx, 0.5f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
- .load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Slowing down
- .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
-
- .load(0.2f * edist, 60.f * m_filpar[m_current_tool].loading_speed_start)
- .load_move_x_advanced(turning_point, 0.7f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
- .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
-
- .travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate
- .resume_preview();
-
- // Reset the extruder current to the normal value.
- if (m_set_extruder_trimpot)
- writer.set_extruder_trimpot(550);
+{
+ if (m_semm && (m_parking_pos_retraction != 0 || m_extra_loading_move != 0)) {
+ float xl = cleaning_box.ld.x() + m_perimeter_width * 0.75f;
+ float xr = cleaning_box.rd.x() - m_perimeter_width * 0.75f;
+ float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position
+
+ // Load the filament while moving left / right, so the excess material will not create a blob at a single position.
+ float turning_point = ( oldx-xl < xr-oldx ? xr : xl );
+ float edist = m_parking_pos_retraction+m_extra_loading_move;
+
+ writer.append("; CP TOOLCHANGE LOAD\n")
+ .suppress_preview()
+ /*.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration
+ .load_move_x_advanced(oldx, 0.5f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
+ .load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Slowing down
+ .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
+
+ .load(0.2f * edist, 60.f * m_filpar[m_current_tool].loading_speed_start)
+ .load_move_x_advanced(turning_point, 0.7f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
+ .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
+
+ .travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate
+ .resume_preview();
+
+ // Reset the extruder current to the normal value.
+ if (m_set_extruder_trimpot)
+ writer.set_extruder_trimpot(550);
+ }
}
// Wipe the newly loaded filament until the end of the assigned wipe area.
-void WipeTowerPrusaMM::toolchange_Wipe(
- PrusaMultiMaterial::Writer &writer,
+void WipeTower::toolchange_Wipe(
+ WipeTowerWriter &writer,
const box_coordinates &cleaning_box,
float wipe_volume)
{
@@ -965,8 +967,8 @@ void WipeTowerPrusaMM::toolchange_Wipe(
writer.set_extrusion_flow(m_extrusion_flow * (m_is_first_layer ? 1.18f : 1.f))
.append("; CP TOOLCHANGE WIPE\n");
float wipe_coeff = m_is_first_layer ? 0.5f : 1.f;
- const float& xl = cleaning_box.ld.x;
- const float& xr = cleaning_box.rd.x;
+ const float& xl = cleaning_box.ld.x();
+ const float& xr = cleaning_box.rd.x();
// Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least
// the ordered volume, even if it means violating the box. This can later be removed and simply
@@ -997,7 +999,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
else
writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
- if (writer.y()+EPSILON > cleaning_box.lu.y-0.5f*m_perimeter_width)
+ if (writer.y()+EPSILON > cleaning_box.lu.y()-0.5f*m_perimeter_width)
break; // in case next line would not fit
traversed_x -= writer.x();
@@ -1024,13 +1026,15 @@ void WipeTowerPrusaMM::toolchange_Wipe(
-WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
+WipeTower::ToolChangeResult WipeTower::finish_layer()
{
// This should only be called if the layer is not finished yet.
// Otherwise the caller would likely travel to the wipe tower in vain.
assert(! this->layer_finished());
- PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor);
+ int old_tool = m_current_tool;
+
+ WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar);
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
.set_initial_tool(m_current_tool)
@@ -1042,7 +1046,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
// Slow down on the 1st layer.
float speed_factor = m_is_first_layer ? 0.5f : 1.f;
float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth();
- box_coordinates fill_box(xy(m_perimeter_width, m_depth_traversed + m_perimeter_width),
+ box_coordinates fill_box(Vec2f(m_perimeter_width, m_depth_traversed + m_perimeter_width),
m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width);
@@ -1056,44 +1060,44 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
else box.expand(-m_perimeter_width);
}
else i=2; // only draw the inner perimeter, outer has been already drawn by tool_change(...)
- writer.rectangle(box.ld,box.rd.x-box.ld.x,box.ru.y-box.rd.y,2900*speed_factor);
+ writer.rectangle(box.ld, box.rd.x()-box.ld.x(), box.ru.y()-box.rd.y(), 2900*speed_factor);
}
// we are in one of the corners, travel to ld along the perimeter:
- if (writer.x() > fill_box.ld.x+EPSILON) writer.travel(fill_box.ld.x,writer.y());
- if (writer.y() > fill_box.ld.y+EPSILON) writer.travel(writer.x(),fill_box.ld.y);
+ if (writer.x() > fill_box.ld.x()+EPSILON) writer.travel(fill_box.ld.x(),writer.y());
+ if (writer.y() > fill_box.ld.y()+EPSILON) writer.travel(writer.x(),fill_box.ld.y());
if (m_is_first_layer && m_adhesion) {
// Extrude a dense infill at the 1st layer to improve 1st layer adhesion of the wipe tower.
box.expand(-m_perimeter_width/2.f);
- int nsteps = int(floor((box.lu.y - box.ld.y) / (2*m_perimeter_width)));
- float step = (box.lu.y - box.ld.y) / nsteps;
- writer.travel(box.ld-xy(m_perimeter_width/2.f,m_perimeter_width/2.f));
+ int nsteps = int(floor((box.lu.y() - box.ld.y()) / (2*m_perimeter_width)));
+ float step = (box.lu.y() - box.ld.y()) / nsteps;
+ writer.travel(box.ld - Vec2f(m_perimeter_width/2.f, m_perimeter_width/2.f));
if (nsteps >= 0)
for (int i = 0; i < nsteps; ++i) {
- writer.extrude(box.ld.x+m_perimeter_width/2.f, writer.y() + 0.5f * step);
- writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y());
- writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y() + 0.5f * step);
- writer.extrude(box.ld.x + m_perimeter_width / 2.f, writer.y());
+ writer.extrude(box.ld.x()+m_perimeter_width/2.f, writer.y() + 0.5f * step);
+ writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y());
+ writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y() + 0.5f * step);
+ writer.extrude(box.ld.x() + m_perimeter_width / 2.f, writer.y());
}
- writer.travel(box.rd.x-m_perimeter_width/2.f,writer.y()); // wipe the nozzle
+ writer.travel(box.rd.x()-m_perimeter_width/2.f,writer.y()); // wipe the nozzle
}
else { // Extrude a sparse infill to support the material to be printed above.
- const float dy = (fill_box.lu.y - fill_box.ld.y - m_perimeter_width);
- const float left = fill_box.lu.x+2*m_perimeter_width;
- const float right = fill_box.ru.x - 2 * m_perimeter_width;
+ const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width);
+ const float left = fill_box.lu.x() + 2*m_perimeter_width;
+ const float right = fill_box.ru.x() - 2 * m_perimeter_width;
if (dy > m_perimeter_width)
{
// Extrude an inverse U at the left of the region.
- writer.travel(fill_box.ld + xy(m_perimeter_width * 2, 0.f))
- .extrude(fill_box.lu + xy(m_perimeter_width * 2, 0.f), 2900 * speed_factor);
+ writer.travel(fill_box.ld + Vec2f(m_perimeter_width * 2, 0.f))
+ .extrude(fill_box.lu + Vec2f(m_perimeter_width * 2, 0.f), 2900 * speed_factor);
const int n = 1+(right-left)/(m_bridging);
const float dx = (right-left)/n;
for (int i=1;i<=n;++i) {
float x=left+dx*i;
writer.travel(x,writer.y());
- writer.extrude(x,i%2 ? fill_box.rd.y : fill_box.ru.y);
+ writer.extrude(x,i%2 ? fill_box.rd.y() : fill_box.ru.y());
}
writer.travel(left,writer.y(),7200); // wipes the nozzle before moving away from the wipe tower
}
@@ -1111,6 +1115,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
ToolChangeResult result;
result.priming = false;
+ result.initial_tool = old_tool;
+ result.new_tool = m_current_tool;
result.print_z = this->m_z_pos;
result.layer_height = this->m_layer_height;
result.gcode = writer.gcode();
@@ -1122,7 +1128,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
}
// Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box
-void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume)
+void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume)
{
assert(m_plan.empty() || m_plan.back().z <= z_par + WT_EPSILON); // refuses to add a layer below the last one
@@ -1158,7 +1164,7 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
-void WipeTowerPrusaMM::plan_tower()
+void WipeTower::plan_tower()
{
// Calculate m_wipe_tower_depth (maximum depth for all the layers) and propagate depths downwards
m_wipe_tower_depth = 0.f;
@@ -1181,7 +1187,7 @@ void WipeTowerPrusaMM::plan_tower()
}
}
-void WipeTowerPrusaMM::save_on_last_wipe()
+void WipeTower::save_on_last_wipe()
{
for (m_layer_info=m_plan.begin();m_layer_info<m_plan.end();++m_layer_info) {
set_layer(m_layer_info->z, m_layer_info->height, 0, m_layer_info->z == m_plan.front().z, m_layer_info->z == m_plan.back().z);
@@ -1206,7 +1212,7 @@ 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<std::vector<WipeTower::ToolChangeResult>> &result)
+void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
{
if (m_plan.empty())
@@ -1224,7 +1230,15 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
make_wipe_tower_square();
m_layer_info = m_plan.begin();
- m_current_tool = (unsigned int)(-2); // we don't know which extruder to start with - we'll set it according to the first toolchange
+
+ // we don't know which extruder to start with - we'll set it according to the first toolchange
+ for (const auto& layer : m_plan) {
+ if (!layer.tool_changes.empty()) {
+ m_current_tool = layer.tool_changes.front().old_tool;
+ break;
+ }
+ }
+
for (auto& used : m_used_filament_length) // reset used filament stats
used = 0.f;
@@ -1240,11 +1254,8 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
if (!m_peters_wipe_tower && m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f;
- for (const auto &toolchange : layer.tool_changes) {
- if (m_current_tool == (unsigned int)(-2))
- m_current_tool = toolchange.old_tool;
+ for (const auto &toolchange : layer.tool_changes)
layer_result.emplace_back(tool_change(toolchange.new_tool, false));
- }
if (! layer_finished()) {
auto finish_layer_toolchange = finish_layer();
@@ -1252,7 +1263,7 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
auto& last_toolchange = layer_result.back();
if (last_toolchange.end_pos != finish_layer_toolchange.start_pos) {
char buf[2048]; // Add a travel move from tc1.end_pos to tc2.start_pos.
- sprintf(buf, "G1 X%.3f Y%.3f F7200\n", finish_layer_toolchange.start_pos.x, finish_layer_toolchange.start_pos.y);
+ sprintf(buf, "G1 X%.3f Y%.3f F7200\n", finish_layer_toolchange.start_pos.x(), finish_layer_toolchange.start_pos.y());
last_toolchange.gcode += buf;
}
last_toolchange.gcode += finish_layer_toolchange.gcode;
@@ -1268,7 +1279,7 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
}
}
-void WipeTowerPrusaMM::make_wipe_tower_square()
+void WipeTower::make_wipe_tower_square()
{
const float width = m_wipe_tower_width - 3 * m_perimeter_width;
const float depth = m_wipe_tower_depth - m_perimeter_width;
diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp
index 8ea3abd93..c18a502b1 100644
--- a/src/libslic3r/GCode/WipeTower.hpp
+++ b/src/libslic3r/GCode/WipeTower.hpp
@@ -1,95 +1,30 @@
-#ifndef slic3r_WipeTower_hpp_
-#define slic3r_WipeTower_hpp_
+#ifndef WipeTower_
+#define WipeTower_
-#include <math.h>
-#include <utility>
+#include <cmath>
#include <string>
-#include <vector>
-
-namespace Slic3r
-{
-
-// A pure virtual WipeTower definition.
-class WipeTower
-{
-public:
- // Internal point class, to make the wipe tower independent from other slic3r modules.
- // This is important for Prusa Research as we want to build the wipe tower post-processor independently from slic3r.
- struct xy
- {
- xy(float x = 0.f, float y = 0.f) : x(x), y(y) {}
- xy(const xy& pos,float xp,float yp) : x(pos.x+xp), y(pos.y+yp) {}
- xy operator+(const xy &rhs) const { xy out(*this); out.x += rhs.x; out.y += rhs.y; return out; }
- xy operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; }
- xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; }
- xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; }
- bool operator==(const xy &rhs) const { return x == rhs.x && y == rhs.y; }
- bool operator!=(const xy &rhs) const { return x != rhs.x || y != rhs.y; }
-
- // Rotate the point around center of the wipe tower about given angle (in degrees)
- xy rotate(float width, float depth, float angle) const {
- xy out(0,0);
- float temp_x = x - width / 2.f;
- float temp_y = y - depth / 2.f;
- angle *= float(M_PI/180.);
- out.x += temp_x * cos(angle) - temp_y * sin(angle) + width / 2.f;
- out.y += temp_x * sin(angle) + temp_y * cos(angle) + depth / 2.f;
- return out;
- }
-
- // Rotate the point around origin about given angle in degrees
- void rotate(float angle) {
- float temp_x = x * cos(angle) - y * sin(angle);
- y = x * sin(angle) + y * cos(angle);
- x = temp_x;
- }
-
- void translate(const xy& vect) {
- x += vect.x;
- y += vect.y;
- }
+#include <sstream>
+#include <utility>
+#include <algorithm>
- float x;
- float y;
- };
+#include "libslic3r/PrintConfig.hpp"
- WipeTower() {}
- virtual ~WipeTower() {}
- // Return the wipe tower position.
- virtual const xy& position() const = 0;
+namespace Slic3r
+{
- // Return the wipe tower width.
- virtual float width() const = 0;
+class WipeTowerWriter;
- // The wipe tower is finished, there should be no more tool changes or wipe tower prints.
- virtual bool finished() const = 0;
- // Switch to a next layer.
- virtual void set_layer(
- // Print height of this layer.
- float print_z,
- // Layer height, used to calculate extrusion the rate.
- float layer_height,
- // Maximum number of tool changes on this layer or the layers below.
- size_t max_tool_changes,
- // Is this the first layer of the print? In that case print the brim first.
- bool is_first_layer,
- // Is this the last layer of the wipe tower?
- bool is_last_layer) = 0;
-
- enum Purpose {
- PURPOSE_MOVE_TO_TOWER,
- PURPOSE_EXTRUDE,
- PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE,
- };
- // Extrusion path of the wipe tower, for 3D preview of the generated tool paths.
- struct Extrusion
+class WipeTower
+{
+public:
+ struct Extrusion
{
- Extrusion(const xy &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {}
+ Extrusion(const Vec2f &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {}
// End position of this extrusion.
- xy pos;
+ Vec2f pos;
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
// This is left zero if it is a travel move.
float width;
@@ -108,10 +43,10 @@ public:
std::vector<Extrusion> extrusions;
// Initial position, at which the wipe tower starts its action.
// At this position the extruder is loaded and there is no Z-hop applied.
- xy start_pos;
+ Vec2f start_pos;
// Last point, at which the normal G-code generator of Slic3r shall continue.
// At this position the extruder is loaded and there is no Z-hop applied.
- xy end_pos;
+ Vec2f end_pos;
// Time elapsed over this tool change.
// This is useful not only for the print time estimation, but also for the control of layer cooling.
float elapsed_time;
@@ -119,50 +54,375 @@ public:
// Is this a priming extrusion? (If so, the wipe tower rotation & translation will not be applied later)
bool priming;
+ // Initial tool
+ int initial_tool;
+
+ // New tool
+ int new_tool;
+
// Sum the total length of the extrusion.
float total_extrusion_length_in_plane() {
float e_length = 0.f;
for (size_t i = 1; i < this->extrusions.size(); ++ i) {
const Extrusion &e = this->extrusions[i];
if (e.width > 0) {
- xy v = e.pos - (&e - 1)->pos;
- e_length += sqrt(v.x*v.x+v.y*v.y);
+ Vec2f v = e.pos - (&e - 1)->pos;
+ e_length += v.norm();
}
}
return e_length;
}
};
+ // x -- x coordinates of wipe tower in mm ( left bottom corner )
+ // y -- y coordinates of wipe tower in mm ( left bottom corner )
+ // width -- width of wipe tower in mm ( default 60 mm - leave as it is )
+ // wipe_area -- space available for one toolchange in mm
+ WipeTower(bool semm, float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
+ float cooling_tube_length, float parking_pos_retraction, float extra_loading_move,
+ float bridging, bool set_extruder_trimpot, GCodeFlavor flavor,
+ const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
+ m_semm(semm),
+ m_wipe_tower_pos(x, y),
+ m_wipe_tower_width(width),
+ m_wipe_tower_rotation_angle(rotation_angle),
+ m_y_shift(0.f),
+ m_z_pos(0.f),
+ m_is_first_layer(false),
+ m_gcode_flavor(flavor),
+ m_bridging(bridging),
+ m_current_tool(initial_tool),
+ wipe_volumes(wiping_matrix)
+ {
+ // If this is a single extruder MM printer, we will use all the SE-specific config values.
+ // Otherwise, the defaults will be used to turn off the SE stuff.
+ if (m_semm) {
+ m_cooling_tube_retraction = cooling_tube_retraction;
+ m_cooling_tube_length = cooling_tube_length;
+ m_parking_pos_retraction = parking_pos_retraction;
+ m_extra_loading_move = extra_loading_move;
+ m_set_extruder_trimpot = set_extruder_trimpot;
+ }
+ }
+
+ virtual ~WipeTower() {}
+
+
+ // Set the extruder properties.
+ void set_extruder(size_t idx, std::string material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start,
+ float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
+ float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float max_volumetric_speed, float nozzle_diameter)
+ {
+ //while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
+ m_filpar.push_back(FilamentParameters());
+
+ m_filpar[idx].material = material;
+ m_filpar[idx].temperature = temp;
+ m_filpar[idx].first_layer_temperature = first_layer_temp;
+
+ // If this is a single extruder MM printer, we will use all the SE-specific config values.
+ // Otherwise, the defaults will be used to turn off the SE stuff.
+ if (m_semm) {
+ m_filpar[idx].loading_speed = loading_speed;
+ m_filpar[idx].loading_speed_start = loading_speed_start;
+ m_filpar[idx].unloading_speed = unloading_speed;
+ m_filpar[idx].unloading_speed_start = unloading_speed_start;
+ m_filpar[idx].delay = delay;
+ m_filpar[idx].cooling_moves = cooling_moves;
+ m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
+ m_filpar[idx].cooling_final_speed = cooling_final_speed;
+ }
+
+ if (max_volumetric_speed != 0.f)
+ m_filpar[idx].max_e_speed = (max_volumetric_speed / Filament_Area);
+ m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
+
+ m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
+
+ std::stringstream stream{m_semm ? ramming_parameters : std::string()};
+ float speed = 0.f;
+ stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
+ m_filpar[idx].ramming_line_width_multiplicator /= 100;
+ m_filpar[idx].ramming_step_multiplicator /= 100;
+ while (stream >> speed)
+ m_filpar[idx].ramming_speed.push_back(speed);
+
+ m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
+ }
+
+
+ // Appends into internal structure m_plan containing info about the future wipe tower
+ // to be used before building begins. The entries must be added ordered in z.
+ void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume = 0.f);
+
+ // Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
+ void generate(std::vector<std::vector<ToolChangeResult>> &result);
+
+ float get_depth() const { return m_wipe_tower_depth; }
+
+
+
+ // Switch to a next layer.
+ void set_layer(
+ // Print height of this layer.
+ float print_z,
+ // Layer height, used to calculate extrusion the rate.
+ float layer_height,
+ // Maximum number of tool changes on this layer or the layers below.
+ size_t max_tool_changes,
+ // Is this the first layer of the print? In that case print the brim first.
+ bool is_first_layer,
+ // Is this the last layer of the waste tower?
+ bool is_last_layer)
+ {
+ m_z_pos = print_z;
+ m_layer_height = layer_height;
+ m_is_first_layer = is_first_layer;
+ m_print_brim = is_first_layer;
+ m_depth_traversed = 0.f;
+ m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
+ if (is_first_layer) {
+ this->m_num_layer_changes = 0;
+ this->m_num_tool_changes = 0;
+ }
+ else
+ ++ m_num_layer_changes;
+
+ // Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
+ m_extrusion_flow = extrusion_flow(layer_height);
+
+ // Advance m_layer_info iterator, making sure we got it right
+ while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
+ ++m_layer_info;
+ }
+
+ // Return the wipe tower position.
+ const Vec2f& position() const { return m_wipe_tower_pos; }
+ // Return the wipe tower width.
+ float width() const { return m_wipe_tower_width; }
+ // The wipe tower is finished, there should be no more tool changes or wipe tower prints.
+ bool finished() const { return m_max_color_changes == 0; }
+
// Returns gcode to prime the nozzles at the front edge of the print bed.
- virtual ToolChangeResult prime(
+ std::vector<ToolChangeResult> prime(
// print_z of the first layer.
float first_layer_height,
// Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object.
const std::vector<unsigned int> &tools,
// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
- bool last_wipe_inside_wipe_tower) = 0;
+ bool last_wipe_inside_wipe_tower);
- // Returns gcode for toolchange and the end position.
- // if new_tool == -1, just unload the current filament over the wipe tower.
- virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer) = 0;
+ // Returns gcode for a toolchange and a final print head position.
+ // On the first layer, extrude a brim around the future wipe tower first.
+ ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer);
- // Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
+ // Fill the unfilled space with a sparse infill.
// Call this method only if layer_finished() is false.
- virtual ToolChangeResult finish_layer() = 0;
+ ToolChangeResult finish_layer();
+
+ // Is the current layer finished?
+ bool layer_finished() const {
+ return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
+ }
+
+ std::vector<float> get_used_filament() const { return m_used_filament_length; }
+ int get_number_of_toolchanges() const { return m_num_tool_changes; }
+
+ struct FilamentParameters {
+ std::string material = "PLA";
+ int temperature = 0;
+ int first_layer_temperature = 0;
+ float loading_speed = 0.f;
+ float loading_speed_start = 0.f;
+ float unloading_speed = 0.f;
+ float unloading_speed_start = 0.f;
+ float delay = 0.f ;
+ int cooling_moves = 0;
+ float cooling_initial_speed = 0.f;
+ float cooling_final_speed = 0.f;
+ float ramming_line_width_multiplicator = 0.f;
+ float ramming_step_multiplicator = 0.f;
+ float max_e_speed = std::numeric_limits<float>::max();
+ std::vector<float> ramming_speed;
+ float nozzle_diameter;
+ };
+
+private:
+ WipeTower();
+
+ enum wipe_shape // A fill-in direction
+ {
+ SHAPE_NORMAL = 1,
+ SHAPE_REVERSED = -1
+ };
+
+
+ const bool m_peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
+ const float Filament_Area = float(M_PI * 1.75f * 1.75f / 4.f); // filament area in mm^2
+ const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
+ const float WT_EPSILON = 1e-3f;
+
+
+ bool m_semm = true; // Are we using a single extruder multimaterial printer?
+ Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
+ float m_wipe_tower_width; // Width of the wipe tower.
+ float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
+ float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
+ float m_internal_rotation = 0.f;
+ float m_y_shift = 0.f; // y shift passed to writer
+ float m_z_pos = 0.f; // Current Z position.
+ float m_layer_height = 0.f; // Current layer height.
+ size_t m_max_color_changes = 0; // Maximum number of color changes per layer.
+ bool m_is_first_layer = false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
+ int m_old_temperature = -1; // To keep track of what was the last temp that we set (so we don't issue the command when not neccessary)
+
+ // G-code generator parameters.
+ float m_cooling_tube_retraction = 0.f;
+ float m_cooling_tube_length = 0.f;
+ float m_parking_pos_retraction = 0.f;
+ float m_extra_loading_move = 0.f;
+ float m_bridging = 0.f;
+ bool m_set_extruder_trimpot = false;
+ bool m_adhesion = true;
+ GCodeFlavor m_gcode_flavor;
+
+ float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
+ float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
+
+ // Extruder specific parameters.
+ std::vector<FilamentParameters> m_filpar;
+
- // Is the current layer finished? A layer is finished if either the wipe tower is finished, or
- // the wipe tower has been completely covered by the tool change extrusions,
- // or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
- virtual bool layer_finished() const = 0;
+ // State of the wipe tower generator.
+ unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics.
+ unsigned int m_num_tool_changes = 0; // Tool change change counter for the output statistics.
+ ///unsigned int m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes.
+ bool m_print_brim = true;
+ // A fill-in direction (positive Y, negative Y) alternates with each layer.
+ wipe_shape m_current_shape = SHAPE_NORMAL;
+ unsigned int m_current_tool = 0;
+ const std::vector<std::vector<float>> wipe_volumes;
- // Returns used filament length per extruder:
- virtual std::vector<float> get_used_filament() const = 0;
+ float m_depth_traversed = 0.f; // Current y position at the wipe tower.
+ bool m_left_to_right = true;
+ float m_extra_spacing = 1.f;
- // Returns total number of toolchanges:
- virtual int get_number_of_toolchanges() const = 0;
+ // Calculates extrusion flow needed to produce required line width for given layer height
+ float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
+ {
+ if ( layer_height < 0 )
+ return m_extrusion_flow;
+ return layer_height * ( m_perimeter_width - layer_height * (1.f-float(M_PI)/4.f)) / Filament_Area;
+ }
+
+ // Calculates length of extrusion line to extrude given volume
+ float volume_to_length(float volume, float line_width, float layer_height) const {
+ return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
+ }
+
+ // Calculates depth for all layers and propagates them downwards
+ void plan_tower();
+
+ // Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
+ void make_wipe_tower_square();
+
+ // Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
+ void save_on_last_wipe();
+
+
+ struct box_coordinates
+ {
+ box_coordinates(float left, float bottom, float width, float height) :
+ ld(left , bottom ),
+ lu(left , bottom + height),
+ rd(left + width, bottom ),
+ ru(left + width, bottom + height) {}
+ box_coordinates(const Vec2f &pos, float width, float height) : box_coordinates(pos(0), pos(1), width, height) {}
+ void translate(const Vec2f &shift) {
+ ld += shift; lu += shift;
+ rd += shift; ru += shift;
+ }
+ void translate(const float dx, const float dy) { translate(Vec2f(dx, dy)); }
+ void expand(const float offset) {
+ ld += Vec2f(- offset, - offset);
+ lu += Vec2f(- offset, offset);
+ rd += Vec2f( offset, - offset);
+ ru += Vec2f( offset, offset);
+ }
+ void expand(const float offset_x, const float offset_y) {
+ ld += Vec2f(- offset_x, - offset_y);
+ lu += Vec2f(- offset_x, offset_y);
+ rd += Vec2f( offset_x, - offset_y);
+ ru += Vec2f( offset_x, offset_y);
+ }
+ Vec2f ld; // left down
+ Vec2f lu; // left upper
+ Vec2f rd; // right lower
+ Vec2f ru; // right upper
+ };
+
+
+ // to store information about tool changes for a given layer
+ struct WipeTowerInfo{
+ struct ToolChange {
+ unsigned int old_tool;
+ unsigned int new_tool;
+ float required_depth;
+ float ramming_depth;
+ float first_wipe_line;
+ float wipe_volume;
+ ToolChange(unsigned int old, unsigned int newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f)
+ : old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {}
+ };
+ float z; // z position of the layer
+ float height; // layer height
+ float depth; // depth of the layer based on all layers above
+ float extra_spacing;
+ float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
+
+ std::vector<ToolChange> tool_changes;
+
+ WipeTowerInfo(float z_par, float layer_height_par)
+ : z{z_par}, height{layer_height_par}, depth{0}, extra_spacing{1.f} {}
+ };
+
+ std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
+ std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
+
+ // Stores information about used filament length per extruder:
+ std::vector<float> m_used_filament_length;
+
+
+ // Returns gcode for wipe tower brim
+ // sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
+ // offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower
+ ToolChangeResult toolchange_Brim(bool sideOnly = false, float y_offset = 0.f);
+
+ void toolchange_Unload(
+ WipeTowerWriter &writer,
+ const box_coordinates &cleaning_box,
+ const std::string& current_material,
+ const int new_temperature);
+
+ void toolchange_Change(
+ WipeTowerWriter &writer,
+ const unsigned int new_tool,
+ const std::string& new_material);
+
+ void toolchange_Load(
+ WipeTowerWriter &writer,
+ const box_coordinates &cleaning_box);
+
+ void toolchange_Wipe(
+ WipeTowerWriter &writer,
+ const box_coordinates &cleaning_box,
+ float wipe_volume);
};
+
+
+
}; // namespace Slic3r
-#endif /* slic3r_WipeTower_hpp_ */
+#endif // WipeTowerPrusaMM_hpp_
diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
deleted file mode 100644
index f8adf4c5f..000000000
--- a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ /dev/null
@@ -1,388 +0,0 @@
-#ifndef WipeTowerPrusaMM_hpp_
-#define WipeTowerPrusaMM_hpp_
-
-#include <cmath>
-#include <string>
-#include <sstream>
-#include <utility>
-#include <algorithm>
-
-#include "WipeTower.hpp"
-#include "PrintConfig.hpp"
-
-
-namespace Slic3r
-{
-
-namespace PrusaMultiMaterial {
- class Writer;
-};
-
-
-
-class WipeTowerPrusaMM : public WipeTower
-{
-public:
- enum material_type
- {
- INVALID = -1,
- PLA = 0, // E:210C B:55C
- ABS = 1, // E:255C B:100C
- PET = 2, // E:240C B:90C
- HIPS = 3, // E:220C B:100C
- FLEX = 4, // E:245C B:80C
- SCAFF = 5, // E:215C B:55C
- EDGE = 6, // E:240C B:80C
- NGEN = 7, // E:230C B:80C
- PVA = 8, // E:210C B:80C
- PC = 9
- };
-
- // Parse material name into material_type.
- static material_type parse_material(const char *name);
- static std::string to_string(material_type material);
-
- // x -- x coordinates of wipe tower in mm ( left bottom corner )
- // y -- y coordinates of wipe tower in mm ( left bottom corner )
- // width -- width of wipe tower in mm ( default 60 mm - leave as it is )
- // wipe_area -- space available for one toolchange in mm
- WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
- float cooling_tube_length, float parking_pos_retraction, float extra_loading_move,
- float bridging, bool set_extruder_trimpot, GCodeFlavor flavor,
- const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
- m_wipe_tower_pos(x, y),
- m_wipe_tower_width(width),
- m_wipe_tower_rotation_angle(rotation_angle),
- m_y_shift(0.f),
- m_z_pos(0.f),
- m_is_first_layer(false),
- m_cooling_tube_retraction(cooling_tube_retraction),
- m_cooling_tube_length(cooling_tube_length),
- m_parking_pos_retraction(parking_pos_retraction),
- m_extra_loading_move(extra_loading_move),
- m_bridging(bridging),
- m_set_extruder_trimpot(set_extruder_trimpot),
- m_gcode_flavor(flavor),
- m_current_tool(initial_tool),
- wipe_volumes(wiping_matrix)
- {}
-
- virtual ~WipeTowerPrusaMM() {}
-
-
- // Set the extruder properties.
- void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start,
- float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
- float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
- {
- //while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
- m_filpar.push_back(FilamentParameters());
-
- m_filpar[idx].material = material;
- if (material == FLEX || material == SCAFF || material == PVA) {
- // MMU2 lowers the print speed using the speed override (M220) for printing of soluble PVA/BVOH and flex materials.
- // Therefore it does not make sense to use the new M220 B and M220 R (backup / restore).
- m_retain_speed_override = false;
- }
- m_filpar[idx].temperature = temp;
- m_filpar[idx].first_layer_temperature = first_layer_temp;
- m_filpar[idx].loading_speed = loading_speed;
- m_filpar[idx].loading_speed_start = loading_speed_start;
- m_filpar[idx].unloading_speed = unloading_speed;
- m_filpar[idx].unloading_speed_start = unloading_speed_start;
- m_filpar[idx].delay = delay;
- m_filpar[idx].cooling_moves = cooling_moves;
- m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
- m_filpar[idx].cooling_final_speed = cooling_final_speed;
- m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
-
- m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
-
- std::stringstream stream{ramming_parameters};
- float speed = 0.f;
- stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
- m_filpar[idx].ramming_line_width_multiplicator /= 100;
- m_filpar[idx].ramming_step_multiplicator /= 100;
- while (stream >> speed)
- m_filpar[idx].ramming_speed.push_back(speed);
-
- m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
- }
-
-
- // Appends into internal structure m_plan containing info about the future wipe tower
- // to be used before building begins. The entries must be added ordered in z.
- void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume = 0.f);
-
- // Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
- void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
-
- float get_depth() const { return m_wipe_tower_depth; }
-
-
-
- // Switch to a next layer.
- virtual void set_layer(
- // Print height of this layer.
- float print_z,
- // Layer height, used to calculate extrusion the rate.
- float layer_height,
- // Maximum number of tool changes on this layer or the layers below.
- size_t max_tool_changes,
- // Is this the first layer of the print? In that case print the brim first.
- bool is_first_layer,
- // Is this the last layer of the waste tower?
- bool is_last_layer)
- {
- m_z_pos = print_z;
- m_layer_height = layer_height;
- m_is_first_layer = is_first_layer;
- m_print_brim = is_first_layer;
- m_depth_traversed = 0.f;
- m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
- if (is_first_layer) {
- this->m_num_layer_changes = 0;
- this->m_num_tool_changes = 0;
- }
- else
- ++ m_num_layer_changes;
-
- // Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
- m_extrusion_flow = extrusion_flow(layer_height);
-
- // Advance m_layer_info iterator, making sure we got it right
- while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
- ++m_layer_info;
- }
-
- // Return the wipe tower position.
- virtual const xy& position() const { return m_wipe_tower_pos; }
- // Return the wipe tower width.
- virtual float width() const { return m_wipe_tower_width; }
- // The wipe tower is finished, there should be no more tool changes or wipe tower prints.
- virtual bool finished() const { return m_max_color_changes == 0; }
-
- // Returns gcode to prime the nozzles at the front edge of the print bed.
- virtual ToolChangeResult prime(
- // print_z of the first layer.
- float first_layer_height,
- // Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object.
- const std::vector<unsigned int> &tools,
- // If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
- // If false, the last priming are will be large enough to wipe the last extruder sufficiently.
- bool last_wipe_inside_wipe_tower);
-
- // Returns gcode for a toolchange and a final print head position.
- // On the first layer, extrude a brim around the future wipe tower first.
- virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer);
-
- // Fill the unfilled space with a sparse infill.
- // Call this method only if layer_finished() is false.
- virtual ToolChangeResult finish_layer();
-
- // Is the current layer finished?
- virtual bool layer_finished() const {
- return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
- }
-
- virtual std::vector<float> get_used_filament() const override { return m_used_filament_length; }
- virtual int get_number_of_toolchanges() const override { return m_num_tool_changes; }
-
-
-private:
- WipeTowerPrusaMM();
-
- enum wipe_shape // A fill-in direction
- {
- SHAPE_NORMAL = 1,
- SHAPE_REVERSED = -1
- };
-
-
- const bool m_peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
- const float Filament_Area = float(M_PI * 1.75f * 1.75f / 4.f); // filament area in mm^2
- const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
- const float WT_EPSILON = 1e-3f;
-
-
- xy m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
- float m_wipe_tower_width; // Width of the wipe tower.
- float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
- float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
- float m_internal_rotation = 0.f;
- float m_y_shift = 0.f; // y shift passed to writer
- float m_z_pos = 0.f; // Current Z position.
- float m_layer_height = 0.f; // Current layer height.
- size_t m_max_color_changes = 0; // Maximum number of color changes per layer.
- bool m_is_first_layer = false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
- int m_old_temperature = -1; // To keep track of what was the last temp that we set (so we don't issue the command when not neccessary)
-
- // G-code generator parameters.
- float m_cooling_tube_retraction = 0.f;
- float m_cooling_tube_length = 0.f;
- float m_parking_pos_retraction = 0.f;
- float m_extra_loading_move = 0.f;
- float m_bridging = 0.f;
- bool m_set_extruder_trimpot = false;
- bool m_retain_speed_override = true;
- bool m_adhesion = true;
- GCodeFlavor m_gcode_flavor;
-
- float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
- float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
-
-
- struct FilamentParameters {
- material_type material = PLA;
- int temperature = 0;
- int first_layer_temperature = 0;
- float loading_speed = 0.f;
- float loading_speed_start = 0.f;
- float unloading_speed = 0.f;
- float unloading_speed_start = 0.f;
- float delay = 0.f ;
- int cooling_moves = 0;
- float cooling_initial_speed = 0.f;
- float cooling_final_speed = 0.f;
- float ramming_line_width_multiplicator = 0.f;
- float ramming_step_multiplicator = 0.f;
- std::vector<float> ramming_speed;
- float nozzle_diameter;
- };
-
- // Extruder specific parameters.
- std::vector<FilamentParameters> m_filpar;
-
-
- // State of the wipe tower generator.
- unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics.
- unsigned int m_num_tool_changes = 0; // Tool change change counter for the output statistics.
- ///unsigned int m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes.
- bool m_print_brim = true;
- // A fill-in direction (positive Y, negative Y) alternates with each layer.
- wipe_shape m_current_shape = SHAPE_NORMAL;
- unsigned int m_current_tool = 0;
- const std::vector<std::vector<float>> wipe_volumes;
-
- float m_depth_traversed = 0.f; // Current y position at the wipe tower.
- bool m_left_to_right = true;
- float m_extra_spacing = 1.f;
-
- // Calculates extrusion flow needed to produce required line width for given layer height
- float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
- {
- if ( layer_height < 0 )
- return m_extrusion_flow;
- return layer_height * ( m_perimeter_width - layer_height * (1.f-float(M_PI)/4.f)) / Filament_Area;
- }
-
- // Calculates length of extrusion line to extrude given volume
- float volume_to_length(float volume, float line_width, float layer_height) const {
- return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
- }
-
- // Calculates depth for all layers and propagates them downwards
- void plan_tower();
-
- // Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
- void make_wipe_tower_square();
-
- // Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
- void save_on_last_wipe();
-
-
- struct box_coordinates
- {
- box_coordinates(float left, float bottom, float width, float height) :
- ld(left , bottom ),
- lu(left , bottom + height),
- rd(left + width, bottom ),
- ru(left + width, bottom + height) {}
- box_coordinates(const xy &pos, float width, float height) : box_coordinates(pos.x, pos.y, width, height) {}
- void translate(const xy &shift) {
- ld += shift; lu += shift;
- rd += shift; ru += shift;
- }
- void translate(const float dx, const float dy) { translate(xy(dx, dy)); }
- void expand(const float offset) {
- ld += xy(- offset, - offset);
- lu += xy(- offset, offset);
- rd += xy( offset, - offset);
- ru += xy( offset, offset);
- }
- void expand(const float offset_x, const float offset_y) {
- ld += xy(- offset_x, - offset_y);
- lu += xy(- offset_x, offset_y);
- rd += xy( offset_x, - offset_y);
- ru += xy( offset_x, offset_y);
- }
- xy ld; // left down
- xy lu; // left upper
- xy rd; // right lower
- xy ru; // right upper
- };
-
-
- // to store information about tool changes for a given layer
- struct WipeTowerInfo{
- struct ToolChange {
- unsigned int old_tool;
- unsigned int new_tool;
- float required_depth;
- float ramming_depth;
- float first_wipe_line;
- float wipe_volume;
- ToolChange(unsigned int old, unsigned int newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f)
- : old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {}
- };
- float z; // z position of the layer
- float height; // layer height
- float depth; // depth of the layer based on all layers above
- float extra_spacing;
- float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
-
- std::vector<ToolChange> tool_changes;
-
- WipeTowerInfo(float z_par, float layer_height_par)
- : z{z_par}, height{layer_height_par}, depth{0}, extra_spacing{1.f} {}
- };
-
- std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
- std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
-
- // Stores information about used filament length per extruder:
- std::vector<float> m_used_filament_length;
-
-
- // Returns gcode for wipe tower brim
- // sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
- // offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower
- ToolChangeResult toolchange_Brim(bool sideOnly = false, float y_offset = 0.f);
-
- void toolchange_Unload(
- PrusaMultiMaterial::Writer &writer,
- const box_coordinates &cleaning_box,
- const material_type current_material,
- const int new_temperature);
-
- void toolchange_Change(
- PrusaMultiMaterial::Writer &writer,
- const unsigned int new_tool,
- material_type new_material);
-
- void toolchange_Load(
- PrusaMultiMaterial::Writer &writer,
- const box_coordinates &cleaning_box);
-
- void toolchange_Wipe(
- PrusaMultiMaterial::Writer &writer,
- const box_coordinates &cleaning_box,
- float wipe_volume);
-};
-
-
-
-
-}; // namespace Slic3r
-
-#endif /* WipeTowerPrusaMM_hpp_ */
diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp
index b87305da8..33dc9f4b7 100644
--- a/src/libslic3r/GCodeTimeEstimator.cpp
+++ b/src/libslic3r/GCodeTimeEstimator.cpp
@@ -174,7 +174,7 @@ namespace Slic3r {
const std::string GCodeTimeEstimator::Silent_Last_M73_Output_Placeholder_Tag = "; SILENT_LAST_M73_OUTPUT_PLACEHOLDER";
GCodeTimeEstimator::GCodeTimeEstimator(EMode mode)
- : _mode(mode)
+ : m_mode(mode)
{
reset();
set_default();
@@ -183,7 +183,7 @@ namespace Slic3r {
void GCodeTimeEstimator::add_gcode_line(const std::string& gcode_line)
{
PROFILE_FUNC();
- _parser.parse_line(gcode_line,
+ m_parser.parse_line(gcode_line,
[this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
{ this->_process_gcode_line(reader, line); });
}
@@ -196,7 +196,7 @@ namespace Slic3r {
{ this->_process_gcode_line(reader, line); };
for (; *ptr != 0;) {
gline.reset();
- ptr = _parser.parse_line(ptr, gline, action);
+ ptr = m_parser.parse_line(ptr, gline, action);
}
}
@@ -206,10 +206,13 @@ namespace Slic3r {
if (start_from_beginning)
{
_reset_time();
- _last_st_synchronized_block_id = -1;
+ m_last_st_synchronized_block_id = -1;
}
_calculate_time();
+ if (m_needs_color_times && (m_color_time_cache != 0.0f))
+ m_color_times.push_back(m_color_time_cache);
+
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
@@ -219,12 +222,15 @@ namespace Slic3r {
{
reset();
- _parser.parse_buffer(gcode,
+ m_parser.parse_buffer(gcode,
[this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
{ this->_process_gcode_line(reader, line); });
_calculate_time();
+ if (m_needs_color_times && (m_color_time_cache != 0.0f))
+ m_color_times.push_back(m_color_time_cache);
+
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
@@ -234,9 +240,12 @@ namespace Slic3r {
{
reset();
- _parser.parse_file(file, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2));
+ m_parser.parse_file(file, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2));
_calculate_time();
+ if (m_needs_color_times && (m_color_time_cache != 0.0f))
+ m_color_times.push_back(m_color_time_cache);
+
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
@@ -249,9 +258,12 @@ namespace Slic3r {
auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
{ this->_process_gcode_line(reader, line); };
for (const std::string& line : gcode_lines)
- _parser.parse_line(line, action);
+ m_parser.parse_line(line, action);
_calculate_time();
+ if (m_needs_color_times && (m_color_time_cache != 0.0f))
+ m_color_times.push_back(m_color_time_cache);
+
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
@@ -270,7 +282,7 @@ namespace Slic3r {
throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for writing.\n"));
std::string time_mask;
- switch (_mode)
+ switch (m_mode)
{
default:
case Normal:
@@ -291,7 +303,7 @@ namespace Slic3r {
// buffer line to export only when greater than 64K to reduce writing calls
std::string export_line;
char time_line[64];
- G1LineIdToBlockIdMap::const_iterator it_line_id = _g1_line_ids.begin();
+ G1LineIdToBlockIdMap::const_iterator it_line_id = m_g1_line_ids.begin();
while (std::getline(in, gcode_line))
{
if (!in.good())
@@ -301,15 +313,15 @@ namespace Slic3r {
}
// replaces placeholders for initial line M73 with the real lines
- if (((_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) ||
- ((_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag)))
+ if (((m_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) ||
+ ((m_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag)))
{
- sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(_time).c_str());
+ sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(m_time).c_str());
gcode_line = time_line;
}
// replaces placeholders for final line M73 with the real lines
- else if (((_mode == Normal) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) ||
- ((_mode == Silent) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag)))
+ else if (((m_mode == Normal) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) ||
+ ((m_mode == Silent) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag)))
{
sprintf(time_line, time_mask.c_str(), "100", "0");
gcode_line = time_line;
@@ -319,27 +331,27 @@ namespace Slic3r {
// add remaining time lines where needed
- _parser.parse_line(gcode_line,
+ m_parser.parse_line(gcode_line,
[this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line)
{
if (line.cmd_is("G1"))
{
++g1_lines_count;
- assert(it_line_id == _g1_line_ids.end() || it_line_id->first >= g1_lines_count);
+ assert(it_line_id == m_g1_line_ids.end() || it_line_id->first >= g1_lines_count);
const Block *block = nullptr;
- if (it_line_id != _g1_line_ids.end() && it_line_id->first == g1_lines_count) {
- if (line.has_e() && it_line_id->second < (unsigned int)_blocks.size())
- block = &_blocks[it_line_id->second];
+ if (it_line_id != m_g1_line_ids.end() && it_line_id->first == g1_lines_count) {
+ if (line.has_e() && it_line_id->second < (unsigned int)m_blocks.size())
+ block = &m_blocks[it_line_id->second];
++it_line_id;
}
if (block != nullptr && block->elapsed_time != -1.0f) {
- float block_remaining_time = _time - block->elapsed_time;
+ float block_remaining_time = m_time - block->elapsed_time;
if (std::abs(last_recorded_time - block_remaining_time) > interval)
{
- sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str());
+ sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / m_time)).c_str(), _get_time_minutes(block_remaining_time).c_str());
gcode_line += time_line;
last_recorded_time = block_remaining_time;
@@ -387,240 +399,240 @@ namespace Slic3r {
void GCodeTimeEstimator::set_axis_position(EAxis axis, float position)
{
- _state.axis[axis].position = position;
+ m_state.axis[axis].position = position;
}
void GCodeTimeEstimator::set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec)
{
- _state.axis[axis].max_feedrate = feedrate_mm_sec;
+ m_state.axis[axis].max_feedrate = feedrate_mm_sec;
}
void GCodeTimeEstimator::set_axis_max_acceleration(EAxis axis, float acceleration)
{
- _state.axis[axis].max_acceleration = acceleration;
+ m_state.axis[axis].max_acceleration = acceleration;
}
void GCodeTimeEstimator::set_axis_max_jerk(EAxis axis, float jerk)
{
- _state.axis[axis].max_jerk = jerk;
+ m_state.axis[axis].max_jerk = jerk;
}
float GCodeTimeEstimator::get_axis_position(EAxis axis) const
{
- return _state.axis[axis].position;
+ return m_state.axis[axis].position;
}
float GCodeTimeEstimator::get_axis_max_feedrate(EAxis axis) const
{
- return _state.axis[axis].max_feedrate;
+ return m_state.axis[axis].max_feedrate;
}
float GCodeTimeEstimator::get_axis_max_acceleration(EAxis axis) const
{
- return _state.axis[axis].max_acceleration;
+ return m_state.axis[axis].max_acceleration;
}
float GCodeTimeEstimator::get_axis_max_jerk(EAxis axis) const
{
- return _state.axis[axis].max_jerk;
+ return m_state.axis[axis].max_jerk;
}
void GCodeTimeEstimator::set_feedrate(float feedrate_mm_sec)
{
- _state.feedrate = feedrate_mm_sec;
+ m_state.feedrate = feedrate_mm_sec;
}
float GCodeTimeEstimator::get_feedrate() const
{
- return _state.feedrate;
+ return m_state.feedrate;
}
void GCodeTimeEstimator::set_acceleration(float acceleration_mm_sec2)
{
- _state.acceleration = (_state.max_acceleration == 0) ?
+ m_state.acceleration = (m_state.max_acceleration == 0) ?
acceleration_mm_sec2 :
// Clamp the acceleration with the maximum.
- std::min(_state.max_acceleration, acceleration_mm_sec2);
+ std::min(m_state.max_acceleration, acceleration_mm_sec2);
}
float GCodeTimeEstimator::get_acceleration() const
{
- return _state.acceleration;
+ return m_state.acceleration;
}
void GCodeTimeEstimator::set_max_acceleration(float acceleration_mm_sec2)
{
- _state.max_acceleration = acceleration_mm_sec2;
+ m_state.max_acceleration = acceleration_mm_sec2;
if (acceleration_mm_sec2 > 0)
- _state.acceleration = acceleration_mm_sec2;
+ m_state.acceleration = acceleration_mm_sec2;
}
float GCodeTimeEstimator::get_max_acceleration() const
{
- return _state.max_acceleration;
+ return m_state.max_acceleration;
}
void GCodeTimeEstimator::set_retract_acceleration(float acceleration_mm_sec2)
{
- _state.retract_acceleration = acceleration_mm_sec2;
+ m_state.retract_acceleration = acceleration_mm_sec2;
}
float GCodeTimeEstimator::get_retract_acceleration() const
{
- return _state.retract_acceleration;
+ return m_state.retract_acceleration;
}
void GCodeTimeEstimator::set_minimum_feedrate(float feedrate_mm_sec)
{
- _state.minimum_feedrate = feedrate_mm_sec;
+ m_state.minimum_feedrate = feedrate_mm_sec;
}
float GCodeTimeEstimator::get_minimum_feedrate() const
{
- return _state.minimum_feedrate;
+ return m_state.minimum_feedrate;
}
void GCodeTimeEstimator::set_minimum_travel_feedrate(float feedrate_mm_sec)
{
- _state.minimum_travel_feedrate = feedrate_mm_sec;
+ m_state.minimum_travel_feedrate = feedrate_mm_sec;
}
float GCodeTimeEstimator::get_minimum_travel_feedrate() const
{
- return _state.minimum_travel_feedrate;
+ return m_state.minimum_travel_feedrate;
}
void GCodeTimeEstimator::set_filament_load_times(const std::vector<double> &filament_load_times)
{
- _state.filament_load_times.clear();
+ m_state.filament_load_times.clear();
for (double t : filament_load_times)
- _state.filament_load_times.push_back((float)t);
+ m_state.filament_load_times.push_back((float)t);
}
void GCodeTimeEstimator::set_filament_unload_times(const std::vector<double> &filament_unload_times)
{
- _state.filament_unload_times.clear();
+ m_state.filament_unload_times.clear();
for (double t : filament_unload_times)
- _state.filament_unload_times.push_back((float)t);
+ m_state.filament_unload_times.push_back((float)t);
}
float GCodeTimeEstimator::get_filament_load_time(unsigned int id_extruder)
{
return
- (_state.filament_load_times.empty() || id_extruder == _state.extruder_id_unloaded) ?
+ (m_state.filament_load_times.empty() || id_extruder == m_state.extruder_id_unloaded) ?
0 :
- (_state.filament_load_times.size() <= id_extruder) ?
- _state.filament_load_times.front() :
- _state.filament_load_times[id_extruder];
+ (m_state.filament_load_times.size() <= id_extruder) ?
+ m_state.filament_load_times.front() :
+ m_state.filament_load_times[id_extruder];
}
float GCodeTimeEstimator::get_filament_unload_time(unsigned int id_extruder)
{
return
- (_state.filament_unload_times.empty() || id_extruder == _state.extruder_id_unloaded) ?
+ (m_state.filament_unload_times.empty() || id_extruder == m_state.extruder_id_unloaded) ?
0 :
- (_state.filament_unload_times.size() <= id_extruder) ?
- _state.filament_unload_times.front() :
- _state.filament_unload_times[id_extruder];
+ (m_state.filament_unload_times.size() <= id_extruder) ?
+ m_state.filament_unload_times.front() :
+ m_state.filament_unload_times[id_extruder];
}
void GCodeTimeEstimator::set_extrude_factor_override_percentage(float percentage)
{
- _state.extrude_factor_override_percentage = percentage;
+ m_state.extrude_factor_override_percentage = percentage;
}
float GCodeTimeEstimator::get_extrude_factor_override_percentage() const
{
- return _state.extrude_factor_override_percentage;
+ return m_state.extrude_factor_override_percentage;
}
void GCodeTimeEstimator::set_dialect(GCodeFlavor dialect)
{
- _state.dialect = dialect;
+ m_state.dialect = dialect;
}
GCodeFlavor GCodeTimeEstimator::get_dialect() const
{
PROFILE_FUNC();
- return _state.dialect;
+ return m_state.dialect;
}
void GCodeTimeEstimator::set_units(GCodeTimeEstimator::EUnits units)
{
- _state.units = units;
+ m_state.units = units;
}
GCodeTimeEstimator::EUnits GCodeTimeEstimator::get_units() const
{
- return _state.units;
+ return m_state.units;
}
void GCodeTimeEstimator::set_global_positioning_type(GCodeTimeEstimator::EPositioningType type)
{
- _state.global_positioning_type = type;
+ m_state.global_positioning_type = type;
}
GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_global_positioning_type() const
{
- return _state.global_positioning_type;
+ return m_state.global_positioning_type;
}
void GCodeTimeEstimator::set_e_local_positioning_type(GCodeTimeEstimator::EPositioningType type)
{
- _state.e_local_positioning_type = type;
+ m_state.e_local_positioning_type = type;
}
GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_e_local_positioning_type() const
{
- return _state.e_local_positioning_type;
+ return m_state.e_local_positioning_type;
}
int GCodeTimeEstimator::get_g1_line_id() const
{
- return _state.g1_line_id;
+ return m_state.g1_line_id;
}
void GCodeTimeEstimator::increment_g1_line_id()
{
- ++_state.g1_line_id;
+ ++m_state.g1_line_id;
}
void GCodeTimeEstimator::reset_g1_line_id()
{
- _state.g1_line_id = 0;
+ m_state.g1_line_id = 0;
}
void GCodeTimeEstimator::set_extruder_id(unsigned int id)
{
- _state.extruder_id = id;
+ m_state.extruder_id = id;
}
unsigned int GCodeTimeEstimator::get_extruder_id() const
{
- return _state.extruder_id;
+ return m_state.extruder_id;
}
void GCodeTimeEstimator::reset_extruder_id()
{
// Set the initial extruder ID to unknown. For the multi-material setup it means
// that all the filaments are parked in the MMU and no filament is loaded yet.
- _state.extruder_id = _state.extruder_id_unloaded;
+ m_state.extruder_id = m_state.extruder_id_unloaded;
}
void GCodeTimeEstimator::add_additional_time(float timeSec)
{
PROFILE_FUNC();
- _state.additional_time += timeSec;
+ m_state.additional_time += timeSec;
}
void GCodeTimeEstimator::set_additional_time(float timeSec)
{
- _state.additional_time = timeSec;
+ m_state.additional_time = timeSec;
}
float GCodeTimeEstimator::get_additional_time() const
{
- return _state.additional_time;
+ return m_state.additional_time;
}
void GCodeTimeEstimator::set_default()
@@ -648,8 +660,8 @@ namespace Slic3r {
set_axis_max_jerk(axis, DEFAULT_AXIS_MAX_JERK[a]);
}
- _state.filament_load_times.clear();
- _state.filament_unload_times.clear();
+ m_state.filament_load_times.clear();
+ m_state.filament_unload_times.clear();
}
void GCodeTimeEstimator::reset()
@@ -664,7 +676,7 @@ namespace Slic3r {
float GCodeTimeEstimator::get_time() const
{
- return _time;
+ return m_time;
}
std::string GCodeTimeEstimator::get_time_dhms() const
@@ -677,19 +689,44 @@ namespace Slic3r {
return _get_time_minutes(get_time());
}
+ std::vector<float> GCodeTimeEstimator::get_color_times() const
+ {
+ return m_color_times;
+ }
+
+ std::vector<std::string> GCodeTimeEstimator::get_color_times_dhms() const
+ {
+ std::vector<std::string> ret;
+ for (float t : m_color_times)
+ {
+ ret.push_back(_get_time_dhms(t));
+ }
+ return ret;
+ }
+
+ std::vector<std::string> GCodeTimeEstimator::get_color_times_minutes() const
+ {
+ std::vector<std::string> ret;
+ for (float t : m_color_times)
+ {
+ ret.push_back(_get_time_minutes(t));
+ }
+ return ret;
+ }
+
// Return an estimate of the memory consumed by the time estimator.
size_t GCodeTimeEstimator::memory_used() const
{
size_t out = sizeof(*this);
- out += SLIC3R_STDVEC_MEMSIZE(this->_blocks, Block);
- out += SLIC3R_STDVEC_MEMSIZE(this->_g1_line_ids, G1LineIdToBlockId);
+ out += SLIC3R_STDVEC_MEMSIZE(this->m_blocks, Block);
+ out += SLIC3R_STDVEC_MEMSIZE(this->m_g1_line_ids, G1LineIdToBlockId);
return out;
}
void GCodeTimeEstimator::_reset()
{
- _curr.reset();
- _prev.reset();
+ m_curr.reset();
+ m_prev.reset();
set_axis_position(X, 0.0f);
set_axis_position(Y, 0.0f);
@@ -701,19 +738,23 @@ namespace Slic3r {
reset_extruder_id();
reset_g1_line_id();
- _g1_line_ids.clear();
+ m_g1_line_ids.clear();
+
+ m_last_st_synchronized_block_id = -1;
- _last_st_synchronized_block_id = -1;
+ m_needs_color_times = false;
+ m_color_times.clear();
+ m_color_time_cache = 0.0f;
}
void GCodeTimeEstimator::_reset_time()
{
- _time = 0.0f;
+ m_time = 0.0f;
}
void GCodeTimeEstimator::_reset_blocks()
{
- _blocks.clear();
+ m_blocks.clear();
}
void GCodeTimeEstimator::_calculate_time()
@@ -723,35 +764,32 @@ namespace Slic3r {
_reverse_pass();
_recalculate_trapezoids();
- _time += get_additional_time();
+ m_time += get_additional_time();
+ m_color_time_cache += get_additional_time();
- for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i)
+ for (int i = m_last_st_synchronized_block_id + 1; i < (int)m_blocks.size(); ++i)
{
- Block& block = _blocks[i];
-
-#if ENABLE_MOVE_STATS
+ Block& block = m_blocks[i];
float block_time = 0.0f;
block_time += block.acceleration_time();
block_time += block.cruise_time();
block_time += block.deceleration_time();
- _time += block_time;
- block.elapsed_time = _time;
+ m_time += block_time;
+ block.elapsed_time = m_time;
+#if ENABLE_MOVE_STATS
MovesStatsMap::iterator it = _moves_stats.find(block.move_type);
if (it == _moves_stats.end())
it = _moves_stats.insert(MovesStatsMap::value_type(block.move_type, MoveStats())).first;
it->second.count += 1;
it->second.time += block_time;
-#else
- _time += block.acceleration_time();
- _time += block.cruise_time();
- _time += block.deceleration_time();
- block.elapsed_time = _time;
#endif // ENABLE_MOVE_STATS
+
+ m_color_time_cache += block_time;
}
- _last_st_synchronized_block_id = (int)_blocks.size() - 1;
+ m_last_st_synchronized_block_id = (int)m_blocks.size() - 1;
// The additional time has been consumed (added to the total time), reset it to zero.
set_additional_time(0.);
}
@@ -866,6 +904,11 @@ namespace Slic3r {
_processM566(line);
break;
}
+ case 600: // Set color change
+ {
+ _processM600(line);
+ break;
+ }
case 702: // MK3 MMU2: Process the final filament unload.
{
_processM702(line);
@@ -934,7 +977,7 @@ namespace Slic3r {
return;
// calculates block feedrate
- _curr.feedrate = std::max(get_feedrate(), block.is_travel_move() ? get_minimum_travel_feedrate() : get_minimum_feedrate());
+ m_curr.feedrate = std::max(get_feedrate(), block.is_travel_move() ? get_minimum_travel_feedrate() : get_minimum_feedrate());
float distance = block.move_length();
float invDistance = 1.0f / distance;
@@ -942,23 +985,23 @@ namespace Slic3r {
float min_feedrate_factor = 1.0f;
for (unsigned char a = X; a < Num_Axis; ++a)
{
- _curr.axis_feedrate[a] = _curr.feedrate * block.delta_pos[a] * invDistance;
+ m_curr.axis_feedrate[a] = m_curr.feedrate * block.delta_pos[a] * invDistance;
if (a == E)
- _curr.axis_feedrate[a] *= get_extrude_factor_override_percentage();
+ m_curr.axis_feedrate[a] *= get_extrude_factor_override_percentage();
- _curr.abs_axis_feedrate[a] = std::abs(_curr.axis_feedrate[a]);
- if (_curr.abs_axis_feedrate[a] > 0.0f)
- min_feedrate_factor = std::min(min_feedrate_factor, get_axis_max_feedrate((EAxis)a) / _curr.abs_axis_feedrate[a]);
+ m_curr.abs_axis_feedrate[a] = std::abs(m_curr.axis_feedrate[a]);
+ if (m_curr.abs_axis_feedrate[a] > 0.0f)
+ min_feedrate_factor = std::min(min_feedrate_factor, get_axis_max_feedrate((EAxis)a) / m_curr.abs_axis_feedrate[a]);
}
- block.feedrate.cruise = min_feedrate_factor * _curr.feedrate;
+ block.feedrate.cruise = min_feedrate_factor * m_curr.feedrate;
if (min_feedrate_factor < 1.0f)
{
for (unsigned char a = X; a < Num_Axis; ++a)
{
- _curr.axis_feedrate[a] *= min_feedrate_factor;
- _curr.abs_axis_feedrate[a] *= min_feedrate_factor;
+ m_curr.axis_feedrate[a] *= min_feedrate_factor;
+ m_curr.abs_axis_feedrate[a] *= min_feedrate_factor;
}
}
@@ -975,25 +1018,25 @@ namespace Slic3r {
block.acceleration = acceleration;
// calculates block exit feedrate
- _curr.safe_feedrate = block.feedrate.cruise;
+ m_curr.safe_feedrate = block.feedrate.cruise;
for (unsigned char a = X; a < Num_Axis; ++a)
{
float axis_max_jerk = get_axis_max_jerk((EAxis)a);
- if (_curr.abs_axis_feedrate[a] > axis_max_jerk)
- _curr.safe_feedrate = std::min(_curr.safe_feedrate, axis_max_jerk);
+ if (m_curr.abs_axis_feedrate[a] > axis_max_jerk)
+ m_curr.safe_feedrate = std::min(m_curr.safe_feedrate, axis_max_jerk);
}
- block.feedrate.exit = _curr.safe_feedrate;
+ block.feedrate.exit = m_curr.safe_feedrate;
// calculates block entry feedrate
- float vmax_junction = _curr.safe_feedrate;
- if (!_blocks.empty() && (_prev.feedrate > PREVIOUS_FEEDRATE_THRESHOLD))
+ float vmax_junction = m_curr.safe_feedrate;
+ if (!m_blocks.empty() && (m_prev.feedrate > PREVIOUS_FEEDRATE_THRESHOLD))
{
- bool prev_speed_larger = _prev.feedrate > block.feedrate.cruise;
- float smaller_speed_factor = prev_speed_larger ? (block.feedrate.cruise / _prev.feedrate) : (_prev.feedrate / block.feedrate.cruise);
+ bool prev_speed_larger = m_prev.feedrate > block.feedrate.cruise;
+ float smaller_speed_factor = prev_speed_larger ? (block.feedrate.cruise / m_prev.feedrate) : (m_prev.feedrate / block.feedrate.cruise);
// Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
- vmax_junction = prev_speed_larger ? block.feedrate.cruise : _prev.feedrate;
+ vmax_junction = prev_speed_larger ? block.feedrate.cruise : m_prev.feedrate;
float v_factor = 1.0f;
bool limited = false;
@@ -1001,8 +1044,8 @@ namespace Slic3r {
for (unsigned char a = X; a < Num_Axis; ++a)
{
// Limit an axis. We have to differentiate coasting from the reversal of an axis movement, or a full stop.
- float v_exit = _prev.axis_feedrate[a];
- float v_entry = _curr.axis_feedrate[a];
+ float v_exit = m_prev.axis_feedrate[a];
+ float v_entry = m_curr.axis_feedrate[a];
if (prev_speed_larger)
v_exit *= smaller_speed_factor;
@@ -1044,23 +1087,23 @@ namespace Slic3r {
float vmax_junction_threshold = vmax_junction * 0.99f;
// Not coasting. The machine will stop and start the movements anyway, better to start the segment from start.
- if ((_prev.safe_feedrate > vmax_junction_threshold) && (_curr.safe_feedrate > vmax_junction_threshold))
- vmax_junction = _curr.safe_feedrate;
+ if ((m_prev.safe_feedrate > vmax_junction_threshold) && (m_curr.safe_feedrate > vmax_junction_threshold))
+ vmax_junction = m_curr.safe_feedrate;
}
- float v_allowable = Block::max_allowable_speed(-acceleration, _curr.safe_feedrate, distance);
+ float v_allowable = Block::max_allowable_speed(-acceleration, m_curr.safe_feedrate, distance);
block.feedrate.entry = std::min(vmax_junction, v_allowable);
block.max_entry_speed = vmax_junction;
block.flags.nominal_length = (block.feedrate.cruise <= v_allowable);
block.flags.recalculate = true;
- block.safe_feedrate = _curr.safe_feedrate;
+ block.safe_feedrate = m_curr.safe_feedrate;
// calculates block trapezoid
block.calculate_trapezoid();
// updates previous
- _prev = _curr;
+ m_prev = m_curr;
// updates axis positions
for (unsigned char a = X; a < Num_Axis; ++a)
@@ -1091,8 +1134,8 @@ namespace Slic3r {
#endif // ENABLE_MOVE_STATS
// adds block to blocks list
- _blocks.emplace_back(block);
- _g1_line_ids.emplace_back(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1));
+ m_blocks.emplace_back(block);
+ m_g1_line_ids.emplace_back(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)m_blocks.size() - 1));
}
void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line)
@@ -1336,6 +1379,18 @@ namespace Slic3r {
set_axis_max_jerk(E, line.e() * MMMIN_TO_MMSEC);
}
+ void GCodeTimeEstimator::_processM600(const GCodeReader::GCodeLine& line)
+ {
+ PROFILE_FUNC();
+ m_needs_color_times = true;
+ _calculate_time();
+ if (m_color_time_cache != 0.0f)
+ {
+ m_color_times.push_back(m_color_time_cache);
+ m_color_time_cache = 0.0f;
+ }
+ }
+
void GCodeTimeEstimator::_processM702(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
@@ -1376,11 +1431,11 @@ namespace Slic3r {
void GCodeTimeEstimator::_forward_pass()
{
PROFILE_FUNC();
- if (_blocks.size() > 1)
+ if (m_blocks.size() > 1)
{
- for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size() - 1; ++i)
+ for (int i = m_last_st_synchronized_block_id + 1; i < (int)m_blocks.size() - 1; ++i)
{
- _planner_forward_pass_kernel(_blocks[i], _blocks[i + 1]);
+ _planner_forward_pass_kernel(m_blocks[i], m_blocks[i + 1]);
}
}
}
@@ -1388,11 +1443,11 @@ namespace Slic3r {
void GCodeTimeEstimator::_reverse_pass()
{
PROFILE_FUNC();
- if (_blocks.size() > 1)
+ if (m_blocks.size() > 1)
{
- for (int i = (int)_blocks.size() - 1; i >= _last_st_synchronized_block_id + 2; --i)
+ for (int i = (int)m_blocks.size() - 1; i >= m_last_st_synchronized_block_id + 2; --i)
{
- _planner_reverse_pass_kernel(_blocks[i - 1], _blocks[i]);
+ _planner_reverse_pass_kernel(m_blocks[i - 1], m_blocks[i]);
}
}
}
@@ -1444,9 +1499,9 @@ namespace Slic3r {
Block* curr = nullptr;
Block* next = nullptr;
- for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i)
+ for (int i = m_last_st_synchronized_block_id + 1; i < (int)m_blocks.size(); ++i)
{
- Block& b = _blocks[i];
+ Block& b = m_blocks[i];
curr = next;
next = &b;
@@ -1517,7 +1572,7 @@ namespace Slic3r {
{
std::cout << MOVE_TYPE_STR[move.first];
std::cout << ": count " << move.second.count << " (" << 100.0f * (float)move.second.count / moves_count << "%)";
- std::cout << " - time: " << move.second.time << "s (" << 100.0f * move.second.time / _time << "%)";
+ std::cout << " - time: " << move.second.time << "s (" << 100.0f * move.second.time / m_time << "%)";
std::cout << std::endl;
}
std::cout << std::endl;
diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp
index 1fbc1c14b..840d58778 100644
--- a/src/libslic3r/GCodeTimeEstimator.hpp
+++ b/src/libslic3r/GCodeTimeEstimator.hpp
@@ -215,17 +215,22 @@ namespace Slic3r {
typedef std::vector<G1LineIdToBlockId> G1LineIdToBlockIdMap;
private:
- EMode _mode;
- GCodeReader _parser;
- State _state;
- Feedrates _curr;
- Feedrates _prev;
- BlocksList _blocks;
+ EMode m_mode;
+ GCodeReader m_parser;
+ State m_state;
+ Feedrates m_curr;
+ Feedrates m_prev;
+ BlocksList m_blocks;
// Map between g1 line id and blocks id, used to speed up export of remaining times
- G1LineIdToBlockIdMap _g1_line_ids;
+ G1LineIdToBlockIdMap m_g1_line_ids;
// Index of the last block already st_synchronized
- int _last_st_synchronized_block_id;
- float _time; // s
+ int m_last_st_synchronized_block_id;
+ float m_time; // s
+
+ // data to calculate color print times
+ bool m_needs_color_times;
+ std::vector<float> m_color_times;
+ float m_color_time_cache;
#if ENABLE_MOVE_STATS
MovesStatsMap _moves_stats;
@@ -341,6 +346,15 @@ namespace Slic3r {
// Returns the estimated time, in minutes (integer)
std::string get_time_minutes() const;
+ // Returns the estimated time, in seconds, for each color
+ std::vector<float> get_color_times() const;
+
+ // Returns the estimated time, in format DDd HHh MMm SSs, for each color
+ std::vector<std::string> get_color_times_dhms() const;
+
+ // Returns the estimated time, in minutes (integer), for each color
+ std::vector<std::string> get_color_times_minutes() const;
+
// Return an estimate of the memory consumed by the time estimator.
size_t memory_used() const;
@@ -409,6 +423,9 @@ namespace Slic3r {
// Set allowable instantaneous speed change
void _processM566(const GCodeReader::GCodeLine& line);
+ // Set color change
+ void _processM600(const GCodeReader::GCodeLine& line);
+
// Unload the current filament into the MK3 MMU2 unit at the end of print.
void _processM702(const GCodeReader::GCodeLine& line);
diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp
index accb4e286..cc8a86a96 100644
--- a/src/libslic3r/Geometry.cpp
+++ b/src/libslic3r/Geometry.cpp
@@ -912,7 +912,7 @@ MedialAxis::build(ThickPolylines* polylines)
}
*/
- typedef const VD::vertex_type vert_t;
+ //typedef const VD::vertex_type vert_t;
typedef const VD::edge_type edge_t;
// collect valid edges (i.e. prune those not belonging to MAT)
diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp
index bcbe80a0d..dca1872d8 100644
--- a/src/libslic3r/Geometry.hpp
+++ b/src/libslic3r/Geometry.hpp
@@ -7,6 +7,9 @@
#include "Polygon.hpp"
#include "Polyline.hpp"
+// Serialization through the Cereal library
+#include <cereal/access.hpp>
+
#include "boost/polygon/voronoi.hpp"
using boost::polygon::voronoi_builder;
using boost::polygon::voronoi_diagram;
@@ -263,6 +266,17 @@ public:
// as possible in least squares norm in regard to the 8 corners of bbox.
// Bounding box is expected to be centered around zero in all axes.
static Transformation volume_to_bed_transformation(const Transformation& instance_transformation, const BoundingBoxf3& bbox);
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive & ar) { ar(m_offset, m_rotation, m_scaling_factor, m_mirror); }
+ explicit Transformation(int) : m_dirty(true) {}
+ template <class Archive> static void load_and_construct(Archive &ar, cereal::construct<Transformation> &construct)
+ {
+ // Calling a private constructor with special "int" parameter to indicate that no construction is necessary.
+ construct(1);
+ ar(construct.ptr()->m_offset, construct.ptr()->m_rotation, construct.ptr()->m_scaling_factor, construct.ptr()->m_mirror);
+ }
};
// Rotation when going from the first coordinate system with rotation rot_xyz_from applied
diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp
index c1d92c6bb..a8160867a 100644
--- a/src/libslic3r/Layer.cpp
+++ b/src/libslic3r/Layer.cpp
@@ -128,7 +128,7 @@ void Layer::make_perimeters()
&& config.external_perimeter_speed == other_config.external_perimeter_speed
&& config.gap_fill_speed == other_config.gap_fill_speed
&& config.overhangs == other_config.overhangs
- && config.serialize("perimeter_extrusion_width").compare(other_config.serialize("perimeter_extrusion_width")) == 0
+ && config.opt_serialize("perimeter_extrusion_width") == other_config.opt_serialize("perimeter_extrusion_width")
&& config.thin_walls == other_config.thin_walls
&& config.external_perimeters_first == other_config.external_perimeters_first) {
layerms.push_back(other_layerm);
diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp
index 19fbb3bb6..caf8dc20f 100644
--- a/src/libslic3r/LayerRegion.cpp
+++ b/src/libslic3r/LayerRegion.cpp
@@ -201,7 +201,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
size_t n_groups = 0;
for (size_t i = 0; i < bridges.size(); ++ i) {
// A grup id for this bridge.
- size_t group_id = (bridge_group[i] == -1) ? (n_groups ++) : bridge_group[i];
+ size_t group_id = (bridge_group[i] == size_t(-1)) ? (n_groups ++) : bridge_group[i];
bridge_group[i] = group_id;
// For all possibly overlaping bridges:
for (size_t j = i + 1; j < bridges.size(); ++ j) {
@@ -210,7 +210,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
if (intersection(bridges_grown[i], bridges_grown[j], false).empty())
continue;
// The two bridge regions intersect. Give them the same group id.
- if (bridge_group[j] != -1) {
+ if (bridge_group[j] != size_t(-1)) {
// The j'th bridge has been merged with some other bridge before.
size_t group_id_new = bridge_group[j];
for (size_t k = 0; k < j; ++ k)
diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp
index 02f1cb7c2..baa04795a 100644
--- a/src/libslic3r/Line.cpp
+++ b/src/libslic3r/Line.cpp
@@ -22,7 +22,6 @@ Linef3 transform(const Linef3& line, const Transform3d& t)
bool Line::intersection_infinite(const Line &other, Point* point) const
{
Vec2d a1 = this->a.cast<double>();
- Vec2d a2 = other.a.cast<double>();
Vec2d v12 = (other.a - this->a).cast<double>();
Vec2d v1 = (this->b - this->a).cast<double>();
Vec2d v2 = (other.b - other.a).cast<double>();
diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp
index a9f2c0274..1fef4b475 100644
--- a/src/libslic3r/MTUtils.hpp
+++ b/src/libslic3r/MTUtils.hpp
@@ -1,11 +1,13 @@
#ifndef MTUTILS_HPP
#define MTUTILS_HPP
-#include <atomic> // for std::atomic_flag and memory orders
-#include <mutex> // for std::lock_guard
-#include <functional> // for std::function
-#include <utility> // for std::forward
+#include <atomic> // for std::atomic_flag and memory orders
+#include <mutex> // for std::lock_guard
+#include <functional> // for std::function
+#include <utility> // for std::forward
+#include <vector>
#include <algorithm>
+#include <cmath>
#include "libslic3r.h"
#include "Point.hpp"
@@ -242,6 +244,58 @@ template<class C> bool all_of(const C &container)
});
}
+template<class T> struct remove_cvref
+{
+ using type =
+ typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+};
+
+template<class T> using remove_cvref_t = typename remove_cvref<T>::type;
+
+template<template<class> class C, class T>
+class Container : public C<remove_cvref_t<T>>
+{
+public:
+ explicit Container(size_t count, T &&initval)
+ : C<remove_cvref_t<T>>(count, initval)
+ {}
+};
+
+template<class T> using DefaultContainer = std::vector<T>;
+
+/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
+template<class T, class I, template<class> class C = DefaultContainer>
+inline C<remove_cvref_t<T>> linspace(const T &start, const T &stop, const I &n)
+{
+ Container<C, T> vals(n, T());
+
+ T stride = (stop - start) / n;
+ size_t i = 0;
+ std::generate(vals.begin(), vals.end(), [&i, start, stride] {
+ return start + i++ * stride;
+ });
+
+ return vals;
+}
+
+/// A set of equidistant values starting from 'start' (inclusive), ending
+/// in the closest multiple of 'stride' less than or equal to 'end' and
+/// leaving 'stride' space between each value.
+/// Very similar to Matlab [start:stride:end] notation.
+template<class T, template<class> class C = DefaultContainer>
+inline C<remove_cvref_t<T>> grid(const T &start, const T &stop, const T &stride)
+{
+ Container<C, T> vals(size_t(std::ceil((stop - start) / stride)), T());
+
+ int i = 0;
+ std::generate(vals.begin(), vals.end(), [&i, start, stride] {
+ return start + i++ * stride;
+ });
+
+ return vals;
+}
+
+
// A shorter C++14 style form of the enable_if metafunction
template<bool B, class T>
using enable_if_t = typename std::enable_if<B, T>::type;
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index abe8af16a..ed1c1bc00 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -23,21 +23,6 @@ namespace Slic3r {
unsigned int Model::s_auto_extruder_id = 1;
-size_t ModelBase::s_last_id = 0;
-
-// Unique object / instance ID for the wipe tower.
-ModelID wipe_tower_object_id()
-{
- static ModelBase mine;
- return mine.id();
-}
-
-ModelID wipe_tower_instance_id()
-{
- static ModelBase mine;
- return mine.id();
-}
-
Model& Model::assign_copy(const Model &rhs)
{
this->copy_id(rhs);
@@ -88,6 +73,19 @@ void Model::assign_new_unique_ids_recursive()
model_object->assign_new_unique_ids_recursive();
}
+void Model::update_links_bottom_up_recursive()
+{
+ for (std::pair<const t_model_material_id, ModelMaterial*> &kvp : this->materials)
+ kvp.second->set_model(this);
+ for (ModelObject *model_object : this->objects) {
+ model_object->set_model(this);
+ for (ModelInstance *model_instance : model_object->instances)
+ model_instance->set_model_object(model_object);
+ for (ModelVolume *model_volume : model_object->volumes)
+ model_volume->set_model_object(model_object);
+ }
+}
+
Model Model::read_from_file(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances)
{
Model model;
@@ -222,7 +220,7 @@ bool Model::delete_object(ModelObject* object)
return false;
}
-bool Model::delete_object(ModelID id)
+bool Model::delete_object(ObjectID id)
{
if (id.id != 0) {
size_t idx = 0;
@@ -633,14 +631,18 @@ ModelObject::~ModelObject()
// maintains the m_model pointer
ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
{
- this->copy_id(rhs);
+ assert(this->id().invalid() || this->id() == rhs.id());
+ assert(this->config.id().invalid() || this->config.id() == rhs.config.id());
+ this->copy_id(rhs);
this->name = rhs.name;
this->input_file = rhs.input_file;
+ // Copies the config's ID
this->config = rhs.config;
+ assert(this->config.id() == rhs.config.id());
this->sla_support_points = rhs.sla_support_points;
this->sla_points_status = rhs.sla_points_status;
- this->layer_height_ranges = rhs.layer_height_ranges;
+ this->layer_config_ranges = rhs.layer_config_ranges; // #ys_FIXME_experiment
this->layer_height_profile = rhs.layer_height_profile;
this->origin_translation = rhs.origin_translation;
m_bounding_box = rhs.m_bounding_box;
@@ -669,14 +671,17 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
// maintains the m_model pointer
ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
{
+ assert(this->id().invalid());
this->copy_id(rhs);
this->name = std::move(rhs.name);
this->input_file = std::move(rhs.input_file);
+ // Moves the config's ID
this->config = std::move(rhs.config);
+ assert(this->config.id() == rhs.config.id());
this->sla_support_points = std::move(rhs.sla_support_points);
this->sla_points_status = std::move(rhs.sla_points_status);
- this->layer_height_ranges = std::move(rhs.layer_height_ranges);
+ this->layer_config_ranges = std::move(rhs.layer_config_ranges); // #ys_FIXME_experiment
this->layer_height_profile = std::move(rhs.layer_height_profile);
this->origin_translation = std::move(rhs.origin_translation);
m_bounding_box = std::move(rhs.m_bounding_box);
@@ -1081,11 +1086,11 @@ void ModelObject::mirror(Axis axis)
}
// This method could only be called before the meshes of this ModelVolumes are not shared!
-void ModelObject::scale_mesh(const Vec3d &versor)
+void ModelObject::scale_mesh_after_creation(const Vec3d &versor)
{
for (ModelVolume *v : this->volumes)
{
- v->scale_geometry(versor);
+ v->scale_geometry_after_creation(versor);
v->set_offset(versor.cwiseProduct(v->get_offset()));
}
this->invalidate_bounding_box();
@@ -1202,13 +1207,19 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
if (keep_upper && upper_mesh.facets_count() > 0) {
ModelVolume* vol = upper->add_volume(upper_mesh);
vol->name = volume->name;
- vol->config = volume->config;
+ // Don't copy the config's ID.
+ static_cast<DynamicPrintConfig&>(vol->config) = static_cast<const DynamicPrintConfig&>(volume->config);
+ assert(vol->config.id().valid());
+ assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
}
if (keep_lower && lower_mesh.facets_count() > 0) {
ModelVolume* vol = lower->add_volume(lower_mesh);
vol->name = volume->name;
- vol->config = volume->config;
+ // Don't copy the config's ID.
+ static_cast<DynamicPrintConfig&>(vol->config) = static_cast<const DynamicPrintConfig&>(volume->config);
+ assert(vol->config.id().valid());
+ assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
// Compute the lower part instances' bounding boxes to figure out where to place
@@ -1283,7 +1294,10 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
ModelObject* new_object = m_model->add_object();
new_object->name = this->name;
- new_object->config = this->config;
+ // Don't copy the config's ID.
+ static_cast<DynamicPrintConfig&>(new_object->config) = static_cast<const DynamicPrintConfig&>(this->config);
+ assert(new_object->config.id().valid());
+ assert(new_object->config.id() != this->config.id());
new_object->instances.reserve(this->instances.size());
for (const ModelInstance *model_instance : this->instances)
new_object->add_instance(*model_instance);
@@ -1576,9 +1590,9 @@ void ModelVolume::center_geometry_after_creation()
if (!shift.isApprox(Vec3d::Zero()))
{
if (m_mesh)
- m_mesh->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
+ const_cast<TriangleMesh*>(m_mesh.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
if (m_convex_hull)
- m_convex_hull->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
+ const_cast<TriangleMesh*>(m_convex_hull.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
translate(shift);
}
}
@@ -1731,10 +1745,10 @@ void ModelVolume::mirror(Axis axis)
}
// This method could only be called before the meshes of this ModelVolumes are not shared!
-void ModelVolume::scale_geometry(const Vec3d& versor)
+void ModelVolume::scale_geometry_after_creation(const Vec3d& versor)
{
- m_mesh->scale(versor);
- m_convex_hull->scale(versor);
+ const_cast<TriangleMesh*>(m_mesh.get())->scale(versor);
+ const_cast<TriangleMesh*>(m_convex_hull.get())->scale(versor);
}
void ModelVolume::transform_this_mesh(const Transform3d &mesh_trafo, bool fix_left_handed)
@@ -1891,7 +1905,7 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO
if (!mv_old.get_matrix().isApprox(mv_new.get_matrix()))
return true;
- ++i_old;
+ ++ i_old;
++ i_new;
}
for (; i_old < model_object_old.volumes.size(); ++ i_old) {
@@ -1913,21 +1927,26 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
void check_model_ids_validity(const Model &model)
{
- std::set<ModelID> ids;
- auto check = [&ids](ModelID id) {
- assert(id.id > 0);
+ std::set<ObjectID> ids;
+ auto check = [&ids](ObjectID id) {
+ assert(id.valid());
assert(ids.find(id) == ids.end());
ids.insert(id);
};
for (const ModelObject *model_object : model.objects) {
check(model_object->id());
- for (const ModelVolume *model_volume : model_object->volumes)
+ check(model_object->config.id());
+ for (const ModelVolume *model_volume : model_object->volumes) {
check(model_volume->id());
+ check(model_volume->config.id());
+ }
for (const ModelInstance *model_instance : model_object->instances)
check(model_instance->id());
}
- for (const auto mm : model.materials)
+ for (const auto mm : model.materials) {
check(mm.second->id());
+ check(mm.second->config.id());
+ }
}
void check_model_ids_equal(const Model &model1, const Model &model2)
@@ -1938,10 +1957,13 @@ void check_model_ids_equal(const Model &model1, const Model &model2)
const ModelObject &model_object1 = *model1.objects[idx_model];
const ModelObject &model_object2 = * model2.objects[idx_model];
assert(model_object1.id() == model_object2.id());
+ assert(model_object1.config.id() == model_object2.config.id());
assert(model_object1.volumes.size() == model_object2.volumes.size());
assert(model_object1.instances.size() == model_object2.instances.size());
- for (size_t i = 0; i < model_object1.volumes.size(); ++ i)
+ for (size_t i = 0; i < model_object1.volumes.size(); ++ i) {
assert(model_object1.volumes[i]->id() == model_object2.volumes[i]->id());
+ assert(model_object1.volumes[i]->config.id() == model_object2.volumes[i]->config.id());
+ }
for (size_t i = 0; i < model_object1.instances.size(); ++ i)
assert(model_object1.instances[i]->id() == model_object2.instances[i]->id());
}
@@ -1952,9 +1974,22 @@ void check_model_ids_equal(const Model &model1, const Model &model2)
for (; it1 != model1.materials.end(); ++ it1, ++ it2) {
assert(it1->first == it2->first); // compare keys
assert(it1->second->id() == it2->second->id());
+ assert(it1->second->config.id() == it2->second->config.id());
}
}
}
#endif /* NDEBUG */
}
+
+#if 0
+CEREAL_REGISTER_TYPE(Slic3r::ModelObject)
+CEREAL_REGISTER_TYPE(Slic3r::ModelVolume)
+CEREAL_REGISTER_TYPE(Slic3r::ModelInstance)
+CEREAL_REGISTER_TYPE(Slic3r::Model)
+
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelObject)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelVolume)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelInstance)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::Model)
+#endif \ No newline at end of file
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index 5a0dc9863..463d7b58e 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -2,11 +2,14 @@
#define slic3r_Model_hpp_
#include "libslic3r.h"
-#include "PrintConfig.hpp"
+#include "Geometry.hpp"
#include "Layer.hpp"
+#include "ObjectID.hpp"
#include "Point.hpp"
-#include "TriangleMesh.hpp"
+#include "PrintConfig.hpp"
#include "Slicing.hpp"
+#include "SLA/SLACommon.hpp"
+#include "TriangleMesh.hpp"
#include "Arrange.hpp"
#include <map>
@@ -14,8 +17,6 @@
#include <string>
#include <utility>
#include <vector>
-#include "Geometry.hpp"
-#include <libslic3r/SLA/SLACommon.hpp>
namespace Slic3r {
@@ -27,83 +28,54 @@ class ModelVolume;
class Print;
class SLAPrint;
-typedef std::string t_model_material_id;
-typedef std::string t_model_material_attribute;
-typedef std::map<t_model_material_attribute, std::string> t_model_material_attributes;
-
-typedef std::map<t_model_material_id, ModelMaterial*> ModelMaterialMap;
-typedef std::vector<ModelObject*> ModelObjectPtrs;
-typedef std::vector<ModelVolume*> ModelVolumePtrs;
-typedef std::vector<ModelInstance*> ModelInstancePtrs;
-
-// Unique identifier of a Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial.
-// Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject)
-// Valid IDs are strictly positive (non zero).
-// It is declared as an object, as some compilers (notably msvcc) consider a typedef size_t equivalent to size_t
-// for parameter overload.
-struct ModelID
-{
- ModelID(size_t id) : id(id) {}
-
- bool operator==(const ModelID &rhs) const { return this->id == rhs.id; }
- bool operator!=(const ModelID &rhs) const { return this->id != rhs.id; }
- bool operator< (const ModelID &rhs) const { return this->id < rhs.id; }
- bool operator> (const ModelID &rhs) const { return this->id > rhs.id; }
- bool operator<=(const ModelID &rhs) const { return this->id <= rhs.id; }
- bool operator>=(const ModelID &rhs) const { return this->id >= rhs.id; }
-
- bool valid() const { return id != 0; }
-
- size_t id;
-};
-
-// Unique object / instance ID for the wipe tower.
-extern ModelID wipe_tower_object_id();
-extern ModelID wipe_tower_instance_id();
+namespace UndoRedo {
+ class StackImpl;
+}
-// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID
-// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject).
-// Achtung! The s_last_id counter is not thread safe, so it is expected, that the ModelBase derived instances
-// are only instantiated from the main thread.
-class ModelBase
+class ModelConfig : public ObjectBase, public DynamicPrintConfig
{
-public:
- ModelID id() const { return m_id; }
+private:
+ friend class cereal::access;
+ friend class UndoRedo::StackImpl;
+ friend class ModelObject;
+ friend class ModelVolume;
+ friend class ModelMaterial;
-protected:
// Constructors to be only called by derived classes.
// Default constructor to assign a unique ID.
- ModelBase() : m_id(generate_new_id()) {}
+ explicit ModelConfig() {}
// Constructor with ignored int parameter to assign an invalid ID, to be replaced
// by an existing ID copied from elsewhere.
- ModelBase(int) : m_id(ModelID(0)) {}
-
- // Use with caution!
- void set_new_unique_id() { m_id = generate_new_id(); }
- void set_invalid_id() { m_id = 0; }
- // Use with caution!
- void copy_id(const ModelBase &rhs) { m_id = rhs.id(); }
-
- // Override this method if a ModelBase derived class owns other ModelBase derived instances.
- void assign_new_unique_ids_recursive() { this->set_new_unique_id(); }
+ explicit ModelConfig(int) : ObjectBase(-1) {}
+ // Copy constructor copies the ID.
+ explicit ModelConfig(const ModelConfig &cfg) : ObjectBase(-1), DynamicPrintConfig(cfg) { this->copy_id(cfg); }
+ // Move constructor copies the ID.
+ explicit ModelConfig(ModelConfig &&cfg) : ObjectBase(-1), DynamicPrintConfig(std::move(cfg)) { this->copy_id(cfg); }
+
+ ModelConfig& operator=(const ModelConfig &rhs) = default;
+ ModelConfig& operator=(ModelConfig &&rhs) = default;
+
+ template<class Archive> void serialize(Archive &ar) {
+ ar(cereal::base_class<DynamicPrintConfig>(this));
+ }
+};
-private:
- ModelID m_id;
+typedef std::string t_model_material_id;
+typedef std::string t_model_material_attribute;
+typedef std::map<t_model_material_attribute, std::string> t_model_material_attributes;
- static inline ModelID generate_new_id() { return ModelID(++ s_last_id); }
- static size_t s_last_id;
-
- friend ModelID wipe_tower_object_id();
- friend ModelID wipe_tower_instance_id();
-};
+typedef std::map<t_model_material_id, ModelMaterial*> ModelMaterialMap;
+typedef std::vector<ModelObject*> ModelObjectPtrs;
+typedef std::vector<ModelVolume*> ModelVolumePtrs;
+typedef std::vector<ModelInstance*> ModelInstancePtrs;
-#define MODELBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \
+#define OBJECTBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \
/* Copy a model, copy the IDs. The Print::apply() will call the TYPE::copy() method */ \
/* to make a private copy for background processing. */ \
- static TYPE* new_copy(const TYPE &rhs) { return new TYPE(rhs); } \
- static TYPE* new_copy(TYPE &&rhs) { return new TYPE(std::move(rhs)); } \
- static TYPE make_copy(const TYPE &rhs) { return TYPE(rhs); } \
- static TYPE make_copy(TYPE &&rhs) { return TYPE(std::move(rhs)); } \
+ static TYPE* new_copy(const TYPE &rhs) { auto *ret = new TYPE(rhs); assert(ret->id() == rhs.id()); return ret; } \
+ static TYPE* new_copy(TYPE &&rhs) { auto *ret = new TYPE(std::move(rhs)); assert(ret->id() == rhs.id()); return ret; } \
+ static TYPE make_copy(const TYPE &rhs) { TYPE ret(rhs); assert(ret.id() == rhs.id()); return ret; } \
+ static TYPE make_copy(TYPE &&rhs) { TYPE ret(std::move(rhs)); assert(ret.id() == rhs.id()); return ret; } \
TYPE& assign_copy(const TYPE &rhs); \
TYPE& assign_copy(TYPE &&rhs); \
/* Copy a TYPE, generate new IDs. The front end will use this call. */ \
@@ -111,52 +83,62 @@ private:
/* Default constructor assigning an invalid ID. */ \
auto obj = new TYPE(-1); \
obj->assign_clone(rhs); \
+ assert(obj->id().valid() && obj->id() != rhs.id()); \
return obj; \
} \
TYPE make_clone(const TYPE &rhs) { \
/* Default constructor assigning an invalid ID. */ \
TYPE obj(-1); \
obj.assign_clone(rhs); \
+ assert(obj.id().valid() && obj.id() != rhs.id()); \
return obj; \
} \
TYPE& assign_clone(const TYPE &rhs) { \
this->assign_copy(rhs); \
+ assert(this->id().valid() && this->id() == rhs.id()); \
this->assign_new_unique_ids_recursive(); \
+ assert(this->id().valid() && this->id() != rhs.id()); \
return *this; \
}
-#define MODELBASE_DERIVED_PRIVATE_COPY_MOVE(TYPE) \
-private: \
- /* Private constructor with an unused int parameter will create a TYPE instance with an invalid ID. */ \
- explicit TYPE(int) : ModelBase(-1) {}; \
- void assign_new_unique_ids_recursive();
-
// Material, which may be shared across multiple ModelObjects of a single Model.
-class ModelMaterial : public ModelBase
+class ModelMaterial final : public ObjectBase
{
public:
// Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose.
t_model_material_attributes attributes;
// Dynamic configuration storage for the object specific configuration values, overriding the global configuration.
- DynamicPrintConfig config;
+ ModelConfig config;
Model* get_model() const { return m_model; }
void apply(const t_model_material_attributes &attributes)
{ this->attributes.insert(attributes.begin(), attributes.end()); }
-protected:
- friend class Model;
- // Constructor, which assigns a new unique ID.
- ModelMaterial(Model *model) : m_model(model) {}
- // Copy constructor copies the ID and m_model!
- ModelMaterial(const ModelMaterial &rhs) = default;
- void set_model(Model *model) { m_model = model; }
-
private:
// Parent, owning this material.
Model *m_model;
-
- ModelMaterial() = delete;
+
+ // To be accessed by the Model.
+ friend class Model;
+ // Constructor, which assigns a new unique ID to the material and to its config.
+ ModelMaterial(Model *model) : m_model(model) { assert(this->id().valid()); }
+ // Copy constructor copies the IDs of the ModelMaterial and its config, and m_model!
+ ModelMaterial(const ModelMaterial &rhs) = default;
+ void set_model(Model *model) { m_model = model; }
+ void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); }
+
+ // To be accessed by the serialization and Undo/Redo code.
+ friend class cereal::access;
+ friend class UndoRedo::StackImpl;
+ // Create an object for deserialization, don't allocate IDs for ModelMaterial and its config.
+ ModelMaterial() : ObjectBase(-1), config(-1), m_model(nullptr) { assert(this->id().invalid()); assert(this->config.id().invalid()); }
+ template<class Archive> void serialize(Archive &ar) {
+ assert(this->id().invalid()); assert(this->config.id().invalid());
+ ar(attributes, config);
+ // assert(this->id().valid()); assert(this->config.id().valid());
+ }
+
+ // Disabled methods.
ModelMaterial(ModelMaterial &&rhs) = delete;
ModelMaterial& operator=(const ModelMaterial &rhs) = delete;
ModelMaterial& operator=(ModelMaterial &&rhs) = delete;
@@ -166,9 +148,8 @@ private:
// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials.
// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed,
// different rotation and different uniform scaling.
-class ModelObject : public ModelBase
+class ModelObject final : public ObjectBase
{
- friend class Model;
public:
std::string name;
std::string input_file; // XXX: consider fs::path
@@ -179,9 +160,9 @@ public:
// ModelVolumes are owned by this ModelObject.
ModelVolumePtrs volumes;
// Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings.
- DynamicPrintConfig config;
- // Variation of a layer thickness for spans of Z coordinates.
- t_layer_height_ranges layer_height_ranges;
+ ModelConfig config;
+ // Variation of a layer thickness for spans of Z coordinates + optional parameter overrides.
+ t_layer_config_ranges layer_config_ranges;
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
// The pairs of <z, layer_height> are packed into a 1D array.
std::vector<coordf_t> layer_height_profile;
@@ -265,7 +246,7 @@ public:
void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
- void scale_mesh(const Vec3d& versor);
+ void scale_mesh_after_creation(const Vec3d& versor);
size_t materials_count() const;
size_t facets_count() const;
@@ -289,31 +270,53 @@ public:
std::string get_export_filename() const;
- // Get full stl statistics for all object's meshes
+ // Get full stl statistics for all object's meshes
stl_stats get_object_stl_stats() const;
- // Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
+ // Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
int get_mesh_errors_count(const int vol_idx = -1) const;
-protected:
- friend class Print;
- friend class SLAPrint;
- // Called by Print::apply() to set the model pointer after making a copy.
- void set_model(Model *model) { m_model = model; }
-
private:
- ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()),
- m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {}
- ~ModelObject();
-
- /* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
- /* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */
- ModelObject(const ModelObject &rhs) : ModelBase(-1), m_model(rhs.m_model) { this->assign_copy(rhs); }
- explicit ModelObject(ModelObject &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); }
- ModelObject& operator=(const ModelObject &rhs) { this->assign_copy(rhs); m_model = rhs.m_model; return *this; }
- ModelObject& operator=(ModelObject &&rhs) { this->assign_copy(std::move(rhs)); m_model = rhs.m_model; return *this; }
+ friend class Model;
+ // This constructor assigns new ID to this ModelObject and its config.
+ explicit ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()),
+ m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false)
+ { assert(this->id().valid()); }
+ explicit ModelObject(int) : ObjectBase(-1), config(-1), m_model(nullptr), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false)
+ { assert(this->id().invalid()); assert(this->config.id().invalid()); }
+ ~ModelObject();
+ void assign_new_unique_ids_recursive() override;
+
+ // To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision"
+ // (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics).
+ ModelObject(const ModelObject &rhs) : ObjectBase(-1), config(-1), m_model(rhs.m_model) {
+ assert(this->id().invalid()); assert(this->config.id().invalid()); assert(rhs.id() != rhs.config.id());
+ this->assign_copy(rhs);
+ assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
+ assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id());
+ }
+ explicit ModelObject(ModelObject &&rhs) : ObjectBase(-1), config(-1) {
+ assert(this->id().invalid()); assert(this->config.id().invalid()); assert(rhs.id() != rhs.config.id());
+ this->assign_copy(std::move(rhs));
+ assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
+ assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id());
+ }
+ ModelObject& operator=(const ModelObject &rhs) {
+ this->assign_copy(rhs);
+ m_model = rhs.m_model;
+ assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
+ assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id());
+ return *this;
+ }
+ ModelObject& operator=(ModelObject &&rhs) {
+ this->assign_copy(std::move(rhs));
+ m_model = rhs.m_model;
+ assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
+ assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id());
+ return *this;
+ }
+ void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); }
- MODELBASE_DERIVED_COPY_MOVE_CLONE(ModelObject)
- MODELBASE_DERIVED_PRIVATE_COPY_MOVE(ModelObject)
+ OBJECTBASE_DERIVED_COPY_MOVE_CLONE(ModelObject)
// Parent object, owning this ModelObject. Set to nullptr here, so the macros above will have it initialized.
Model *m_model = nullptr;
@@ -324,7 +327,25 @@ private:
mutable BoundingBoxf3 m_raw_bounding_box;
mutable bool m_raw_bounding_box_valid;
mutable BoundingBoxf3 m_raw_mesh_bounding_box;
- mutable bool m_raw_mesh_bounding_box_valid;
+ mutable bool m_raw_mesh_bounding_box_valid;
+
+ // Called by Print::apply() to set the model pointer after making a copy.
+ friend class Print;
+ friend class SLAPrint;
+ void set_model(Model *model) { m_model = model; }
+
+ // Undo / Redo through the cereal serialization library
+ friend class cereal::access;
+ friend class UndoRedo::StackImpl;
+ // Used for deserialization -> Don't allocate any IDs for the ModelObject or its config.
+ ModelObject() : ObjectBase(-1), config(-1), m_model(nullptr), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {
+ assert(this->id().invalid()); assert(this->config.id().invalid());
+ }
+ template<class Archive> void serialize(Archive &ar) {
+ ar(cereal::base_class<ObjectBase>(this));
+ ar(name, input_file, instances, volumes, config, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, origin_translation,
+ m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid);
+ }
};
// Declared outside of ModelVolume, so it could be forward declared.
@@ -338,20 +359,20 @@ enum class ModelVolumeType : int {
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
// ModelVolume instances are owned by a ModelObject.
-class ModelVolume : public ModelBase
+class ModelVolume final : public ObjectBase
{
public:
std::string name;
// The triangular model.
const TriangleMesh& mesh() const { return *m_mesh.get(); }
- void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<TriangleMesh>(mesh); }
- void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<TriangleMesh>(std::move(mesh)); }
- void set_mesh(std::shared_ptr<TriangleMesh> &mesh) { m_mesh = mesh; }
- void set_mesh(std::unique_ptr<TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
- void reset_mesh() { m_mesh = std::make_shared<TriangleMesh>(); }
+ void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
+ void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<const TriangleMesh>(std::move(mesh)); }
+ void set_mesh(std::shared_ptr<const TriangleMesh> &mesh) { m_mesh = mesh; }
+ void set_mesh(std::unique_ptr<const TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
+ void reset_mesh() { m_mesh = std::make_shared<const TriangleMesh>(); }
// Configuration parameters specific to an object model geometry or a modifier volume,
// overriding the global Slic3r settings and the ModelObject settings.
- DynamicPrintConfig config;
+ ModelConfig config;
// A parent object owning this modifier volume.
ModelObject* get_object() const { return this->object; };
@@ -386,7 +407,7 @@ public:
void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
- void scale_geometry(const Vec3d& versor);
+ void scale_geometry_after_creation(const Vec3d& versor);
// Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box.
// Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared!
@@ -432,66 +453,88 @@ public:
const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); }
- using ModelBase::set_new_unique_id;
+ void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); }
protected:
friend class Print;
friend class SLAPrint;
+ friend class Model;
friend class ModelObject;
+ // Copies IDs of both the ModelVolume and its config.
explicit ModelVolume(const ModelVolume &rhs) = default;
void set_model_object(ModelObject *model_object) { object = model_object; }
+ void assign_new_unique_ids_recursive() override { ObjectBase::set_new_unique_id(); config.set_new_unique_id(); }
void transform_this_mesh(const Transform3d& t, bool fix_left_handed);
void transform_this_mesh(const Matrix3d& m, bool fix_left_handed);
private:
// Parent object owning this ModelVolume.
- ModelObject* object;
+ ModelObject* object;
// The triangular model.
- std::shared_ptr<TriangleMesh> m_mesh;
+ std::shared_ptr<const TriangleMesh> m_mesh;
// Is it an object to be printed, or a modifier volume?
- ModelVolumeType m_type;
- t_model_material_id m_material_id;
+ ModelVolumeType m_type;
+ t_model_material_id m_material_id;
// The convex hull of this model's mesh.
- std::shared_ptr<TriangleMesh> m_convex_hull;
- Geometry::Transformation m_transformation;
+ std::shared_ptr<const TriangleMesh> m_convex_hull;
+ Geometry::Transformation m_transformation;
// flag to optimize the checking if the volume is splittable
// -1 -> is unknown value (before first cheking)
// 0 -> is not splittable
// 1 -> is splittable
- mutable int m_is_splittable{ -1 };
+ mutable int m_is_splittable{ -1 };
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : m_mesh(new TriangleMesh(mesh)), m_type(ModelVolumeType::MODEL_PART), object(object)
{
+ assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) :
- m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) {}
+ m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) {
+ assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
+ }
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other) :
- ModelBase(other), // copy the ID
+ ObjectBase(other),
name(other.name), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{
+ assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
+ assert(this->id() == other.id() && this->config.id() == other.config.id());
this->set_material_id(other.material_id());
}
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
name(other.name), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{
+ assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
+ assert(this->id() != other.id() && this->config.id() == other.config.id());
this->set_material_id(other.material_id());
+ this->config.set_new_unique_id();
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
+ assert(this->config.id().valid()); assert(this->config.id() != other.config.id()); assert(this->id() != this->config.id());
}
ModelVolume& operator=(ModelVolume &rhs) = delete;
+
+ friend class cereal::access;
+ friend class UndoRedo::StackImpl;
+ // Used for deserialization, therefore no IDs are allocated.
+ ModelVolume() : ObjectBase(-1), config(-1), object(nullptr) {
+ assert(this->id().invalid()); assert(this->config.id().invalid());
+ }
+ template<class Archive> void serialize(Archive &ar) {
+ ar(name, config, m_mesh, m_type, m_material_id, m_convex_hull, m_transformation, m_is_splittable);
+ }
};
// A single instance of a ModelObject.
// Knows the affine transformation of an object.
-class ModelInstance : public ModelBase
+class ModelInstance final : public ObjectBase
{
public:
enum EPrintVolumeState : unsigned char
@@ -569,6 +612,7 @@ public:
protected:
friend class Print;
friend class SLAPrint;
+ friend class Model;
friend class ModelObject;
explicit ModelInstance(const ModelInstance &rhs) = default;
@@ -579,27 +623,28 @@ private:
ModelObject* object;
// Constructor, which assigns a new unique ID.
- explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside)
- {
- get_arrange_polygon(); // initialize the arrange cache
- }
+ explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) { assert(this->id().valid()); }
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object, const ModelInstance &other) :
- m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside)
- {
- get_arrange_polygon(); // initialize the arrange cache
- }
+ m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) { assert(this->id().valid() && this->id() != other.id()); }
- ModelInstance() = delete;
explicit ModelInstance(ModelInstance &&rhs) = delete;
ModelInstance& operator=(const ModelInstance &rhs) = delete;
ModelInstance& operator=(ModelInstance &&rhs) = delete;
+
+ friend class cereal::access;
+ friend class UndoRedo::StackImpl;
+ // Used for deserialization, therefore no IDs are allocated.
+ ModelInstance() : ObjectBase(-1), object(nullptr) { assert(this->id().invalid()); }
+ template<class Archive> void serialize(Archive &ar) {
+ ar(m_transformation, print_volume_state);
+ }
// Warning! This object is not guarded against concurrency.
- mutable struct ArrangeCache {
- bool valid = false;
- ExPolygon poly;
- } m_arrange_cache;
+ // mutable struct ArrangeCache {
+ // bool valid = false;
+ // ExPolygon poly;
+ // } m_arrange_cache;
};
// The print bed content.
@@ -607,7 +652,7 @@ private:
// and with multiple modifier meshes.
// A model groups multiple objects, each object having possibly multiple instances,
// all objects may share mutliple materials.
-class Model : public ModelBase
+class Model final : public ObjectBase
{
static unsigned int s_auto_extruder_id;
@@ -619,17 +664,17 @@ public:
ModelObjectPtrs objects;
// Default constructor assigns a new ID to the model.
- Model() {}
+ Model() { assert(this->id().valid()); }
~Model() { this->clear_objects(); this->clear_materials(); }
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
/* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */
- Model(const Model &rhs) : ModelBase(-1) { this->assign_copy(rhs); }
- explicit Model(Model &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); }
- Model& operator=(const Model &rhs) { this->assign_copy(rhs); return *this; }
- Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); return *this; }
+ Model(const Model &rhs) : ObjectBase(-1) { assert(this->id().invalid()); this->assign_copy(rhs); assert(this->id().valid()); assert(this->id() == rhs.id()); }
+ explicit Model(Model &&rhs) : ObjectBase(-1) { assert(this->id().invalid()); this->assign_copy(std::move(rhs)); assert(this->id().valid()); assert(this->id() == rhs.id()); }
+ Model& operator=(const Model &rhs) { this->assign_copy(rhs); assert(this->id().valid()); assert(this->id() == rhs.id()); return *this; }
+ Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); assert(this->id().valid()); assert(this->id() == rhs.id()); return *this; }
- MODELBASE_DERIVED_COPY_MOVE_CLONE(Model)
+ OBJECTBASE_DERIVED_COPY_MOVE_CLONE(Model)
static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true);
static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true);
@@ -640,7 +685,7 @@ public:
ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh);
ModelObject* add_object(const ModelObject &other);
void delete_object(size_t idx);
- bool delete_object(ModelID id);
+ bool delete_object(ObjectID id);
bool delete_object(ModelObject* object);
void clear_objects();
@@ -658,24 +703,24 @@ public:
BoundingBoxf3 bounding_box() const;
// Set the print_volume_state of PrintObject::instances,
// return total number of printable objects.
- unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume);
+ unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume);
// Returns true if any ModelObject was modified.
- bool center_instances_around_point(const Vec2d &point);
- void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
- TriangleMesh mesh() const;
- bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);
+ bool center_instances_around_point(const Vec2d &point);
+ void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
+ TriangleMesh mesh() const;
+ bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);
// Croaks if the duplicated objects do not fit the print bed.
- void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
- void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
- void duplicate_objects_grid(size_t x, size_t y, coordf_t dist);
+ void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
+ void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
+ void duplicate_objects_grid(size_t x, size_t y, coordf_t dist);
- bool looks_like_multipart_object() const;
- void convert_multipart_object(unsigned int max_extruders);
+ bool looks_like_multipart_object() const;
+ void convert_multipart_object(unsigned int max_extruders);
// Ensures that the min z of the model is not negative
- void adjust_min_z();
+ void adjust_min_z();
- void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); }
+ void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); }
static unsigned int get_auto_extruder_id(unsigned int max_extruders);
static std::string get_auto_extruder_id_as_string(unsigned int max_extruders);
@@ -687,11 +732,19 @@ public:
std::string propose_export_file_name_and_path(const std::string &new_extension) const;
private:
- MODELBASE_DERIVED_PRIVATE_COPY_MOVE(Model)
+ explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); };
+ void assign_new_unique_ids_recursive();
+ void update_links_bottom_up_recursive();
+
+ friend class cereal::access;
+ friend class UndoRedo::StackImpl;
+ template<class Archive> void serialize(Archive &ar) {
+ ar(materials, objects);
+ }
};
-#undef MODELBASE_DERIVED_COPY_MOVE_CLONE
-#undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE
+#undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE
+#undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing.
@@ -711,6 +764,6 @@ void check_model_ids_validity(const Model &model);
void check_model_ids_equal(const Model &model1, const Model &model2);
#endif /* NDEBUG */
-}
+} // namespace Slic3r
-#endif
+#endif /* slic3r_Model_hpp_ */
diff --git a/src/libslic3r/MotionPlanner.cpp b/src/libslic3r/MotionPlanner.cpp
index 198c39f31..dd3443879 100644
--- a/src/libslic3r/MotionPlanner.cpp
+++ b/src/libslic3r/MotionPlanner.cpp
@@ -332,7 +332,7 @@ Polyline MotionPlannerGraph::shortest_path(size_t node_start, size_t node_end) c
queue.pop();
map_node_to_queue_id[u] = size_t(-1);
// Stop searching if we reached our destination.
- if (u == node_end)
+ if (size_t(u) == node_end)
break;
// Visit each edge starting at node u.
for (const Neighbor& neighbor : m_adjacency_list[u])
diff --git a/src/libslic3r/ObjectID.cpp b/src/libslic3r/ObjectID.cpp
new file mode 100644
index 000000000..b188d84c0
--- /dev/null
+++ b/src/libslic3r/ObjectID.cpp
@@ -0,0 +1,22 @@
+#include "ObjectID.hpp"
+
+namespace Slic3r {
+
+size_t ObjectBase::s_last_id = 0;
+
+// Unique object / instance ID for the wipe tower.
+ObjectID wipe_tower_object_id()
+{
+ static ObjectBase mine;
+ return mine.id();
+}
+
+ObjectID wipe_tower_instance_id()
+{
+ static ObjectBase mine;
+ return mine.id();
+}
+
+} // namespace Slic3r
+
+// CEREAL_REGISTER_TYPE(Slic3r::ObjectBase)
diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp
new file mode 100644
index 000000000..484d1173b
--- /dev/null
+++ b/src/libslic3r/ObjectID.hpp
@@ -0,0 +1,93 @@
+#ifndef slic3r_ObjectID_hpp_
+#define slic3r_ObjectID_hpp_
+
+#include <cereal/access.hpp>
+
+namespace Slic3r {
+
+namespace UndoRedo {
+ class StackImpl;
+};
+
+// Unique identifier of a mutable object accross the application.
+// Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject)
+// (for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial classes)
+// and to serialize / deserialize an object onto the Undo / Redo stack.
+// Valid IDs are strictly positive (non zero).
+// It is declared as an object, as some compilers (notably msvcc) consider a typedef size_t equivalent to size_t
+// for parameter overload.
+class ObjectID
+{
+public:
+ ObjectID(size_t id) : id(id) {}
+ // Default constructor constructs an invalid ObjectID.
+ ObjectID() : id(0) {}
+
+ bool operator==(const ObjectID &rhs) const { return this->id == rhs.id; }
+ bool operator!=(const ObjectID &rhs) const { return this->id != rhs.id; }
+ bool operator< (const ObjectID &rhs) const { return this->id < rhs.id; }
+ bool operator> (const ObjectID &rhs) const { return this->id > rhs.id; }
+ bool operator<=(const ObjectID &rhs) const { return this->id <= rhs.id; }
+ bool operator>=(const ObjectID &rhs) const { return this->id >= rhs.id; }
+
+ bool valid() const { return id != 0; }
+ bool invalid() const { return id == 0; }
+
+ size_t id;
+
+private:
+ friend class cereal::access;
+ template<class Archive> void serialize(Archive &ar) { ar(id); }
+};
+
+// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID
+// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject).
+// Achtung! The s_last_id counter is not thread safe, so it is expected, that the ObjectBase derived instances
+// are only instantiated from the main thread.
+class ObjectBase
+{
+public:
+ ObjectID id() const { return m_id; }
+
+protected:
+ // Constructors to be only called by derived classes.
+ // Default constructor to assign a unique ID.
+ ObjectBase() : m_id(generate_new_id()) {}
+ // Constructor with ignored int parameter to assign an invalid ID, to be replaced
+ // by an existing ID copied from elsewhere.
+ ObjectBase(int) : m_id(ObjectID(0)) {}
+ // The class tree will have virtual tables and type information.
+ virtual ~ObjectBase() {}
+
+ // Use with caution!
+ void set_new_unique_id() { m_id = generate_new_id(); }
+ void set_invalid_id() { m_id = 0; }
+ // Use with caution!
+ void copy_id(const ObjectBase &rhs) { m_id = rhs.id(); }
+
+ // Override this method if a ObjectBase derived class owns other ObjectBase derived instances.
+ virtual void assign_new_unique_ids_recursive() { this->set_new_unique_id(); }
+
+private:
+ ObjectID m_id;
+
+ static inline ObjectID generate_new_id() { return ObjectID(++ s_last_id); }
+ static size_t s_last_id;
+
+ friend ObjectID wipe_tower_object_id();
+ friend ObjectID wipe_tower_instance_id();
+
+ friend class cereal::access;
+ friend class Slic3r::UndoRedo::StackImpl;
+ template<class Archive> void serialize(Archive &ar) { ar(m_id); }
+ ObjectBase(const ObjectID id) : m_id(id) {}
+ template<class Archive> static void load_and_construct(Archive & ar, cereal::construct<ObjectBase> &construct) { ObjectID id; ar(id); construct(id); }
+};
+
+// Unique object / instance ID for the wipe tower.
+extern ObjectID wipe_tower_object_id();
+extern ObjectID wipe_tower_instance_id();
+
+} // namespace Slic3r
+
+#endif /* slic3r_ObjectID_hpp_ */
diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp
index de8aeeb2a..4dac192ad 100644
--- a/src/libslic3r/PerimeterGenerator.cpp
+++ b/src/libslic3r/PerimeterGenerator.cpp
@@ -175,7 +175,7 @@ void PerimeterGenerator::process()
const PerimeterGeneratorLoop &loop = contours_d[i];
// find the contour loop that contains it
for (int t = d - 1; t >= 0; -- t) {
- for (int j = 0; j < contours[t].size(); ++ j) {
+ for (size_t j = 0; j < contours[t].size(); ++ j) {
PerimeterGeneratorLoop &candidate_parent = contours[t][j];
if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
candidate_parent.children.push_back(loop);
@@ -397,7 +397,7 @@ static inline ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyli
pp.push_back(line.b);
width.push_back(line.b_width);
- assert(pp.size() == segments + 1);
+ assert(pp.size() == segments + 1u);
assert(width.size() == segments*2);
}
diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp
index ae66178aa..2bfe9b745 100644
--- a/src/libslic3r/PlaceholderParser.cpp
+++ b/src/libslic3r/PlaceholderParser.cpp
@@ -521,7 +521,6 @@ namespace client
static void regex_op(expr &lhs, boost::iterator_range<Iterator> &rhs, char op)
{
const std::string *subject = nullptr;
- const std::string *mask = nullptr;
if (lhs.type == TYPE_STRING) {
// One type is string, the other could be converted to string.
subject = &lhs.s();
@@ -563,7 +562,6 @@ namespace client
static void ternary_op(expr &lhs, expr &rhs1, expr &rhs2)
{
- bool value = false;
if (lhs.type != TYPE_BOOL)
lhs.throw_exception("Not a boolean expression");
if (lhs.b())
@@ -975,7 +973,7 @@ namespace client
// depending on the context->just_boolean_expression flag. This way a single static expression parser
// could serve both purposes.
start = eps[px::bind(&MyContext::evaluate_full_macro, _r1, _a)] >
- ( eps(_a==true) > text_block(_r1) [_val=_1]
+ ( (eps(_a==true) > text_block(_r1) [_val=_1])
| conditional_expression(_r1) [ px::bind(&expr<Iterator>::evaluate_boolean_to_string, _1, _val) ]
) > eoi;
start.name("start");
@@ -1245,7 +1243,7 @@ static std::string process_macro(const std::string &templ, client::MyContext &co
std::string::const_iterator end = templ.end();
// Accumulator for the processed template.
std::string output;
- bool res = phrase_parse(iter, end, macro_processor_instance(&context), space, output);
+ phrase_parse(iter, end, macro_processor_instance(&context), space, output);
if (!context.error_message.empty()) {
if (context.error_message.back() != '\n' && context.error_message.back() != '\r')
context.error_message += '\n';
diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp
index b02ead299..994f45e59 100644
--- a/src/libslic3r/Point.hpp
+++ b/src/libslic3r/Point.hpp
@@ -62,8 +62,8 @@ inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); }
inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); }
inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); }
-inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); }
-inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); }
+inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); }
+inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); }
inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(int64_t(v(0)), int64_t(v(1)), int64_t(z)); }
inline Vec3crd to_3d(const Vec3crd &p, coord_t z) { return Vec3crd(p(0), p(1), z); }
@@ -291,4 +291,21 @@ namespace boost { namespace polygon {
} }
// end Boost
+// Serialization through the Cereal library
+namespace cereal {
+// template<class Archive> void serialize(Archive& archive, Slic3r::Vec2crd &v) { archive(v.x(), v.y()); }
+// template<class Archive> void serialize(Archive& archive, Slic3r::Vec3crd &v) { archive(v.x(), v.y(), v.z()); }
+ template<class Archive> void serialize(Archive& archive, Slic3r::Vec2i &v) { archive(v.x(), v.y()); }
+ template<class Archive> void serialize(Archive& archive, Slic3r::Vec3i &v) { archive(v.x(), v.y(), v.z()); }
+// template<class Archive> void serialize(Archive& archive, Slic3r::Vec2i64 &v) { archive(v.x(), v.y()); }
+// template<class Archive> void serialize(Archive& archive, Slic3r::Vec3i64 &v) { archive(v.x(), v.y(), v.z()); }
+ template<class Archive> void serialize(Archive& archive, Slic3r::Vec2f &v) { archive(v.x(), v.y()); }
+ template<class Archive> void serialize(Archive& archive, Slic3r::Vec3f &v) { archive(v.x(), v.y(), v.z()); }
+ template<class Archive> void serialize(Archive& archive, Slic3r::Vec2d &v) { archive(v.x(), v.y()); }
+ template<class Archive> void serialize(Archive& archive, Slic3r::Vec3d &v) { archive(v.x(), v.y(), v.z()); }
+
+ template<class Archive> void load(Archive& archive, Slic3r::Matrix2f &m) { archive.loadBinary((char*)m.data(), sizeof(float) * 4); }
+ template<class Archive> void save(Archive& archive, Slic3r::Matrix2f &m) { archive.saveBinary((char*)m.data(), sizeof(float) * 4); }
+}
+
#endif
diff --git a/src/libslic3r/PolylineCollection.cpp b/src/libslic3r/PolylineCollection.cpp
index 1304161c3..0c66c371a 100644
--- a/src/libslic3r/PolylineCollection.cpp
+++ b/src/libslic3r/PolylineCollection.cpp
@@ -61,7 +61,7 @@ Polylines PolylineCollection::_chained_path_from(
while (! endpoints.empty()) {
// find nearest point
int endpoint_index = nearest_point_index<double>(endpoints, start_near, no_reverse);
- assert(endpoint_index >= 0 && endpoint_index < endpoints.size() * 2);
+ assert(endpoint_index >= 0 && size_t(endpoint_index) < endpoints.size() * 2);
if (move_from_src) {
retval.push_back(std::move(src[endpoints[endpoint_index/2].idx]));
} else {
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index f9129f15a..a9b8a827f 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -7,18 +7,20 @@
#include "I18N.hpp"
#include "SupportMaterial.hpp"
#include "GCode.hpp"
-#include "GCode/WipeTowerPrusaMM.hpp"
+#include "GCode/WipeTower.hpp"
#include "Utils.hpp"
//#include "PrintExport.hpp"
+#include <float.h>
+
#include <algorithm>
#include <limits>
#include <unordered_set>
#include <boost/filesystem/path.hpp>
#include <boost/log/trivial.hpp>
-//! macro used to mark string used at localization,
+//! macro used to mark string used at localization,
//! return same string
#define L(s) Slic3r::I18N::translate(s)
@@ -41,36 +43,6 @@ void Print::clear()
m_model.clear_objects();
}
-// Only used by the Perl test cases.
-void Print::reload_object(size_t /* idx */)
-{
- ModelObjectPtrs model_objects;
- {
- tbb::mutex::scoped_lock lock(this->state_mutex());
- // The following call should stop background processing if it is running.
- this->invalidate_all_steps();
- /* 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()
- 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();
- }
- // re-add model objects
- for (ModelObject *mo : model_objects)
- this->add_model_object(mo);
-}
-
PrintRegion* Print::add_region()
{
m_regions.emplace_back(new PrintRegion(this));
@@ -121,7 +93,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
"filament_density",
"filament_notes",
"filament_cost",
- "filament_max_volumetric_speed",
"first_layer_acceleration",
"first_layer_bed_temperature",
"first_layer_speed",
@@ -216,6 +187,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|| opt_key == "filament_cooling_initial_speed"
|| opt_key == "filament_cooling_final_speed"
|| opt_key == "filament_ramming_parameters"
+ || opt_key == "filament_max_volumetric_speed"
|| opt_key == "gcode_flavor"
|| opt_key == "high_current_on_filament_swap"
|| opt_key == "infill_first"
@@ -335,7 +307,7 @@ unsigned int Print::num_object_instances() const
{
unsigned int instances = 0;
for (const PrintObject *print_object : m_objects)
- instances += print_object->copies().size();
+ instances += (unsigned int)print_object->copies().size();
return instances;
}
@@ -358,198 +330,6 @@ double Print::max_allowed_layer_height() const
return nozzle_diameter_max;
}
-// Caller is responsible for supplying models whose objects don't collide
-// and have explicit instance positions.
-void Print::add_model_object(ModelObject* model_object, int idx)
-{
- tbb::mutex::scoped_lock lock(this->state_mutex());
- // Add a copy of this ModelObject to this Print.
- m_model.objects.emplace_back(ModelObject::new_copy(*model_object));
- m_model.objects.back()->set_model(&m_model);
- // Initialize a new print object and store it at the given position.
- PrintObject *object = new PrintObject(this, model_object, true);
- if (idx != -1) {
- delete m_objects[idx];
- m_objects[idx] = object;
- } else
- m_objects.emplace_back(object);
- // Invalidate all print steps.
- this->invalidate_all_steps();
-
- // Set the transformation matrix without translation from the first instance.
- if (! model_object->instances.empty()) {
- // Trafo and bounding box, both in world coordinate system.
- Transform3d trafo = model_object->instances.front()->get_matrix();
- BoundingBoxf3 bbox = model_object->instance_bounding_box(0);
- // Now shift the object up to align it with the print bed.
- trafo.data()[14] -= bbox.min(2);
- // and reset the XY translation.
- trafo.data()[12] = 0;
- trafo.data()[13] = 0;
- object->set_trafo(trafo);
- }
-
- size_t volume_id = 0;
- for (const ModelVolume *volume : model_object->volumes) {
- if (! volume->is_model_part() && ! volume->is_modifier())
- continue;
- // Get the config applied to this volume.
- PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, 99999);
- // Find an existing print region with the same config.
- size_t region_id = size_t(-1);
- for (size_t i = 0; i < m_regions.size(); ++ i)
- 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 = m_regions.size();
- this->add_region(config);
- }
- // Assign volume to a region.
- object->add_region_volume(region_id, volume_id);
- ++ volume_id;
- }
-
- // Apply config to print object.
- 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);
- }
-}
-
-// This function is only called through the Perl-C++ binding from the unit tests, should be
-// removed when unit tests are rewritten to C++.
-bool Print::apply_config_perl_tests_only(DynamicPrintConfig config)
-{
- tbb::mutex::scoped_lock lock(this->state_mutex());
-
-
- // Perl unit tests were failing in case the preset was not normalized (e.g. https://github.com/prusa3d/PrusaSlicer/issues/2288 was caused
- // by too short max_layer_height vector. Calling the necessary function Preset::normalize(...) is not currently possible because there is no
- // access to preset. This should be solved when the unit tests are rewritten to C++. For now we just copy-pasted code from Preset.cpp
- // to make sure the unit tests pass (functions set_num_extruders and nozzle_options()).
- auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter", true));
- assert(nozzle_diameter != nullptr);
- const auto &defaults = FullPrintConfig::defaults();
- for (const std::string &key : { "nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset",
- "retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_speed", "deretract_speed",
- "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "wipe",
- "retract_layer_change", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour" })
- {
- auto *opt = config.option(key, true);
- assert(opt != nullptr);
- assert(opt->is_vector());
- unsigned int num_extruders = (unsigned int)nozzle_diameter->values.size();
- static_cast<ConfigOptionVectorBase*>(opt)->resize(num_extruders, defaults.option(key));
- }
-
- // 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);
-
- // handle changes to print config
- 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
- 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();
- // we override the new config with object-specific options
- normalize_and_apply_config(new_config, object->model_object()->config);
- // 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);
- invalidated |= object->invalidate_state_by_config_options(diff);
- }
-
- // handle changes to regions config defaults
- 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.
- bool rearrange_regions = false;
- {
- // Collect the already visited region configs into other_region_configs,
- // so one may check for duplicates.
- std::vector<PrintRegionConfig> other_region_configs;
- for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) {
- PrintRegion &region = *m_regions[region_id];
- PrintRegionConfig this_region_config;
- bool this_region_config_set = false;
- 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];
- if (this_region_config_set) {
- // If the new config for this volume differs from the other
- // volume configs currently associated to this region, it means
- // the region subdivision does not make sense anymore.
- if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999))) {
- rearrange_regions = true;
- goto exit_for_rearrange_regions;
- }
- } else {
- this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999);
- this_region_config_set = true;
- }
- for (const PrintRegionConfig &cfg : other_region_configs) {
- // If the new config for this volume equals any of the other
- // volume configs that are not currently associated to this
- // region, it means the region subdivision does not make
- // sense anymore.
- if (cfg.equals(this_region_config)) {
- rearrange_regions = true;
- goto exit_for_rearrange_regions;
- }
- }
- }
- }
- }
- if (this_region_config_set) {
- t_config_option_keys diff = region.config().diff(this_region_config);
- if (! diff.empty()) {
- 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);
- }
- other_region_configs.emplace_back(std::move(this_region_config));
- }
- }
- }
-
-exit_for_rearrange_regions:
-
- if (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(m_objects.size());
- for (PrintObject *object : m_objects)
- model_objects.push_back(object->model_object());
- this->clear();
- for (ModelObject *mo : model_objects)
- this->add_model_object(mo);
- invalidated = true;
- }
-
- for (PrintObject *object : m_objects)
- object->update_slicing_parameters();
-
- return invalidated;
-}
-
// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new
// in the exact order and with the same IDs.
// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
@@ -612,7 +392,7 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst,
assert(mv_src.id() == mv_dst.id());
// Copy the ModelVolume data.
mv_dst.name = mv_src.name;
- mv_dst.config = mv_src.config;
+ static_cast<DynamicPrintConfig&>(mv_dst.config) = static_cast<const DynamicPrintConfig&>(mv_src.config);
//FIXME what to do with the materials?
// mv_dst.m_material_id = mv_src.m_material_id;
++ i_src;
@@ -620,6 +400,20 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst,
}
}
+static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_dst, const t_layer_config_ranges &lr_src)
+{
+ assert(lr_dst.size() == lr_src.size());
+ auto it_src = lr_src.cbegin();
+ for (auto &kvp_dst : lr_dst) {
+ const auto &kvp_src = *it_src ++;
+ assert(std::abs(kvp_dst.first.first - kvp_src.first.first ) <= EPSILON);
+ assert(std::abs(kvp_dst.first.second - kvp_src.first.second) <= EPSILON);
+ // Layer heights are allowed do differ in case the layer height table is being overriden by the smooth profile.
+ // assert(std::abs(kvp_dst.second.option("layer_height")->getFloat() - kvp_src.second.option("layer_height")->getFloat()) <= EPSILON);
+ kvp_dst.second = kvp_src.second;
+ }
+}
+
static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
{
typedef Transform3d::Scalar T;
@@ -674,6 +468,23 @@ static std::vector<PrintInstances> print_objects_from_model_object(const ModelOb
return std::vector<PrintInstances>(trafos.begin(), trafos.end());
}
+// Compare just the layer ranges and their layer heights, not the associated configs.
+// Ignore the layer heights if check_layer_heights is false.
+bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
+{
+ if (lr1.size() != lr2.size())
+ return false;
+ auto it2 = lr2.begin();
+ for (const auto &kvp1 : lr1) {
+ const auto &kvp2 = *it2 ++;
+ if (std::abs(kvp1.first.first - kvp2.first.first ) > EPSILON ||
+ std::abs(kvp1.first.second - kvp2.first.second) > EPSILON ||
+ (check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON))
+ return false;
+ }
+ return true;
+}
+
Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in)
{
#ifdef _DEBUG
@@ -724,6 +535,50 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
// Handle changes to regions config defaults
m_default_region_config.apply_only(config, region_diff, true);
+ class LayerRanges
+ {
+ public:
+ LayerRanges() {}
+ // Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs.
+ void assign(const t_layer_config_ranges &in) {
+ m_ranges.clear();
+ m_ranges.reserve(in.size());
+ // Input ranges are sorted lexicographically. First range trims the other ranges.
+ coordf_t last_z = 0;
+ for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range : in) {
+// for (auto &range : in) {
+ if (range.first.second > last_z) {
+ coordf_t min_z = std::max(range.first.first, 0.);
+ if (min_z > last_z + EPSILON) {
+ m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
+ last_z = min_z;
+ }
+ if (range.first.second > last_z + EPSILON) {
+ const DynamicPrintConfig* cfg = &range.second;
+ m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
+ last_z = range.first.second;
+ }
+ }
+ }
+ if (m_ranges.empty())
+ m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
+ else if (m_ranges.back().second == nullptr)
+ m_ranges.back().first.second = DBL_MAX;
+ else
+ m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
+ }
+ const DynamicPrintConfig* config(const t_layer_height_range &range) const {
+ auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr));
+ assert(it != m_ranges.end());
+ assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON);
+ assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON);
+ return (it == m_ranges.end()) ? nullptr : it->second;
+ }
+ std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator begin() const { return m_ranges.cbegin(); }
+ std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator end() const { return m_ranges.cend(); }
+ private:
+ std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>> m_ranges;
+ };
struct ModelObjectStatus {
enum Status {
Unknown,
@@ -732,9 +587,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
Moved,
Deleted,
};
- ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
- ModelID id;
- Status status;
+ ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {}
+ ObjectID id;
+ Status status;
+ LayerRanges layer_ranges;
// Search by id.
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
};
@@ -839,9 +695,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
print_object(print_object),
trafo(print_object->trafo()),
status(status) {}
- PrintObjectStatus(ModelID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
+ PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
// ID of the ModelObject & PrintObject
- ModelID id;
+ ObjectID id;
// Pointer to the old PrintObject
PrintObject *print_object;
// Trafo generated with model_object->world_matrix(true)
@@ -861,21 +717,23 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
+ const ModelObject& model_object_new = *model.objects[idx_model_object];
+ const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
if (it_status->status == ModelObjectStatus::New)
// PrintObject instances will be added in the next loop.
continue;
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
- const ModelObject &model_object_new = *model.objects[idx_model_object];
// Check whether a model part volume was added or removed, their transformations or order changed.
+ // Only volume IDs, volume types and their order are checked, configuration and other parameters are NOT checked.
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER);
bool support_enforcers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
if (model_parts_differ || modifiers_differ ||
model_object.origin_translation != model_object_new.origin_translation ||
- model_object.layer_height_ranges != model_object_new.layer_height_ranges ||
- model_object.layer_height_profile != model_object_new.layer_height_profile) {
+ model_object.layer_height_profile != model_object_new.layer_height_profile ||
+ ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) {
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) {
@@ -899,7 +757,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
// Synchronize Object's config.
bool object_config_changed = model_object.config != model_object_new.config;
if (object_config_changed)
- model_object.config = model_object_new.config;
+ static_cast<DynamicPrintConfig&>(model_object.config) = static_cast<const DynamicPrintConfig&>(model_object_new.config);
if (! object_diff.empty() || object_config_changed) {
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders);
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
@@ -915,7 +773,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
//FIXME What to do with m_material_id?
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
- // Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
+ layer_height_ranges_copy_configs(model_object.layer_config_ranges /* dst */, model_object_new.layer_config_ranges /* src */);
+ // Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step.
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
model_object.clear_instances();
@@ -1027,19 +886,27 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
PrintRegionConfig this_region_config;
bool this_region_config_set = false;
for (PrintObject *print_object : m_objects) {
+ const LayerRanges *layer_ranges;
+ {
+ auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
+ assert(it_status != model_object_status.end());
+ assert(it_status->status != ModelObjectStatus::Deleted);
+ layer_ranges = &it_status->layer_ranges;
+ }
if (region_id < print_object->region_volumes.size()) {
- for (int volume_id : print_object->region_volumes[region_id]) {
- const ModelVolume &volume = *print_object->model_object()->volumes[volume_id];
+ for (const std::pair<t_layer_height_range, int> &volume_and_range : print_object->region_volumes[region_id]) {
+ const ModelVolume &volume = *print_object->model_object()->volumes[volume_and_range.second];
+ const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first);
if (this_region_config_set) {
// If the new config for this volume differs from the other
// volume configs currently associated to this region, it means
// the region subdivision does not make sense anymore.
- if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders)))
+ if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders)))
// Regions were split. Reset this print_object.
goto print_object_end;
} else {
- this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders);
- for (size_t i = 0; i < region_id; ++i) {
+ this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders);
+ for (size_t i = 0; i < region_id; ++ i) {
const PrintRegion &region_other = *m_regions[i];
if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
// Regions were merged. Reset this print_object.
@@ -1054,7 +921,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
update_apply_status(print_object->invalidate_all_steps());
// Decrease the references to regions from this volume.
int ireg = 0;
- for (const std::vector<int> &volumes : print_object->region_volumes) {
+ for (const std::vector<std::pair<t_layer_height_range, int>> &volumes : print_object->region_volumes) {
if (! volumes.empty())
-- m_regions[ireg]->m_refcnt;
++ ireg;
@@ -1076,52 +943,65 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) {
PrintObject &print_object0 = *m_objects[idx_print_object];
const ModelObject &model_object = *print_object0.model_object();
- std::vector<int> map_volume_to_region(model_object.volumes.size(), -1);
+ const LayerRanges *layer_ranges;
+ {
+ auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
+ assert(it_status != model_object_status.end());
+ assert(it_status->status != ModelObjectStatus::Deleted);
+ layer_ranges = &it_status->layer_ranges;
+ }
+ std::vector<int> regions_in_object;
+ regions_in_object.reserve(64);
for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) {
PrintObject &print_object = *m_objects[i];
bool fresh = print_object.region_volumes.empty();
unsigned int volume_id = 0;
+ unsigned int idx_region_in_object = 0;
for (const ModelVolume *volume : model_object.volumes) {
if (! volume->is_model_part() && ! volume->is_modifier()) {
++ volume_id;
continue;
}
- int region_id = -1;
- if (&print_object == &print_object0) {
- // Get the config applied to this volume.
- PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, num_extruders);
- // Find an existing print region with the same config.
- int idx_empty_slot = -1;
- for (int i = 0; i < (int)m_regions.size(); ++ i) {
- if (m_regions[i]->m_refcnt == 0) {
- if (idx_empty_slot == -1)
- idx_empty_slot = i;
- } else if (config.equals(m_regions[i]->config())) {
- region_id = i;
- break;
+ // Filter the layer ranges, so they do not overlap and they contain at least a single layer.
+ // Now insert a volume with a layer range to its own region.
+ for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) {
+ int region_id = -1;
+ if (&print_object == &print_object0) {
+ // Get the config applied to this volume.
+ PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders);
+ // Find an existing print region with the same config.
+ int idx_empty_slot = -1;
+ for (int i = 0; i < (int)m_regions.size(); ++ i) {
+ if (m_regions[i]->m_refcnt == 0) {
+ if (idx_empty_slot == -1)
+ idx_empty_slot = i;
+ } else 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 == -1) {
+ if (idx_empty_slot == -1) {
+ region_id = (int)m_regions.size();
+ this->add_region(config);
+ } else {
+ region_id = idx_empty_slot;
+ m_regions[region_id]->set_config(std::move(config));
+ }
}
- }
- // If no region exists with the same config, create a new one.
- if (region_id == -1) {
- if (idx_empty_slot == -1) {
- region_id = (int)m_regions.size();
- this->add_region(config);
- } else {
- region_id = idx_empty_slot;
- m_regions[region_id]->set_config(std::move(config));
- }
- }
- map_volume_to_region[volume_id] = region_id;
- } else
- region_id = map_volume_to_region[volume_id];
- // Assign volume to a region.
- if (fresh) {
- if (region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
- ++ m_regions[region_id]->m_refcnt;
- print_object.add_region_volume(region_id, volume_id);
- }
- ++ volume_id;
- }
+ regions_in_object.emplace_back(region_id);
+ } else
+ region_id = regions_in_object[idx_region_in_object ++];
+ // Assign volume to a region.
+ if (fresh) {
+ if ((size_t)region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
+ ++ m_regions[region_id]->m_refcnt;
+ print_object.add_region_volume(region_id, volume_id, it_range->first);
+ }
+ }
+ ++ volume_id;
+ }
}
}
@@ -1175,9 +1055,9 @@ std::string Print::validate() const
Polygon convex_hull0 = offset(
print_object->model_object()->convex_hull_2d(
Geometry::assemble_transform(Vec3d::Zero(), rotation, model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
- scale_(m_config.extruder_clearance_radius.value) / 2., jtRound, scale_(0.1)).front();
+ float(scale_(0.5 * m_config.extruder_clearance_radius.value)), jtRound, float(scale_(0.1))).front();
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
- for (const Point &copy : print_object->m_copies) {
+ for (const Point &copy : print_object->copies()) {
Polygon convex_hull = convex_hull0;
convex_hull.translate(copy);
if (! intersection(convex_hulls_other, convex_hull).empty())
@@ -1211,23 +1091,21 @@ std::string Print::validate() const
return L("The Spiral Vase option can only be used when printing single material objects.");
}
- if (m_config.single_extruder_multi_material) {
- for (size_t i=1; i<m_config.nozzle_diameter.values.size(); ++i)
- if (m_config.nozzle_diameter.values[i] != m_config.nozzle_diameter.values[i-1])
- return L("All extruders must have the same diameter for single extruder multimaterial printer.");
- }
-
if (this->has_wipe_tower() && ! m_objects.empty()) {
if (m_config.gcode_flavor != gcfRepRap && m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlin)
return L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter and Repetier G-code flavors.");
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).");
+
+ for (size_t i=1; i<m_config.nozzle_diameter.values.size(); ++i)
+ if (m_config.nozzle_diameter.values[i] != m_config.nozzle_diameter.values[i-1])
+ return L("All extruders must have the same diameter for the Wipe Tower.");
if (m_objects.size() > 1) {
bool has_custom_layering = false;
std::vector<std::vector<coordf_t>> layer_height_profiles;
for (const PrintObject *object : m_objects) {
- has_custom_layering = ! object->model_object()->layer_height_ranges.empty() || ! object->model_object()->layer_height_profile.empty();
+ has_custom_layering = ! object->model_object()->layer_config_ranges.empty() || ! object->model_object()->layer_height_profile.empty(); // #ys_FIXME_experiment
if (has_custom_layering) {
layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
break;
@@ -1259,11 +1137,10 @@ std::string Print::validate() const
if (has_custom_layering) {
const std::vector<coordf_t> &layer_height_profile_tallest = layer_height_profiles[tallest_object_idx];
for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) {
- const PrintObject *object = m_objects[idx_object];
const std::vector<coordf_t> &layer_height_profile = layer_height_profiles[idx_object];
bool failed = false;
if (layer_height_profile_tallest.size() >= layer_height_profile.size()) {
- int i = 0;
+ size_t i = 0;
while (i < layer_height_profile.size() && i < layer_height_profile_tallest.size()) {
if (std::abs(layer_height_profile_tallest[i] - layer_height_profile[i])) {
failed = true;
@@ -1435,9 +1312,9 @@ Flow Print::brim_flow() const
generation as well. */
return Flow::new_from_config_width(
frPerimeter,
- width,
- m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1),
- this->skirt_first_layer_height(),
+ width,
+ (float)m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1),
+ (float)this->skirt_first_layer_height(),
0
);
}
@@ -1457,9 +1334,9 @@ Flow Print::skirt_flow() const
generation as well. */
return Flow::new_from_config_width(
frPerimeter,
- width,
- m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1),
- this->skirt_first_layer_height(),
+ width,
+ (float)m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1),
+ (float)this->skirt_first_layer_height(),
0
);
}
@@ -1628,26 +1505,26 @@ void Print::_make_skirt()
}
// Number of skirt loops per skirt layer.
- int n_skirts = m_config.skirts.value;
+ size_t 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();
+ Flow brim_flow = this->brim_flow();
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.);
+ auto distance = float(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<coordf_t> extruded_length(extruders.size(), 0.);
- for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) {
+ for (size_t i = n_skirts, extruder_idx = 0; i > 0; -- i) {
this->throw_if_canceled();
// Offset the skirt outside.
- distance += coord_t(scale_(spacing));
+ distance += float(scale_(spacing));
// Generate the skirt centerline.
Polygon loop;
{
- Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, scale_(0.1));
+ Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
if (loops.empty())
break;
@@ -1658,9 +1535,9 @@ void Print::_make_skirt()
eloop.paths.emplace_back(ExtrusionPath(
ExtrusionPath(
erSkirt,
- mm3_per_mm, // this will be overridden at G-code export time
+ (float)mm3_per_mm, // this will be overridden at G-code export time
flow.width,
- first_layer_height // this will be overridden at G-code export time
+ (float)first_layer_height // this will be overridden at G-code export time
)));
eloop.paths.back().polyline = loop.split_at_first_point();
m_skirt.append(eloop);
@@ -1730,7 +1607,6 @@ void Print::_make_brim()
bool Print::has_wipe_tower() const
{
return
- m_config.single_extruder_multi_material.value &&
! m_config.spiral_vase.value &&
m_config.wipe_tower.value &&
m_config.nozzle_diameter.values.size() > 1;
@@ -1786,7 +1662,7 @@ void Print::_make_wipe_tower()
// Insert the new support layer.
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.
- 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 = m_objects.front()->insert_support_layer(it_layer, -1, height, lt.print_z, lt.print_z - 0.5 * height);
++ it_layer;
}
}
@@ -1794,12 +1670,13 @@ void Print::_make_wipe_tower()
this->throw_if_canceled();
// Initialize the wipe tower.
- WipeTowerPrusaMM wipe_tower(
+ WipeTower wipe_tower(
+ m_config.single_extruder_multi_material.value,
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),
+ float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging),
m_config.high_current_on_filament_swap.value, m_config.gcode_flavor, wipe_volumes,
m_wipe_tower_data.tool_ordering.first_extruder());
@@ -1808,24 +1685,26 @@ void Print::_make_wipe_tower()
// Set the extruder & material properties at the wipe tower object.
for (size_t i = 0; i < number_of_extruders; ++ i)
+
wipe_tower.set_extruder(
i,
- WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()),
+ m_config.filament_type.get_at(i),
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),
+ (float)m_config.filament_loading_speed.get_at(i),
+ (float)m_config.filament_loading_speed_start.get_at(i),
+ (float)m_config.filament_unloading_speed.get_at(i),
+ (float)m_config.filament_unloading_speed_start.get_at(i),
+ (float)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),
+ (float)m_config.filament_cooling_initial_speed.get_at(i),
+ (float)m_config.filament_cooling_final_speed.get_at(i),
m_config.filament_ramming_parameters.get_at(i),
+ m_config.filament_max_volumetric_speed.get_at(i),
m_config.nozzle_diameter.get_at(i));
- m_wipe_tower_data.priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
- wipe_tower.prime(this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
+ m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
+ wipe_tower.prime((float)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)
@@ -1834,21 +1713,21 @@ void Print::_make_wipe_tower()
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_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);
+ wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)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_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 -= m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
+ volume_to_wipe -= (float)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 += m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
+ volume_to_wipe += (float)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,
+ wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id,
first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back(), volume_to_wipe);
current_extruder_id = extruder_id;
}
diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp
index 53d6d692d..368bf4ee8 100644
--- a/src/libslic3r/Print.hpp
+++ b/src/libslic3r/Print.hpp
@@ -80,8 +80,8 @@ private: // Prevents erroneous use by other classes.
typedef PrintObjectBaseWithState<Print, PrintObjectStep, posCount> Inherited;
public:
- // vector of (vectors of volume ids), indexed by region_id
- std::vector<std::vector<int>> region_volumes;
+ // vector of (layer height ranges and vectors of volume ids), indexed by region_id
+ std::vector<std::vector<std::pair<t_layer_height_range, int>>> region_volumes;
// 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
@@ -99,10 +99,10 @@ public:
BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }
// adds region_id, too, if necessary
- void add_region_volume(unsigned int region_id, int volume_id) {
+ void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) {
if (region_id >= region_volumes.size())
region_volumes.resize(region_id + 1);
- region_volumes[region_id].emplace_back(volume_id);
+ region_volumes[region_id].emplace_back(layer_range, volume_id);
}
// This is the *total* layer count (including support layers)
// this value is not supposed to be compared with Layer::id
@@ -120,7 +120,7 @@ public:
void clear_support_layers();
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);
+ SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, size_t id, coordf_t height, coordf_t print_z, coordf_t slice_z);
void delete_support_layer(int idx);
// Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters.
@@ -141,8 +141,9 @@ public:
void slice();
// Helpers to slice support enforcer / blocker meshes by the support generator.
- std::vector<ExPolygons> slice_support_enforcers() const;
- std::vector<ExPolygons> slice_support_blockers() const;
+ std::vector<ExPolygons> slice_support_volumes(const ModelVolumeType &model_volume_type) const;
+ std::vector<ExPolygons> slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); }
+ std::vector<ExPolygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
protected:
// to be called from Print only.
@@ -165,7 +166,7 @@ protected:
void update_slicing_parameters();
static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders);
- static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders);
+ static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders);
private:
void make_perimeters();
@@ -201,9 +202,11 @@ private:
LayerPtrs m_layers;
SupportLayerPtrs m_support_layers;
- std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
- std::vector<ExPolygons> _slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
- std::vector<ExPolygons> _slice_volume(const std::vector<float> &z, const ModelVolume &volume) const;
+ std::vector<ExPolygons> slice_region(size_t region_id, const std::vector<float> &z) const;
+ std::vector<ExPolygons> slice_modifiers(size_t region_id, const std::vector<float> &z) const;
+ std::vector<ExPolygons> slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
+ std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const ModelVolume &volume) const;
+ std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, const ModelVolume &volume) const;
};
struct WipeTowerData
@@ -213,7 +216,7 @@ struct WipeTowerData
// 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<WipeTower::ToolChangeResult> priming;
+ std::unique_ptr<std::vector<WipeTower::ToolChangeResult>> priming;
std::vector<std::vector<WipeTower::ToolChangeResult>> tool_changes;
std::unique_ptr<WipeTower::ToolChangeResult> final_purge;
std::vector<float> used_filament;
@@ -238,6 +241,8 @@ struct PrintStatistics
PrintStatistics() { clear(); }
std::string estimated_normal_print_time;
std::string estimated_silent_print_time;
+ std::vector<std::string> estimated_normal_color_print_times;
+ std::vector<std::string> estimated_silent_color_print_times;
double total_used_filament;
double total_extruded_volume;
double total_cost;
@@ -256,6 +261,8 @@ struct PrintStatistics
void clear() {
estimated_normal_print_time.clear();
estimated_silent_print_time.clear();
+ estimated_normal_color_print_times.clear();
+ estimated_silent_color_print_times.clear();
total_used_filament = 0.;
total_extruded_volume = 0.;
total_cost = 0.;
@@ -291,11 +298,6 @@ public:
ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override;
- // The following three methods are used by the Perl tests only. Get rid of them!
- void reload_object(size_t idx);
- void add_model_object(ModelObject* model_object, int idx = -1);
- bool apply_config_perl_tests_only(DynamicPrintConfig config);
-
void process() override;
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
// If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp
index d4c39499c..d84d492a0 100644
--- a/src/libslic3r/PrintBase.hpp
+++ b/src/libslic3r/PrintBase.hpp
@@ -246,7 +246,7 @@ public:
struct TaskParams {
TaskParams() : single_model_object(0), single_model_instance_only(false), to_object_step(-1), to_print_step(-1) {}
// If non-empty, limit the processing to this ModelObject.
- ModelID single_model_object;
+ ObjectID single_model_object;
// If set, only process single_model_object. Otherwise process everything, but single_model_object first.
bool single_model_instance_only;
// If non-negative, stop processing at the successive object step.
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 89e21934a..7d451a4cb 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -36,7 +36,6 @@ PrintConfigDef::PrintConfigDef()
void PrintConfigDef::init_common_params()
{
- t_optiondef_map &Options = this->options;
ConfigOptionDef* def;
def = this->add("printer_technology", coEnum);
@@ -102,7 +101,6 @@ void PrintConfigDef::init_common_params()
void PrintConfigDef::init_fff_params()
{
- t_optiondef_map &Options = this->options;
ConfigOptionDef* def;
// Maximum extruder temperature, bumped to 1500 to support printing of glass.
@@ -368,7 +366,8 @@ void PrintConfigDef::init_fff_params()
def = this->add("end_filament_gcode", coStrings);
def->label = L("End G-code");
- def->tooltip = L("This end procedure is inserted at the end of the output file, before the printer end gcode. "
+ def->tooltip = L("This end procedure is inserted at the end of the output file, before the printer end gcode (and "
+ "before any toolchange from this filament in case of multimaterial printers). "
"Note that you can use placeholder variables for all Slic3r settings. "
"If you have multiple extruders, the gcode is processed in extruder order.");
def->multiline = true;
@@ -406,10 +405,13 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
def = this->add("bottom_fill_pattern", coEnum);
- *def = *def_top_fill_pattern;
def->label = L("Bottom fill pattern");
+ def->category = L("Infill");
def->tooltip = L("Fill pattern for bottom infill. This only affects the bottom external visible layer, and not its adjacent solid shells.");
def->cli = "bottom-fill-pattern|external-fill-pattern|solid-fill-pattern";
+ def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
+ def->enum_values = def_top_fill_pattern->enum_values;
+ def->aliases = def_top_fill_pattern->aliases;
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
def = this->add("external_perimeter_extrusion_width", coFloatOrPercent);
@@ -1085,16 +1087,16 @@ void PrintConfigDef::init_fff_params()
// Add the machine feedrate limits for XYZE axes. (M203)
def = this->add("machine_max_feedrate_" + axis.name, coFloats);
def->full_label = (boost::format("Maximum feedrate %1%") % axis_upper).str();
- L("Maximum feedrate X");
- L("Maximum feedrate Y");
- L("Maximum feedrate Z");
- L("Maximum feedrate E");
+ (void)L("Maximum feedrate X");
+ (void)L("Maximum feedrate Y");
+ (void)L("Maximum feedrate Z");
+ (void)L("Maximum feedrate E");
def->category = L("Machine limits");
def->tooltip = (boost::format("Maximum feedrate of the %1% axis") % axis_upper).str();
- L("Maximum feedrate of the X axis");
- L("Maximum feedrate of the Y axis");
- L("Maximum feedrate of the Z axis");
- L("Maximum feedrate of the E axis");
+ (void)L("Maximum feedrate of the X axis");
+ (void)L("Maximum feedrate of the Y axis");
+ (void)L("Maximum feedrate of the Z axis");
+ (void)L("Maximum feedrate of the E axis");
def->sidetext = L("mm/s");
def->min = 0;
def->width = machine_limits_opt_width;
@@ -1103,16 +1105,16 @@ void PrintConfigDef::init_fff_params()
// Add the machine acceleration limits for XYZE axes (M201)
def = this->add("machine_max_acceleration_" + axis.name, coFloats);
def->full_label = (boost::format("Maximum acceleration %1%") % axis_upper).str();
- L("Maximum acceleration X");
- L("Maximum acceleration Y");
- L("Maximum acceleration Z");
- L("Maximum acceleration E");
+ (void)L("Maximum acceleration X");
+ (void)L("Maximum acceleration Y");
+ (void)L("Maximum acceleration Z");
+ (void)L("Maximum acceleration E");
def->category = L("Machine limits");
def->tooltip = (boost::format("Maximum acceleration of the %1% axis") % axis_upper).str();
- L("Maximum acceleration of the X axis");
- L("Maximum acceleration of the Y axis");
- L("Maximum acceleration of the Z axis");
- L("Maximum acceleration of the E axis");
+ (void)L("Maximum acceleration of the X axis");
+ (void)L("Maximum acceleration of the Y axis");
+ (void)L("Maximum acceleration of the Z axis");
+ (void)L("Maximum acceleration of the E axis");
def->sidetext = L("mm/s²");
def->min = 0;
def->width = machine_limits_opt_width;
@@ -1121,16 +1123,16 @@ void PrintConfigDef::init_fff_params()
// Add the machine jerk limits for XYZE axes (M205)
def = this->add("machine_max_jerk_" + axis.name, coFloats);
def->full_label = (boost::format("Maximum jerk %1%") % axis_upper).str();
- L("Maximum jerk X");
- L("Maximum jerk Y");
- L("Maximum jerk Z");
- L("Maximum jerk E");
+ (void)L("Maximum jerk X");
+ (void)L("Maximum jerk Y");
+ (void)L("Maximum jerk Z");
+ (void)L("Maximum jerk E");
def->category = L("Machine limits");
def->tooltip = (boost::format("Maximum jerk of the %1% axis") % axis_upper).str();
- L("Maximum jerk of the X axis");
- L("Maximum jerk of the Y axis");
- L("Maximum jerk of the Z axis");
- L("Maximum jerk of the E axis");
+ (void)L("Maximum jerk of the X axis");
+ (void)L("Maximum jerk of the Y axis");
+ (void)L("Maximum jerk of the Z axis");
+ (void)L("Maximum jerk of the E axis");
def->sidetext = L("mm/s");
def->min = 0;
def->width = machine_limits_opt_width;
@@ -1784,7 +1786,8 @@ void PrintConfigDef::init_fff_params()
def = this->add("start_filament_gcode", coStrings);
def->label = L("Start G-code");
- def->tooltip = L("This start procedure is inserted at the beginning, after any printer start gcode. "
+ def->tooltip = L("This start procedure is inserted at the beginning, after any printer start gcode (and "
+ "after any toolchange to this filament in case of multi-material printers). "
"This is used to override settings for a specific filament. If Slic3r detects "
"M104, M109, M140 or M190 in your custom codes, such commands will "
"not be prepended automatically so you're free to customize the order "
@@ -2039,9 +2042,10 @@ void PrintConfigDef::init_fff_params()
def = this->add("toolchange_gcode", coString);
def->label = L("Tool change G-code");
- def->tooltip = L("This custom code is inserted right before every extruder change. "
- "Note that you can use placeholder variables for all Slic3r settings as well "
- "as [previous_extruder] and [next_extruder].");
+ def->tooltip = L("This custom code is inserted at every extruder change. If you don't leave this empty, you are "
+ "expected to take care of the toolchange yourself - PrusaSlicer will not output any other G-code to "
+ "change the filament. You can use placeholder variables for all Slic3r settings as well as [previous_extruder] "
+ "and [next_extruder], so e.g. the standard toolchange command can be scripted as T[next_extruder].");
def->multiline = true;
def->full_width = true;
def->height = 5;
@@ -2228,7 +2232,6 @@ void PrintConfigDef::init_fff_params()
void PrintConfigDef::init_sla_params()
{
- t_optiondef_map &Options = this->options;
ConfigOptionDef* def;
// SLA Printer settings
@@ -2504,6 +2507,19 @@ void PrintConfigDef::init_sla_params()
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(1.0));
+
+ def = this->add("support_base_safety_distance", coFloat);
+ def->label = L("Support base safety distance");
+ def->category = L("Supports");
+ def->tooltip = L(
+ "The minimum distance of the pillar base from the model in mm. "
+ "Makes sense in zero elevation mode where a gap according "
+ "to this parameter is inserted between the model and the pad.");
+ def->sidetext = L("mm");
+ def->min = 0;
+ def->max = 10;
+ def->mode = comExpert;
+ def->set_default_value(new ConfigOptionFloat(1));
def = this->add("support_critical_angle", coFloat);
def->label = L("Critical angle");
@@ -2537,7 +2553,9 @@ void PrintConfigDef::init_sla_params()
def = this->add("support_object_elevation", coFloat);
def->label = L("Object elevation");
def->category = L("Supports");
- def->tooltip = L("How much the supports should lift up the supported object.");
+ def->tooltip = L("How much the supports should lift up the supported object. "
+ "If this value is zero, the bottom of the model geometry "
+ "will be considered as part of the pad.");
def->sidetext = L("mm");
def->min = 0;
def->max = 150; // This is the max height of print on SL1
@@ -2623,6 +2641,47 @@ void PrintConfigDef::init_sla_params()
def->max = 90;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(45.0));
+
+ def = this->add("pad_object_gap", coFloat);
+ def->label = L("Pad object gap");
+ def->category = L("Pad");
+ def->tooltip = L("The gap between the object bottom and the generated "
+ "pad in zero elevation mode.");
+ def->sidetext = L("mm");
+ def->min = 0;
+ def->max = 10;
+ def->mode = comExpert;
+ def->set_default_value(new ConfigOptionFloat(1));
+
+ def = this->add("pad_object_connector_stride", coFloat);
+ def->label = L("Pad object connector stride");
+ def->category = L("Pad");
+ def->tooltip = L("Distance between two connector sticks between "
+ "the object pad and the generated pad.");
+ def->sidetext = L("mm");
+ def->min = 0;
+ def->mode = comExpert;
+ def->set_default_value(new ConfigOptionFloat(10));
+
+ def = this->add("pad_object_connector_width", coFloat);
+ def->label = L("Pad object connector width");
+ def->category = L("Pad");
+ def->tooltip = L("The width of the connectors sticks which connect the "
+ "object pad and the generated pad.");
+ def->sidetext = L("mm");
+ def->min = 0;
+ def->mode = comExpert;
+ def->set_default_value(new ConfigOptionFloat(0.5));
+
+ def = this->add("pad_object_connector_penetration", coFloat);
+ def->label = L("Pad object connector penetration");
+ def->category = L("Pad");
+ def->tooltip = L(
+ "How much should the tiny connectors penetrate into the model body.");
+ def->sidetext = L("mm");
+ def->min = 0;
+ def->mode = comExpert;
+ def->set_default_value(new ConfigOptionFloat(0.3));
}
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
@@ -3194,3 +3253,7 @@ void DynamicPrintAndCLIConfig::handle_legacy(t_config_option_key &opt_key, std::
}
}
+
+#include <cereal/types/polymorphic.hpp>
+CEREAL_REGISTER_TYPE(Slic3r::DynamicPrintConfig)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::DynamicConfig, Slic3r::DynamicPrintConfig)
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 248b89e32..5731bef00 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -983,6 +983,9 @@ public:
// The height of the pillar base cone in mm.
ConfigOptionFloat support_base_height /*= 1.0*/;
+
+ // The minimum distance of the pillar base from the model in mm.
+ ConfigOptionFloat support_base_safety_distance; /*= 1.0*/;
// The default angle for connecting support sticks and junctions.
ConfigOptionFloat support_critical_angle /*= 45*/;
@@ -996,7 +999,7 @@ public:
// The elevation in Z direction upwards. This is the space between the pad
// and the model object's bounding box bottom. Units in mm.
ConfigOptionFloat support_object_elevation /*= 5.0*/;
-
+
/////// Following options influence automatic support points placement:
ConfigOptionInt support_points_density_relative;
ConfigOptionFloat support_points_minimal_distance;
@@ -1021,6 +1024,26 @@ public:
// The slope of the pad wall...
ConfigOptionFloat pad_wall_slope;
+
+ // /////////////////////////////////////////////////////////////////////////
+ // Zero elevation mode parameters:
+ // - The object pad will be derived from the the model geometry.
+ // - There will be a gap between the object pad and the generated pad
+ // according to the support_base_safety_distance parameter.
+ // - The two pads will be connected with tiny connector sticks
+ // /////////////////////////////////////////////////////////////////////////
+
+ // This is the gap between the object bottom and the generated pad
+ ConfigOptionFloat pad_object_gap;
+
+ // How far to place the connector sticks on the object pad perimeter
+ ConfigOptionFloat pad_object_connector_stride;
+
+ // The width of the connectors sticks
+ ConfigOptionFloat pad_object_connector_width;
+
+ // How much should the tiny connectors penetrate into the model body
+ ConfigOptionFloat pad_object_connector_penetration;
protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
@@ -1038,6 +1061,7 @@ protected:
OPT_PTR(support_pillar_widening_factor);
OPT_PTR(support_base_diameter);
OPT_PTR(support_base_height);
+ OPT_PTR(support_base_safety_distance);
OPT_PTR(support_critical_angle);
OPT_PTR(support_max_bridge_length);
OPT_PTR(support_max_pillar_link_distance);
@@ -1050,6 +1074,10 @@ protected:
OPT_PTR(pad_max_merge_distance);
OPT_PTR(pad_edge_radius);
OPT_PTR(pad_wall_slope);
+ OPT_PTR(pad_object_gap);
+ OPT_PTR(pad_object_connector_stride);
+ OPT_PTR(pad_object_connector_width);
+ OPT_PTR(pad_object_connector_penetration);
}
};
@@ -1190,6 +1218,8 @@ private:
this->options.insert(cli_actions_config_def.options.begin(), cli_actions_config_def.options.end());
this->options.insert(cli_transform_config_def.options.begin(), cli_transform_config_def.options.end());
this->options.insert(cli_misc_config_def.options.begin(), cli_misc_config_def.options.end());
+ for (const auto &kvp : this->options)
+ this->by_serialization_key_ordinal[kvp.second.serialization_key_ordinal] = &kvp.second;
}
// Do not release the default values, they are handled by print_config_def & cli_actions_config_def / cli_transform_config_def / cli_misc_config_def.
~PrintAndCLIConfigDef() { this->options.clear(); }
@@ -1199,4 +1229,38 @@ private:
} // namespace Slic3r
+// Serialization through the Cereal library
+namespace cereal {
+ // Let cereal know that there are load / save non-member functions declared for DynamicPrintConfig, ignore serialize / load / save from parent class DynamicConfig.
+ template <class Archive> struct specialize<Archive, Slic3r::DynamicPrintConfig, cereal::specialization::non_member_load_save> {};
+
+ template<class Archive> void load(Archive& archive, Slic3r::DynamicPrintConfig &config)
+ {
+ size_t cnt;
+ archive(cnt);
+ config.clear();
+ for (size_t i = 0; i < cnt; ++ i) {
+ size_t serialization_key_ordinal;
+ archive(serialization_key_ordinal);
+ assert(serialization_key_ordinal > 0);
+ auto it = Slic3r::print_config_def.by_serialization_key_ordinal.find(serialization_key_ordinal);
+ assert(it != Slic3r::print_config_def.by_serialization_key_ordinal.end());
+ config.set_key_value(it->second->opt_key, it->second->load_option_from_archive(archive));
+ }
+ }
+
+ template<class Archive> void save(Archive& archive, const Slic3r::DynamicPrintConfig &config)
+ {
+ size_t cnt = config.size();
+ archive(cnt);
+ for (auto it = config.cbegin(); it != config.cend(); ++it) {
+ const Slic3r::ConfigOptionDef* optdef = Slic3r::print_config_def.get(it->first);
+ assert(optdef != nullptr);
+ assert(optdef->serialization_key_ordinal > 0);
+ archive(optdef->serialization_key_ordinal);
+ optdef->save_option_to_archive(archive, it->second.get());
+ }
+ }
+}
+
#endif
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index d99aceabf..37cf0cccc 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -18,7 +18,7 @@
#include <Shiny/Shiny.h>
-//! macro used to mark string used at localization,
+//! macro used to mark string used at localization,
//! return same string
#define L(s) Slic3r::I18N::translate(s)
@@ -49,7 +49,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta
{
// Translate meshes so that our toolpath generation algorithms work with smaller
// XY coordinates; this translation is an optimization and not strictly required.
- // A cloned mesh will be aligned to 0 before slicing in _slice_region() since we
+ // A cloned mesh will be aligned to 0 before slicing in slice_region() since we
// 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).
@@ -430,7 +430,7 @@ SupportLayer* PrintObject::add_support_layer(int id, coordf_t height, coordf_t p
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)
+SupportLayerPtrs::const_iterator PrintObject::insert_support_layer(SupportLayerPtrs::const_iterator pos, size_t 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));
}
@@ -590,7 +590,12 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
bool PrintObject::invalidate_all_steps()
{
- return Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
+ // First call the "invalidate" functions, which may cancel background processing.
+ bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
+ // Then reset some of the depending values.
+ this->m_slicing_params.valid = false;
+ this->region_volumes.clear();
+ return result;
}
bool PrintObject::has_support_material() const
@@ -620,7 +625,7 @@ void PrintObject::detect_surfaces_type()
// should be visible.
bool interface_shells = m_config.interface_shells.value;
- for (int idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
+ for (size_t idx_region = 0; idx_region < this->region_volumes.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 : m_layers)
@@ -806,8 +811,6 @@ void PrintObject::process_external_surfaces()
BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..." << log_memory_info();
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) {
- const PrintRegion &region = *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<size_t>(0, m_layers.size()),
@@ -1028,7 +1031,6 @@ void PrintObject::discover_vertical_shells()
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)m_layers.size()) {
- Layer &neighbor_layer = *m_layers[n];
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[n];
if (hole_first) {
hole_first = false;
@@ -1354,10 +1356,12 @@ PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObject
return config;
}
-PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders)
+PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders)
{
PrintRegionConfig config = default_region_config;
normalize_and_apply_config(config, volume.get_object()->config);
+ if (layer_range_config != nullptr)
+ normalize_and_apply_config(config, *layer_range_config);
normalize_and_apply_config(config, volume.config);
if (! volume.material_id().empty())
normalize_and_apply_config(config, volume.material()->config);
@@ -1375,28 +1379,37 @@ void PrintObject::update_slicing_parameters()
this->print()->config(), m_config, unscale<double>(this->size(2)), this->object_extruders());
}
-SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z)
+SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
{
- PrintConfig print_config;
- PrintObjectConfig object_config;
- PrintRegionConfig default_region_config;
- print_config .apply(full_config, true);
- object_config.apply(full_config, true);
- default_region_config.apply(full_config, true);
- size_t num_extruders = print_config.nozzle_diameter.size();
- object_config = object_config_from_model_object(object_config, model_object, num_extruders);
-
- std::vector<unsigned int> object_extruders;
- for (const ModelVolume *model_volume : model_object.volumes)
- if (model_volume->is_model_part())
- PrintRegion::collect_object_printing_extruders(
- print_config,
- region_config_from_model_volume(default_region_config, *model_volume, num_extruders),
- object_extruders);
+ PrintConfig print_config;
+ PrintObjectConfig object_config;
+ PrintRegionConfig default_region_config;
+ print_config.apply(full_config, true);
+ object_config.apply(full_config, true);
+ default_region_config.apply(full_config, true);
+ size_t num_extruders = print_config.nozzle_diameter.size();
+ object_config = object_config_from_model_object(object_config, model_object, num_extruders);
+
+ std::vector<unsigned int> object_extruders;
+ for (const ModelVolume* model_volume : model_object.volumes)
+ if (model_volume->is_model_part()) {
+ PrintRegion::collect_object_printing_extruders(
+ print_config,
+ region_config_from_model_volume(default_region_config, nullptr, *model_volume, num_extruders),
+ object_extruders);
+ for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range_and_config : model_object.layer_config_ranges)
+ if (range_and_config.second.has("perimeter_extruder") ||
+ range_and_config.second.has("infill_extruder") ||
+ range_and_config.second.has("solid_infill_extruder"))
+ PrintRegion::collect_object_printing_extruders(
+ print_config,
+ region_config_from_model_volume(default_region_config, &range_and_config.second, *model_volume, num_extruders),
+ object_extruders);
+ }
sort_remove_duplicates(object_extruders);
if (object_max_z <= 0.f)
- object_max_z = model_object.raw_bounding_box().size().z();
+ object_max_z = (float)model_object.raw_bounding_box().size().z();
return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
}
@@ -1430,12 +1443,12 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
layer_height_profile.clear();
if (layer_height_profile.empty()) {
- if (0)
+ if (0)
// if (this->layer_height_profile.empty())
- layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_height_ranges, model_object.volumes);
+ layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
else
- layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_height_ranges);
- updated = true;
+ layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); // #ys_FIXME_experiment
+ updated = true;
}
return updated;
}
@@ -1489,22 +1502,28 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
}
// Count model parts and modifier meshes, check whether the model parts are of the same region.
- int single_volume_region = -2; // not set yet
+ int all_volumes_single_region = -2; // not set yet
+ bool has_z_ranges = false;
size_t num_volumes = 0;
size_t num_modifiers = 0;
- std::vector<int> map_volume_to_region(this->model_object()->volumes.size());
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
- for (int volume_id : this->region_volumes[region_id]) {
+ int last_volume_id = -1;
+ for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
+ const int volume_id = volume_and_range.second;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_model_part()) {
- map_volume_to_region[volume_id] = region_id;
- if (single_volume_region == -2)
- // first model volume met
- single_volume_region = region_id;
- else if (single_volume_region != region_id)
- // multiple volumes met and they are not equal
- single_volume_region = -1;
- ++ num_volumes;
+ if (last_volume_id == volume_id) {
+ has_z_ranges = true;
+ } else {
+ last_volume_id = volume_id;
+ if (all_volumes_single_region == -2)
+ // first model volume met
+ all_volumes_single_region = region_id;
+ else if (all_volumes_single_region != region_id)
+ // multiple volumes met and they are not equal
+ all_volumes_single_region = -1;
+ ++ num_volumes;
+ }
} else if (model_volume->is_modifier())
++ num_modifiers;
}
@@ -1514,13 +1533,13 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
// Slice all non-modifier volumes.
bool clipped = false;
bool upscaled = false;
- if (! m_config.clip_multipart_objects.value || single_volume_region >= 0) {
+ if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) {
// Cheap path: Slice regions without mutual clipping.
// The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
// slicing in parallel
- std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false);
+ std::vector<ExPolygons> expolygons_by_layer = this->slice_region(region_id, slice_zs);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start";
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
@@ -1541,15 +1560,29 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
};
std::vector<SlicedVolume> sliced_volumes;
sliced_volumes.reserve(num_volumes);
- for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
- for (int volume_id : this->region_volumes[region_id]) {
+ for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
+ const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
+ for (size_t i = 0; i < volumes_and_ranges.size(); ) {
+ int volume_id = volumes_and_ranges[i].second;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_model_part()) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id;
+ // Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
+ std::vector<t_layer_height_range> ranges;
+ ranges.emplace_back(volumes_and_ranges[i].first);
+ size_t j = i + 1;
+ for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
+ if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON)
+ ranges.back().second = volumes_and_ranges[j].first.second;
+ else
+ ranges.emplace_back(volumes_and_ranges[j].first);
// slicing in parallel
- sliced_volumes.emplace_back(volume_id, map_volume_to_region[volume_id], this->_slice_volume(slice_zs, *model_volume));
- }
+ sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, *model_volume));
+ i = j;
+ } else
+ ++ i;
}
+ }
// Second clip the volumes in the order they are presented at the user interface.
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - start";
tbb::parallel_for(
@@ -1603,7 +1636,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
// slicing in parallel
- std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, true);
+ std::vector<ExPolygons> expolygons_by_layer = this->slice_modifiers(region_id, slice_zs);
m_print->throw_if_canceled();
if (expolygons_by_layer.empty())
continue;
@@ -1619,7 +1652,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
Layer *layer = m_layers[layer_id];
LayerRegion *layerm = layer->m_regions[region_id];
LayerRegion *other_layerm = layer->m_regions[other_region_id];
- if (layerm == nullptr || other_layerm == nullptr)
+ if (layerm == nullptr || other_layerm == nullptr || other_layerm->slices.empty() || expolygons_by_layer[layer_id].empty())
continue;
Polygons other_slices = to_polygons(other_layerm->slices);
ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
@@ -1752,46 +1785,127 @@ end:
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end";
}
-std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
+// To be used only if there are no layer span specific configurations applied, which would lead to z ranges being generated for this region.
+std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z) const
{
- std::vector<const ModelVolume*> volumes;
+ std::vector<const ModelVolume*> volumes;
if (region_id < this->region_volumes.size()) {
- for (int volume_id : this->region_volumes[region_id]) {
- const ModelVolume *volume = this->model_object()->volumes[volume_id];
- if (modifier ? volume->is_modifier() : volume->is_model_part())
- volumes.emplace_back(volume);
- }
+ for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
+ const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second];
+ if (volume->is_model_part())
+ volumes.emplace_back(volume);
+ }
}
- return this->_slice_volumes(z, volumes);
+ return this->slice_volumes(z, volumes);
}
-std::vector<ExPolygons> PrintObject::slice_support_enforcers() const
+// Z ranges are not applicable to modifier meshes, therefore a sinle volume will be found in volume_and_range at most once.
+std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std::vector<float> &slice_zs) const
{
- std::vector<const ModelVolume*> volumes;
- for (const ModelVolume *volume : this->model_object()->volumes)
- if (volume->is_support_enforcer())
- volumes.emplace_back(volume);
- std::vector<float> zs;
- zs.reserve(this->layers().size());
- for (const Layer *l : this->layers())
- zs.emplace_back((float)l->slice_z);
- return this->_slice_volumes(zs, volumes);
+ std::vector<ExPolygons> out;
+ if (region_id < this->region_volumes.size())
+ {
+ std::vector<std::vector<t_layer_height_range>> volume_ranges;
+ const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
+ volume_ranges.reserve(volumes_and_ranges.size());
+ for (size_t i = 0; i < volumes_and_ranges.size(); ) {
+ int volume_id = volumes_and_ranges[i].second;
+ const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
+ if (model_volume->is_modifier()) {
+ std::vector<t_layer_height_range> ranges;
+ ranges.emplace_back(volumes_and_ranges[i].first);
+ size_t j = i + 1;
+ for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j) {
+ if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON)
+ ranges.back().second = volumes_and_ranges[j].first.second;
+ else
+ ranges.emplace_back(volumes_and_ranges[j].first);
+ }
+ volume_ranges.emplace_back(std::move(ranges));
+ i = j;
+ } else
+ ++ i;
+ }
+
+ if (! volume_ranges.empty())
+ {
+ bool equal_ranges = true;
+ for (size_t i = 1; i < volume_ranges.size(); ++ i) {
+ assert(! volume_ranges[i].empty());
+ if (volume_ranges.front() != volume_ranges[i]) {
+ equal_ranges = false;
+ break;
+ }
+ }
+
+ if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX)) {
+ // No modifier in this region was split to layer spans.
+ std::vector<const ModelVolume*> volumes;
+ for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
+ const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second];
+ if (volume->is_modifier())
+ volumes.emplace_back(volume);
+ }
+ out = this->slice_volumes(slice_zs, volumes);
+ } else {
+ // Some modifier in this region was split to layer spans.
+ std::vector<char> merge;
+ for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
+ const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
+ for (size_t i = 0; i < volumes_and_ranges.size(); ) {
+ int volume_id = volumes_and_ranges[i].second;
+ const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
+ if (model_volume->is_modifier()) {
+ BOOST_LOG_TRIVIAL(debug) << "Slicing modifiers - volume " << volume_id;
+ // Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
+ std::vector<t_layer_height_range> ranges;
+ ranges.emplace_back(volumes_and_ranges[i].first);
+ size_t j = i + 1;
+ for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
+ ranges.emplace_back(volumes_and_ranges[j].first);
+ // slicing in parallel
+ std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, *model_volume);
+ if (out.empty()) {
+ out = std::move(this_slices);
+ merge.assign(out.size(), false);
+ } else {
+ for (size_t i = 0; i < out.size(); ++ i)
+ if (! this_slices[i].empty())
+ if (! out[i].empty()) {
+ append(out[i], this_slices[i]);
+ merge[i] = true;
+ } else
+ out[i] = std::move(this_slices[i]);
+ }
+ i = j;
+ } else
+ ++ i;
+ }
+ }
+ for (size_t i = 0; i < merge.size(); ++ i)
+ if (merge[i])
+ out[i] = union_ex(out[i]);
+ }
+ }
+ }
+
+ return out;
}
-std::vector<ExPolygons> PrintObject::slice_support_blockers() const
+std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType &model_volume_type) const
{
std::vector<const ModelVolume*> volumes;
for (const ModelVolume *volume : this->model_object()->volumes)
- if (volume->is_support_blocker())
+ if (volume->type() == model_volume_type)
volumes.emplace_back(volume);
std::vector<float> zs;
zs.reserve(this->layers().size());
for (const Layer *l : this->layers())
zs.emplace_back((float)l->slice_z);
- return this->_slice_volumes(zs, volumes);
+ return this->slice_volumes(zs, volumes);
}
-std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const
+std::vector<ExPolygons> PrintObject::slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const
{
std::vector<ExPolygons> layers;
if (! volumes.empty()) {
@@ -1828,34 +1942,71 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
return layers;
}
-std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z, const ModelVolume &volume) const
+std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const ModelVolume &volume) const
{
std::vector<ExPolygons> layers;
- // Compose mesh.
- //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
- TriangleMesh mesh(volume.mesh());
- mesh.transform(volume.get_matrix(), true);
- if (mesh.repaired) {
- //FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
- stl_check_facets_exact(&mesh.stl);
+ if (! z.empty()) {
+ // Compose mesh.
+ //FIXME better to split the mesh into separate shells, perform slicing over each shell separately and then to use a Boolean operation to merge them.
+ TriangleMesh mesh(volume.mesh());
+ mesh.transform(volume.get_matrix(), true);
+ if (mesh.repaired) {
+ //FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
+ stl_check_facets_exact(&mesh.stl);
+ }
+ if (mesh.stl.stats.number_of_facets > 0) {
+ mesh.transform(m_trafo, true);
+ // apply XY shift
+ mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
+ // perform actual slicing
+ TriangleMeshSlicer mslicer;
+ const Print *print = this->print();
+ auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
+ // TriangleMeshSlicer needs the shared vertices.
+ mesh.require_shared_vertices();
+ mslicer.init(&mesh, callback);
+ mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
+ m_print->throw_if_canceled();
+ }
}
- if (mesh.stl.stats.number_of_facets > 0) {
- mesh.transform(m_trafo, true);
- // apply XY shift
- mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
- // perform actual slicing
- TriangleMeshSlicer mslicer;
- const Print *print = this->print();
- auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
- // TriangleMeshSlicer needs the shared vertices.
- mesh.require_shared_vertices();
- mslicer.init(&mesh, callback);
- mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
- m_print->throw_if_canceled();
- }
return layers;
}
+// Filter the zs not inside the ranges. The ranges are closed at the botton and open at the top, they are sorted lexicographically and non overlapping.
+std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, const ModelVolume &volume) const
+{
+ std::vector<ExPolygons> out;
+ if (! z.empty() && ! ranges.empty()) {
+ if (ranges.size() == 1 && z.front() >= ranges.front().first && z.back() < ranges.front().second) {
+ // All layers fit into a single range.
+ out = this->slice_volume(z, volume);
+ } else {
+ std::vector<float> z_filtered;
+ std::vector<std::pair<size_t, size_t>> n_filtered;
+ z_filtered.reserve(z.size());
+ n_filtered.reserve(2 * ranges.size());
+ size_t i = 0;
+ for (const t_layer_height_range &range : ranges) {
+ for (; i < z.size() && z[i] < range.first; ++ i) ;
+ size_t first = i;
+ for (; i < z.size() && z[i] < range.second; ++ i)
+ z_filtered.emplace_back(z[i]);
+ if (i > first)
+ n_filtered.emplace_back(std::make_pair(first, i));
+ }
+ if (! n_filtered.empty()) {
+ std::vector<ExPolygons> layers = this->slice_volume(z_filtered, volume);
+ out.assign(z.size(), ExPolygons());
+ i = 0;
+ for (const std::pair<size_t, size_t> &span : n_filtered)
+ for (size_t j = span.first; j < span.second; ++ j)
+ out[j] = std::move(layers[i ++]);
+ }
+ }
+ }
+ return out;
+}
+
std::string PrintObject::_fix_slicing_errors()
{
// Collect layers with slicing errors.
@@ -2119,7 +2270,7 @@ void PrintObject::clip_fill_surfaces()
//Should the pw not be half of the current value?
float pw = FLT_MAX;
for (const LayerRegion *layerm : layer->m_regions)
- pw = std::min<float>(pw, layerm->flow(frPerimeter).scaled_width());
+ pw = std::min(pw, (float)layerm->flow(frPerimeter).scaled_width());
// Append such thick perimeters to the areas that need support
polygons_append(overhangs, offset2(perimeters, -pw, +pw));
}
@@ -2154,7 +2305,7 @@ void PrintObject::discover_horizontal_shells()
BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()";
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
- for (int i = 0; i < int(m_layers.size()); ++ i) {
+ for (size_t i = 0; i < m_layers.size(); ++ i) {
m_print->throw_if_canceled();
LayerRegion *layerm = m_layers[i]->regions()[region_id];
const PrintRegionConfig &region_config = layerm->region()->config();
@@ -2171,7 +2322,7 @@ void PrintObject::discover_horizontal_shells()
if (region_config.ensure_vertical_shell_thickness.value)
continue;
- for (int idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) {
+ for (size_t idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) {
m_print->throw_if_canceled();
SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge;
// Find slices of current type for current layer.
@@ -2200,7 +2351,7 @@ void PrintObject::discover_horizontal_shells()
// Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == S_TYPE_TOP) ? 'top' : 'bottom';
size_t solid_layers = (type == stTop) ? region_config.top_solid_layers.value : region_config.bottom_solid_layers.value;
- for (int n = (type == stTop) ? i-1 : i+1; std::abs(n - i) < solid_layers; (type == stTop) ? -- n : ++ n) {
+ for (int n = (type == stTop) ? i-1 : i+1; std::abs(n - (int)i) < solid_layers; (type == stTop) ? -- n : ++ n) {
if (n < 0 || n >= int(m_layers.size()))
continue;
// Slic3r::debugf " looking for neighbors on layer %d...\n", $n;
@@ -2345,7 +2496,7 @@ void PrintObject::combine_infill()
// Work on each region separately.
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const PrintRegion *region = this->print()->regions()[region_id];
- const int every = region->config().infill_every_layers.value;
+ const size_t every = region->config().infill_every_layers.value;
if (every < 2 || region->config().fill_density == 0.)
continue;
// Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp
index 0a2537b91..91b7dfe5d 100644
--- a/src/libslic3r/SLA/SLAAutoSupports.cpp
+++ b/src/libslic3r/SLA/SLAAutoSupports.cpp
@@ -59,8 +59,6 @@ SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3
void SLAAutoSupports::project_onto_mesh(std::vector<sla::SupportPoint>& points) const
{
// The function makes sure that all the points are really exactly placed on the mesh.
- igl::Hit hit_up{0, 0, 0.f, 0.f, 0.f};
- igl::Hit hit_down{0, 0, 0.f, 0.f, 0.f};
// Use a reasonable granularity to account for the worker thread synchronization cost.
tbb::parallel_for(tbb::blocked_range<size_t>(0, points.size(), 64),
@@ -140,7 +138,6 @@ static std::vector<SLAAutoSupports::MyLayer> make_layers(
SLAAutoSupports::MyLayer &layer_above = layers[layer_id];
SLAAutoSupports::MyLayer &layer_below = layers[layer_id - 1];
//FIXME WTF?
- const float height = (layer_id>2 ? heights[layer_id-3] : heights[0]-(heights[1]-heights[0]));
const float layer_height = (layer_id!=0 ? heights[layer_id]-heights[layer_id-1] : heights[0]);
const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports
const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle)));
@@ -212,7 +209,7 @@ void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::
for (Structure &top : layer_top->islands)
for (Structure::Link &bottom_link : top.islands_below) {
Structure &bottom = *bottom_link.island;
- float centroids_dist = (bottom.centroid - top.centroid).norm();
+ //float centroids_dist = (bottom.centroid - top.centroid).norm();
// Penalization resulting from centroid offset:
// bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area));
float &support_force = support_force_bottom[&bottom - layer_bottom->islands.data()];
@@ -239,7 +236,7 @@ void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::
// s.supports_force_inherited /= std::max(1.f, (layer_height / 0.3f) * e_area / s.area);
s.supports_force_inherited /= std::max(1.f, 0.17f * (s.overhangs_area) / s.area);
- float force_deficit = s.support_force_deficit(m_config.tear_pressure());
+ //float force_deficit = s.support_force_deficit(m_config.tear_pressure());
if (s.islands_below.empty()) { // completely new island - needs support no doubt
uniformly_cover({ *s.polygon }, s, point_grid, true);
} else if (! s.dangling_areas.empty()) {
@@ -380,7 +377,7 @@ static inline std::vector<Vec2f> poisson_disk_from_samples(const std::vector<Vec
{
typename Cells::iterator last_cell_id_it;
Vec2i last_cell_id(-1, -1);
- for (int i = 0; i < raw_samples_sorted.size(); ++ i) {
+ for (size_t i = 0; i < raw_samples_sorted.size(); ++ i) {
const RawSample &sample = raw_samples_sorted[i];
if (sample.cell_id == last_cell_id) {
// This sample is in the same cell as the previous, so just increase the count. Cells are
diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp
index 04cbd7824..4ce84ba02 100644
--- a/src/libslic3r/SLA/SLABasePool.cpp
+++ b/src/libslic3r/SLA/SLABasePool.cpp
@@ -8,9 +8,9 @@
#include "MTUtils.hpp"
// For debugging:
-//#include <fstream>
-//#include <libnest2d/tools/benchmark.h>
-//#include "SVG.hpp"
+// #include <fstream>
+// #include <libnest2d/tools/benchmark.h>
+// #include "SVG.hpp"
namespace Slic3r { namespace sla {
@@ -184,9 +184,10 @@ Contour3D walls(const Polygon& lower, const Polygon& upper,
}
/// Offsetting with clipper and smoothing the edges into a curvature.
-void offset(ExPolygon& sh, coord_t distance) {
+void offset(ExPolygon& sh, coord_t distance, bool edgerounding = true) {
using ClipperLib::ClipperOffset;
using ClipperLib::jtRound;
+ using ClipperLib::jtMiter;
using ClipperLib::etClosedPolygon;
using ClipperLib::Paths;
using ClipperLib::Path;
@@ -203,11 +204,13 @@ void offset(ExPolygon& sh, coord_t distance) {
return;
}
+ auto jointype = edgerounding? jtRound : jtMiter;
+
ClipperOffset offs;
offs.ArcTolerance = scaled<double>(0.01);
Paths result;
- offs.AddPath(ctour, jtRound, etClosedPolygon);
- offs.AddPaths(holes, jtRound, etClosedPolygon);
+ offs.AddPath(ctour, jointype, etClosedPolygon);
+ offs.AddPaths(holes, jointype, etClosedPolygon);
offs.Execute(result, static_cast<double>(distance));
// Offsetting reverts the orientation and also removes the last vertex
@@ -237,6 +240,50 @@ void offset(ExPolygon& sh, coord_t distance) {
}
}
+void offset(Polygon &sh, coord_t distance, bool edgerounding = true)
+{
+ using ClipperLib::ClipperOffset;
+ using ClipperLib::jtRound;
+ using ClipperLib::jtMiter;
+ using ClipperLib::etClosedPolygon;
+ using ClipperLib::Paths;
+ using ClipperLib::Path;
+
+ auto &&ctour = Slic3rMultiPoint_to_ClipperPath(sh);
+
+ // If the input is not at least a triangle, we can not do this algorithm
+ if (ctour.size() < 3) {
+ BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!";
+ return;
+ }
+
+ ClipperOffset offs;
+ offs.ArcTolerance = 0.01 * scaled(1.);
+ Paths result;
+ offs.AddPath(ctour, edgerounding ? jtRound : jtMiter, etClosedPolygon);
+ offs.Execute(result, static_cast<double>(distance));
+
+ // Offsetting reverts the orientation and also removes the last vertex
+ // so boost will not have a closed polygon.
+
+ bool found_the_contour = false;
+ for (auto &r : result) {
+ if (ClipperLib::Orientation(r)) {
+ // We don't like if the offsetting generates more than one contour
+ // but throwing would be an overkill. Instead, we should warn the
+ // caller about the inability to create correct geometries
+ if (!found_the_contour) {
+ auto rr = ClipperPath_to_Slic3rPolygon(r);
+ sh.points.swap(rr.points);
+ found_the_contour = true;
+ } else {
+ BOOST_LOG_TRIVIAL(warning)
+ << "Warning: offsetting result is invalid!";
+ }
+ }
+ }
+}
+
/// Unification of polygons (with clipper) preserving holes as well.
ExPolygons unify(const ExPolygons& shapes) {
using ClipperLib::ptSubject;
@@ -307,6 +354,116 @@ ExPolygons unify(const ExPolygons& shapes) {
return retv;
}
+Polygons unify(const Polygons& shapes) {
+ using ClipperLib::ptSubject;
+
+ bool closed = true;
+ bool valid = true;
+
+ ClipperLib::Clipper clipper;
+
+ for(auto& path : shapes) {
+ auto clipperpath = Slic3rMultiPoint_to_ClipperPath(path);
+
+ if(!clipperpath.empty())
+ valid &= clipper.AddPath(clipperpath, ptSubject, closed);
+ }
+
+ if(!valid) BOOST_LOG_TRIVIAL(warning) << "Unification of invalid shapes!";
+
+ ClipperLib::Paths result;
+ clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero);
+
+ Polygons ret;
+ for (ClipperLib::Path &p : result) {
+ Polygon pp = ClipperPath_to_Slic3rPolygon(p);
+ if (!pp.is_clockwise()) ret.emplace_back(std::move(pp));
+ }
+
+ return ret;
+}
+
+// Function to cut tiny connector cavities for a given polygon. The input poly
+// will be offsetted by "padding" and small rectangle shaped cavities will be
+// inserted along the perimeter in every "stride" distance. The stick rectangles
+// will have a with about "stick_width". The input dimensions are in world
+// measure, not the scaled clipper units.
+void breakstick_holes(ExPolygon& poly,
+ double padding,
+ double stride,
+ double stick_width,
+ double penetration)
+{
+ // SVG svg("bridgestick_plate.svg");
+ // svg.draw(poly);
+
+ auto transf = [stick_width, penetration, padding, stride](Points &pts) {
+ // The connector stick will be a small rectangle with dimensions
+ // stick_width x (penetration + padding) to have some penetration
+ // into the input polygon.
+
+ Points out;
+ out.reserve(2 * pts.size()); // output polygon points
+
+ // stick bottom and right edge dimensions
+ double sbottom = scaled(stick_width);
+ double sright = scaled(penetration + padding);
+
+ // scaled stride distance
+ double sstride = scaled(stride);
+ double t = 0;
+
+ // process pairs of vertices as an edge, start with the last and
+ // first point
+ for (size_t i = pts.size() - 1, j = 0; j < pts.size(); i = j, ++j) {
+ // Get vertices and the direction vectors
+ const Point &a = pts[i], &b = pts[j];
+ Vec2d dir = b.cast<double>() - a.cast<double>();
+ double nrm = dir.norm();
+ dir /= nrm;
+ Vec2d dirp(-dir(Y), dir(X));
+
+ // Insert start point
+ out.emplace_back(a);
+
+ // dodge the start point, do not make sticks on the joins
+ while (t < sbottom) t += sbottom;
+ double tend = nrm - sbottom;
+
+ while (t < tend) { // insert the stick on the polygon perimeter
+
+ // calculate the stick rectangle vertices and insert them
+ // into the output.
+ Point p1 = a + (t * dir).cast<coord_t>();
+ Point p2 = p1 + (sright * dirp).cast<coord_t>();
+ Point p3 = p2 + (sbottom * dir).cast<coord_t>();
+ Point p4 = p3 + (sright * -dirp).cast<coord_t>();
+ out.insert(out.end(), {p1, p2, p3, p4});
+
+ // continue along the perimeter
+ t += sstride;
+ }
+
+ t = t - nrm;
+
+ // Insert edge endpoint
+ out.emplace_back(b);
+ }
+
+ // move the new points
+ out.shrink_to_fit();
+ pts.swap(out);
+ };
+
+ if(stride > 0.0 && stick_width > 0.0 && padding > 0.0) {
+ transf(poly.contour.points);
+ for (auto &h : poly.holes) transf(h.points);
+ }
+
+ // svg.draw(poly);
+ // svg.Close();
+}
+
/// This method will create a rounded edge around a flat polygon in 3d space.
/// 'base_plate' parameter is the target plate.
/// 'radius' is the radius of the edges.
@@ -426,41 +583,38 @@ inline Point centroid(Points& pp) {
return c;
}
-inline Point centroid(const ExPolygon& poly) {
- return poly.contour.centroid();
+inline Point centroid(const Polygon& poly) {
+ return poly.centroid();
}
/// A fake concave hull that is constructed by connecting separate shapes
/// with explicit bridges. Bridges are generated from each shape's centroid
/// to the center of the "scene" which is the centroid calculated from the shape
/// centroids (a star is created...)
-ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
- ThrowOnCancel throw_on_cancel = [](){})
+Polygons concave_hull(const Polygons& polys, double max_dist_mm = 50,
+ ThrowOnCancel throw_on_cancel = [](){})
{
namespace bgi = boost::geometry::index;
- using SpatElement = std::pair<BoundingBox, unsigned>;
+ using SpatElement = std::pair<Point, unsigned>;
using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
- if(polys.empty()) return ExPolygons();
+ if(polys.empty()) return Polygons();
+
+ const double max_dist = scaled(max_dist_mm);
- ExPolygons punion = unify(polys); // could be redundant
+ Polygons punion = unify(polys); // could be redundant
if(punion.size() == 1) return punion;
// We get the centroids of all the islands in the 2D slice
Points centroids; centroids.reserve(punion.size());
std::transform(punion.begin(), punion.end(), std::back_inserter(centroids),
- [](const ExPolygon& poly) { return centroid(poly); });
-
-
- SpatIndex boxindex; unsigned idx = 0;
- std::for_each(punion.begin(), punion.end(),
- [&boxindex, &idx](const ExPolygon& expo) {
- BoundingBox bb(expo);
- boxindex.insert(std::make_pair(bb, idx++));
- });
-
+ [](const Polygon& poly) { return centroid(poly); });
+ SpatIndex ctrindex;
+ unsigned idx = 0;
+ for(const Point &ct : centroids) ctrindex.insert(std::make_pair(ct, idx++));
+
// Centroid of the centroids of islands. This is where the additional
// connector sticks are routed.
Point cc = centroid(centroids);
@@ -470,25 +624,32 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
idx = 0;
std::transform(centroids.begin(), centroids.end(),
std::back_inserter(punion),
- [&punion, &boxindex, cc, max_dist_mm, &idx, throw_on_cancel]
+ [&centroids, &ctrindex, cc, max_dist, &idx, throw_on_cancel]
(const Point& c)
{
throw_on_cancel();
double dx = x(c) - x(cc), dy = y(c) - y(cc);
double l = std::sqrt(dx * dx + dy * dy);
double nx = dx / l, ny = dy / l;
- double max_dist = scaled<double>(max_dist_mm);
-
- ExPolygon& expo = punion[idx++];
- BoundingBox querybb(expo);
-
- querybb.offset(max_dist);
+
+ Point& ct = centroids[idx];
+
std::vector<SpatElement> result;
- boxindex.query(bgi::intersects(querybb), std::back_inserter(result));
- if(result.size() <= 1) return ExPolygon();
+ ctrindex.query(bgi::nearest(ct, 2), std::back_inserter(result));
- ExPolygon r;
- auto& ctour = r.contour.points;
+ double dist = max_dist;
+ for (const SpatElement &el : result)
+ if (el.second != idx) {
+ dist = Line(el.first, ct).length();
+ break;
+ }
+
+ idx++;
+
+ if (dist >= max_dist) return Polygon();
+
+ Polygon r;
+ auto& ctour = r.points;
ctour.reserve(3);
ctour.emplace_back(cc);
@@ -507,24 +668,20 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
return punion;
}
-void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
- float layerh, ThrowOnCancel thrfn)
+void base_plate(const TriangleMesh & mesh,
+ ExPolygons & output,
+ const std::vector<float> &heights,
+ ThrowOnCancel thrfn)
{
- TriangleMesh m = mesh;
- m.require_shared_vertices(); // TriangleMeshSlicer needs this
- TriangleMeshSlicer slicer(&m);
-
- auto bb = mesh.bounding_box();
- float gnd = float(bb.min(Z));
- std::vector<float> heights = {float(bb.min(Z))};
- for(float hi = gnd + layerh; hi <= gnd + h; hi += layerh)
- heights.emplace_back(hi);
-
- std::vector<ExPolygons> out; out.reserve(size_t(std::ceil(h/layerh)));
+ if (mesh.empty()) return;
+ // m.require_shared_vertices(); // TriangleMeshSlicer needs this
+ TriangleMeshSlicer slicer(&mesh);
+
+ std::vector<ExPolygons> out; out.reserve(heights.size());
slicer.slice(heights, 0.f, &out, thrfn);
-
+
size_t count = 0; for(auto& o : out) count += o.size();
-
+
// Now we have to unify all slice layers which can be an expensive operation
// so we will try to simplify the polygons
ExPolygons tmp; tmp.reserve(count);
@@ -533,16 +690,33 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
auto&& exss = e.simplify(scaled<double>(0.1));
for(ExPolygon& ep : exss) tmp.emplace_back(std::move(ep));
}
-
+
ExPolygons utmp = unify(tmp);
-
+
for(auto& o : utmp) {
auto&& smp = o.simplify(scaled<double>(0.1));
output.insert(output.end(), smp.begin(), smp.end());
}
}
-Contour3D create_base_pool(const ExPolygons &ground_layer,
+void base_plate(const TriangleMesh &mesh,
+ ExPolygons & output,
+ float h,
+ float layerh,
+ ThrowOnCancel thrfn)
+{
+ auto bb = mesh.bounding_box();
+ float gnd = float(bb.min(Z));
+ std::vector<float> heights = {float(bb.min(Z))};
+
+ for(float hi = gnd + layerh; hi <= gnd + h; hi += layerh)
+ heights.emplace_back(hi);
+
+ base_plate(mesh, output, heights, thrfn);
+}
+
+Contour3D create_base_pool(const Polygons &ground_layer,
+ const ExPolygons &obj_self_pad = {},
const PoolConfig& cfg = PoolConfig())
{
// for debugging:
@@ -557,7 +731,7 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
// serve as the bottom plate of the pad. We will offset this concave hull
// and then offset back the result with clipper with rounding edges ON. This
// trick will create a nice rounded pad shape.
- ExPolygons concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel);
+ Polygons concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel);
const double thickness = cfg.min_wall_thickness_mm;
const double wingheight = cfg.min_wall_height_mm;
@@ -577,42 +751,37 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
Contour3D pool;
- for(ExPolygon& concaveh : concavehs) {
- if(concaveh.contour.points.empty()) return pool;
-
- // Get rid of any holes in the concave hull output.
- concaveh.holes.clear();
+ for(Polygon& concaveh : concavehs) {
+ if(concaveh.points.empty()) return pool;
// Here lies the trick that does the smoothing only with clipper offset
// calls. The offset is configured to round edges. Inner edges will
// be rounded because we offset twice: ones to get the outer (top) plate
// and again to get the inner (bottom) plate
auto outer_base = concaveh;
- outer_base.holes.clear();
offset(outer_base, s_safety_dist + s_wingdist + s_thickness);
- ExPolygon bottom_poly = outer_base;
- bottom_poly.holes.clear();
+ ExPolygon bottom_poly; bottom_poly.contour = outer_base;
offset(bottom_poly, -s_bottom_offs);
// Punching a hole in the top plate for the cavity
ExPolygon top_poly;
ExPolygon middle_base;
ExPolygon inner_base;
- top_poly.contour = outer_base.contour;
+ top_poly.contour = outer_base;
if(wingheight > 0) {
- inner_base = outer_base;
+ inner_base.contour = outer_base;
offset(inner_base, -(s_thickness + s_wingdist + s_eradius));
- middle_base = outer_base;
+ middle_base.contour = outer_base;
offset(middle_base, -s_thickness);
top_poly.holes.emplace_back(middle_base.contour);
auto& tph = top_poly.holes.back().points;
std::reverse(tph.begin(), tph.end());
}
- ExPolygon ob = outer_base; double wh = 0;
+ ExPolygon ob; ob.contour = outer_base; double wh = 0;
// now we will calculate the angle or portion of the circle from
// pi/2 that will connect perfectly with the bottom plate.
@@ -659,6 +828,7 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
if(wingheight > 0) {
// Generate the smoothed edge geometry
wh = 0;
+ ob = middle_base;
if(s_eradius) pool.merge(round_edges(middle_base,
r,
phi - 90, // from tangent lines
@@ -673,11 +843,59 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
wh, -wingdist, thrcl));
}
- // Now we need to triangulate the top and bottom plates as well as the
- // cavity bottom plate which is the same as the bottom plate but it is
- // elevated by the thickness.
+ if (cfg.embed_object) {
+ ExPolygons bttms = diff_ex(to_polygons(bottom_poly),
+ to_polygons(obj_self_pad));
+
+ assert(!bttms.empty());
+
+ std::sort(bttms.begin(), bttms.end(),
+ [](const ExPolygon& e1, const ExPolygon& e2) {
+ return e1.contour.area() > e2.contour.area();
+ });
+
+ if(wingheight > 0) inner_base.holes = bttms.front().holes;
+ else top_poly.holes = bttms.front().holes;
+
+ auto straight_walls =
+ [&pool](const Polygon &cntr, coord_t z_low, coord_t z_high) {
+
+ auto lines = cntr.lines();
+
+ for (auto &l : lines) {
+ auto s = coord_t(pool.points.size());
+ auto& pts = pool.points;
+ pts.emplace_back(unscale(l.a.x(), l.a.y(), z_low));
+ pts.emplace_back(unscale(l.b.x(), l.b.y(), z_low));
+ pts.emplace_back(unscale(l.a.x(), l.a.y(), z_high));
+ pts.emplace_back(unscale(l.b.x(), l.b.y(), z_high));
+
+ pool.indices.emplace_back(s, s + 1, s + 3);
+ pool.indices.emplace_back(s, s + 3, s + 2);
+ }
+ };
+
+ coord_t z_lo = -scaled(fullheight), z_hi = -scaled(wingheight);
+ for (ExPolygon &ep : bttms) {
+ pool.merge(triangulate_expolygon_3d(ep, -fullheight, true));
+ for (auto &h : ep.holes) straight_walls(h, z_lo, z_hi);
+ }
+
+ // Skip the outer contour, triangulate the holes
+ for (auto it = std::next(bttms.begin()); it != bttms.end(); ++it) {
+ pool.merge(triangulate_expolygon_3d(*it, -wingheight));
+ straight_walls(it->contour, z_lo, z_hi);
+ }
+
+ } else {
+ // Now we need to triangulate the top and bottom plates as well as
+ // the cavity bottom plate which is the same as the bottom plate
+ // but it is elevated by the thickness.
+
+ pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true));
+ }
+
pool.merge(triangulate_expolygon_3d(top_poly));
- pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true));
if(wingheight > 0)
pool.merge(triangulate_expolygon_3d(inner_base, -wingheight));
@@ -687,8 +905,8 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
return pool;
}
-void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
- const PoolConfig& cfg)
+void create_base_pool(const Polygons &ground_layer, TriangleMesh& out,
+ const ExPolygons &holes, const PoolConfig& cfg)
{
@@ -698,7 +916,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
// std::fstream fout("pad_debug.obj", std::fstream::out);
// if(fout.good()) pool.to_obj(fout);
- out.merge(mesh(create_base_pool(ground_layer, cfg)));
+ out.merge(mesh(create_base_pool(ground_layer, holes, cfg)));
}
}
diff --git a/src/libslic3r/SLA/SLABasePool.hpp b/src/libslic3r/SLA/SLABasePool.hpp
index 3c88e58c8..67b9ccdcb 100644
--- a/src/libslic3r/SLA/SLABasePool.hpp
+++ b/src/libslic3r/SLA/SLABasePool.hpp
@@ -8,7 +8,9 @@
namespace Slic3r {
class ExPolygon;
+class Polygon;
using ExPolygons = std::vector<ExPolygon>;
+using Polygons = std::vector<Polygon>;
class TriangleMesh;
@@ -19,16 +21,40 @@ using ThrowOnCancel = std::function<void(void)>;
/// Calculate the polygon representing the silhouette from the specified height
void base_plate(const TriangleMesh& mesh, // input mesh
ExPolygons& output, // Output will be merged with
- float zlevel = 0.1f, // Plate creation level
+ float samplingheight = 0.1f, // The height range to sample
float layerheight = 0.05f, // The sampling height
ThrowOnCancel thrfn = [](){}); // Will be called frequently
+void base_plate(const TriangleMesh& mesh, // input mesh
+ ExPolygons& output, // Output will be merged with
+ const std::vector<float>&, // Exact Z levels to sample
+ ThrowOnCancel thrfn = [](){}); // Will be called frequently
+
+// Function to cut tiny connector cavities for a given polygon. The input poly
+// will be offsetted by "padding" and small rectangle shaped cavities will be
+// inserted along the perimeter in every "stride" distance. The stick rectangles
+// will have a with about "stick_width". The input dimensions are in world
+// measure, not the scaled clipper units.
+void breakstick_holes(ExPolygon &poly,
+ double padding,
+ double stride,
+ double stick_width,
+ double penetration = 0.0);
+
struct PoolConfig {
double min_wall_thickness_mm = 2;
double min_wall_height_mm = 5;
double max_merge_distance_mm = 50;
double edge_radius_mm = 1;
double wall_slope = std::atan(1.0); // Universal constant for Pi/4
+ struct EmbedObject {
+ double object_gap_mm = 0.5;
+ double stick_stride_mm = 10;
+ double stick_width_mm = 0.3;
+ double stick_penetration_mm = 0.1;
+ bool enabled = false;
+ operator bool() const { return enabled; }
+ } embed_object;
ThrowOnCancel throw_on_cancel = [](){};
@@ -42,15 +68,12 @@ struct PoolConfig {
};
/// Calculate the pool for the mesh for SLA printing
-void create_base_pool(const ExPolygons& base_plate,
+void create_base_pool(const Polygons& base_plate,
TriangleMesh& output_mesh,
+ const ExPolygons& holes,
const PoolConfig& = PoolConfig());
-/// TODO: Currently the base plate of the pool will have half the height of the
-/// whole pool. So the carved out space has also half the height. This is not
-/// a particularly elegant solution, the thickness should be exactly
-/// min_wall_thickness and it should be corrected in the future. This method
-/// will return the correct value for further processing.
+/// Returns the elevation needed for compensating the pad.
inline double get_pad_elevation(const PoolConfig& cfg) {
return cfg.min_wall_thickness_mm;
}
diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp
index 855802759..874388e05 100644
--- a/src/libslic3r/SLA/SLACommon.hpp
+++ b/src/libslic3r/SLA/SLACommon.hpp
@@ -43,6 +43,8 @@ struct SupportPoint {
bool operator==(const SupportPoint& sp) const { return (pos==sp.pos) && head_front_radius==sp.head_front_radius && is_new_island==sp.is_new_island; }
bool operator!=(const SupportPoint& sp) const { return !(sp == (*this)); }
+
+ template<class Archive> void serialize(Archive &ar) { ar(pos, head_front_radius, is_new_island); }
};
/// An index-triangle structure for libIGL functions. Also serves as an
@@ -60,7 +62,7 @@ class EigenMesh3D {
Eigen::MatrixXd m_V;
Eigen::MatrixXi m_F;
- double m_ground_level = 0;
+ double m_ground_level = 0, m_gnd_offset = 0;
std::unique_ptr<AABBImpl> m_aabb;
public:
@@ -71,7 +73,9 @@ public:
~EigenMesh3D();
- inline double ground_level() const { return m_ground_level; }
+ inline double ground_level() const { return m_ground_level + m_gnd_offset; }
+ inline void ground_level_offset(double o) { m_gnd_offset = o; }
+ inline double ground_level_offset() const { return m_gnd_offset; }
inline const Eigen::MatrixXd& V() const { return m_V; }
inline const Eigen::MatrixXi& F() const { return m_F; }
@@ -149,6 +153,12 @@ public:
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
double squared_distance(const Vec3d& p, int& i, Vec3d& c) const;
+ inline double squared_distance(const Vec3d &p) const
+ {
+ int i;
+ Vec3d c;
+ return squared_distance(p, i, c);
+ }
};
diff --git a/src/libslic3r/SLA/SLASpatIndex.hpp b/src/libslic3r/SLA/SLASpatIndex.hpp
index e5fbfa7d4..90dcdc362 100644
--- a/src/libslic3r/SLA/SLASpatIndex.hpp
+++ b/src/libslic3r/SLA/SLASpatIndex.hpp
@@ -7,13 +7,15 @@
#include <Eigen/Geometry>
+#include <libslic3r/BoundingBox.hpp>
+
namespace Slic3r {
namespace sla {
typedef Eigen::Matrix<double, 3, 1, Eigen::DontAlign> Vec3d;
-using SpatElement = std::pair<Vec3d, unsigned>;
+using PointIndexEl = std::pair<Vec3d, unsigned>;
-class SpatIndex {
+class PointIndex {
class Impl;
// We use Pimpl because it takes a long time to compile boost headers which
@@ -21,30 +23,67 @@ class SpatIndex {
std::unique_ptr<Impl> m_impl;
public:
- SpatIndex();
- ~SpatIndex();
+ PointIndex();
+ ~PointIndex();
- SpatIndex(const SpatIndex&);
- SpatIndex(SpatIndex&&);
- SpatIndex& operator=(const SpatIndex&);
- SpatIndex& operator=(SpatIndex&&);
+ PointIndex(const PointIndex&);
+ PointIndex(PointIndex&&);
+ PointIndex& operator=(const PointIndex&);
+ PointIndex& operator=(PointIndex&&);
- void insert(const SpatElement&);
- bool remove(const SpatElement&);
+ void insert(const PointIndexEl&);
+ bool remove(const PointIndexEl&);
inline void insert(const Vec3d& v, unsigned idx)
{
insert(std::make_pair(v, unsigned(idx)));
}
- std::vector<SpatElement> query(std::function<bool(const SpatElement&)>);
- std::vector<SpatElement> nearest(const Vec3d&, unsigned k);
+ std::vector<PointIndexEl> query(std::function<bool(const PointIndexEl&)>);
+ std::vector<PointIndexEl> nearest(const Vec3d&, unsigned k);
// For testing
size_t size() const;
bool empty() const { return size() == 0; }
- void foreach(std::function<void(const SpatElement& el)> fn);
+ void foreach(std::function<void(const PointIndexEl& el)> fn);
+};
+
+using BoxIndexEl = std::pair<Slic3r::BoundingBox, unsigned>;
+
+class BoxIndex {
+ class Impl;
+
+ // We use Pimpl because it takes a long time to compile boost headers which
+ // is the engine of this class. We include it only in the cpp file.
+ std::unique_ptr<Impl> m_impl;
+public:
+
+ BoxIndex();
+ ~BoxIndex();
+
+ BoxIndex(const BoxIndex&);
+ BoxIndex(BoxIndex&&);
+ BoxIndex& operator=(const BoxIndex&);
+ BoxIndex& operator=(BoxIndex&&);
+
+ void insert(const BoxIndexEl&);
+ inline void insert(const BoundingBox& bb, unsigned idx)
+ {
+ insert(std::make_pair(bb, unsigned(idx)));
+ }
+
+ bool remove(const BoxIndexEl&);
+
+ enum QueryType { qtIntersects, qtWithin };
+
+ std::vector<BoxIndexEl> query(const BoundingBox&, QueryType qt);
+
+ // For testing
+ size_t size() const;
+ bool empty() const { return size() == 0; }
+
+ void foreach(std::function<void(const BoxIndexEl& el)> fn);
};
}
diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp
index ae033c62f..4751dad6c 100644
--- a/src/libslic3r/SLA/SLASupportTree.cpp
+++ b/src/libslic3r/SLA/SLASupportTree.cpp
@@ -9,10 +9,12 @@
#include "SLASpatIndex.hpp"
#include "SLABasePool.hpp"
+#include <libslic3r/MTUtils.hpp>
#include <libslic3r/ClipperUtils.hpp>
#include <libslic3r/Model.hpp>
#include <libnest2d/optimizers/nlopt/genetic.hpp>
+#include <libnest2d/optimizers/nlopt/subplex.hpp>
#include <boost/log/trivial.hpp>
#include <tbb/parallel_for.h>
#include <libslic3r/I18N.hpp>
@@ -413,7 +415,7 @@ struct Pillar {
assert(steps > 0);
height = jp(Z) - endp(Z);
- if(height > 0) { // Endpoint is below the starting point
+ if(height > EPSILON) { // Endpoint is below the starting point
// We just create a bridge geometry with the pillar parameters and
// move the data.
@@ -528,6 +530,7 @@ struct CompactBridge {
const Vec3d& ep,
const Vec3d& n,
double r,
+ bool endball = true,
size_t steps = 45)
{
Vec3d startp = sp + r * n;
@@ -541,12 +544,14 @@ struct CompactBridge {
double fa = 2*PI/steps;
auto upperball = sphere(r, Portion{PI / 2 - fa, PI}, fa);
for(auto& p : upperball.points) p += startp;
-
- auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa);
- for(auto& p : lowerball.points) p += endp;
-
+
+ if(endball) {
+ auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa);
+ for(auto& p : lowerball.points) p += endp;
+ mesh.merge(lowerball);
+ }
+
mesh.merge(upperball);
- mesh.merge(lowerball);
}
};
@@ -556,28 +561,111 @@ struct Pad {
PoolConfig cfg;
double zlevel = 0;
- Pad() {}
+ Pad() = default;
- Pad(const TriangleMesh& object_support_mesh,
- const ExPolygons& baseplate,
+ Pad(const TriangleMesh& support_mesh,
+ const ExPolygons& modelbase,
double ground_level,
const PoolConfig& pcfg) :
cfg(pcfg),
- zlevel(ground_level +
- (sla::get_pad_fullheight(pcfg) - sla::get_pad_elevation(pcfg)) )
+ zlevel(ground_level +
+ sla::get_pad_fullheight(pcfg) -
+ sla::get_pad_elevation(pcfg))
{
- ExPolygons basep;
- cfg.throw_on_cancel();
+ Polygons basep;
+ auto &thr = cfg.throw_on_cancel;
+
+ thr();
+
+ // Get a sample for the pad from the support mesh
+ {
+ ExPolygons platetmp;
- // The 0.1f is the layer height with which the mesh is sampled and then
- // the layers are unified into one vector of polygons.
- base_plate(object_support_mesh, basep,
- float(cfg.min_wall_height_mm + cfg.min_wall_thickness_mm),
- 0.1f, pcfg.throw_on_cancel);
+ float zstart = float(zlevel);
+ float zend = zstart + float(get_pad_fullheight(pcfg) + EPSILON);
- for(auto& bp : baseplate) basep.emplace_back(bp);
+ base_plate(support_mesh, platetmp, grid(zstart, zend, 0.1f), thr);
+
+ // We don't need no... holes control...
+ for (const ExPolygon &bp : platetmp)
+ basep.emplace_back(std::move(bp.contour));
+ }
+
+ if(pcfg.embed_object) {
+
+ // If the zero elevation mode is ON, we need to process the model
+ // base silhouette. Create the offsetted version and punch the
+ // breaksticks across its perimeter.
+
+ ExPolygons modelbase_offs = modelbase;
+
+ if (pcfg.embed_object.object_gap_mm > 0.0)
+ modelbase_offs
+ = offset_ex(modelbase_offs,
+ float(scaled(pcfg.embed_object.object_gap_mm)));
+
+ // Create a spatial index of the support silhouette polygons.
+ // This will be used to check for intersections with the model
+ // silhouette polygons. If there is no intersection, then a certain
+ // part of the pad is redundant as it does not host any supports.
+ BoxIndex bindex;
+ {
+ unsigned idx = 0;
+ for(auto &bp : basep) {
+ auto bb = bp.bounding_box();
+ bb.offset(float(scaled(pcfg.min_wall_thickness_mm)));
+ bindex.insert(bb, idx++);
+ }
+ }
+
+ // Punching the breaksticks across the offsetted polygon perimeters
+ ExPolygons pad_stickholes; pad_stickholes.reserve(modelbase.size());
+ for(auto& poly : modelbase_offs) {
+
+ std::vector<BoxIndexEl> qres =
+ bindex.query(poly.contour.bounding_box(),
+ BoxIndex::qtIntersects);
+
+ if (!qres.empty()) {
+
+ // The model silhouette polygon 'poly' HAS an intersection
+ // with the support silhouettes. Include this polygon
+ // in the pad holes with the breaksticks and merge the
+ // original (offsetted) version with the rest of the pad
+ // base plate.
+
+ basep.emplace_back(poly.contour);
+
+ // The holes of 'poly' will become positive parts of the
+ // pad, so they has to be checked for intersections as well
+ // and erased if there is no intersection with the supports
+ auto it = poly.holes.begin();
+ while(it != poly.holes.end()) {
+ if (bindex.query(it->bounding_box(),
+ BoxIndex::qtIntersects).empty())
+ it = poly.holes.erase(it);
+ else
+ ++it;
+ }
+
+ // Punch the breaksticks
+ sla::breakstick_holes(
+ poly,
+ pcfg.embed_object.object_gap_mm, // padding
+ pcfg.embed_object.stick_stride_mm,
+ pcfg.embed_object.stick_width_mm,
+ pcfg.embed_object.stick_penetration_mm);
+
+ pad_stickholes.emplace_back(poly);
+ }
+ }
+
+ create_base_pool(basep, tmesh, pad_stickholes, cfg);
+ } else {
+ for (const ExPolygon &bp : modelbase) basep.emplace_back(bp.contour);
+ create_base_pool(basep, tmesh, {}, cfg);
+ }
- create_base_pool(basep, tmesh, cfg);
tmesh.translate(0, 0, float(zlevel));
}
@@ -603,7 +691,7 @@ inline Vec2d to_vec2(const Vec3d& v3) {
return {v3(X), v3(Y)};
}
-bool operator==(const SpatElement& e1, const SpatElement& e2) {
+bool operator==(const PointIndexEl& e1, const PointIndexEl& e2) {
return e1.second == e2.second;
}
@@ -620,7 +708,7 @@ ClusteredPoints cluster(const PointSet& points,
ClusteredPoints cluster(
const std::vector<unsigned>& indices,
std::function<Vec3d(unsigned)> pointfn,
- std::function<bool(const SpatElement&, const SpatElement&)> predicate,
+ std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
unsigned max_points);
// This class will hold the support tree meshes with some additional bookkeeping
@@ -763,9 +851,9 @@ public:
}
const Pad& create_pad(const TriangleMesh& object_supports,
- const ExPolygons& baseplate,
+ const ExPolygons& modelbase,
const PoolConfig& cfg) {
- m_pad = Pad(object_supports, baseplate, ground_level, cfg);
+ m_pad = Pad(object_supports, modelbase, ground_level, cfg);
return m_pad;
}
@@ -808,7 +896,6 @@ public:
merged.merge(bs.mesh);
}
-
if(m_ctl.stopcondition()) {
// In case of failure we have to return an empty mesh
meshcache = TriangleMesh();
@@ -819,7 +906,7 @@ public:
// The mesh will be passed by const-pointer to TriangleMeshSlicer,
// which will need this.
- meshcache.require_shared_vertices();
+ if (!meshcache.empty()) meshcache.require_shared_vertices();
// TODO: Is this necessary?
//meshcache.repair();
@@ -947,7 +1034,7 @@ class SLASupportTree::Algorithm {
ThrowOnCancel m_thr;
// A spatial index to easily find strong pillars to connect to.
- SpatIndex m_pillar_index;
+ PointIndex m_pillar_index;
inline double ray_mesh_intersect(const Vec3d& s,
const Vec3d& dir)
@@ -1149,7 +1236,7 @@ class SLASupportTree::Algorithm {
auto hr = m.query_ray_hit(p + sd*dir, dir);
if(ins_check && hr.is_inside()) {
- if(hr.distance() > r + sd) hits[i] = HitResult(0.0);
+ if(hr.distance() > 2 * r + sd) hits[i] = HitResult(0.0);
else {
// re-cast the ray from the outside of the object
auto hr2 =
@@ -1264,9 +1351,12 @@ class SLASupportTree::Algorithm {
// For connecting a head to a nearby pillar.
bool connect_to_nearpillar(const Head& head, long nearpillar_id) {
-
- auto nearpillar = [this, nearpillar_id]() { return m_result.pillar(nearpillar_id); };
- if(nearpillar().bridges > m_cfg.max_bridges_on_pillar) return false;
+
+ auto nearpillar = [this, nearpillar_id]() {
+ return m_result.pillar(nearpillar_id);
+ };
+
+ if (nearpillar().bridges > m_cfg.max_bridges_on_pillar) return false;
Vec3d headjp = head.junction_point();
Vec3d nearjp_u = nearpillar().startpoint();
@@ -1337,7 +1427,7 @@ class SLASupportTree::Algorithm {
}
bool search_pillar_and_connect(const Head& head) {
- SpatIndex spindex = m_pillar_index;
+ PointIndex spindex = m_pillar_index;
long nearest_id = -1;
@@ -1369,6 +1459,120 @@ class SLASupportTree::Algorithm {
return nearest_id >= 0;
}
+
+ // This is a proxy function for pillar creation which will mind the gap
+ // between the pad and the model bottom in zero elevation mode.
+ void create_ground_pillar(const Vec3d &jp,
+ const Vec3d &sourcedir,
+ double radius,
+ int head_id = -1)
+ {
+ // People were killed for this number (seriously)
+ static const double SQR2 = std::sqrt(2.0);
+ static const Vec3d DOWN = {0.0, 0.0, -1.0};
+
+ double gndlvl = m_result.ground_level;
+ Vec3d endp = {jp(X), jp(Y), gndlvl};
+ double sd = m_cfg.pillar_base_safety_distance_mm;
+ int pillar_id = -1;
+ double min_dist = sd + m_cfg.base_radius_mm + EPSILON;
+ double dist = 0;
+ bool can_add_base = true;
+ bool normal_mode = true;
+
+ if (m_cfg.object_elevation_mm < EPSILON
+ && (dist = std::sqrt(m_mesh.squared_distance(endp))) < min_dist) {
+ // Get the distance from the mesh. This can be later optimized
+ // to get the distance in 2D plane because we are dealing with
+ // the ground level only.
+
+ normal_mode = false;
+ double mv = min_dist - dist;
+ double azimuth = std::atan2(sourcedir(Y), sourcedir(X));
+ double sinpolar = std::sin(PI - m_cfg.bridge_slope);
+ double cospolar = std::cos(PI - m_cfg.bridge_slope);
+ double cosazm = std::cos(azimuth);
+ double sinazm = std::sin(azimuth);
+
+ auto dir = Vec3d(cosazm * sinpolar, sinazm * sinpolar, cospolar)
+ .normalized();
+
+ using namespace libnest2d::opt;
+ StopCriteria scr;
+ scr.stop_score = min_dist;
+ SubplexOptimizer solver(scr);
+
+ auto result = solver.optimize_max(
+ [this, dir, jp, gndlvl](double mv) {
+ Vec3d endp = jp + SQR2 * mv * dir;
+ endp(Z) = gndlvl;
+ return std::sqrt(m_mesh.squared_distance(endp));
+ },
+ initvals(mv), bound(0.0, 2 * min_dist));
+
+ mv = std::get<0>(result.optimum);
+ endp = jp + SQR2 * mv * dir;
+ Vec3d pgnd = {endp(X), endp(Y), gndlvl};
+ can_add_base = result.score > min_dist;
+
+ double gnd_offs = m_mesh.ground_level_offset();
+ auto abort_in_shame =
+ [gnd_offs, &normal_mode, &can_add_base, &endp, jp, gndlvl]()
+ {
+ normal_mode = true;
+ can_add_base = false; // Nothing left to do, hope for the best
+ endp = {jp(X), jp(Y), gndlvl - gnd_offs };
+ };
+
+ // We have to check if the bridge is feasible.
+ if (bridge_mesh_intersect(jp, dir, radius) < (endp - jp).norm())
+ abort_in_shame();
+ else {
+ // If the new endpoint is below ground, do not make a pillar
+ if (endp(Z) < gndlvl)
+ endp = endp - SQR2 * (gndlvl - endp(Z)) * dir; // back off
+ else {
+
+ auto hit = bridge_mesh_intersect(endp, DOWN, radius);
+ if (!std::isinf(hit.distance())) abort_in_shame();
+
+ Pillar &plr = m_result.add_pillar(endp, pgnd, radius);
+
+ if (can_add_base)
+ plr.add_base(m_cfg.base_height_mm,
+ m_cfg.base_radius_mm);
+
+ pillar_id = plr.id;
+ }
+
+ m_result.add_bridge(jp, endp, radius);
+ m_result.add_junction(endp, radius);
+
+ // Add a degenerated pillar and the bridge.
+ // The degenerate pillar will have zero length and it will
+ // prevent from queries of head_pillar() to have non-existing
+ // pillar when the head should have one.
+ if (head_id >= 0)
+ m_result.add_pillar(unsigned(head_id), jp, radius);
+ }
+ }
+
+ if (normal_mode) {
+ Pillar &plr = head_id >= 0
+ ? m_result.add_pillar(unsigned(head_id),
+ endp,
+ radius)
+ : m_result.add_pillar(jp, endp, radius);
+
+ if (can_add_base)
+ plr.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm);
+
+ pillar_id = plr.id;
+ }
+
+ if(pillar_id >= 0) // Save the pillar endpoint in the spatial index
+ m_pillar_index.insert(endp, pillar_id);
+ }
public:
@@ -1447,9 +1651,9 @@ public:
// (Quaternion::FromTwoVectors) and apply the rotation to the
// arrow head.
- double z = n(2);
- double r = 1.0; // for normalized vector
- double polar = std::acos(z / r);
+ double z = n(2);
+ double r = 1.0; // for normalized vector
+ double polar = std::acos(z / r);
double azimuth = std::atan2(n(1), n(0));
// skip if the tilt is not sane
@@ -1473,14 +1677,14 @@ public:
std::cos(polar)).normalized();
// check available distance
- double t = pinhead_mesh_intersect(
- hp, // touching point
- nn, // normal
- pin_r,
- m_cfg.head_back_radius_mm,
- w);
+ EigenMesh3D::hit_result t
+ = pinhead_mesh_intersect(hp, // touching point
+ nn, // normal
+ pin_r,
+ m_cfg.head_back_radius_mm,
+ w);
- if(t <= w) {
+ if(t.distance() <= w) {
// Let's try to optimize this angle, there might be a
// viable normal that doesn't collide with the model
@@ -1523,12 +1727,17 @@ public:
// save the verified and corrected normal
m_support_nmls.row(fidx) = nn;
- if(t > w) {
- // mark the point for needing a head.
- m_iheads.emplace_back(fidx);
- } else if( polar >= 3*PI/4 ) {
- // Headless supports do not tilt like the headed ones so
- // the normal should point almost to the ground.
+ if (t.distance() > w) {
+ // Check distance from ground, we might have zero elevation.
+ if (hp(Z) + w * nn(Z) < m_result.ground_level) {
+ m_iheadless.emplace_back(fidx);
+ } else {
+ // mark the point for needing a head.
+ m_iheads.emplace_back(fidx);
+ }
+ } else if (polar >= 3 * PI / 4) {
+ // Headless supports do not tilt like the headed ones
+ // so the normal should point almost to the ground.
m_iheadless.emplace_back(fidx);
}
}
@@ -1594,16 +1803,22 @@ public:
// from each other in the XY plane to not cross their pillar bases
// These clusters of support points will join in one pillar,
// possibly in their centroid support point.
+
auto pointfn = [this](unsigned i) {
return m_result.head(i).junction_point();
};
- auto predicate = [this](const SpatElement& e1, const SpatElement& e2) {
+
+ auto predicate = [this](const PointIndexEl &e1,
+ const PointIndexEl &e2) {
double d2d = distance(to_2d(e1.first), to_2d(e2.first));
double d3d = distance(e1.first, e2.first);
- return d2d < 2 * m_cfg.base_radius_mm &&
- d3d < m_cfg.max_bridge_length_mm;
+ return d2d < 2 * m_cfg.base_radius_mm
+ && d3d < m_cfg.max_bridge_length_mm;
};
- m_pillar_clusters = cluster(ground_head_indices, pointfn, predicate,
+
+ m_pillar_clusters = cluster(ground_head_indices,
+ pointfn,
+ predicate,
m_cfg.max_bridges_on_pillar);
}
@@ -1615,7 +1830,7 @@ public:
void routing_to_ground()
{
const double pradius = m_cfg.head_back_radius_mm;
- const double gndlvl = m_result.ground_level;
+ // const double gndlvl = m_result.ground_level;
ClusterEl cl_centroids;
cl_centroids.reserve(m_pillar_clusters.size());
@@ -1648,13 +1863,8 @@ public:
Head& h = m_result.head(hid);
h.transform();
- Vec3d p = h.junction_point(); p(Z) = gndlvl;
- auto& plr = m_result.add_pillar(hid, p, h.r_back_mm)
- .add_base(m_cfg.base_height_mm,
- m_cfg.base_radius_mm);
- // Save the pillar endpoint and the pillar id in the spatial index
- m_pillar_index.insert(plr.endpoint(), unsigned(plr.id));
+ create_ground_pillar(h.junction_point(), h.dir, h.r_back_mm, h.id);
}
// now we will go through the clusters ones again and connect the
@@ -1681,15 +1891,12 @@ public:
!search_pillar_and_connect(sidehead))
{
Vec3d pstart = sidehead.junction_point();
- Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl};
+ //Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl};
// Could not find a pillar, create one
- auto& pillar = m_result.add_pillar(unsigned(sidehead.id),
- pend, pradius)
- .add_base(m_cfg.base_height_mm,
- m_cfg.base_radius_mm);
-
- // connects to ground, eligible for bridging
- m_pillar_index.insert(pend, unsigned(pillar.id));
+ create_ground_pillar(pstart,
+ sidehead.dir,
+ pradius,
+ sidehead.id);
}
}
}
@@ -1718,12 +1925,7 @@ public:
m_result.add_bridge(hjp, endp, head.r_back_mm);
m_result.add_junction(endp, head.r_back_mm);
- auto groundp = endp;
- groundp(Z) = m_result.ground_level;
- auto& newpillar = m_result.add_pillar(endp, groundp, head.r_back_mm)
- .add_base(m_cfg.base_height_mm,
- m_cfg.base_radius_mm);
- m_pillar_index.insert(groundp, unsigned(newpillar.id));
+ this->create_ground_pillar(endp, dir, head.r_back_mm);
};
std::vector<unsigned> modelpillars;
@@ -1883,6 +2085,28 @@ public:
m_pillar_index.insert(pillar.endpoint(), pillid);
}
}
+
+ // Helper function for interconnect_pillars where pairs of already connected
+ // pillars should be checked for not to be processed again. This can be done
+ // in O(log) or even constant time with a set or an unordered set of hash
+ // values uniquely representing a pair of integers. The order of numbers
+ // within the pair should not matter, it has the same unique hash.
+ template<class I> static I pairhash(I a, I b)
+ {
+ using std::ceil; using std::log2; using std::max; using std::min;
+
+ static_assert(std::is_integral<I>::value,
+ "This function works only for integral types.");
+
+ I g = min(a, b), l = max(a, b);
+
+ auto bits_g = g ? int(ceil(log2(g))) : 0;
+
+ // Assume the hash will fit into the output variable
+ assert((l ? (ceil(log2(l))) : 0) + bits_g < int(sizeof(I) * CHAR_BIT));
+
+ return (l << bits_g) + g;
+ }
void interconnect_pillars() {
// Now comes the algorithm that connects pillars with each other.
@@ -1900,45 +2124,51 @@ public:
double min_height_ratio = 0.5;
std::set<unsigned long> pairs;
-
+
+ // A function to connect one pillar with its neighbors. THe number of
+ // neighbors is given in the configuration. This function if called
+ // for every pillar in the pillar index. A pair of pillar will not
+ // be connected multiple times this is ensured by the 'pairs' set which
+ // remembers the processed pillar pairs
auto cascadefn =
- [this, d, &pairs, min_height_ratio, H1] (const SpatElement& el)
+ [this, d, &pairs, min_height_ratio, H1] (const PointIndexEl& el)
{
- Vec3d qp = el.first;
-
- const Pillar& pillar = m_result.pillar(el.second);
+ Vec3d qp = el.first; // endpoint of the pillar
+ const Pillar& pillar = m_result.pillar(el.second); // actual pillar
+
+ // Get the max number of neighbors a pillar should connect to
unsigned neighbors = m_cfg.pillar_cascade_neighbors;
- // connections are enough for one pillar
+ // connections are already enough for the pillar
if(pillar.links >= neighbors) return;
// Query all remaining points within reach
- auto qres = m_pillar_index.query([qp, d](const SpatElement& e){
+ auto qres = m_pillar_index.query([qp, d](const PointIndexEl& e){
return distance(e.first, qp) < d;
});
// sort the result by distance (have to check if this is needed)
std::sort(qres.begin(), qres.end(),
- [qp](const SpatElement& e1, const SpatElement& e2){
+ [qp](const PointIndexEl& e1, const PointIndexEl& e2){
return distance(e1.first, qp) < distance(e2.first, qp);
});
- for(auto& re : qres) {
+ for(auto& re : qres) { // process the queried neighbors
- if(re.second == el.second) continue;
+ if(re.second == el.second) continue; // Skip self
auto a = el.second, b = re.second;
- // I hope that the area of a square is never equal to its
- // circumference
- auto hashval = 2 * (a + b) + a * b;
-
+ // Get unique hash for the given pair (order doesn't matter)
+ auto hashval = pairhash(a, b);
+
+ // Search for the pair amongst the remembered pairs
if(pairs.find(hashval) != pairs.end()) continue;
const Pillar& neighborpillar = m_result.pillars()[re.second];
- // this neighbor is occupied
+ // this neighbor is occupied, skip
if(neighborpillar.links >= neighbors) continue;
if(interconnect(pillar, neighborpillar)) {
@@ -1960,47 +2190,79 @@ public:
if(pillar.links >= neighbors) break;
}
};
-
+
+ // Run the cascade for the pillars in the index
m_pillar_index.foreach(cascadefn);
-
+
+ // We would be done here if we could allow some pillars to not be
+ // connected with any neighbors. But this might leave the support tree
+ // unprintable.
+ //
+ // The current solution is to insert additional pillars next to these
+ // lonely pillars. One or even two additional pillar might get inserted
+ // depending on the length of the lonely pillar.
+
size_t pillarcount = m_result.pillars().size();
-
+
+ // Again, go through all pillars, this time in the whole support tree
+ // not just the index.
for(size_t pid = 0; pid < pillarcount; pid++) {
auto pillar = [this, pid]() { return m_result.pillar(pid); };
-
+
+ // Decide how many additional pillars will be needed:
+
unsigned needpillars = 0;
- if(pillar().bridges > m_cfg.max_bridges_on_pillar) needpillars = 3;
- else if(pillar().links < 2 && pillar().height > H2) {
+ if (pillar().bridges > m_cfg.max_bridges_on_pillar)
+ needpillars = 3;
+ else if (pillar().links < 2 && pillar().height > H2) {
// Not enough neighbors to support this pillar
needpillars = 2 - pillar().links;
- }
- else if(pillar().links < 1 && pillar().height > H1) {
+ } else if (pillar().links < 1 && pillar().height > H1) {
// No neighbors could be found and the pillar is too long.
needpillars = 1;
}
- // Search for new pillar locations
- bool found = false;
- double alpha = 0; // goes to 2Pi
- double r = 2 * m_cfg.base_radius_mm;
- Vec3d pillarsp = pillar().startpoint();
+ // Search for new pillar locations:
+
+ bool found = false;
+ double alpha = 0; // goes to 2Pi
+ double r = 2 * m_cfg.base_radius_mm;
+ Vec3d pillarsp = pillar().startpoint();
+
+ // temp value for starting point detection
Vec3d sp(pillarsp(X), pillarsp(Y), pillarsp(Z) - r);
- std::vector<bool> tv(needpillars, false);
- std::vector<Vec3d> spts(needpillars);
- while(!found && alpha < 2*PI) {
+ // A vector of bool for placement feasbility
+ std::vector<bool> canplace(needpillars, false);
+ std::vector<Vec3d> spts(needpillars); // vector of starting points
- for(unsigned n = 0; n < needpillars; n++) {
- double a = alpha + n * PI/3;
- Vec3d s = sp;
+ double gnd = m_result.ground_level;
+ double min_dist = m_cfg.pillar_base_safety_distance_mm +
+ m_cfg.base_radius_mm + EPSILON;
+
+ while(!found && alpha < 2*PI) {
+ for (unsigned n = 0;
+ n < needpillars && (!n || canplace[n - 1]);
+ n++)
+ {
+ double a = alpha + n * PI / 3;
+ Vec3d s = sp;
s(X) += std::cos(a) * r;
s(Y) += std::sin(a) * r;
spts[n] = s;
+
+ // Check the path vertically down
auto hr = bridge_mesh_intersect(s, {0, 0, -1}, pillar().r);
- tv[n] = std::isinf(hr.distance());
+ Vec3d gndsp{s(X), s(Y), gnd};
+
+ // If the path is clear, check for pillar base collisions
+ canplace[n] = std::isinf(hr.distance()) &&
+ std::sqrt(m_mesh.squared_distance(gndsp)) >
+ min_dist;
}
- found = std::all_of(tv.begin(), tv.end(), [](bool v){return v;});
+ found = std::all_of(canplace.begin(), canplace.end(),
+ [](bool v) { return v; });
// 20 angles will be tried...
alpha += 0.1 * PI;
@@ -2010,7 +2272,7 @@ public:
newpills.reserve(needpillars);
if(found) for(unsigned n = 0; n < needpillars; n++) {
- Vec3d s = spts[n]; double gnd = m_result.ground_level;
+ Vec3d s = spts[n];
Pillar p(s, Vec3d(s(X), s(Y), gnd), pillar().r);
p.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm);
@@ -2075,9 +2337,13 @@ public:
// This is only for checking
double idist = bridge_mesh_intersect(sph, dir, R, true);
double dist = ray_mesh_intersect(sj, dir);
+ if (std::isinf(dist))
+ dist = sph(Z) - m_mesh.ground_level()
+ + m_mesh.ground_level_offset();
- if(std::isinf(idist) || std::isnan(idist) || idist < 2*R ||
- std::isinf(dist) || std::isnan(dist) || dist < 2*R) {
+ if(std::isnan(idist) || idist < 2*R ||
+ std::isnan(dist) || dist < 2*R)
+ {
BOOST_LOG_TRIVIAL(warning) << "Can not find route for headless"
<< " support stick at: "
<< sj.transpose();
@@ -2085,7 +2351,7 @@ public:
}
Vec3d ej = sj + (dist + HWIDTH_MM)* dir;
- m_result.add_compact_bridge(sp, ej, n, R);
+ m_result.add_compact_bridge(sp, ej, n, R, !std::isinf(dist));
}
}
};
@@ -2214,7 +2480,9 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
return pc == ABORT;
}
-SLASupportTree::SLASupportTree(): m_impl(new Impl()) {}
+SLASupportTree::SLASupportTree(double gnd_lvl): m_impl(new Impl()) {
+ m_impl->ground_level = gnd_lvl;
+}
const TriangleMesh &SLASupportTree::merged_mesh() const
{
@@ -2226,7 +2494,7 @@ void SLASupportTree::merged_mesh_with_pad(TriangleMesh &outmesh) const {
outmesh.merge(get_pad());
}
-SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
+std::vector<ExPolygons> SLASupportTree::slice(float layerh, float init_layerh) const
{
if(init_layerh < 0) init_layerh = layerh;
auto& stree = get();
@@ -2245,36 +2513,31 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
TriangleMesh fullmesh = m_impl->merged_mesh();
fullmesh.merge(get_pad());
- fullmesh.require_shared_vertices(); // TriangleMeshSlicer needs this
+ if (!fullmesh.empty()) fullmesh.require_shared_vertices();
TriangleMeshSlicer slicer(&fullmesh);
- SlicedSupports ret;
+ std::vector<ExPolygons> ret;
slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn);
return ret;
}
-SlicedSupports SLASupportTree::slice(const std::vector<float> &heights,
+std::vector<ExPolygons> SLASupportTree::slice(const std::vector<float> &heights,
float cr) const
{
TriangleMesh fullmesh = m_impl->merged_mesh();
fullmesh.merge(get_pad());
- fullmesh.require_shared_vertices(); // TriangleMeshSlicer needs this
+ if (!fullmesh.empty()) fullmesh.require_shared_vertices();
TriangleMeshSlicer slicer(&fullmesh);
- SlicedSupports ret;
+ std::vector<ExPolygons> ret;
slicer.slice(heights, cr, &ret, get().ctl().cancelfn);
return ret;
}
-const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate,
+const TriangleMesh &SLASupportTree::add_pad(const ExPolygons& modelbase,
const PoolConfig& pcfg) const
{
-// PoolConfig pcfg;
-// pcfg.min_wall_thickness_mm = min_wall_thickness_mm;
-// pcfg.min_wall_height_mm = min_wall_height_mm;
-// pcfg.max_merge_distance_mm = max_merge_distance_mm;
-// pcfg.edge_radius_mm = edge_radius_mm;
- return m_impl->create_pad(merged_mesh(), baseplate, pcfg).tmesh;
+ return m_impl->create_pad(merged_mesh(), modelbase, pcfg).tmesh;
}
const TriangleMesh &SLASupportTree::get_pad() const
diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp
index 66677e4d7..8602d8a46 100644
--- a/src/libslic3r/SLA/SLASupportTree.hpp
+++ b/src/libslic3r/SLA/SLASupportTree.hpp
@@ -24,10 +24,11 @@ class TriangleMesh;
class Model;
class ModelInstance;
class ModelObject;
+class Polygon;
class ExPolygon;
-using SliceLayer = std::vector<ExPolygon>;
-using SlicedSupports = std::vector<SliceLayer>;
+using Polygons = std::vector<Polygon>;
+using ExPolygons = std::vector<ExPolygon>;
namespace sla {
@@ -80,6 +81,10 @@ struct SupportConfig {
// The elevation in Z direction upwards. This is the space between the pad
// and the model object's bounding box bottom.
double object_elevation_mm = 10;
+
+ // The shortest distance between a pillar base perimeter from the model
+ // body. This is only useful when elevation is set to zero.
+ double pillar_base_safety_distance_mm = 0.5;
// /////////////////////////////////////////////////////////////////////////
// Compile time configuration values (candidates for runtime)
@@ -160,7 +165,7 @@ class SLASupportTree {
public:
- SLASupportTree();
+ SLASupportTree(double ground_level = 0.0);
SLASupportTree(const std::vector<SupportPoint>& pts,
const EigenMesh3D& em,
@@ -179,12 +184,17 @@ public:
void merged_mesh_with_pad(TriangleMesh&) const;
/// Get the sliced 2d layers of the support geometry.
- SlicedSupports slice(float layerh, float init_layerh = -1.0) const;
+ std::vector<ExPolygons> slice(float layerh, float init_layerh = -1.0) const;
- SlicedSupports slice(const std::vector<float>&, float closing_radius) const;
+ std::vector<ExPolygons> slice(const std::vector<float> &,
+ float closing_radius) const;
/// Adding the "pad" (base pool) under the supports
- const TriangleMesh& add_pad(const SliceLayer& baseplate,
+ /// modelbase will be used according to the embed_object flag in PoolConfig.
+ /// If set, the plate will interpreted as the model's intrinsic pad.
+ /// Otherwise, the modelbase will be unified with the base plate calculated
+ /// from the supports.
+ const TriangleMesh& add_pad(const ExPolygons& modelbase,
const PoolConfig& pcfg) const;
/// Get the pad geometry
diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp
index 1609b9ac4..4b6498a20 100644
--- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp
+++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp
@@ -29,69 +29,137 @@ namespace sla {
using igl::PI;
/* **************************************************************************
- * SpatIndex implementation
+ * PointIndex implementation
* ************************************************************************** */
-class SpatIndex::Impl {
+class PointIndex::Impl {
public:
- using BoostIndex = boost::geometry::index::rtree< SpatElement,
+ using BoostIndex = boost::geometry::index::rtree< PointIndexEl,
boost::geometry::index::rstar<16, 4> /* ? */ >;
BoostIndex m_store;
};
-SpatIndex::SpatIndex(): m_impl(new Impl()) {}
-SpatIndex::~SpatIndex() {}
+PointIndex::PointIndex(): m_impl(new Impl()) {}
+PointIndex::~PointIndex() {}
-SpatIndex::SpatIndex(const SpatIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
-SpatIndex::SpatIndex(SpatIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
+PointIndex::PointIndex(const PointIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
+PointIndex::PointIndex(PointIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
-SpatIndex& SpatIndex::operator=(const SpatIndex &cpy)
+PointIndex& PointIndex::operator=(const PointIndex &cpy)
{
m_impl.reset(new Impl(*cpy.m_impl));
return *this;
}
-SpatIndex& SpatIndex::operator=(SpatIndex &&cpy)
+PointIndex& PointIndex::operator=(PointIndex &&cpy)
{
m_impl.swap(cpy.m_impl);
return *this;
}
-void SpatIndex::insert(const SpatElement &el)
+void PointIndex::insert(const PointIndexEl &el)
{
m_impl->m_store.insert(el);
}
-bool SpatIndex::remove(const SpatElement& el)
+bool PointIndex::remove(const PointIndexEl& el)
{
return m_impl->m_store.remove(el) == 1;
}
-std::vector<SpatElement>
-SpatIndex::query(std::function<bool(const SpatElement &)> fn)
+std::vector<PointIndexEl>
+PointIndex::query(std::function<bool(const PointIndexEl &)> fn)
{
namespace bgi = boost::geometry::index;
- std::vector<SpatElement> ret;
+ std::vector<PointIndexEl> ret;
m_impl->m_store.query(bgi::satisfies(fn), std::back_inserter(ret));
return ret;
}
-std::vector<SpatElement> SpatIndex::nearest(const Vec3d &el, unsigned k = 1)
+std::vector<PointIndexEl> PointIndex::nearest(const Vec3d &el, unsigned k = 1)
{
namespace bgi = boost::geometry::index;
- std::vector<SpatElement> ret; ret.reserve(k);
+ std::vector<PointIndexEl> ret; ret.reserve(k);
m_impl->m_store.query(bgi::nearest(el, k), std::back_inserter(ret));
return ret;
}
-size_t SpatIndex::size() const
+size_t PointIndex::size() const
{
return m_impl->m_store.size();
}
-void SpatIndex::foreach(std::function<void (const SpatElement &)> fn)
+void PointIndex::foreach(std::function<void (const PointIndexEl &)> fn)
+{
+ for(auto& el : m_impl->m_store) fn(el);
+}
+
+/* **************************************************************************
+ * BoxIndex implementation
+ * ************************************************************************** */
+
+class BoxIndex::Impl {
+public:
+ using BoostIndex = boost::geometry::index::
+ rtree<BoxIndexEl, boost::geometry::index::rstar<16, 4> /* ? */>;
+
+ BoostIndex m_store;
+};
+
+BoxIndex::BoxIndex(): m_impl(new Impl()) {}
+BoxIndex::~BoxIndex() {}
+
+BoxIndex::BoxIndex(const BoxIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
+BoxIndex::BoxIndex(BoxIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
+
+BoxIndex& BoxIndex::operator=(const BoxIndex &cpy)
+{
+ m_impl.reset(new Impl(*cpy.m_impl));
+ return *this;
+}
+
+BoxIndex& BoxIndex::operator=(BoxIndex &&cpy)
+{
+ m_impl.swap(cpy.m_impl);
+ return *this;
+}
+
+void BoxIndex::insert(const BoxIndexEl &el)
+{
+ m_impl->m_store.insert(el);
+}
+
+bool BoxIndex::remove(const BoxIndexEl& el)
+{
+ return m_impl->m_store.remove(el) == 1;
+}
+
+std::vector<BoxIndexEl> BoxIndex::query(const BoundingBox &qrbb,
+ BoxIndex::QueryType qt)
+{
+ namespace bgi = boost::geometry::index;
+
+ std::vector<BoxIndexEl> ret; ret.reserve(m_impl->m_store.size());
+
+ switch (qt) {
+ case qtIntersects:
+ m_impl->m_store.query(bgi::intersects(qrbb), std::back_inserter(ret));
+ break;
+ case qtWithin:
+ m_impl->m_store.query(bgi::within(qrbb), std::back_inserter(ret));
+ }
+
+ return ret;
+}
+
+size_t BoxIndex::size() const
+{
+ return m_impl->m_store.size();
+}
+
+void BoxIndex::foreach(std::function<void (const BoxIndexEl &)> fn)
{
for(auto& el : m_impl->m_store) fn(el);
}
@@ -343,12 +411,14 @@ PointSet normals(const PointSet& points,
return ret;
}
namespace bgi = boost::geometry::index;
-using Index3D = bgi::rtree< SpatElement, bgi::rstar<16, 4> /* ? */ >;
+using Index3D = bgi::rtree< PointIndexEl, bgi::rstar<16, 4> /* ? */ >;
-ClusteredPoints cluster(Index3D& sindex, unsigned max_points,
- std::function<std::vector<SpatElement>(const Index3D&, const SpatElement&)> qfn)
+ClusteredPoints cluster(Index3D &sindex,
+ unsigned max_points,
+ std::function<std::vector<PointIndexEl>(
+ const Index3D &, const PointIndexEl &)> qfn)
{
- using Elems = std::vector<SpatElement>;
+ using Elems = std::vector<PointIndexEl>;
// Recursive function for visiting all the points in a given distance to
// each other
@@ -356,8 +426,8 @@ ClusteredPoints cluster(Index3D& sindex, unsigned max_points,
[&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster)
{
for(auto& p : pts) {
- std::vector<SpatElement> tmp = qfn(sindex, p);
- auto cmp = [](const SpatElement& e1, const SpatElement& e2){
+ std::vector<PointIndexEl> tmp = qfn(sindex, p);
+ auto cmp = [](const PointIndexEl& e1, const PointIndexEl& e2){
return e1.second < e2.second;
};
@@ -401,12 +471,12 @@ ClusteredPoints cluster(Index3D& sindex, unsigned max_points,
}
namespace {
-std::vector<SpatElement> distance_queryfn(const Index3D& sindex,
- const SpatElement& p,
+std::vector<PointIndexEl> distance_queryfn(const Index3D& sindex,
+ const PointIndexEl& p,
double dist,
unsigned max_points)
{
- std::vector<SpatElement> tmp; tmp.reserve(max_points);
+ std::vector<PointIndexEl> tmp; tmp.reserve(max_points);
sindex.query(
bgi::nearest(p.first, max_points),
std::back_inserter(tmp)
@@ -433,7 +503,7 @@ ClusteredPoints cluster(
for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
return cluster(sindex, max_points,
- [dist, max_points](const Index3D& sidx, const SpatElement& p)
+ [dist, max_points](const Index3D& sidx, const PointIndexEl& p)
{
return distance_queryfn(sidx, p, dist, max_points);
});
@@ -443,7 +513,7 @@ ClusteredPoints cluster(
ClusteredPoints cluster(
const std::vector<unsigned>& indices,
std::function<Vec3d(unsigned)> pointfn,
- std::function<bool(const SpatElement&, const SpatElement&)> predicate,
+ std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
unsigned max_points)
{
// A spatial index for querying the nearest points
@@ -453,10 +523,10 @@ ClusteredPoints cluster(
for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
return cluster(sindex, max_points,
- [max_points, predicate](const Index3D& sidx, const SpatElement& p)
+ [max_points, predicate](const Index3D& sidx, const PointIndexEl& p)
{
- std::vector<SpatElement> tmp; tmp.reserve(max_points);
- sidx.query(bgi::satisfies([p, predicate](const SpatElement& e){
+ std::vector<PointIndexEl> tmp; tmp.reserve(max_points);
+ sidx.query(bgi::satisfies([p, predicate](const PointIndexEl& e){
return predicate(p, e);
}), std::back_inserter(tmp));
return tmp;
@@ -473,7 +543,7 @@ ClusteredPoints cluster(const PointSet& pts, double dist, unsigned max_points)
sindex.insert(std::make_pair(Vec3d(pts.row(i)), unsigned(i)));
return cluster(sindex, max_points,
- [dist, max_points](const Index3D& sidx, const SpatElement& p)
+ [dist, max_points](const Index3D& sidx, const PointIndexEl& p)
{
return distance_queryfn(sidx, p, dist, max_points);
});
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 1902e74ae..fb2a5d7df 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -31,11 +31,10 @@ using SupportTreePtr = std::unique_ptr<sla::SLASupportTree>;
class SLAPrintObject::SupportData
{
public:
- sla::EigenMesh3D emesh; // index-triangle representation
- std::vector<sla::SupportPoint>
- support_points; // all the support points (manual/auto)
- SupportTreePtr support_tree_ptr; // the supports
- SlicedSupports support_slices; // sliced supports
+ sla::EigenMesh3D emesh; // index-triangle representation
+ std::vector<sla::SupportPoint> support_points; // all the support points (manual/auto)
+ SupportTreePtr support_tree_ptr; // the supports
+ std::vector<ExPolygons> support_slices; // sliced supports
inline SupportData(const TriangleMesh &trmesh) : emesh(trmesh) {}
};
@@ -212,8 +211,8 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
Moved,
Deleted,
};
- ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
- ModelID id;
+ ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {}
+ ObjectID id;
Status status;
// Search by id.
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
@@ -316,9 +315,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
print_object(print_object),
trafo(print_object->trafo()),
status(status) {}
- PrintObjectStatus(ModelID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
+ PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
// ID of the ModelObject & PrintObject
- ModelID id;
+ ObjectID id;
// Pointer to the old PrintObject
SLAPrintObject *print_object;
// Trafo generated with model_object->world_matrix(true)
@@ -368,7 +367,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
// Synchronize Object's config.
bool object_config_changed = model_object.config != model_object_new.config;
if (object_config_changed)
- model_object.config = model_object_new.config;
+ static_cast<DynamicPrintConfig&>(model_object.config) = static_cast<const DynamicPrintConfig&>(model_object_new.config);
if (! object_diff.empty() || object_config_changed) {
SLAPrintObjectConfig new_config = m_default_object_config;
normalize_and_apply_config(new_config, model_object.config);
@@ -441,12 +440,10 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
update_apply_status(this->invalidate_all_steps());
m_objects = print_objects_new;
// Delete the PrintObjects marked as Unknown or Deleted.
- bool deleted_objects = false;
for (auto &pos : print_object_status)
if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
update_apply_status(pos.print_object->invalidate_all_steps());
delete pos.print_object;
- deleted_objects = true;
}
if (new_objects)
update_apply_status(false);
@@ -473,7 +470,7 @@ void SLAPrint::set_task(const TaskParams &params)
int n_object_steps = int(params.to_object_step) + 1;
if (n_object_steps == 0)
- n_object_steps = (int)slaposCount;
+ n_object_steps = int(slaposCount);
if (params.single_model_object.valid()) {
// Find the print object to be processed with priority.
@@ -488,7 +485,7 @@ void SLAPrint::set_task(const TaskParams &params)
// Find out whether the priority print object is being currently processed.
bool running = false;
for (int istep = 0; istep < n_object_steps; ++ istep) {
- if (! print_object->m_stepmask[istep])
+ if (! print_object->m_stepmask[size_t(istep)])
// Step was skipped, cancel.
break;
if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) {
@@ -504,7 +501,7 @@ void SLAPrint::set_task(const TaskParams &params)
if (params.single_model_instance_only) {
// Suppress all the steps of other instances.
for (SLAPrintObject *po : m_objects)
- for (int istep = 0; istep < (int)slaposCount; ++ istep)
+ for (size_t istep = 0; istep < slaposCount; ++ istep)
po->m_stepmask[istep] = false;
} else if (! running) {
// Swap the print objects, so that the selected print_object is first in the row.
@@ -514,15 +511,15 @@ void SLAPrint::set_task(const TaskParams &params)
}
// and set the steps for the current object.
for (int istep = 0; istep < n_object_steps; ++ istep)
- print_object->m_stepmask[istep] = true;
- for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep)
- print_object->m_stepmask[istep] = false;
+ print_object->m_stepmask[size_t(istep)] = true;
+ for (int istep = n_object_steps; istep < int(slaposCount); ++ istep)
+ print_object->m_stepmask[size_t(istep)] = false;
} else {
// Slicing all objects.
bool running = false;
for (SLAPrintObject *print_object : m_objects)
for (int istep = 0; istep < n_object_steps; ++ istep) {
- if (! print_object->m_stepmask[istep]) {
+ if (! print_object->m_stepmask[size_t(istep)]) {
// Step may have been skipped. Restart.
goto loop_end;
}
@@ -538,8 +535,8 @@ void SLAPrint::set_task(const TaskParams &params)
this->call_cancel_callback();
for (SLAPrintObject *po : m_objects) {
for (int istep = 0; istep < n_object_steps; ++ istep)
- po->m_stepmask[istep] = true;
- for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep)
+ po->m_stepmask[size_t(istep)] = true;
+ for (auto istep = size_t(n_object_steps); istep < slaposCount; ++ istep)
po->m_stepmask[istep] = false;
}
}
@@ -557,9 +554,9 @@ void SLAPrint::set_task(const TaskParams &params)
void SLAPrint::finalize()
{
for (SLAPrintObject *po : m_objects)
- for (int istep = 0; istep < (int)slaposCount; ++ istep)
+ for (size_t istep = 0; istep < slaposCount; ++ istep)
po->m_stepmask[istep] = true;
- for (int istep = 0; istep < (int)slapsCount; ++ istep)
+ for (size_t istep = 0; istep < slapsCount; ++ istep)
m_stepmask[istep] = true;
}
@@ -597,21 +594,48 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
scfg.pillar_widening_factor = c.support_pillar_widening_factor.getFloat();
scfg.base_radius_mm = 0.5*c.support_base_diameter.getFloat();
scfg.base_height_mm = c.support_base_height.getFloat();
-
+ scfg.pillar_base_safety_distance_mm =
+ c.support_base_safety_distance.getFloat() < EPSILON ?
+ scfg.safety_distance_mm : c.support_base_safety_distance.getFloat();
+
return scfg;
}
+sla::PoolConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c) {
+ sla::PoolConfig::EmbedObject ret;
+
+ ret.enabled = c.support_object_elevation.getFloat() <= EPSILON &&
+ c.pad_enable.getBool() && c.supports_enable.getBool();
+
+ if(ret.enabled) {
+ ret.object_gap_mm = c.pad_object_gap.getFloat();
+ ret.stick_width_mm = c.pad_object_connector_width.getFloat();
+ ret.stick_stride_mm = c.pad_object_connector_stride.getFloat();
+ ret.stick_penetration_mm = c.pad_object_connector_penetration
+ .getFloat();
+ }
+
+ return ret;
+}
+
sla::PoolConfig make_pool_config(const SLAPrintObjectConfig& c) {
sla::PoolConfig pcfg;
pcfg.min_wall_thickness_mm = c.pad_wall_thickness.getFloat();
- pcfg.wall_slope = c.pad_wall_slope.getFloat();
- pcfg.edge_radius_mm = c.pad_edge_radius.getFloat();
+ pcfg.wall_slope = c.pad_wall_slope.getFloat() * PI / 180.0;
+
+ // We do not support radius for now
+ pcfg.edge_radius_mm = 0.0; //c.pad_edge_radius.getFloat();
+
pcfg.max_merge_distance_mm = c.pad_max_merge_distance.getFloat();
pcfg.min_wall_height_mm = c.pad_wall_height.getFloat();
+ // set builtin pad implicitly ON
+ pcfg.embed_object = builtin_pad_cfg(c);
+
return pcfg;
}
+
}
std::string SLAPrint::validate() const
@@ -634,9 +658,21 @@ std::string SLAPrint::validate() const
cfg.head_width_mm +
2 * cfg.head_back_radius_mm -
cfg.head_penetration_mm;
+
+ double elv = cfg.object_elevation_mm;
- if(supports_en && pinhead_width > cfg.object_elevation_mm)
+ if(supports_en && elv > EPSILON && elv < pinhead_width )
return L("Elevation is too low for object.");
+
+ sla::PoolConfig::EmbedObject builtinpad = builtin_pad_cfg(po->config());
+ if(supports_en && builtinpad.enabled &&
+ cfg.pillar_base_safety_distance_mm < builtinpad.object_gap_mm) {
+ return L(
+ "The endings of the support pillars will be deployed on the "
+ "gap between the object and the pad. 'Support base safety "
+ "distance' has to be greater than the 'Pad object gap' "
+ "parameter to avoid this.");
+ }
}
return "";
@@ -751,18 +787,27 @@ void SLAPrint::process()
mit->set_model_slice_idx(po, id); ++mit;
}
+
+ if(po.m_config.supports_enable.getBool() ||
+ po.m_config.pad_enable.getBool())
+ {
+ po.m_supportdata.reset(
+ new SLAPrintObject::SupportData(po.transformed_mesh()) );
+ }
};
// In this step we check the slices, identify island and cover them with
// support points. Then we sprinkle the rest of the mesh.
auto support_points = [this, ostepd](SLAPrintObject& po) {
- const ModelObject& mo = *po.m_model_object;
- po.m_supportdata.reset(
- new SLAPrintObject::SupportData(po.transformed_mesh()) );
-
// If supports are disabled, we can skip the model scan.
if(!po.m_config.supports_enable.getBool()) return;
+ if (!po.m_supportdata)
+ po.m_supportdata.reset(
+ new SLAPrintObject::SupportData(po.transformed_mesh()));
+
+ const ModelObject& mo = *po.m_model_object;
+
BOOST_LOG_TRIVIAL(debug) << "Support point count "
<< mo.sla_support_points.size();
@@ -771,7 +816,7 @@ void SLAPrint::process()
// into the backend cache.
if (mo.sla_points_status != sla::PointsStatus::UserModified) {
- // Hypotetical use of the slice index:
+ // Hypothetical use of the slice index:
// auto bb = po.transformed_mesh().bounding_box();
// auto range = po.get_slice_records(bb.min(Z));
// std::vector<float> heights; heights.reserve(range.size());
@@ -820,23 +865,59 @@ void SLAPrint::process()
BOOST_LOG_TRIVIAL(debug) << "Automatic support points: "
<< po.m_supportdata->support_points.size();
- // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass the update status to GLGizmoSlaSupports
- m_report_status(*this, -1, L("Generating support points"), SlicingStatus::RELOAD_SLA_SUPPORT_POINTS);
+ // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass
+ // the update status to GLGizmoSlaSupports
+ m_report_status(*this,
+ -1,
+ L("Generating support points"),
+ SlicingStatus::RELOAD_SLA_SUPPORT_POINTS);
}
else {
- // There are either some points on the front-end, or the user removed them on purpose. No calculation will be done.
+ // There are either some points on the front-end, or the user
+ // removed them on purpose. No calculation will be done.
po.m_supportdata->support_points = po.transformed_support_points();
}
+
+ // If the zero elevation mode is engaged, we have to filter out all the
+ // points that are on the bottom of the object
+ if (po.config().support_object_elevation.getFloat() <= EPSILON) {
+ double gnd = po.m_supportdata->emesh.ground_level();
+ auto & pts = po.m_supportdata->support_points;
+ double tolerance = po.config().pad_enable.getBool()
+ ? po.m_config.pad_wall_thickness.getFloat()
+ : po.m_config.support_base_height.getFloat();
+
+ // get iterator to the reorganized vector end
+ auto endit = std::remove_if(
+ pts.begin(),
+ pts.end(),
+ [tolerance, gnd](const sla::SupportPoint &sp) {
+ double diff = std::abs(gnd - double(sp.pos(Z)));
+ return diff <= tolerance;
+ });
+
+ // erase all elements after the new end
+ pts.erase(endit, pts.end());
+ }
};
// In this step we create the supports
auto support_tree = [this, ostepd](SLAPrintObject& po)
{
if(!po.m_supportdata) return;
+
+ sla::PoolConfig pcfg = make_pool_config(po.m_config);
+
+ if (pcfg.embed_object)
+ po.m_supportdata->emesh.ground_level_offset(
+ pcfg.min_wall_thickness_mm);
if(!po.m_config.supports_enable.getBool()) {
+
// Generate empty support tree. It can still host a pad
- po.m_supportdata->support_tree_ptr.reset(new SLASupportTree());
+ po.m_supportdata->support_tree_ptr.reset(
+ new SLASupportTree(po.m_supportdata->emesh.ground_level()));
+
return;
}
@@ -858,7 +939,7 @@ void SLAPrint::process()
ctl.stopcondition = [this](){ return canceled(); };
ctl.cancelfn = [this]() { throw_if_canceled(); };
-
+
po.m_supportdata->support_tree_ptr.reset(
new SLASupportTree(po.m_supportdata->support_points,
po.m_supportdata->emesh, scfg, ctl));
@@ -888,40 +969,33 @@ void SLAPrint::process()
// and before the supports had been sliced. (or the slicing has to be
// repeated)
- if(!po.m_supportdata || !po.m_supportdata->support_tree_ptr) {
- BOOST_LOG_TRIVIAL(error) << "Uninitialized support data at "
- << "pad creation.";
- return;
- }
-
if(po.m_config.pad_enable.getBool())
{
- double wt = po.m_config.pad_wall_thickness.getFloat();
- double h = po.m_config.pad_wall_height.getFloat();
- double md = po.m_config.pad_max_merge_distance.getFloat();
- // Radius is disabled for now...
- double er = 0; // po.m_config.pad_edge_radius.getFloat();
- double tilt = po.m_config.pad_wall_slope.getFloat() * PI / 180.0;
- double lh = po.m_config.layer_height.getFloat();
- double elevation = po.m_config.support_object_elevation.getFloat();
- if(!po.m_config.supports_enable.getBool()) elevation = 0;
- sla::PoolConfig pcfg(wt, h, md, er, tilt);
-
- ExPolygons bp;
- double pad_h = sla::get_pad_fullheight(pcfg);
- auto&& trmesh = po.transformed_mesh();
+ // Get the distilled pad configuration from the config
+ sla::PoolConfig pcfg = make_pool_config(po.m_config);
+
+ ExPolygons bp; // This will store the base plate of the pad.
+ double pad_h = sla::get_pad_fullheight(pcfg);
+ const TriangleMesh &trmesh = po.transformed_mesh();
// This call can get pretty time consuming
auto thrfn = [this](){ throw_if_canceled(); };
- if(elevation < pad_h) {
- // we have to count with the model geometry for the base plate
- sla::base_plate(trmesh, bp, float(pad_h), float(lh), thrfn);
+ if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) {
+ // No support (thus no elevation) or zero elevation mode
+ // we sometimes call it "builtin pad" is enabled so we will
+ // get a sample from the bottom of the mesh and use it for pad
+ // creation.
+ sla::base_plate(trmesh,
+ bp,
+ float(pad_h),
+ float(po.m_config.layer_height.getFloat()),
+ thrfn);
}
pcfg.throw_on_cancel = thrfn;
po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg);
- } else {
+ } else if(po.m_supportdata && po.m_supportdata->support_tree_ptr) {
po.m_supportdata->support_tree_ptr->remove_pad();
}
@@ -938,6 +1012,11 @@ void SLAPrint::process()
if(sd) sd->support_slices.clear();
+ // Don't bother if no supports and no pad is present.
+ if (!po.m_config.supports_enable.getBool() &&
+ !po.m_config.pad_enable.getBool())
+ return;
+
if(sd && sd->support_tree_ptr) {
std::vector<float> heights; heights.reserve(po.m_slice_index.size());
@@ -964,7 +1043,8 @@ void SLAPrint::process()
po.m_slice_index[i].set_support_slice_idx(po, i);
}
- // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update status to the 3D preview to load the SLA slices.
+ // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update
+ // status to the 3D preview to load the SLA slices.
m_report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
};
@@ -1536,14 +1616,17 @@ bool SLAPrint::is_step_done(SLAPrintObjectStep step) const
return true;
}
-SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
- Inherited(print, model_object),
- m_stepmask(slaposCount, true),
- m_transformed_rmesh( [this](TriangleMesh& obj){
- obj = m_model_object->raw_mesh(); obj.transform(m_trafo); obj.require_shared_vertices();
- })
-{
-}
+SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object)
+ : Inherited(print, model_object)
+ , m_stepmask(slaposCount, true)
+ , m_transformed_rmesh([this](TriangleMesh &obj) {
+ obj = m_model_object->raw_mesh();
+ if (!obj.empty()) {
+ obj.transform(m_trafo);
+ obj.require_shared_vertices();
+ }
+ })
+{}
SLAPrintObject::~SLAPrintObject() {}
@@ -1582,13 +1665,19 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
|| opt_key == "support_critical_angle"
|| opt_key == "support_max_bridge_length"
|| opt_key == "support_max_pillar_link_distance"
+ || opt_key == "support_base_safety_distance"
) {
steps.emplace_back(slaposSupportTree);
} else if (
opt_key == "pad_wall_height"
|| opt_key == "pad_max_merge_distance"
|| opt_key == "pad_wall_slope"
- || opt_key == "pad_edge_radius") {
+ || opt_key == "pad_edge_radius"
+ || opt_key == "pad_object_gap"
+ || opt_key == "pad_object_connector_stride"
+ || opt_key == "pad_object_connector_width"
+ || opt_key == "pad_object_connector_penetration"
+ ) {
steps.emplace_back(slaposBasePool);
} else {
// All keys should be covered.
@@ -1629,17 +1718,16 @@ bool SLAPrintObject::invalidate_all_steps()
}
double SLAPrintObject::get_elevation() const {
- bool se = m_config.supports_enable.getBool();
- double ret = se? m_config.support_object_elevation.getFloat() : 0;
+ bool en = m_config.supports_enable.getBool();
+ double ret = en ? m_config.support_object_elevation.getFloat() : 0.;
- // if the pad is enabled, then half of the pad height is its base plate
if(m_config.pad_enable.getBool()) {
// Normally the elevation for the pad itself would be the thickness of
// its walls but currently it is half of its thickness. Whatever it
// will be in the future, we provide the config to the get_pad_elevation
// method and we will have the correct value
sla::PoolConfig pcfg = make_pool_config(m_config);
- ret += sla::get_pad_elevation(pcfg);
+ if(!pcfg.embed_object) ret += sla::get_pad_elevation(pcfg);
}
return ret;
@@ -1647,14 +1735,14 @@ double SLAPrintObject::get_elevation() const {
double SLAPrintObject::get_current_elevation() const
{
- bool se = m_config.supports_enable.getBool();
bool has_supports = is_step_done(slaposSupportTree);
bool has_pad = is_step_done(slaposBasePool);
if(!has_supports && !has_pad)
return 0;
- else if(has_supports && !has_pad)
- return se ? m_config.support_object_elevation.getFloat() : 0;
+ else if(has_supports && !has_pad) {
+ return m_config.support_object_elevation.getFloat();
+ }
return get_elevation();
}
@@ -1682,13 +1770,14 @@ namespace { // dummy empty static containers for return values in some methods
const std::vector<ExPolygons> EMPTY_SLICES;
const TriangleMesh EMPTY_MESH;
const ExPolygons EMPTY_SLICE;
+const std::vector<sla::SupportPoint> EMPTY_SUPPORT_POINTS;
}
const SliceRecord SliceRecord::EMPTY(0, std::nanf(""), 0.f);
const std::vector<sla::SupportPoint>& SLAPrintObject::get_support_points() const
{
- return m_supportdata->support_points;
+ return m_supportdata? m_supportdata->support_points : EMPTY_SUPPORT_POINTS;
}
const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const
diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp
index b0c5a8fdc..9620e9b68 100644
--- a/src/libslic3r/SLAPrint.hpp
+++ b/src/libslic3r/SLAPrint.hpp
@@ -54,10 +54,10 @@ public:
bool is_left_handed() const { return m_left_handed; }
struct Instance {
- Instance(ModelID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {}
+ Instance(ObjectID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {}
bool operator==(const Instance &rhs) const { return this->instance_id == rhs.instance_id && this->shift == rhs.shift && this->rotation == rhs.rotation; }
// ID of the corresponding ModelInstance.
- ModelID instance_id;
+ ObjectID instance_id;
// Slic3r::Point objects in scaled G-code coordinates
Point shift;
// Rotation along the Z axis, in radians.
diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp
index e1bb4b313..c62736ffe 100644
--- a/src/libslic3r/Slicing.cpp
+++ b/src/libslic3r/Slicing.cpp
@@ -153,24 +153,33 @@ SlicingParameters SlicingParameters::create_from_config(
return params;
}
-// Convert layer_height_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for
+std::vector<std::pair<t_layer_height_range, coordf_t>> layer_height_ranges(const t_layer_config_ranges &config_ranges)
+{
+ std::vector<std::pair<t_layer_height_range, coordf_t>> out;
+ out.reserve(config_ranges.size());
+ for (const auto &kvp : config_ranges)
+ out.emplace_back(kvp.first, kvp.second.option("layer_height")->getFloat());
+ return out;
+}
+
+// Convert layer_config_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for
// in the height profile and the printed object may be lifted by the raft thickness at the time of the G-code generation.
std::vector<coordf_t> layer_height_profile_from_ranges(
const SlicingParameters &slicing_params,
- const t_layer_height_ranges &layer_height_ranges)
+ const t_layer_config_ranges &layer_config_ranges) // #ys_FIXME_experiment
{
// 1) If there are any height ranges, trim one by the other to make them non-overlapping. Insert the 1st layer if fixed.
std::vector<std::pair<t_layer_height_range,coordf_t>> ranges_non_overlapping;
- ranges_non_overlapping.reserve(layer_height_ranges.size() * 4);
+ ranges_non_overlapping.reserve(layer_config_ranges.size() * 4); // #ys_FIXME_experiment
if (slicing_params.first_object_layer_height_fixed())
ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(
t_layer_height_range(0., slicing_params.first_object_layer_height),
slicing_params.first_object_layer_height));
// The height ranges are sorted lexicographically by low / high layer boundaries.
- for (t_layer_height_ranges::const_iterator it_range = layer_height_ranges.begin(); it_range != layer_height_ranges.end(); ++ it_range) {
+ for (t_layer_config_ranges::const_iterator it_range = layer_config_ranges.begin(); it_range != layer_config_ranges.end(); ++ it_range) {
coordf_t lo = it_range->first.first;
coordf_t hi = std::min(it_range->first.second, slicing_params.object_print_z_height());
- coordf_t height = it_range->second;
+ coordf_t height = it_range->second.option("layer_height")->getFloat();
if (! ranges_non_overlapping.empty())
// Trim current low with the last high.
lo = std::max(lo, ranges_non_overlapping.back().first.second);
@@ -187,7 +196,6 @@ std::vector<coordf_t> layer_height_profile_from_ranges(
coordf_t hi = it_range->first.second;
coordf_t height = it_range->second;
coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2];
- coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1];
if (lo > last_z + EPSILON) {
// Insert a step of normal layer height.
layer_height_profile.push_back(last_z);
@@ -203,7 +211,6 @@ std::vector<coordf_t> layer_height_profile_from_ranges(
}
coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2];
- coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1];
if (last_z < slicing_params.object_print_z_height()) {
// Insert a step of normal layer height up to the object top.
layer_height_profile.push_back(last_z);
@@ -219,7 +226,7 @@ std::vector<coordf_t> layer_height_profile_from_ranges(
// Fill layer_height_profile by heights ensuring a prescribed maximum cusp height.
std::vector<coordf_t> layer_height_profile_adaptive(
const SlicingParameters &slicing_params,
- const t_layer_height_ranges &layer_height_ranges,
+ const t_layer_config_ranges & /* layer_config_ranges */,
const ModelVolumePtrs &volumes)
{
// 1) Initialize the SlicingAdaptive class with the object meshes.
@@ -245,7 +252,6 @@ std::vector<coordf_t> layer_height_profile_adaptive(
}
coordf_t slice_z = slicing_params.first_object_layer_height;
coordf_t height = slicing_params.first_object_layer_height;
- coordf_t cusp_height = 0.;
int current_facet = 0;
while ((slice_z - height) <= slicing_params.object_print_z_height()) {
height = 999;
@@ -401,7 +407,6 @@ void adjust_layer_height_profile(
}
// Adjust height by layer_thickness_delta.
coordf_t weight = std::abs(zz - z) < 0.5 * band_width ? (0.5 + 0.5 * cos(2. * M_PI * (zz - z) / band_width)) : 0.;
- coordf_t height_new = height;
switch (action) {
case LAYER_HEIGHT_EDIT_ACTION_INCREASE:
case LAYER_HEIGHT_EDIT_ACTION_DECREASE:
diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp
index fa5a12f9c..064363ec2 100644
--- a/src/libslic3r/Slicing.hpp
+++ b/src/libslic3r/Slicing.hpp
@@ -11,6 +11,8 @@
#include "libslic3r.h"
#include "Utils.hpp"
+#include "PrintConfig.hpp"
+
namespace Slic3r
{
@@ -128,15 +130,17 @@ inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters
}
typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
-typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges;
+typedef std::map<t_layer_height_range, DynamicPrintConfig> t_layer_config_ranges;
+
+extern std::vector<std::pair<t_layer_height_range, coordf_t>> layer_height_ranges(const t_layer_config_ranges &config_ranges);
extern std::vector<coordf_t> layer_height_profile_from_ranges(
const SlicingParameters &slicing_params,
- const t_layer_height_ranges &layer_height_ranges);
+ const t_layer_config_ranges &layer_config_ranges);
extern std::vector<coordf_t> layer_height_profile_adaptive(
const SlicingParameters &slicing_params,
- const t_layer_height_ranges &layer_height_ranges,
+ const t_layer_config_ranges &layer_config_ranges,
const ModelVolumePtrs &volumes);
@@ -171,4 +175,9 @@ extern int generate_layer_height_texture(
}; // namespace Slic3r
+namespace cereal
+{
+ template<class Archive> void serialize(Archive& archive, Slic3r::t_layer_height_range &lhr) { archive(lhr.first, lhr.second); }
+}
+
#endif /* slic3r_Slicing_hpp_ */
diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp
index fde35ca1e..505fdcaf5 100644
--- a/src/libslic3r/SupportMaterial.cpp
+++ b/src/libslic3r/SupportMaterial.cpp
@@ -361,17 +361,17 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
std::sort(layers_sorted.begin(), layers_sorted.end(), MyLayersPtrCompare());
int layer_id = 0;
assert(object.support_layers().empty());
- for (int i = 0; i < int(layers_sorted.size());) {
+ for (size_t i = 0; i < layers_sorted.size();) {
// Find the last layer with roughly the same print_z, find the minimum layer height of all.
// Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should.
- int j = i + 1;
+ size_t j = i + 1;
coordf_t zmax = layers_sorted[i]->print_z + EPSILON;
for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) ;
// Assign an average print_z to the set of layers with nearly equal print_z.
coordf_t zavg = 0.5 * (layers_sorted[i]->print_z + layers_sorted[j - 1]->print_z);
coordf_t height_min = layers_sorted[i]->height;
bool empty = true;
- for (int u = i; u < j; ++u) {
+ for (size_t u = i; u < j; ++u) {
MyLayer &layer = *layers_sorted[u];
if (! layer.polygons.empty())
empty = false;
@@ -829,7 +829,7 @@ namespace SupportMaterialInternal {
assert(expansion_scaled >= 0.f);
for (const ExtrusionPath &ep : loop.paths)
if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty()) {
- float exp = 0.5f * scale_(ep.width) + expansion_scaled;
+ float exp = 0.5f * (float)scale_(ep.width) + expansion_scaled;
if (ep.is_closed()) {
if (ep.size() >= 3) {
// This is a complete loop.
@@ -1042,7 +1042,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
float fw = float(layerm->flow(frExternalPerimeter).scaled_width());
no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw);
float lower_layer_offset =
- (layer_id < m_object_config->support_material_enforce_layers.value) ?
+ (layer_id < (size_t)m_object_config->support_material_enforce_layers.value) ?
// Enforce a full possible support, ignore the overhang angle.
0.f :
(threshold_rad > 0. ?
@@ -1352,7 +1352,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
{
// Find the span of layers, which are to be printed at the first layer height.
int j = 0;
- for (; j < contact_out.size() && contact_out[j]->print_z < m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON; ++ j);
+ for (; j < (int)contact_out.size() && contact_out[j]->print_z < m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON; ++ j);
if (j > 0) {
// Merge the contact_out layers (0) to (j - 1) into the contact_out[0].
MyLayer &dst = *contact_out.front();
@@ -1377,7 +1377,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// Find the span of layers closer than m_support_layer_height_min.
int j = i + 1;
coordf_t zmax = contact_out[i]->print_z + m_support_layer_height_min + EPSILON;
- for (; j < contact_out.size() && contact_out[j]->print_z < zmax; ++ j) ;
+ for (; j < (int)contact_out.size() && contact_out[j]->print_z < zmax; ++ j) ;
if (i + 1 < j) {
// Merge the contact_out layers (i + 1) to (j - 1) into the contact_out[i].
MyLayer &dst = *contact_out[i];
@@ -1395,7 +1395,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
contact_out[k] = contact_out[i];
i = j;
}
- if (k < contact_out.size())
+ if (k < (int)contact_out.size())
contact_out.erase(contact_out.begin() + k, contact_out.end());
}
@@ -2214,7 +2214,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
// Expand the bases of the support columns in the 1st layer.
columns_base->polygons = diff(
offset(columns_base->polygons, inflate_factor_1st_layer),
- offset(m_object->layers().front()->slices.expolygons, scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ offset(m_object->layers().front()->slices.expolygons, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (contacts != nullptr)
columns_base->polygons = diff(columns_base->polygons, interface_polygons);
}
@@ -2566,11 +2566,11 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
{
// make more loops
Polygons loop_polygons = loops0;
- for (size_t i = 1; i < n_contact_loops; ++ i)
+ for (int i = 1; i < n_contact_loops; ++ i)
polygons_append(loop_polygons,
offset2(
loops0,
- - int(i) * flow.scaled_spacing() - 0.5f * flow.scaled_spacing(),
+ - i * flow.scaled_spacing() - 0.5f * flow.scaled_spacing(),
0.5f * flow.scaled_spacing()));
// Clip such loops to the side oriented towards the object.
// Collect split points, so they will be recognized after the clipping.
@@ -3226,7 +3226,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// TODO: use brim ordering algorithm
Polygons to_infill_polygons = to_polygons(to_infill);
// TODO: use offset2_ex()
- to_infill = offset_ex(to_infill, - 0.4 * float(flow.scaled_spacing()));
+ to_infill = offset_ex(to_infill, - 0.4f * float(flow.scaled_spacing()));
extrusion_entities_append_paths(
base_layer.extrusions,
to_polylines(std::move(to_infill_polygons)),
diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index a5f93b24f..ae43369d2 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -15,6 +15,8 @@
#define ENABLE_RENDER_STATISTICS 0
// Shows an imgui dialog with camera related data
#define ENABLE_CAMERA_STATISTICS 0
+// Render the picking pass instead of the main scene (use [T] key to toggle between regular rendering and picking pass only rendering)
+#define ENABLE_RENDER_PICKING_PASS 0
//====================
@@ -39,13 +41,4 @@
#define ENABLE_TEXTURES_FROM_SVG (1 && ENABLE_1_42_0_ALPHA7)
-//====================
-// 1.42.0.alpha8 techs
-//====================
-#define ENABLE_1_42_0_ALPHA8 1
-
-// Toolbars and Gizmos use icons imported from svg files
-#define ENABLE_SVG_ICONS (1 && ENABLE_1_42_0_ALPHA8 && ENABLE_TEXTURES_FROM_SVG)
-
-
#endif // _technologies_h_
diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp
index 56accfefa..fbfff90fb 100644
--- a/src/libslic3r/TriangleMesh.cpp
+++ b/src/libslic3r/TriangleMesh.cpp
@@ -547,9 +547,9 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
#if REALfloat
qhull.runQhull("", 3, (int)this->its.vertices.size(), (const realT*)(this->its.vertices.front().data()), "Qt");
#else
- src_vertices.reserve(this->its.vertices() * 3);
+ src_vertices.reserve(this->its.vertices.size() * 3);
// We will now fill the vector with input points for computation:
- for (const stl_vertex &v : ths->its.vertices.size())
+ for (const stl_vertex &v : this->its.vertices)
for (int i = 0; i < 3; ++ i)
src_vertices.emplace_back(v(i));
qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp
index 054a98935..5dd2597a5 100644
--- a/src/libslic3r/TriangleMesh.hpp
+++ b/src/libslic3r/TriangleMesh.hpp
@@ -195,4 +195,24 @@ TriangleMesh make_sphere(double rho, double fa=(2*PI/360));
}
+// Serialization through the Cereal library
+#include <cereal/access.hpp>
+namespace cereal {
+ template <class Archive> struct specialize<Archive, Slic3r::TriangleMesh, cereal::specialization::non_member_load_save> {};
+ template<class Archive> void load(Archive &archive, Slic3r::TriangleMesh &mesh) {
+ stl_file &stl = mesh.stl;
+ stl.stats.type = inmemory;
+ archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
+ stl_allocate(&stl);
+ archive.loadBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
+ stl_get_size(&stl);
+ mesh.repair();
+ }
+ template<class Archive> void save(Archive &archive, const Slic3r::TriangleMesh &mesh) {
+ const stl_file& stl = mesh.stl;
+ archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
+ archive.saveBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
+ }
+}
+
#endif
diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp
index e76fccdb8..3b30e981c 100644
--- a/src/libslic3r/Utils.hpp
+++ b/src/libslic3r/Utils.hpp
@@ -167,7 +167,7 @@ template<class T> size_t next_highest_power_of_2(T v,
extern std::string xml_escape(std::string text);
-#if defined __GNUC__ & __GNUC__ < 5
+#if defined __GNUC__ && __GNUC__ < 5 && !defined __clang__
// Older GCCs don't have std::is_trivially_copyable
// cf. https://gcc.gnu.org/onlinedocs/gcc-4.9.4/libstdc++/manual/manual/status.html#status.iso.2011
#warning "GCC version < 5, faking std::is_trivially_copyable"
@@ -182,7 +182,7 @@ class ScopeGuard
public:
typedef std::function<void()> Closure;
private:
- bool committed;
+// bool committed;
Closure closure;
public:
diff --git a/src/libslic3r/pchheader.hpp b/src/libslic3r/pchheader.hpp
index b27dfe6a2..c0ffe2108 100644
--- a/src/libslic3r/pchheader.hpp
+++ b/src/libslic3r/pchheader.hpp
@@ -100,7 +100,10 @@
#include <tbb/task_scheduler_init.h>
#include <Eigen/Dense>
-#include <Eigen/Geometry>
+#include <Eigen/Geometry>
+
+#include <cereal/access.hpp>
+#include <cereal/types/base_class.hpp>
#include "BoundingBox.hpp"
#include "ClipperUtils.hpp"
diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp
index 3482f708a..519763731 100644
--- a/src/libslic3r/utils.cpp
+++ b/src/libslic3r/utils.cpp
@@ -414,13 +414,13 @@ std::string format_memsize_MB(size_t n)
scale *= 1000;
}
char buf[8];
- sprintf(buf, "%d", n);
+ sprintf(buf, "%d", (int)n);
out = buf;
while (scale != 1) {
scale /= 1000;
n = n2 / scale;
n2 = n2 % scale;
- sprintf(buf, ",%03d", n);
+ sprintf(buf, ",%03d", (int)n);
out += buf;
}
return out + "MB";
diff --git a/src/semver/semver.c b/src/semver/semver.c
index 527738644..e8bd6edcf 100644
--- a/src/semver/semver.c
+++ b/src/semver/semver.c
@@ -22,6 +22,10 @@
static const size_t MAX_SIZE = sizeof(char) * 255;
static const int MAX_SAFE_INT = (unsigned int) -1 >> 1;
+#ifdef _WIN32
+ #define strdup _strdup
+#endif
+
/**
* Define comparison operators, storing the
* ASCII code per each symbol in hexadecimal notation.
@@ -50,8 +54,8 @@ strcut (char *str, int begin, int len) {
if((int)l < 0 || (int)l > MAX_SAFE_INT) return -1;
- if (len < 0) len = l - begin + 1;
- if (begin + len > (int)l) len = l - begin;
+ if (len < 0) len = (int)l - begin + 1;
+ if (begin + len > (int)l) len = (int)l - begin;
memmove(str + begin, str + begin + len, l - len + 1 - begin);
return len;
@@ -104,7 +108,7 @@ parse_int (const char *s) {
static char *
parse_slice (char *buf, char sep) {
char *pr, *part;
- int plen;
+ size_t plen;
/* Find separator in buf */
pr = strchr(buf, sep);
@@ -210,8 +214,9 @@ semver_parse_version (const char *str, semver_t *ver) {
static int
compare_prerelease (char *x, char *y) {
char *lastx, *lasty, *xptr, *yptr, *endptr;
- int xlen, ylen, xisnum, yisnum, xnum, ynum;
- int xn, yn, min, res;
+ size_t xlen, ylen, xn, yn, min;
+ int xisnum, yisnum, xnum, ynum;
+ int res;
if (x == NULL && y == NULL) return 0;
if (y == NULL && x) return -1;
if (x == NULL && y) return 1;
@@ -572,7 +577,7 @@ semver_clean (char *s) {
for (i = 0; i < len; i++) {
if (contains(s[i], VALID_CHARS, mlen) == 0) {
- res = strcut(s, i, 1);
+ res = strcut(s, (int)i, 1);
if(res == -1) return -1;
--len; --i;
}
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 570e23baa..e3a910d6d 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -81,6 +81,8 @@ set(SLIC3R_GUI_SOURCES
GUI/GUI_ObjectManipulation.hpp
GUI/GUI_ObjectSettings.cpp
GUI/GUI_ObjectSettings.hpp
+ GUI/GUI_ObjectLayers.cpp
+ GUI/GUI_ObjectLayers.hpp
GUI/LambdaObjectDialog.cpp
GUI/LambdaObjectDialog.hpp
GUI/Tab.cpp
@@ -146,6 +148,8 @@ set(SLIC3R_GUI_SOURCES
Utils/PresetUpdater.hpp
Utils/Time.cpp
Utils/Time.hpp
+ Utils/UndoRedo.cpp
+ Utils/UndoRedo.hpp
Utils/HexFile.cpp
Utils/HexFile.hpp
)
@@ -159,7 +163,7 @@ endif ()
add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
-target_link_libraries(libslic3r_gui libslic3r avrdude imgui ${GLEW_LIBRARIES})
+target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES})
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
endif ()
diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp
index d73e423e0..482cd58ad 100644
--- a/src/slic3r/GUI/3DBed.cpp
+++ b/src/slic3r/GUI/3DBed.cpp
@@ -274,8 +274,8 @@ void Bed3D::Axes::render_axis(double length) const
Bed3D::Bed3D()
: m_type(Custom)
- , m_requires_canvas_update(false)
#if ENABLE_TEXTURES_FROM_SVG
+ , m_requires_canvas_update(false)
, m_vbo_id(0)
#endif // ENABLE_TEXTURES_FROM_SVG
, m_scale_factor(1.0f)
@@ -330,12 +330,11 @@ Point Bed3D::point_projection(const Point& point) const
}
#if ENABLE_TEXTURES_FROM_SVG
-void Bed3D::render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_factor) const
+void Bed3D::render(GLCanvas3D* canvas, float theta, float scale_factor) const
{
m_scale_factor = scale_factor;
- EType type = useVBOs ? m_type : Custom;
- switch (type)
+ switch (m_type)
{
case MK2:
{
@@ -361,7 +360,7 @@ void Bed3D::render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_fa
}
}
#else
-void Bed3D::render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_factor) const
+void Bed3D::render(float theta, float scale_factor) const
{
m_scale_factor = scale_factor;
@@ -372,17 +371,17 @@ void Bed3D::render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_fa
{
case MK2:
{
- render_prusa(canvas, "mk2", theta, useVBOs);
+ render_prusa("mk2", theta);
break;
}
case MK3:
{
- render_prusa(canvas, "mk3", theta, useVBOs);
+ render_prusa("mk3", theta);
break;
}
case SL1:
{
- render_prusa(canvas, "sl1", theta, useVBOs);
+ render_prusa("sl1", theta);
break;
}
default:
@@ -546,7 +545,7 @@ void Bed3D::render_prusa(GLCanvas3D* canvas, const std::string &key, bool bottom
if (!bottom)
{
filename = model_path + "_bed.stl";
- if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, true)) {
+ if ((m_model.get_filename() != filename) && m_model.init_from_file(filename)) {
Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2));
if (key == "mk2")
// hardcoded value to match the stl model
@@ -628,12 +627,12 @@ void Bed3D::render_prusa_shader(bool transparent) const
if (position_id != -1)
{
glsafe(::glEnableVertexAttribArray(position_id));
- glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)m_triangles.get_position_offset()));
+ glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_position_offset()));
}
if (tex_coords_id != -1)
{
glsafe(::glEnableVertexAttribArray(tex_coords_id));
- glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)m_triangles.get_tex_coords_offset()));
+ glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_tex_coords_offset()));
}
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_triangles.get_vertices_count()));
@@ -651,7 +650,7 @@ void Bed3D::render_prusa_shader(bool transparent) const
}
}
#else
-void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) const
+void Bed3D::render_prusa(const std::string& key, float theta) const
{
std::string tex_path = resources_dir() + "/icons/bed/" + key;
@@ -677,7 +676,7 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons
std::string filename = tex_path + "_top.png";
if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename))
{
- if (!m_top_texture.load_from_file(filename, true))
+ if (!m_top_texture.load_from_file(filename, true, true))
{
render_custom();
return;
@@ -694,7 +693,7 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons
filename = tex_path + "_bottom.png";
if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename))
{
- if (!m_bottom_texture.load_from_file(filename, true))
+ if (!m_bottom_texture.load_from_file(filename, true, true))
{
render_custom();
return;
@@ -711,7 +710,7 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons
if (theta <= 90.0f)
{
filename = model_path + "_bed.stl";
- if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, useVBOs)) {
+ if ((m_model.get_filename() != filename) && m_model.init_from_file(filename)) {
Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2));
if (key == "mk2")
// hardcoded value to match the stl model
diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp
index 68a74f61c..6c8913a9b 100644
--- a/src/slic3r/GUI/3DBed.hpp
+++ b/src/slic3r/GUI/3DBed.hpp
@@ -44,8 +44,8 @@ public:
const float* get_vertices_data() const;
unsigned int get_vertices_data_size() const { return (unsigned int)m_vertices.size() * get_vertex_data_size(); }
unsigned int get_vertex_data_size() const { return (unsigned int)(5 * sizeof(float)); }
- unsigned int get_position_offset() const { return 0; }
- unsigned int get_tex_coords_offset() const { return (unsigned int)(3 * sizeof(float)); }
+ size_t get_position_offset() const { return 0; }
+ size_t get_tex_coords_offset() const { return (size_t)(3 * sizeof(float)); }
unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size(); }
#else
const float* get_vertices() const { return m_vertices.data(); }
@@ -128,7 +128,11 @@ public:
bool contains(const Point& point) const;
Point point_projection(const Point& point) const;
- void render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_factor) const;
+#if ENABLE_TEXTURES_FROM_SVG
+ void render(GLCanvas3D* canvas, float theta, float scale_factor) const;
+#else
+ void render(float theta, float scale_factor) const;
+#endif // ENABLE_TEXTURES_FROM_SVG
void render_axes() const;
private:
@@ -140,7 +144,7 @@ private:
void render_prusa(GLCanvas3D* canvas, const std::string& key, bool bottom) const;
void render_prusa_shader(bool transparent) const;
#else
- void render_prusa(const std::string &key, float theta, bool useVBOs) const;
+ void render_prusa(const std::string& key, float theta) const;
#endif // ENABLE_TEXTURES_FROM_SVG
void render_custom() const;
#if ENABLE_TEXTURES_FROM_SVG
diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp
index f9a79f2d8..9e3eaf41d 100644
--- a/src/slic3r/GUI/3DScene.cpp
+++ b/src/slic3r/GUI/3DScene.cpp
@@ -55,21 +55,6 @@ void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char
namespace Slic3r {
-void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh)
-{
- assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0);
- assert(quad_indices.empty() && triangle_indices_size == 0);
- assert(vertices_and_normals_interleaved.size() % 6 == 0 && quad_indices_size == vertices_and_normals_interleaved.size());
-
- this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count());
-
- for (int i = 0; i < (int)mesh.stl.stats.number_of_facets; ++i) {
- const stl_facet &facet = mesh.stl.facet_start[i];
- for (int j = 0; j < 3; ++ j)
- this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2));
- }
-}
-
void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh)
{
assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0);
@@ -89,37 +74,35 @@ void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh)
}
}
-void GLIndexedVertexArray::finalize_geometry(bool use_VBOs)
+void GLIndexedVertexArray::finalize_geometry() const
{
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
assert(this->triangle_indices_VBO_id == 0);
assert(this->quad_indices_VBO_id == 0);
- this->setup_sizes();
+ this->shrink_to_fit();
- if (use_VBOs) {
- if (! empty()) {
- glsafe(::glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id));
- glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
- glsafe(::glBufferData(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved.size() * 4, this->vertices_and_normals_interleaved.data(), GL_STATIC_DRAW));
- glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
- this->vertices_and_normals_interleaved.clear();
- }
- if (! this->triangle_indices.empty()) {
- glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_id));
- glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
- glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices.size() * 4, this->triangle_indices.data(), GL_STATIC_DRAW));
- this->triangle_indices.clear();
- }
- if (! this->quad_indices.empty()) {
- glsafe(::glGenBuffers(1, &this->quad_indices_VBO_id));
- glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
- glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices.size() * 4, this->quad_indices.data(), GL_STATIC_DRAW));
- this->quad_indices.clear();
- }
+ if (! empty()) {
+ glsafe(::glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id));
+ glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
+ glsafe(::glBufferData(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved.size() * 4, this->vertices_and_normals_interleaved.data(), GL_STATIC_DRAW));
+ glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
+ this->vertices_and_normals_interleaved.clear();
+ }
+ if (! this->triangle_indices.empty()) {
+ glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_id));
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
+ glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices.size() * 4, this->triangle_indices.data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ this->triangle_indices.clear();
+ }
+ if (! this->quad_indices.empty()) {
+ glsafe(::glGenBuffers(1, &this->quad_indices_VBO_id));
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
+ glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices.size() * 4, this->quad_indices.data(), GL_STATIC_DRAW));
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ this->quad_indices.clear();
}
- this->shrink_to_fit();
}
void GLIndexedVertexArray::release_geometry()
@@ -137,89 +120,78 @@ void GLIndexedVertexArray::release_geometry()
this->quad_indices_VBO_id = 0;
}
this->clear();
- this->shrink_to_fit();
}
void GLIndexedVertexArray::render() const
{
- if (this->vertices_and_normals_interleaved_VBO_id) {
- glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
- glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
- glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
- } else {
- glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data() + 3));
- glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data()));
+ if (this->vertices_and_normals_interleaved_VBO_id == 0)
+ {
+ // sends data to gpu, if not done yet
+ finalize_geometry();
+ if (this->vertices_and_normals_interleaved_VBO_id == 0)
+ return;
}
+
+ glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
+ glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
+ glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
+
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
- if (this->indexed()) {
- if (this->vertices_and_normals_interleaved_VBO_id) {
- // Render using the Vertex Buffer Objects.
- if (this->triangle_indices_size > 0) {
- glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
- glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_size), GL_UNSIGNED_INT, nullptr));
- }
- if (this->quad_indices_size > 0) {
- glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
- glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), GL_UNSIGNED_INT, nullptr));
- }
- glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
- } else {
- // Render in an immediate mode.
- if (! this->triangle_indices.empty())
- glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_size), GL_UNSIGNED_INT, this->triangle_indices.data()));
- if (! this->quad_indices.empty())
- glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), GL_UNSIGNED_INT, this->quad_indices.data()));
- }
- } else
- glsafe(::glDrawArrays(GL_TRIANGLES, 0, GLsizei(this->vertices_and_normals_interleaved_size / 6)));
+ // Render using the Vertex Buffer Objects.
+ if (this->triangle_indices_size > 0) {
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
+ glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_size), GL_UNSIGNED_INT, nullptr));
+ glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ }
+ if (this->quad_indices_size > 0) {
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
+ glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), GL_UNSIGNED_INT, nullptr));
+ glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ }
- if (this->vertices_and_normals_interleaved_VBO_id)
- glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
+
+ glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
void GLIndexedVertexArray::render(
- const std::pair<size_t, size_t> &tverts_range,
- const std::pair<size_t, size_t> &qverts_range) const
+ const std::pair<size_t, size_t>& tverts_range,
+ const std::pair<size_t, size_t>& qverts_range) const
{
- assert(this->indexed());
- if (! this->indexed())
- return;
+ if (this->vertices_and_normals_interleaved_VBO_id == 0)
+ {
+ // sends data to gpu, if not done yet
+ finalize_geometry();
+ if (this->vertices_and_normals_interleaved_VBO_id == 0)
+ return;
+ }
- if (this->vertices_and_normals_interleaved_VBO_id) {
- // Render using the Vertex Buffer Objects.
- glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
- glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
- glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
- glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
- glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
- if (this->triangle_indices_size > 0) {
- glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
- glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(std::min(this->triangle_indices_size, tverts_range.second - tverts_range.first)), GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4)));
- }
- if (this->quad_indices_size > 0) {
- glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
- glsafe(::glDrawElements(GL_QUADS, GLsizei(std::min(this->quad_indices_size, qverts_range.second - qverts_range.first)), GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4)));
- }
- glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
+ // Render using the Vertex Buffer Objects.
+ glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
+ glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
+ glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
+
+ glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
+ glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
+
+ if (this->triangle_indices_size > 0) {
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
+ glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(std::min(this->triangle_indices_size, tverts_range.second - tverts_range.first)), GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4)));
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ }
+ if (this->quad_indices_size > 0) {
+ glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
+ glsafe(::glDrawElements(GL_QUADS, GLsizei(std::min(this->quad_indices_size, qverts_range.second - qverts_range.first)), GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4)));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
- } else {
- // Render in an immediate mode.
- glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data() + 3));
- glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data()));
- glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
- glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
- if (! this->triangle_indices.empty())
- glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(std::min(this->triangle_indices_size, tverts_range.second - tverts_range.first)), GL_UNSIGNED_INT, (const void*)(this->triangle_indices.data() + tverts_range.first)));
- if (! this->quad_indices.empty())
- glsafe(::glDrawElements(GL_QUADS, GLsizei(std::min(this->quad_indices_size, qverts_range.second - qverts_range.first)), GL_UNSIGNED_INT, (const void*)(this->quad_indices.data() + qverts_range.first)));
}
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
+
+ glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
@@ -343,11 +315,12 @@ bool GLVolume::is_left_handed() const
const BoundingBoxf3& GLVolume::transformed_bounding_box() const
{
- assert(bounding_box.defined || bounding_box.min(0) >= bounding_box.max(0) || bounding_box.min(1) >= bounding_box.max(1) || bounding_box.min(2) >= bounding_box.max(2));
+ const BoundingBoxf3& box = bounding_box();
+ assert(box.defined || box.min(0) >= box.max(0) || box.min(1) >= box.max(1) || box.min(2) >= box.max(2));
if (m_transformed_bounding_box_dirty)
{
- m_transformed_bounding_box = bounding_box.transformed(world_matrix());
+ m_transformed_bounding_box = box.transformed(world_matrix());
m_transformed_bounding_box_dirty = false;
}
@@ -365,14 +338,15 @@ BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &
{
return (m_convex_hull && m_convex_hull->stl.stats.number_of_facets > 0) ?
m_convex_hull->transformed_bounding_box(trafo) :
- bounding_box.transformed(trafo);
+ bounding_box().transformed(trafo);
}
+
void GLVolume::set_range(double min_z, double max_z)
{
- this->qverts_range.first = 0;
+ this->qverts_range.first = 0;
this->qverts_range.second = this->indexed_vertex_array.quad_indices_size;
- this->tverts_range.first = 0;
+ this->tverts_range.first = 0;
this->tverts_range.second = this->indexed_vertex_array.triangle_indices_size;
if (! this->print_zs.empty()) {
// The Z layer range is specified.
@@ -412,58 +386,17 @@ void GLVolume::render() const
glFrontFace(GL_CW);
glsafe(::glCullFace(GL_BACK));
glsafe(::glPushMatrix());
-
glsafe(::glMultMatrixd(world_matrix().data()));
- if (this->indexed_vertex_array.indexed())
- this->indexed_vertex_array.render(this->tverts_range, this->qverts_range);
- else
- this->indexed_vertex_array.render();
+
+ this->indexed_vertex_array.render(this->tverts_range, this->qverts_range);
+
glsafe(::glPopMatrix());
if (this->is_left_handed())
glFrontFace(GL_CCW);
}
-void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) const
+void GLVolume::render(int color_id, int detection_id, int worldmatrix_id) const
{
- if (!is_active)
- return;
-
- if (!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)
- return;
-
- if (this->is_left_handed())
- glFrontFace(GL_CW);
-
- GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first));
- GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first));
- if (n_triangles + n_quads == 0)
- {
- glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
- glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
-
- if (color_id >= 0)
- {
- float color[4];
- ::memcpy((void*)color, (const void*)render_color, 4 * sizeof(float));
- glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)color));
- }
- else
- glsafe(::glColor4fv(render_color));
-
- if (detection_id != -1)
- glsafe(::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0));
-
- if (worldmatrix_id != -1)
- glsafe(::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast<float>().data()));
-
- render();
-
- glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
- glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
-
- return;
- }
-
if (color_id >= 0)
glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)render_color));
else
@@ -475,187 +408,110 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c
if (worldmatrix_id != -1)
glsafe(::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast<float>().data()));
- glsafe(::glBindBuffer(GL_ARRAY_BUFFER, indexed_vertex_array.vertices_and_normals_interleaved_VBO_id));
- glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
- glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
-
- glsafe(::glPushMatrix());
-
- glsafe(::glMultMatrixd(world_matrix().data()));
-
- if (n_triangles > 0)
- {
- glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.triangle_indices_VBO_id));
- glsafe(::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4)));
- }
- if (n_quads > 0)
- {
- glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.quad_indices_VBO_id));
- glsafe(::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4)));
- }
-
- glsafe(::glPopMatrix());
-
- if (this->is_left_handed())
- glFrontFace(GL_CCW);
-}
-
-void GLVolume::render_legacy() const
-{
- assert(!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
- if (!is_active)
- return;
-
- if (this->is_left_handed())
- glFrontFace(GL_CW);
-
- GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first));
- GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first));
- if (n_triangles + n_quads == 0)
- {
- glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
- glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
-
- glsafe(::glColor4fv(render_color));
- render();
-
- glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
- glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
-
- return;
- }
-
- glsafe(::glColor4fv(render_color));
- glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data() + 3));
- glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data()));
-
- glsafe(::glPushMatrix());
-
- glsafe(::glMultMatrixd(world_matrix().data()));
-
- if (n_triangles > 0)
- glsafe(::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first));
-
- if (n_quads > 0)
- glsafe(::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, indexed_vertex_array.quad_indices.data() + qverts_range.first));
-
- glsafe(::glPopMatrix());
-
- if (this->is_left_handed())
- glFrontFace(GL_CCW);
+ render();
}
bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == -int(slaposSupportTree); }
bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int(slaposBasePool); }
std::vector<int> GLVolumeCollection::load_object(
- const ModelObject *model_object,
+ const ModelObject* model_object,
int obj_idx,
- const std::vector<int> &instance_idxs,
- const std::string &color_by,
- bool use_VBOs)
+ const std::vector<int>& instance_idxs,
+ const std::string& color_by)
{
std::vector<int> volumes_idx;
- for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx)
+ for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx)
for (int instance_idx : instance_idxs)
- volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by, use_VBOs));
- return volumes_idx;
+ volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by));
+ return volumes_idx;
}
int GLVolumeCollection::load_object_volume(
- const ModelObject *model_object,
+ const ModelObject* model_object,
int obj_idx,
int volume_idx,
int instance_idx,
- const std::string &color_by,
- bool use_VBOs)
+ const std::string& color_by)
{
- const ModelVolume *model_volume = model_object->volumes[volume_idx];
- const int extruder_id = model_volume->extruder_id();
- const ModelInstance *instance = model_object->instances[instance_idx];
+ const ModelVolume* model_volume = model_object->volumes[volume_idx];
+ const int extruder_id = model_volume->extruder_id();
+ const ModelInstance* instance = model_object->instances[instance_idx];
const TriangleMesh& mesh = model_volume->mesh();
float color[4];
memcpy(color, GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
-/* if (model_volume->is_support_blocker()) {
- color[0] = 1.0f;
- color[1] = 0.2f;
- color[2] = 0.2f;
- } else if (model_volume->is_support_enforcer()) {
- color[0] = 0.2f;
- color[1] = 0.2f;
- color[2] = 1.0f;
- }
- color[3] = model_volume->is_model_part() ? 1.f : 0.5f; */
+ /* if (model_volume->is_support_blocker()) {
+ color[0] = 1.0f;
+ color[1] = 0.2f;
+ color[2] = 0.2f;
+ } else if (model_volume->is_support_enforcer()) {
+ color[0] = 0.2f;
+ color[1] = 0.2f;
+ color[2] = 1.0f;
+ }
+ color[3] = model_volume->is_model_part() ? 1.f : 0.5f; */
color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
this->volumes.emplace_back(new GLVolume(color));
- GLVolume &v = *this->volumes.back();
+ GLVolume& v = *this->volumes.back();
v.set_color_from_model_volume(model_volume);
- v.indexed_vertex_array.load_mesh(mesh, use_VBOs);
-
- // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
- v.bounding_box = v.indexed_vertex_array.bounding_box();
- v.indexed_vertex_array.finalize_geometry(use_VBOs);
- v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
+ v.indexed_vertex_array.load_mesh(mesh);
+ v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
if (model_volume->is_model_part())
{
- // GLVolume will reference a convex hull from model_volume!
+ // GLVolume will reference a convex hull from model_volume!
v.set_convex_hull(model_volume->get_convex_hull_shared_ptr());
if (extruder_id != -1)
v.extruder_id = extruder_id;
}
- v.is_modifier = ! model_volume->is_model_part();
+ v.is_modifier = !model_volume->is_model_part();
v.shader_outside_printer_detection_enabled = model_volume->is_model_part();
v.set_instance_transformation(instance->get_transformation());
v.set_volume_transformation(model_volume->get_transformation());
- return int(this->volumes.size() - 1);
+ return int(this->volumes.size() - 1);
}
// Load SLA auxiliary GLVolumes (for support trees or pad).
// This function produces volumes for multiple instances in a single shot,
// as some object specific mesh conversions may be expensive.
void GLVolumeCollection::load_object_auxiliary(
- const SLAPrintObject *print_object,
+ const SLAPrintObject* print_object,
int obj_idx,
// pairs of <instance_idx, print_instance_idx>
- const std::vector<std::pair<size_t, size_t>> &instances,
+ const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone
- size_t timestamp,
- bool use_VBOs)
+ size_t timestamp)
{
assert(print_object->is_step_done(milestone));
Transform3d mesh_trafo_inv = print_object->trafo().inverse();
// Get the support mesh.
TriangleMesh mesh = print_object->get_mesh(milestone);
mesh.transform(mesh_trafo_inv);
- // Convex hull is required for out of print bed detection.
- TriangleMesh convex_hull = mesh.convex_hull_3d();
- for (const std::pair<size_t, size_t> &instance_idx : instances) {
- const ModelInstance &model_instance = *print_object->model_object()->instances[instance_idx.first];
+ // Convex hull is required for out of print bed detection.
+ TriangleMesh convex_hull = mesh.convex_hull_3d();
+ for (const std::pair<size_t, size_t>& instance_idx : instances) {
+ const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first];
this->volumes.emplace_back(new GLVolume((milestone == slaposBasePool) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR));
- GLVolume &v = *this->volumes.back();
- v.indexed_vertex_array.load_mesh(mesh, use_VBOs);
- // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
- v.bounding_box = v.indexed_vertex_array.bounding_box();
- v.indexed_vertex_array.finalize_geometry(use_VBOs);
- v.composite_id = GLVolume::CompositeID(obj_idx, - int(milestone), (int)instance_idx.first);
+ GLVolume& v = *this->volumes.back();
+ v.indexed_vertex_array.load_mesh(mesh);
+ v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first);
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
- // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
+ // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
if (&instance_idx == &instances.back())
v.set_convex_hull(std::move(convex_hull));
else
v.set_convex_hull(convex_hull);
- v.is_modifier = false;
+ v.is_modifier = false;
v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree);
v.set_instance_transformation(model_instance.get_transformation());
- // Leave the volume transformation at identity.
+ // Leave the volume transformation at identity.
// v.set_volume_transformation(model_volume->get_transformation());
}
}
int GLVolumeCollection::load_wipe_tower_preview(
- int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width)
+ int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width)
{
if (depth < 0.01f)
return int(this->volumes.size() - 1);
@@ -680,45 +536,41 @@ int GLVolumeCollection::load_wipe_tower_preview(
{ 38.453f, 0, 1 }, { 0, 0, 1 }, { 0, -depth, 1 }, { 100.0f, -depth, 1 }, { 100.0f, 0, 1 }, { 61.547f, 0, 1 }, { 55.7735f, -10.0f, 1 }, { 44.2265f, 10.0f, 1 } };
int out_facets_idx[][3] = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 5, 0 }, { 3, 5, 6 }, { 6, 2, 7 }, { 6, 0, 2 }, { 8, 9, 10 }, { 11, 12, 13 }, { 10, 11, 14 }, { 14, 11, 13 }, { 15, 8, 14 },
{8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8},
- {0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11}};
- for (int i=0;i<16;++i)
- points.push_back(Vec3d(out_points_idx[i][0] / (100.f/min_width), out_points_idx[i][1] + depth, out_points_idx[i][2]));
- for (int i=0;i<28;++i)
+ {0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11} };
+ for (int i = 0; i < 16; ++i)
+ points.push_back(Vec3d(out_points_idx[i][0] / (100.f / min_width), out_points_idx[i][1] + depth, out_points_idx[i][2]));
+ for (int i = 0; i < 28; ++i)
facets.push_back(Vec3crd(out_facets_idx[i][0], out_facets_idx[i][1], out_facets_idx[i][2]));
TriangleMesh tooth_mesh(points, facets);
// We have the mesh ready. It has one tooth and width of min_width. We will now append several of these together until we are close to
// the required width of the block. Than we can scale it precisely.
- size_t n = std::max(1, int(width/min_width)); // How many shall be merged?
- for (size_t i=0;i<n;++i) {
+ size_t n = std::max(1, int(width / min_width)); // How many shall be merged?
+ for (size_t i = 0; i < n; ++i) {
mesh.merge(tooth_mesh);
tooth_mesh.translate(min_width, 0.f, 0.f);
}
- mesh.scale(Vec3d(width/(n*min_width), 1.f, height)); // Scaling to proper width
+ mesh.scale(Vec3d(width / (n * min_width), 1.f, height)); // Scaling to proper width
}
else
mesh = make_cube(width, depth, height);
// We'll make another mesh to show the brim (fixed layer height):
- TriangleMesh brim_mesh = make_cube(width+2.f*brim_width, depth+2.f*brim_width, 0.2f);
+ TriangleMesh brim_mesh = make_cube(width + 2.f * brim_width, depth + 2.f * brim_width, 0.2f);
brim_mesh.translate(-brim_width, -brim_width, 0.f);
mesh.merge(brim_mesh);
this->volumes.emplace_back(new GLVolume(color));
- GLVolume &v = *this->volumes.back();
- v.indexed_vertex_array.load_mesh(mesh, use_VBOs);
+ GLVolume& v = *this->volumes.back();
+ v.indexed_vertex_array.load_mesh(mesh);
v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
- v.set_volume_rotation(Vec3d(0., 0., (M_PI/180.) * rotation_angle));
-
- // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
- v.bounding_box = v.indexed_vertex_array.bounding_box();
- v.indexed_vertex_array.finalize_geometry(use_VBOs);
- v.composite_id = GLVolume::CompositeID(obj_idx, 0, 0);
+ v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
+ v.composite_id = GLVolume::CompositeID(obj_idx, 0, 0);
v.geometry_id.first = 0;
v.geometry_id.second = wipe_tower_instance_id().id;
v.is_wipe_tower = true;
- v.shader_outside_printer_detection_enabled = ! size_unknown;
+ v.shader_outside_printer_detection_enabled = !size_unknown;
return int(this->volumes.size() - 1);
}
@@ -742,7 +594,7 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo
{
for (GLVolumeWithIdAndZ& volume : list)
{
- volume.second.second = volume.first->bounding_box.transformed(view_matrix * volume.first->world_matrix()).max(2);
+ volume.second.second = volume.first->bounding_box().transformed(view_matrix * volume.first->world_matrix()).max(2);
}
std::sort(list.begin(), list.end(),
@@ -759,7 +611,7 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo
return list;
}
-void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func) const
+void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func) const
{
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
@@ -797,7 +649,7 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
for (GLVolumeWithIdAndZ& volume : to_render) {
volume.first->set_render_color();
- volume.first->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id);
+ volume.first->render(color_id, print_box_detection_id, print_box_worldmatrix_id);
}
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
@@ -812,34 +664,6 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
glsafe(::glDisable(GL_BLEND));
}
-void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func) const
-{
- glsafe(::glEnable(GL_BLEND));
- glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
-
- glsafe(::glCullFace(GL_BACK));
- if (disable_cullface)
- glsafe(::glDisable(GL_CULL_FACE));
-
- glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
- glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
-
- GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
- for (GLVolumeWithIdAndZ& volume : to_render)
- {
- volume.first->set_render_color();
- volume.first->render_legacy();
- }
-
- glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
- glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
-
- if (disable_cullface)
- glsafe(::glEnable(GL_CULL_FACE));
-
- glsafe(::glDisable(GL_BLEND));
-}
-
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstance::EPrintVolumeState* out_state)
{
if (config == nullptr)
@@ -1020,19 +844,18 @@ static void thick_lines_to_indexed_vertex_array(
// right, left, top, bottom
int idx_prev[4] = { -1, -1, -1, -1 };
double bottom_z_prev = 0.;
- Vec2d b1_prev(Vec2d::Zero());
- Vec2d v_prev(Vec2d::Zero());
+ Vec2d b1_prev(Vec2d::Zero());
+ Vec2d v_prev(Vec2d::Zero());
int idx_initial[4] = { -1, -1, -1, -1 };
double width_initial = 0.;
double bottom_z_initial = 0.0;
+ double len_prev = 0.0;
// loop once more in case of closed loops
size_t lines_end = closed ? (lines.size() + 1) : lines.size();
for (size_t ii = 0; ii < lines_end; ++ ii) {
size_t i = (ii == lines.size()) ? 0 : ii;
const Line &line = lines[i];
- double len = unscale<double>(line.length());
- double inv_len = 1.0 / len;
double bottom_z = top_z - heights[i];
double middle_z = 0.5 * (top_z + bottom_z);
double width = widths[i];
@@ -1041,8 +864,8 @@ static void thick_lines_to_indexed_vertex_array(
bool is_last = (ii == lines_end - 1);
bool is_closing = closed && is_last;
- Vec2d v = unscale(line.vector());
- v *= inv_len;
+ Vec2d v = unscale(line.vector()).normalized();
+ double len = unscale<double>(line.length());
Vec2d a = unscale(line.a);
Vec2d b = unscale(line.b);
@@ -1061,9 +884,7 @@ static void thick_lines_to_indexed_vertex_array(
}
// calculate new XY normals
- Vector n = line.normal();
- Vec3d xy_right_normal = unscale(n(0), n(1), 0);
- xy_right_normal *= inv_len;
+ Vec2d xy_right_normal = unscale(line.normal()).normalized();
int idx_a[4];
int idx_b[4];
@@ -1091,9 +912,9 @@ static void thick_lines_to_indexed_vertex_array(
idx_a[BOTTOM] = idx_last ++;
volume.push_geometry(a(0), a(1), bottom_z, 0., 0., -1.);
idx_a[LEFT ] = idx_last ++;
- volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2));
+ volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), 0.0);
idx_a[RIGHT] = idx_last ++;
- volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2));
+ volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), 0.0);
}
else {
idx_a[BOTTOM] = idx_prev[BOTTOM];
@@ -1108,18 +929,36 @@ static void thick_lines_to_indexed_vertex_array(
// Continuing a previous segment.
// Share left / right vertices if possible.
double v_dot = v_prev.dot(v);
- bool sharp = v_dot < 0.707; // sin(45 degrees)
+ // To reduce gpu memory usage, we try to reuse vertices
+ // To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges
+ // is longer than a fixed threshold.
+ // The following value is arbitrary, it comes from tests made on a bunch of models showing the visual artifacts
+ double len_threshold = 2.5;
+
+ // Generate new vertices if the angle between adjacent edges is greater than 45 degrees or thresholds conditions are met
+ bool sharp = (v_dot < 0.707) || (len_prev > len_threshold) || (len > len_threshold);
if (sharp) {
if (!bottom_z_different)
{
// Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn.
idx_a[RIGHT] = idx_last++;
- volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2));
+ volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), 0.0);
idx_a[LEFT] = idx_last++;
- volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2));
+ volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), 0.0);
+ if (cross2(v_prev, v) > 0.) {
+ // Right turn. Fill in the right turn wedge.
+ volume.push_triangle(idx_prev[RIGHT], idx_a[RIGHT], idx_prev[TOP]);
+ volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a[RIGHT]);
+ }
+ else {
+ // Left turn. Fill in the left turn wedge.
+ volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a[LEFT]);
+ volume.push_triangle(idx_prev[LEFT], idx_a[LEFT], idx_prev[BOTTOM]);
+ }
}
}
- if (v_dot > 0.9) {
+ else
+ {
if (!bottom_z_different)
{
// The two successive segments are nearly collinear.
@@ -1127,45 +966,6 @@ static void thick_lines_to_indexed_vertex_array(
idx_a[RIGHT] = idx_prev[RIGHT];
}
}
- else if (!sharp) {
- if (!bottom_z_different)
- {
- // Create a sharp corner with an overshot and average the left / right normals.
- // At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc.
- Vec2d intersection(Vec2d::Zero());
- Geometry::ray_ray_intersection(b1_prev, v_prev, a1, v, intersection);
- a1 = intersection;
- a2 = 2. * a - intersection;
- assert((a - a1).norm() < width);
- assert((a - a2).norm() < width);
- float *n_left_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6;
- float *p_left_prev = n_left_prev + 3;
- float *n_right_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6;
- float *p_right_prev = n_right_prev + 3;
- p_left_prev [0] = float(a2(0));
- p_left_prev [1] = float(a2(1));
- p_right_prev[0] = float(a1(0));
- p_right_prev[1] = float(a1(1));
- xy_right_normal(0) += n_right_prev[0];
- xy_right_normal(1) += n_right_prev[1];
- xy_right_normal *= 1. / xy_right_normal.norm();
- n_left_prev [0] = float(-xy_right_normal(0));
- n_left_prev [1] = float(-xy_right_normal(1));
- n_right_prev[0] = float( xy_right_normal(0));
- n_right_prev[1] = float( xy_right_normal(1));
- idx_a[LEFT ] = idx_prev[LEFT ];
- idx_a[RIGHT] = idx_prev[RIGHT];
- }
- }
- else if (cross2(v_prev, v) > 0.) {
- // Right turn. Fill in the right turn wedge.
- volume.push_triangle(idx_prev[RIGHT], idx_a [RIGHT], idx_prev[TOP] );
- volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a [RIGHT] );
- } else {
- // Left turn. Fill in the left turn wedge.
- volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a [LEFT] );
- volume.push_triangle(idx_prev[LEFT], idx_a [LEFT], idx_prev[BOTTOM]);
- }
if (is_closing) {
if (!sharp) {
if (!bottom_z_different)
@@ -1204,14 +1004,15 @@ static void thick_lines_to_indexed_vertex_array(
}
// Generate new vertices for the end of this line segment.
idx_b[LEFT ] = idx_last ++;
- volume.push_geometry(b2(0), b2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2));
+ volume.push_geometry(b2(0), b2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), 0.0);
idx_b[RIGHT ] = idx_last ++;
- volume.push_geometry(b1(0), b1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2));
+ volume.push_geometry(b1(0), b1(1), middle_z, xy_right_normal(0), xy_right_normal(1), 0.0);
memcpy(idx_prev, idx_b, 4 * sizeof(int));
bottom_z_prev = bottom_z;
b1_prev = b1;
v_prev = v;
+ len_prev = len;
if (bottom_z_different && (closed || (!is_first && !is_last)))
{
@@ -1265,9 +1066,10 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
int idx_initial[4] = { -1, -1, -1, -1 };
int idx_prev[4] = { -1, -1, -1, -1 };
double z_prev = 0.0;
- Vec3d n_right_prev = Vec3d::Zero();
- Vec3d n_top_prev = Vec3d::Zero();
- Vec3d unit_v_prev = Vec3d::Zero();
+ double len_prev = 0.0;
+ Vec3d n_right_prev = Vec3d::Zero();
+ Vec3d n_top_prev = Vec3d::Zero();
+ Vec3d unit_v_prev = Vec3d::Zero();
double width_initial = 0.0;
// new vertices around the line endpoints
@@ -1286,21 +1088,23 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
double width = widths[i];
Vec3d unit_v = unscale(line.vector()).normalized();
+ double len = unscale<double>(line.length());
Vec3d n_top = Vec3d::Zero();
Vec3d n_right = Vec3d::Zero();
- Vec3d unit_positive_z(0.0, 0.0, 1.0);
-
+
if ((line.a(0) == line.b(0)) && (line.a(1) == line.b(1)))
{
// vertical segment
- n_right = (line.a(2) < line.b(2)) ? Vec3d(-1.0, 0.0, 0.0) : Vec3d(1.0, 0.0, 0.0);
- n_top = Vec3d(0.0, 1.0, 0.0);
+ n_top = Vec3d::UnitY();
+ n_right = Vec3d::UnitX();
+ if (line.a(2) < line.b(2))
+ n_right = -n_right;
}
else
{
- // generic segment
- n_right = unit_v.cross(unit_positive_z).normalized();
+ // horizontal segment
+ n_right = unit_v.cross(Vec3d::UnitZ()).normalized();
n_top = n_right.cross(unit_v).normalized();
}
@@ -1361,9 +1165,16 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
// Continuing a previous segment.
// Share left / right vertices if possible.
double v_dot = unit_v_prev.dot(unit_v);
- bool is_sharp = v_dot < 0.707; // sin(45 degrees)
bool is_right_turn = n_top_prev.dot(unit_v_prev.cross(unit_v)) > 0.0;
+ // To reduce gpu memory usage, we try to reuse vertices
+ // To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges
+ // is longer than a fixed threshold.
+ // The following value is arbitrary, it comes from tests made on a bunch of models showing the visual artifacts
+ double len_threshold = 2.5;
+
+ // Generate new vertices if the angle between adjacent edges is greater than 45 degrees or thresholds conditions are met
+ bool is_sharp = (v_dot < 0.707) || (len_prev > len_threshold) || (len > len_threshold);
if (is_sharp)
{
// Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn.
@@ -1371,65 +1182,26 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
volume.push_geometry(a[RIGHT], n_right);
idx_a[LEFT] = idx_last++;
volume.push_geometry(a[LEFT], n_left);
- }
- if (v_dot > 0.9)
- {
- // The two successive segments are nearly collinear.
- idx_a[LEFT] = idx_prev[LEFT];
- idx_a[RIGHT] = idx_prev[RIGHT];
+ if (is_right_turn)
+ {
+ // Right turn. Fill in the right turn wedge.
+ volume.push_triangle(idx_prev[RIGHT], idx_a[RIGHT], idx_prev[TOP]);
+ volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a[RIGHT]);
+ }
+ else
+ {
+ // Left turn. Fill in the left turn wedge.
+ volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a[LEFT]);
+ volume.push_triangle(idx_prev[LEFT], idx_a[LEFT], idx_prev[BOTTOM]);
+ }
}
- else if (!is_sharp)
+ else
{
- // Create a sharp corner with an overshot and average the left / right normals.
- // At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc.
-
- // averages normals
- Vec3d average_n_right = 0.5 * (n_right + n_right_prev).normalized();
- Vec3d average_n_left = -average_n_right;
- Vec3d average_rl_displacement = 0.5 * width * average_n_right;
-
- // updates vertices around a
- a[RIGHT] = l_a + average_rl_displacement;
- a[LEFT] = l_a - average_rl_displacement;
-
- // updates previous line normals
- float* normal_left_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT] * 6;
- normal_left_prev[0] = float(average_n_left(0));
- normal_left_prev[1] = float(average_n_left(1));
- normal_left_prev[2] = float(average_n_left(2));
-
- float* normal_right_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6;
- normal_right_prev[0] = float(average_n_right(0));
- normal_right_prev[1] = float(average_n_right(1));
- normal_right_prev[2] = float(average_n_right(2));
-
- // updates previous line's vertices around b
- float* b_left_prev = normal_left_prev + 3;
- b_left_prev[0] = float(a[LEFT](0));
- b_left_prev[1] = float(a[LEFT](1));
- b_left_prev[2] = float(a[LEFT](2));
-
- float* b_right_prev = normal_right_prev + 3;
- b_right_prev[0] = float(a[RIGHT](0));
- b_right_prev[1] = float(a[RIGHT](1));
- b_right_prev[2] = float(a[RIGHT](2));
-
+ // The two successive segments are nearly collinear.
idx_a[LEFT] = idx_prev[LEFT];
idx_a[RIGHT] = idx_prev[RIGHT];
}
- else if (is_right_turn)
- {
- // Right turn. Fill in the right turn wedge.
- volume.push_triangle(idx_prev[RIGHT], idx_a[RIGHT], idx_prev[TOP]);
- volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a[RIGHT]);
- }
- else
- {
- // Left turn. Fill in the left turn wedge.
- volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a[LEFT]);
- volume.push_triangle(idx_prev[LEFT], idx_a[LEFT], idx_prev[BOTTOM]);
- }
if (ii == lines.size())
{
@@ -1481,6 +1253,7 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
n_right_prev = n_right;
n_top_prev = n_top;
unit_v_prev = unit_v;
+ len_prev = len;
if (!closed)
{
@@ -1694,8 +1467,7 @@ void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height
GUI::GLCanvas3DManager _3DScene::s_canvas_mgr;
GLModel::GLModel()
- : m_useVBOs(false)
- , m_filename("")
+ : m_filename("")
{
m_volume.shader_outside_printer_detection_enabled = false;
}
@@ -1743,20 +1515,12 @@ void GLModel::set_scale(const Vec3d& scale)
void GLModel::reset()
{
- m_volume.release_geometry();
+ m_volume.indexed_vertex_array.release_geometry();
m_filename = "";
}
void GLModel::render() const
{
- if (m_useVBOs)
- render_VBOs();
- else
- render_legacy();
-}
-
-void GLModel::render_VBOs() const
-{
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
@@ -1768,7 +1532,8 @@ void GLModel::render_VBOs() const
glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, &current_program_id));
GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1;
glcheck();
- m_volume.render_VBOs(color_id, -1, -1);
+
+ m_volume.render(color_id, -1, -1);
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
@@ -1779,26 +1544,7 @@ void GLModel::render_VBOs() const
glsafe(::glDisable(GL_BLEND));
}
-void GLModel::render_legacy() const
-{
- glsafe(::glEnable(GL_LIGHTING));
- glsafe(::glEnable(GL_BLEND));
- glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
-
- glsafe(::glCullFace(GL_BACK));
- glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
- glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
-
- m_volume.render_legacy();
-
- glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
- glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
-
- glsafe(::glDisable(GL_BLEND));
- glsafe(::glDisable(GL_LIGHTING));
-}
-
-bool GLArrow::on_init(bool useVBOs)
+bool GLArrow::on_init()
{
Pointf3s vertices;
std::vector<Vec3crd> triangles;
@@ -1851,9 +1597,7 @@ bool GLArrow::on_init(bool useVBOs)
triangles.emplace_back(6, 0, 7);
triangles.emplace_back(7, 13, 6);
- m_useVBOs = useVBOs;
- m_volume.indexed_vertex_array.load_mesh(TriangleMesh(vertices, triangles), useVBOs);
- m_volume.finalize_geometry(m_useVBOs);
+ m_volume.indexed_vertex_array.load_mesh(TriangleMesh(vertices, triangles));
return true;
}
@@ -1865,7 +1609,7 @@ GLCurvedArrow::GLCurvedArrow(unsigned int resolution)
m_resolution = 1;
}
-bool GLCurvedArrow::on_init(bool useVBOs)
+bool GLCurvedArrow::on_init()
{
Pointf3s vertices;
std::vector<Vec3crd> triangles;
@@ -1966,14 +1710,11 @@ bool GLCurvedArrow::on_init(bool useVBOs)
triangles.emplace_back(vertices_per_level, vertices_per_level + 1, 0);
triangles.emplace_back(vertices_per_level, 2 * vertices_per_level + 1, vertices_per_level + 1);
- m_useVBOs = useVBOs;
- m_volume.indexed_vertex_array.load_mesh(TriangleMesh(vertices, triangles), useVBOs);
- m_volume.bounding_box = m_volume.indexed_vertex_array.bounding_box();
- m_volume.finalize_geometry(m_useVBOs);
+ m_volume.indexed_vertex_array.load_mesh(TriangleMesh(vertices, triangles));
return true;
}
-bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs)
+bool GLBed::on_init_from_file(const std::string& filename)
{
reset();
@@ -1994,7 +1735,6 @@ bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs)
}
m_filename = filename;
- m_useVBOs = useVBOs;
ModelObject* model_object = model.objects.front();
model_object->center_around_origin();
@@ -2002,14 +1742,11 @@ bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs)
TriangleMesh mesh = model.mesh();
mesh.repair();
- m_volume.indexed_vertex_array.load_mesh(mesh, useVBOs);
+ m_volume.indexed_vertex_array.load_mesh(mesh);
float color[4] = { 0.235f, 0.235f, 0.235f, 1.0f };
set_color(color, 4);
- m_volume.bounding_box = m_volume.indexed_vertex_array.bounding_box();
- m_volume.finalize_geometry(m_useVBOs);
-
return true;
}
diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp
index d197372ab..8ae57eeae 100644
--- a/src/slic3r/GUI/3DScene.hpp
+++ b/src/slic3r/GUI/3DScene.hpp
@@ -56,7 +56,7 @@ public:
vertices_and_normals_interleaved_VBO_id(0),
triangle_indices_VBO_id(0),
quad_indices_VBO_id(0)
- { this->setup_sizes(); }
+ {}
GLIndexedVertexArray(const GLIndexedVertexArray &rhs) :
vertices_and_normals_interleaved(rhs.vertices_and_normals_interleaved),
triangle_indices(rhs.triangle_indices),
@@ -64,7 +64,7 @@ public:
vertices_and_normals_interleaved_VBO_id(0),
triangle_indices_VBO_id(0),
quad_indices_VBO_id(0)
- { this->setup_sizes(); }
+ {}
GLIndexedVertexArray(GLIndexedVertexArray &&rhs) :
vertices_and_normals_interleaved(std::move(rhs.vertices_and_normals_interleaved)),
triangle_indices(std::move(rhs.triangle_indices)),
@@ -72,7 +72,9 @@ public:
vertices_and_normals_interleaved_VBO_id(0),
triangle_indices_VBO_id(0),
quad_indices_VBO_id(0)
- { this->setup_sizes(); }
+ {}
+
+ ~GLIndexedVertexArray() { release_geometry(); }
GLIndexedVertexArray& operator=(const GLIndexedVertexArray &rhs)
{
@@ -82,7 +84,10 @@ public:
this->vertices_and_normals_interleaved = rhs.vertices_and_normals_interleaved;
this->triangle_indices = rhs.triangle_indices;
this->quad_indices = rhs.quad_indices;
- this->setup_sizes();
+ this->m_bounding_box = rhs.m_bounding_box;
+ vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
+ triangle_indices_size = rhs.triangle_indices_size;
+ quad_indices_size = rhs.quad_indices_size;
return *this;
}
@@ -94,30 +99,32 @@ public:
this->vertices_and_normals_interleaved = std::move(rhs.vertices_and_normals_interleaved);
this->triangle_indices = std::move(rhs.triangle_indices);
this->quad_indices = std::move(rhs.quad_indices);
- this->setup_sizes();
+ this->m_bounding_box = std::move(rhs.m_bounding_box);
+ vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
+ triangle_indices_size = rhs.triangle_indices_size;
+ quad_indices_size = rhs.quad_indices_size;
return *this;
}
// Vertices and their normals, interleaved to be used by void glInterleavedArrays(GL_N3F_V3F, 0, x)
- std::vector<float> vertices_and_normals_interleaved;
- std::vector<int> triangle_indices;
- std::vector<int> quad_indices;
+ mutable std::vector<float> vertices_and_normals_interleaved;
+ mutable std::vector<int> triangle_indices;
+ mutable std::vector<int> quad_indices;
// When the geometry data is loaded into the graphics card as Vertex Buffer Objects,
// the above mentioned std::vectors are cleared and the following variables keep their original length.
- size_t vertices_and_normals_interleaved_size;
- size_t triangle_indices_size;
- size_t quad_indices_size;
+ size_t vertices_and_normals_interleaved_size{ 0 };
+ size_t triangle_indices_size{ 0 };
+ size_t quad_indices_size{ 0 };
// IDs of the Vertex Array Objects, into which the geometry has been loaded.
- // Zero if the VBOs are not used.
- unsigned int vertices_and_normals_interleaved_VBO_id;
- unsigned int triangle_indices_VBO_id;
- unsigned int quad_indices_VBO_id;
+ // Zero if the VBOs are not sent to GPU yet.
+ mutable unsigned int vertices_and_normals_interleaved_VBO_id{ 0 };
+ mutable unsigned int triangle_indices_VBO_id{ 0 };
+ mutable unsigned int quad_indices_VBO_id{ 0 };
- void load_mesh_flat_shading(const TriangleMesh &mesh);
void load_mesh_full_shading(const TriangleMesh &mesh);
- void load_mesh(const TriangleMesh &mesh, bool use_VBOs) { use_VBOs ? this->load_mesh_full_shading(mesh) : this->load_mesh_flat_shading(mesh); }
+ void load_mesh(const TriangleMesh& mesh) { this->load_mesh_full_shading(mesh); }
inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; }
@@ -128,6 +135,10 @@ public:
}
inline void push_geometry(float x, float y, float z, float nx, float ny, float nz) {
+ assert(this->vertices_and_normals_interleaved_VBO_id == 0);
+ if (this->vertices_and_normals_interleaved_VBO_id != 0)
+ return;
+
if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity())
this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6));
this->vertices_and_normals_interleaved.push_back(nx);
@@ -136,6 +147,9 @@ public:
this->vertices_and_normals_interleaved.push_back(x);
this->vertices_and_normals_interleaved.push_back(y);
this->vertices_and_normals_interleaved.push_back(z);
+
+ this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size();
+ m_bounding_box.merge(Vec3f(x, y, z).cast<double>());
};
inline void push_geometry(double x, double y, double z, double nx, double ny, double nz) {
@@ -147,80 +161,66 @@ public:
}
inline void push_triangle(int idx1, int idx2, int idx3) {
+ assert(this->vertices_and_normals_interleaved_VBO_id == 0);
+ if (this->vertices_and_normals_interleaved_VBO_id != 0)
+ return;
+
if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity())
this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3));
this->triangle_indices.push_back(idx1);
this->triangle_indices.push_back(idx2);
this->triangle_indices.push_back(idx3);
+ this->triangle_indices_size = this->triangle_indices.size();
};
inline void push_quad(int idx1, int idx2, int idx3, int idx4) {
+ assert(this->vertices_and_normals_interleaved_VBO_id == 0);
+ if (this->vertices_and_normals_interleaved_VBO_id != 0)
+ return;
+
if (this->quad_indices.size() + 4 > this->vertices_and_normals_interleaved.capacity())
this->quad_indices.reserve(next_highest_power_of_2(this->quad_indices.size() + 4));
this->quad_indices.push_back(idx1);
this->quad_indices.push_back(idx2);
this->quad_indices.push_back(idx3);
this->quad_indices.push_back(idx4);
+ this->quad_indices_size = this->quad_indices.size();
};
// Finalize the initialization of the geometry & indices,
// upload the geometry and indices to OpenGL VBO objects
// and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs.
- void finalize_geometry(bool use_VBOs);
+ void finalize_geometry() const;
// Release the geometry data, release OpenGL VBOs.
void release_geometry();
- // Render either using an immediate mode, or the VBOs.
+
void render() const;
- void render(const std::pair<size_t, size_t> &tverts_range, const std::pair<size_t, size_t> &qverts_range) const;
+ void render(const std::pair<size_t, size_t>& tverts_range, const std::pair<size_t, size_t>& qverts_range) const;
// Is there any geometry data stored?
bool empty() const { return vertices_and_normals_interleaved_size == 0; }
- // Is this object indexed, or is it just a set of triangles?
- bool indexed() const { return ! this->empty() && this->triangle_indices_size + this->quad_indices_size > 0; }
-
void clear() {
this->vertices_and_normals_interleaved.clear();
this->triangle_indices.clear();
this->quad_indices.clear();
- this->setup_sizes();
+ this->m_bounding_box.reset();
+ vertices_and_normals_interleaved_size = 0;
+ triangle_indices_size = 0;
+ quad_indices_size = 0;
}
// Shrink the internal storage to tighly fit the data stored.
- void shrink_to_fit() {
- if (! this->has_VBOs())
- this->setup_sizes();
+ void shrink_to_fit() const {
this->vertices_and_normals_interleaved.shrink_to_fit();
this->triangle_indices.shrink_to_fit();
this->quad_indices.shrink_to_fit();
}
- BoundingBoxf3 bounding_box() const {
- BoundingBoxf3 bbox;
- if (! this->vertices_and_normals_interleaved.empty()) {
- bbox.defined = true;
- bbox.min(0) = bbox.max(0) = this->vertices_and_normals_interleaved[3];
- bbox.min(1) = bbox.max(1) = this->vertices_and_normals_interleaved[4];
- bbox.min(2) = bbox.max(2) = this->vertices_and_normals_interleaved[5];
- for (size_t i = 9; i < this->vertices_and_normals_interleaved.size(); i += 6) {
- const float *verts = this->vertices_and_normals_interleaved.data() + i;
- bbox.min(0) = std::min<coordf_t>(bbox.min(0), verts[0]);
- bbox.min(1) = std::min<coordf_t>(bbox.min(1), verts[1]);
- bbox.min(2) = std::min<coordf_t>(bbox.min(2), verts[2]);
- bbox.max(0) = std::max<coordf_t>(bbox.max(0), verts[0]);
- bbox.max(1) = std::max<coordf_t>(bbox.max(1), verts[1]);
- bbox.max(2) = std::max<coordf_t>(bbox.max(2), verts[2]);
- }
- }
- return bbox;
- }
+ const BoundingBoxf3& bounding_box() const { return m_bounding_box; }
private:
- inline void setup_sizes() {
- vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size();
- triangle_indices_size = this->triangle_indices.size();
- quad_indices_size = this->quad_indices.size();
- }
+ BoundingBoxf3 m_bounding_box;
};
class GLVolume {
@@ -263,8 +263,6 @@ private:
mutable bool m_transformed_convex_hull_bounding_box_dirty;
public:
- // Bounding box of this volume, in unscaled coordinates.
- BoundingBoxf3 bounding_box;
// Color of the triangles / quads held by this volume.
float color[4];
// Color used to render this volume.
@@ -329,6 +327,9 @@ public:
// Offset into qverts & tverts, or offsets into indices stored into an OpenGL name_index_buffer.
std::vector<size_t> offsets;
+ // Bounding box of this volume, in unscaled coordinates.
+ const BoundingBoxf3& bounding_box() const { return this->indexed_vertex_array.bounding_box(); }
+
void set_render_color(float r, float g, float b, float a);
void set_render_color(const float* rgba, unsigned int size);
// Sets render color in dependence of current state
@@ -409,16 +410,17 @@ public:
BoundingBoxf3 transformed_convex_hull_bounding_box(const Transform3d &trafo) const;
// caching variant
const BoundingBoxf3& transformed_convex_hull_bounding_box() const;
+ // convex hull
+ const TriangleMesh* convex_hull() const { return m_convex_hull.get(); }
bool empty() const { return this->indexed_vertex_array.empty(); }
- bool indexed() const { return this->indexed_vertex_array.indexed(); }
void set_range(coordf_t low, coordf_t high);
+
void render() const;
- void render_VBOs(int color_id, int detection_id, int worldmatrix_id) const;
- void render_legacy() const;
+ void render(int color_id, int detection_id, int worldmatrix_id) const;
- void finalize_geometry(bool use_VBOs) { this->indexed_vertex_array.finalize_geometry(use_VBOs); }
+ void finalize_geometry() { this->indexed_vertex_array.finalize_geometry(); }
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; }
@@ -459,42 +461,38 @@ public:
~GLVolumeCollection() { clear(); };
std::vector<int> load_object(
- const ModelObject *model_object,
+ const ModelObject* model_object,
int obj_idx,
- const std::vector<int> &instance_idxs,
- const std::string &color_by,
- bool use_VBOs);
+ const std::vector<int>& instance_idxs,
+ const std::string& color_by);
int load_object_volume(
- const ModelObject *model_object,
+ const ModelObject* model_object,
int obj_idx,
int volume_idx,
int instance_idx,
- const std::string &color_by,
- bool use_VBOs);
+ const std::string& color_by);
// Load SLA auxiliary GLVolumes (for support trees or pad).
void load_object_auxiliary(
- const SLAPrintObject *print_object,
+ const SLAPrintObject* print_object,
int obj_idx,
// pairs of <instance_idx, print_instance_idx>
- const std::vector<std::pair<size_t, size_t>> &instances,
+ const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone
- size_t timestamp,
- bool use_VBOs);
+ size_t timestamp);
int load_wipe_tower_preview(
- int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width);
+ int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width);
// Render the volumes by OpenGL.
- void render_VBOs(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
- void render_legacy(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
+ void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
// Finalize the initialization of the geometry & indices,
// upload the geometry and indices to OpenGL VBO objects
// and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs.
- void finalize_geometry(bool use_VBOs) { for (auto *v : volumes) v->finalize_geometry(use_VBOs); }
+ void finalize_geometry() { for (auto* v : volumes) v->finalize_geometry(); }
// Release the geometry data assigned to the volumes.
// If OpenGL VBOs were allocated, an OpenGL context has to be active to release them.
void release_geometry() { for (auto *v : volumes) v->release_geometry(); }
@@ -533,17 +531,16 @@ class GLModel
{
protected:
GLVolume m_volume;
- bool m_useVBOs;
std::string m_filename;
public:
GLModel();
virtual ~GLModel();
- bool init(bool useVBOs) { return on_init(useVBOs); }
- bool init_from_file(const std::string& filename, bool useVBOs) { return on_init_from_file(filename, useVBOs); }
+ bool init() { return on_init(); }
+ bool init_from_file(const std::string& filename) { return on_init_from_file(filename); }
- void center_around(const Vec3d& center) { m_volume.set_volume_offset(center - m_volume.bounding_box.center()); }
+ void center_around(const Vec3d& center) { m_volume.set_volume_offset(center - m_volume.bounding_box().center()); }
void set_color(const float* color, unsigned int size);
const Vec3d& get_offset() const;
@@ -554,7 +551,7 @@ public:
void set_scale(const Vec3d& scale);
const std::string& get_filename() const { return m_filename; }
- const BoundingBoxf3& get_bounding_box() const { return m_volume.bounding_box; }
+ const BoundingBoxf3& get_bounding_box() const { return m_volume.bounding_box(); }
const BoundingBoxf3& get_transformed_bounding_box() const { return m_volume.transformed_bounding_box(); }
void reset();
@@ -562,18 +559,14 @@ public:
void render() const;
protected:
- virtual bool on_init(bool useVBOs) { return false; }
- virtual bool on_init_from_file(const std::string& filename, bool useVBOs) { return false; }
-
-private:
- void render_VBOs() const;
- void render_legacy() const;
+ virtual bool on_init() { return false; }
+ virtual bool on_init_from_file(const std::string& filename) { return false; }
};
class GLArrow : public GLModel
{
protected:
- virtual bool on_init(bool useVBOs);
+ virtual bool on_init();
};
class GLCurvedArrow : public GLModel
@@ -584,13 +577,13 @@ public:
explicit GLCurvedArrow(unsigned int resolution);
protected:
- virtual bool on_init(bool useVBOs);
+ virtual bool on_init();
};
class GLBed : public GLModel
{
protected:
- virtual bool on_init_from_file(const std::string& filename, bool useVBOs);
+ virtual bool on_init_from_file(const std::string& filename);
};
class _3DScene
diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp
index 3b6210058..dfdc79677 100644
--- a/src/slic3r/GUI/AppConfig.cpp
+++ b/src/slic3r/GUI/AppConfig.cpp
@@ -229,6 +229,33 @@ std::string AppConfig::get_last_dir() const
return std::string();
}
+std::vector<std::string> AppConfig::get_recent_projects() const
+{
+ std::vector<std::string> ret;
+ const auto it = m_storage.find("recent_projects");
+ if (it != m_storage.end())
+ {
+ for (const std::map<std::string, std::string>::value_type& item : it->second)
+ {
+ ret.push_back(item.second);
+ }
+ }
+ return ret;
+}
+
+void AppConfig::set_recent_projects(const std::vector<std::string>& recent_projects)
+{
+ auto it = m_storage.find("recent_projects");
+ if (it == m_storage.end())
+ it = m_storage.insert(std::map<std::string, std::map<std::string, std::string>>::value_type("recent_projects", std::map<std::string, std::string>())).first;
+
+ it->second.clear();
+ for (unsigned int i = 0; i < (unsigned int)recent_projects.size(); ++i)
+ {
+ it->second[std::to_string(i + 1)] = recent_projects[i];
+ }
+}
+
void AppConfig::update_config_dir(const std::string &dir)
{
this->set("recent", "config_directory", dir);
diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp
index 5af635a12..230a92294 100644
--- a/src/slic3r/GUI/AppConfig.hpp
+++ b/src/slic3r/GUI/AppConfig.hpp
@@ -122,6 +122,9 @@ public:
// Does the config file exist?
static bool exists();
+ std::vector<std::string> get_recent_projects() const;
+ void set_recent_projects(const std::vector<std::string>& recent_projects);
+
private:
// Map of section, name -> value
std::map<std::string, std::map<std::string, std::string>> m_storage;
diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp
index cec0f5067..d52204d4a 100644
--- a/src/slic3r/GUI/BedShapeDialog.cpp
+++ b/src/slic3r/GUI/BedShapeDialog.cpp
@@ -53,15 +53,12 @@ void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect)
void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
{
-// on_change(nullptr);
-
- auto box = new wxStaticBox(this, wxID_ANY, _(L("Shape")));
- auto sbsizer = new wxStaticBoxSizer(box, wxVERTICAL);
+ auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _(L("Shape")));
// shape options
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition,
wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP);
- sbsizer->Add(m_shape_options_book);
+ sbsizer->Add(m_shape_options_book);
auto optgroup = init_shape_options_page(_(L("Rectangular")));
ConfigOptionDef def;
@@ -92,13 +89,15 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
Line line{ "", "" };
line.full_width = 1;
line.widget = [this](wxWindow* parent) {
- auto btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL...")), wxDefaultPosition, wxDefaultSize);
-
- auto sizer = new wxBoxSizer(wxHORIZONTAL);
- sizer->Add(btn);
+ auto shape_btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL...")));
+ wxSizer* shape_sizer = new wxBoxSizer(wxHORIZONTAL);
+ shape_sizer->Add(shape_btn, 1, wxEXPAND);
+
+ wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
+ sizer->Add(shape_sizer, 1, wxEXPAND);
- btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
- {
+ shape_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
+ {
load_stl();
}));
@@ -106,8 +105,8 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
};
optgroup->append_line(line);
- Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent e)
- {
+ Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent& e)
+ {
update_shape();
}));
@@ -117,8 +116,8 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
// main sizer
auto top_sizer = new wxBoxSizer(wxHORIZONTAL);
- top_sizer->Add(sbsizer, 0, wxEXPAND | wxLeft | wxTOP | wxBOTTOM, 10);
- if (m_canvas)
+ top_sizer->Add(sbsizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 10);
+ if (m_canvas)
top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10) ;
SetSizerAndFit(top_sizer);
@@ -135,8 +134,7 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
// Create a panel for a rectangular / circular / custom bed shape.
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title)
{
-
- auto panel = new wxPanel(m_shape_options_book);
+ auto panel = new wxPanel(m_shape_options_book);
ConfigOptionsGroupShp optgroup;
optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Settings")));
@@ -234,8 +232,8 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points)
// This is a custom bed shape, use the polygon provided.
m_shape_options_book->SetSelection(SHAPE_CUSTOM);
// Copy the polygon to the canvas, make a copy of the array.
- m_canvas->m_bed_shape = points->values;
- update_shape();
+ m_loaded_bed_shape = points->values;
+ update_shape();
}
void BedShapePanel::update_preview()
diff --git a/src/slic3r/GUI/BonjourDialog.cpp b/src/slic3r/GUI/BonjourDialog.cpp
index 1885dda7b..0e05a517c 100644
--- a/src/slic3r/GUI/BonjourDialog.cpp
+++ b/src/slic3r/GUI/BonjourDialog.cpp
@@ -171,7 +171,7 @@ void BonjourDialog::on_reply(BonjourReplyEvent &e)
// Filter replies based on selected technology
const auto model = e.reply.txt_data.find("model");
const bool sl1 = model != e.reply.txt_data.end() && model->second == "SL1";
- if (tech == ptFFF && sl1 || tech == ptSLA && !sl1) {
+ if ((tech == ptFFF && sl1) || (tech == ptSLA && !sl1)) {
return;
}
diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp
index aacbfdc52..8b08f6f7f 100644
--- a/src/slic3r/GUI/ConfigWizard.cpp
+++ b/src/slic3r/GUI/ConfigWizard.cpp
@@ -330,8 +330,8 @@ PagePrinters::PagePrinters(ConfigWizard *parent, wxString title, wxString shortn
const auto families = vendor.families();
for (const auto &family : families) {
const auto filter = [&](const VendorProfile::PrinterModel &model) {
- return (model.technology == ptFFF && technology & T_FFF
- || model.technology == ptSLA && technology & T_SLA)
+ return ((model.technology == ptFFF && technology & T_FFF)
+ || (model.technology == ptSLA && technology & T_SLA))
&& model.family == family;
};
@@ -810,7 +810,7 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt)
const Item& item = items[i];
unsigned x = em_w/2 + item.indent * em_w;
- if (i == item_active || item_hover >= 0 && i == (size_t)item_hover) {
+ if (i == item_active || (item_hover >= 0 && i == (size_t)item_hover)) {
dc.DrawBitmap(bullet_blue.bmp(), x, y + yoff_icon, false);
}
else if (i < item_active) { dc.DrawBitmap(bullet_black.bmp(), x, y + yoff_icon, false); }
diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index 7f42db4d7..e84e9637f 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -113,11 +113,19 @@ wxString Field::get_tooltip_text(const wxString& default_string)
wxString tooltip_text("");
wxString tooltip = _(m_opt.tooltip);
edit_tooltip(tooltip);
+
+ std::string opt_id = m_opt_id;
+ auto hash_pos = opt_id.find("#");
+ if (hash_pos != std::string::npos) {
+ opt_id.replace(hash_pos, 1,"[");
+ opt_id += "]";
+ }
+
if (tooltip.length() > 0)
tooltip_text = tooltip + "\n" + _(L("default value")) + "\t: " +
- (boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") + default_string +
- (boost::iends_with(m_opt_id, "_gcode") ? "" : "\n") +
- _(L("parameter name")) + "\t: " + m_opt_id;
+ (boost::iends_with(opt_id, "_gcode") ? "\n" : "") + default_string +
+ (boost::iends_with(opt_id, "_gcode") ? "" : "\n") +
+ _(L("parameter name")) + "\t: " + opt_id;
return tooltip_text;
}
diff --git a/src/slic3r/GUI/FirmwareDialog.cpp b/src/slic3r/GUI/FirmwareDialog.cpp
index 15a09aa71..7865aecf2 100644
--- a/src/slic3r/GUI/FirmwareDialog.cpp
+++ b/src/slic3r/GUI/FirmwareDialog.cpp
@@ -442,8 +442,7 @@ void FirmwareDialog::priv::avr109_lookup_port(Avr109Pid usb_pid)
auto ports = Utils::scan_serial_ports_extended();
ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
return port.id_vendor != USB_VID_PRUSA ||
- port.id_product != usb_pid.boot &&
- port.id_product != usb_pid.app;
+ (port.id_product != usb_pid.boot && port.id_product != usb_pid.app);
}), ports.end());
if (ports.size() == 0) {
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index c674172e3..77a727108 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -524,7 +524,7 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
glsafe(::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data()));
glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4));
- for (const GLVolume *glvolume : volumes.volumes) {
+ for (const GLVolume* glvolume : volumes.volumes) {
// Render the object using the layer editing shader and texture.
if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
continue;
@@ -543,8 +543,8 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
{
// Something went wrong. Just render the object.
assert(false);
- for (const GLVolume *glvolume : volumes.volumes) {
- // Render the object using the layer editing shader and texture.
+ for (const GLVolume* glvolume : volumes.volumes) {
+ // Render the object using the layer editing shader and texture.
if (!glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
continue;
glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast<float>().data()));
@@ -604,6 +604,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas)
{
if (last_object_id >= 0) {
if (m_layer_height_profile_modified) {
+ wxGetApp().plater()->take_snapshot(_(L("Layers heights")));
const_cast<ModelObject*>(m_model_object)->layer_height_profile = m_layer_height_profile;
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
@@ -912,7 +913,8 @@ GLCanvas3D::LegendTexture::LegendTexture()
void GLCanvas3D::LegendTexture::fill_color_print_legend_values(const GCodePreviewData& preview_data, const GLCanvas3D& canvas,
std::vector<std::pair<double, double>>& cp_legend_values)
{
- if (preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint)
+ if (preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint &&
+ wxGetApp().extruders_edited_cnt() == 1) // show color change legend only for single-material presets
{
auto& config = wxGetApp().preset_bundle->project_config;
const std::vector<double>& color_print_values = config.option<ConfigOptionFloats>("colorprint_heights")->values;
@@ -1187,6 +1189,8 @@ wxDEFINE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
+wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
+wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar)
: m_canvas(canvas)
@@ -1198,11 +1202,8 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
, m_bed(bed)
, m_camera(camera)
, m_view_toolbar(view_toolbar)
-#if ENABLE_SVG_ICONS
, m_toolbar(GLToolbar::Normal, "Top")
-#else
- , m_toolbar(GLToolbar::Normal)
-#endif // ENABLE_SVG_ICONS
+ , m_gizmos(*this)
, m_use_clipping_planes(false)
, m_sidebar_field("")
, m_keep_dirty(false)
@@ -1211,7 +1212,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
, m_model(nullptr)
, m_dirty(true)
, m_initialized(false)
- , m_use_VBOs(false)
, m_apply_zoom_to_volumes_filter(false)
, m_legend_texture_enabled(false)
, m_picking_enabled(false)
@@ -1224,6 +1224,9 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
, m_cursor_type(Standard)
, m_color_by("volume")
, m_reload_delayed(false)
+#if ENABLE_RENDER_PICKING_PASS
+ , m_show_picking_texture(false)
+#endif // ENABLE_RENDER_PICKING_PASS
, m_render_sla_auxiliaries(true)
{
if (m_canvas != nullptr) {
@@ -1249,7 +1252,7 @@ void GLCanvas3D::post_event(wxEvent &&event)
wxPostEvent(m_canvas, event);
}
-bool GLCanvas3D::init(bool useVBOs)
+bool GLCanvas3D::init()
{
if (m_initialized)
return true;
@@ -1300,30 +1303,30 @@ bool GLCanvas3D::init(bool useVBOs)
if (m_multisample_allowed)
glsafe(::glEnable(GL_MULTISAMPLE));
- if (useVBOs && !m_shader.init("gouraud.vs", "gouraud.fs"))
+ if (!m_shader.init("gouraud.vs", "gouraud.fs"))
+ {
+ std::cout << "Unable to initialize gouraud shader: please, check that the files gouraud.vs and gouraud.fs are available" << std::endl;
return false;
+ }
- if (m_toolbar.is_enabled() && useVBOs && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs"))
+ if (m_toolbar.is_enabled() && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs"))
+ {
+ std::cout << "Unable to initialize variable_layer_height shader: please, check that the files variable_layer_height.vs and variable_layer_height.fs are available" << std::endl;
return false;
+ }
- m_use_VBOs = useVBOs;
-
- // on linux the gl context is not valid until the canvas is not shown on screen
- // we defer the geometry finalization of volumes until the first call to render()
- if (!m_volumes.empty())
- m_volumes.finalize_geometry(m_use_VBOs);
+// // on linux the gl context is not valid until the canvas is not shown on screen
+// // we defer the geometry finalization of volumes until the first call to render()
+// if (!m_volumes.empty())
+// m_volumes.finalize_geometry();
- if (m_gizmos.is_enabled()) {
- if (! m_gizmos.init(*this)) {
- std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl;
- return false;
- }
- }
+ if (m_gizmos.is_enabled() && !m_gizmos.init())
+ std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl;
if (!_init_toolbar())
return false;
- if (m_selection.is_enabled() && !m_selection.init(m_use_VBOs))
+ if (m_selection.is_enabled() && !m_selection.init())
return false;
post_event(SimpleEvent(EVT_GLCANVAS_INIT));
@@ -1353,7 +1356,6 @@ void GLCanvas3D::reset_volumes()
if (!m_volumes.empty())
{
m_selection.clear();
- m_volumes.release_geometry();
m_volumes.clear();
m_dirty = true;
}
@@ -1627,6 +1629,10 @@ void GLCanvas3D::render()
_picking_pass();
}
+#if ENABLE_RENDER_PICKING_PASS
+ if (!m_picking_enabled || !m_show_picking_texture)
+ {
+#endif // ENABLE_RENDER_PICKING_PASS
// draw scene
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
_render_background();
@@ -1656,6 +1662,9 @@ void GLCanvas3D::render()
_render_current_gizmo();
_render_selection_sidebar_hints();
+#if ENABLE_RENDER_PICKING_PASS
+ }
+#endif // ENABLE_RENDER_PICKING_PASS
#if ENABLE_SHOW_CAMERA_TARGET
_render_camera_target();
@@ -1712,7 +1721,7 @@ void GLCanvas3D::deselect_all()
m_selection.set_mode(Selection::Instance);
wxGetApp().obj_manipul()->set_dirty();
m_gizmos.reset_all_states();
- m_gizmos.update_data(*this);
+ m_gizmos.update_data();
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
}
@@ -1768,7 +1777,7 @@ std::vector<int> GLCanvas3D::load_object(const ModelObject& model_object, int ob
instance_idxs.push_back(i);
}
}
- return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_use_VBOs && m_initialized);
+ return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by);
}
std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx)
@@ -1786,7 +1795,7 @@ std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx)
void GLCanvas3D::mirror_selection(Axis axis)
{
m_selection.mirror(axis);
- do_mirror();
+ do_mirror(L("Mirror Object"));
wxGetApp().obj_manipul()->set_dirty();
}
@@ -1807,14 +1816,14 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
struct ModelVolumeState {
ModelVolumeState(const GLVolume *volume) :
model_volume(nullptr), geometry_id(volume->geometry_id), volume_idx(-1) {}
- ModelVolumeState(const ModelVolume *model_volume, const ModelID &instance_id, const GLVolume::CompositeID &composite_id) :
+ ModelVolumeState(const ModelVolume *model_volume, const ObjectID &instance_id, const GLVolume::CompositeID &composite_id) :
model_volume(model_volume), geometry_id(std::make_pair(model_volume->id().id, instance_id.id)), composite_id(composite_id), volume_idx(-1) {}
- ModelVolumeState(const ModelID &volume_id, const ModelID &instance_id) :
+ ModelVolumeState(const ObjectID &volume_id, const ObjectID &instance_id) :
model_volume(nullptr), geometry_id(std::make_pair(volume_id.id, instance_id.id)), volume_idx(-1) {}
bool new_geometry() const { return this->volume_idx == size_t(-1); }
const ModelVolume *model_volume;
- // ModelID of ModelVolume + ModelID of ModelInstance
- // or timestamp of an SLAPrintObjectStep + ModelID of ModelInstance
+ // ObjectID of ModelVolume + ObjectID of ModelInstance
+ // or timestamp of an SLAPrintObjectStep + ObjectID of ModelInstance
std::pair<size_t, size_t> geometry_id;
GLVolume::CompositeID composite_id;
// Volume index in the new GLVolume vector.
@@ -1908,7 +1917,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
assert(volume_idx_wipe_tower_old == -1);
volume_idx_wipe_tower_old = (int)volume_id;
}
- volume->release_geometry();
if (! m_reload_delayed)
delete volume;
} else {
@@ -1956,8 +1964,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id);
if (it->new_geometry()) {
// New volume.
- m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_use_VBOs && m_initialized);
- m_volumes.volumes.back()->geometry_id = key.geometry_id;
+ m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by);
+ m_volumes.volumes.back()->geometry_id = key.geometry_id;
update_object_list = true;
} else {
// Recycling an old GLVolume.
@@ -2010,9 +2018,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id);
if (it->new_geometry())
instances[istep].emplace_back(std::pair<size_t, size_t>(instance_idx, print_instance_idx));
- else
- // Recycling an old GLVolume. Update the Object/Instance indices into the current Model.
- m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx);
+ else {
+ // Recycling an old GLVolume. Update the Object/Instance indices into the current Model.
+ m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx);
+ m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation());
+ }
}
}
@@ -2021,7 +2031,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
for (size_t istep = 0; istep < sla_steps.size(); ++istep)
if (!instances[istep].empty())
- m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_use_VBOs && m_initialized);
+ m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp);
}
// Shift-up all volumes of the object so that it has the right elevation with respect to the print bed
@@ -2034,11 +2044,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
// Should the wipe tower be visualized ?
unsigned int extruders_count = (unsigned int)dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter"))->values.size();
- bool semm = dynamic_cast<const ConfigOptionBool*>(m_config->option("single_extruder_multi_material"))->value;
bool wt = dynamic_cast<const ConfigOptionBool*>(m_config->option("wipe_tower"))->value;
bool co = dynamic_cast<const ConfigOptionBool*>(m_config->option("complete_objects"))->value;
- if ((extruders_count > 1) && semm && wt && !co)
+ if ((extruders_count > 1) && wt && !co)
{
// Height of a print (Show at least a slab)
double height = std::max(m_model->bounding_box().max(2), 10.0);
@@ -2058,9 +2067,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
float brim_spacing = print->config().nozzle_diameter.values[0] * 1.25f - first_layer_height * (1. - M_PI_4);
if (!print->is_step_done(psWipeTower))
- depth = (900.f/w) * (float)(extruders_count - 1) ;
+ depth = (900.f/w) * (float)(extruders_count - 1);
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(
- 1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !print->is_step_done(psWipeTower),
+ 1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower),
brim_spacing * 4.5f);
if (volume_idx_wipe_tower_old != -1)
map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new;
@@ -2075,8 +2084,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
m_selection.volumes_changed(map_glvolume_old_to_new);
}
- m_gizmos.update_data(*this);
- m_gizmos.refresh_on_off_state(m_selection);
+ m_gizmos.update_data();
+ m_gizmos.refresh_on_off_state();
// Update the toolbar
if (update_object_list)
@@ -2313,7 +2322,10 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
return;
}
- if (m_gizmos.on_char(evt, *this))
+ if ((keyCode == WXK_ESCAPE) && _deactivate_undo_redo_toolbar_items())
+ return;
+
+ if (m_gizmos.on_char(evt))
return;
//#ifdef __APPLE__
@@ -2345,6 +2357,25 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
#endif /* __APPLE__ */
post_event(SimpleEvent(EVT_GLTOOLBAR_PASTE));
break;
+
+
+#ifdef __APPLE__
+ case 'y':
+ case 'Y':
+#else /* __APPLE__ */
+ case WXK_CONTROL_Y:
+#endif /* __APPLE__ */
+ post_event(SimpleEvent(EVT_GLCANVAS_REDO));
+ break;
+#ifdef __APPLE__
+ case 'z':
+ case 'Z':
+#else /* __APPLE__ */
+ case WXK_CONTROL_Z:
+#endif /* __APPLE__ */
+ post_event(SimpleEvent(EVT_GLCANVAS_UNDO));
+ break;
+
#ifdef __APPLE__
case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead.
#else /* __APPLE__ */
@@ -2365,7 +2396,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
#endif /* __APPLE__ */
post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE));
break;
-
case WXK_ESCAPE: { deselect_all(); break; }
case '0': { select_view("iso"); break; }
case '1': { select_view("top"); break; }
@@ -2397,6 +2427,14 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
case 'k': { m_camera.select_next_type(); m_dirty = true; break; }
case 'O':
case 'o': { set_camera_zoom(-1.0); break; }
+#if ENABLE_RENDER_PICKING_PASS
+ case 'T':
+ case 't': {
+ m_show_picking_texture = !m_show_picking_texture;
+ m_dirty = true;
+ break;
+ }
+#endif // ENABLE_RENDER_PICKING_PASS
case 'Z':
case 'z': { m_selection.is_empty() ? zoom_to_volumes() : zoom_to_selection(); break; }
default: { evt.Skip(); break; }
@@ -2414,7 +2452,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
}
else
{
- if (!m_gizmos.on_key(evt, *this))
+ if (!m_gizmos.on_key(evt))
{
if (evt.GetEventType() == wxEVT_KEY_UP) {
if (m_tab_down && keyCode == WXK_TAB && !evt.HasAnyModifiers()) {
@@ -2524,7 +2562,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
}
// Inform gizmos about the event so they have the opportunity to react.
- if (m_gizmos.on_mouse_wheel(evt, *this))
+ if (m_gizmos.on_mouse_wheel(evt))
return;
// Calculate the zoom delta and apply it to the current zoom factor
@@ -2652,7 +2690,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
return;
}
- if (m_gizmos.on_mouse(evt, *this))
+ if (m_gizmos.on_mouse(evt))
{
if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
mouse_up_cleanup();
@@ -2704,12 +2742,17 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
else if (evt.Leaving())
{
+ _deactivate_undo_redo_toolbar_items();
+
// to remove hover on objects when the mouse goes out of this canvas
m_mouse.position = Vec2d(-1.0, -1.0);
m_dirty = true;
}
- else if (evt.LeftDown() || evt.RightDown())
+ else if (evt.LeftDown() || evt.RightDown() || evt.MiddleDown())
{
+ if (_deactivate_undo_redo_toolbar_items())
+ return;
+
// If user pressed left or right button we first check whether this happened
// on a volume or not.
m_layers_editing.state = LayersEditing::Unknown;
@@ -2774,9 +2817,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (m_selection.is_empty())
m_gizmos.reset_all_states();
else
- m_gizmos.refresh_on_off_state(m_selection);
+ m_gizmos.refresh_on_off_state();
- m_gizmos.update_data(*this);
+ m_gizmos.update_data();
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
m_dirty = true;
}
@@ -2907,9 +2950,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging)
{
m_regenerate_volumes = false;
- do_move();
+ do_move(L("Move Object"));
wxGetApp().obj_manipul()->set_dirty();
- // Let the platter know that the dragging finished, so a delayed refresh
+ // Let the plater know that the dragging finished, so a delayed refresh
// of the scene with the background processing data should be performed.
post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
}
@@ -2944,9 +2987,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
{
// forces the selection of the volume
m_selection.add(volume_idx);
- m_gizmos.refresh_on_off_state(m_selection);
+ m_gizmos.refresh_on_off_state();
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
- m_gizmos.update_data(*this);
+ m_gizmos.update_data();
wxGetApp().obj_manipul()->set_dirty();
// forces a frame render to update the view before the context menu is shown
render();
@@ -3066,11 +3109,14 @@ void GLCanvas3D::set_tooltip(const std::string& tooltip) const
}
-void GLCanvas3D::do_move()
+void GLCanvas3D::do_move(const std::string& snapshot_type)
{
if (m_model == nullptr)
return;
+ if (!snapshot_type.empty())
+ wxGetApp().plater()->take_snapshot(_(snapshot_type));
+
std::set<std::pair<int, int>> done; // keeps track of modified instances
bool object_moved = false;
Vec3d wipe_tower_origin = Vec3d::Zero();
@@ -3121,13 +3167,18 @@ void GLCanvas3D::do_move()
if (wipe_tower_origin != Vec3d::Zero())
post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin)));
+
+ m_dirty = true;
}
-void GLCanvas3D::do_rotate()
+void GLCanvas3D::do_rotate(const std::string& snapshot_type)
{
if (m_model == nullptr)
return;
+ if (!snapshot_type.empty())
+ wxGetApp().plater()->take_snapshot(_(snapshot_type));
+
std::set<std::pair<int, int>> done; // keeps track of modified instances
Selection::EMode selection_mode = m_selection.get_mode();
@@ -3176,13 +3227,18 @@ void GLCanvas3D::do_rotate()
if (!done.empty())
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
+
+ m_dirty = true;
}
-void GLCanvas3D::do_scale()
+void GLCanvas3D::do_scale(const std::string& snapshot_type)
{
if (m_model == nullptr)
return;
+ if (!snapshot_type.empty())
+ wxGetApp().plater()->take_snapshot(_(snapshot_type));
+
std::set<std::pair<int, int>> done; // keeps track of modified instances
Selection::EMode selection_mode = m_selection.get_mode();
@@ -3228,18 +3284,27 @@ void GLCanvas3D::do_scale()
if (!done.empty())
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
+
+ m_dirty = true;
}
-void GLCanvas3D::do_flatten()
+void GLCanvas3D::do_flatten(const Vec3d& normal, const std::string& snapshot_type)
{
- do_rotate();
+ if (!snapshot_type.empty())
+ wxGetApp().plater()->take_snapshot(_(snapshot_type));
+
+ m_selection.flattening_rotate(normal);
+ do_rotate(L("")); // avoid taking another snapshot
}
-void GLCanvas3D::do_mirror()
+void GLCanvas3D::do_mirror(const std::string& snapshot_type)
{
if (m_model == nullptr)
return;
+ if (!snapshot_type.empty())
+ wxGetApp().plater()->take_snapshot(_(snapshot_type));
+
std::set<std::pair<int, int>> done; // keeps track of modified instances
Selection::EMode selection_mode = m_selection.get_mode();
@@ -3278,6 +3343,8 @@ void GLCanvas3D::do_mirror()
}
post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
+
+ m_dirty = true;
}
void GLCanvas3D::set_camera_zoom(double zoom)
@@ -3290,8 +3357,8 @@ void GLCanvas3D::set_camera_zoom(double zoom)
void GLCanvas3D::update_gizmos_on_off_state()
{
set_as_dirty();
- m_gizmos.update_data(*this);
- m_gizmos.refresh_on_off_state(get_selection());
+ m_gizmos.update_data();
+ m_gizmos.refresh_on_off_state();
}
void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool focus_on)
@@ -3305,6 +3372,12 @@ void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool foc
}
}
+void GLCanvas3D::handle_layers_data_focus_event(const t_layer_height_range range, const EditorType type)
+{
+ std::string field = "layer_" + std::to_string(type) + "_" + std::to_string(range.first) + "_" + std::to_string(range.second);
+ handle_sidebar_focus_event(field, true);
+}
+
void GLCanvas3D::update_ui_from_settings()
{
m_camera.set_type(wxGetApp().app_config->get("use_perspective_camera"));
@@ -3337,9 +3410,9 @@ GLCanvas3D::WipeTowerInfo GLCanvas3D::get_wipe_tower_info() const
if (vol->is_wipe_tower) {
wti.m_pos = Vec2d(m_config->opt_float("wipe_tower_x"),
m_config->opt_float("wipe_tower_y"));
- wti.m_rotation = (M_PI/180.) * m_config->opt_float("wipe_tower_rotation_angle");
- const BoundingBoxf3& bb = vol->bounding_box;
- wti.m_bb_size = Vec2d(bb.size().x(), bb.size().y());
+ wti.rotation = (M_PI/180.) * m_config->opt_float("wipe_tower_rotation_angle");
+ const BoundingBoxf3& bb = vol->bounding_box();
+ wti.bb_size = Vec2d(bb.size()(0), bb.size()(1));
break;
}
}
@@ -3383,17 +3456,45 @@ bool GLCanvas3D::_is_shown_on_screen() const
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
}
+// Getter for the const char*[]
+static bool string_getter(const bool is_undo, int idx, const char** out_text)
+{
+ return wxGetApp().plater()->undo_redo_string_getter(is_undo, idx, out_text);
+}
+
+void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x)
+{
+ const wxString& stack_name = _(is_undo ? L("Undo") : L("Redo"));
+ ImGuiWrapper* imgui = wxGetApp().imgui();
+
+ const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width();
+ imgui->set_next_window_pos(x, m_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f);
+ imgui->set_next_window_bg_alpha(0.5f);
+ imgui->begin(wxString::Format(_(L("%s Stack")), stack_name),
+ ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
+
+ int hovered = m_imgui_undo_redo_hovered_pos;
+ int selected = -1;
+ const float em = static_cast<float>(wxGetApp().em_unit());
+
+ if (imgui->undo_redo_list(ImVec2(12 * em, 20 * em), is_undo, &string_getter, hovered, selected))
+ m_imgui_undo_redo_hovered_pos = hovered;
+ else
+ m_imgui_undo_redo_hovered_pos = -1;
+
+ if (selected >= 0)
+ is_undo ? wxGetApp().plater()->undo_to(selected) : wxGetApp().plater()->redo_to(selected);
+
+ imgui->text(wxString::Format(_(L("%s %d Action")), stack_name, hovered + 1));
+
+ imgui->end();
+}
+
bool GLCanvas3D::_init_toolbar()
{
if (!m_toolbar.is_enabled())
return true;
-#if !ENABLE_SVG_ICONS
- ItemsIconsTexture::Metadata icons_data;
- icons_data.filename = "toolbar.png";
- icons_data.icon_size = 37;
-#endif // !ENABLE_SVG_ICONS
-
BackgroundTexture::Metadata background_data;
background_data.filename = "toolbar_background.png";
background_data.left = 16;
@@ -3401,11 +3502,7 @@ bool GLCanvas3D::_init_toolbar()
background_data.right = 16;
background_data.bottom = 16;
-#if ENABLE_SVG_ICONS
if (!m_toolbar.init(background_data))
-#else
- if (!m_toolbar.init(icons_data, background_data))
-#endif // ENABLE_SVG_ICONS
{
// unable to init the toolbar texture, disable it
m_toolbar.set_enabled(false);
@@ -3422,45 +3519,37 @@ bool GLCanvas3D::_init_toolbar()
GLToolbarItem::Data item;
item.name = "add";
-#if ENABLE_SVG_ICONS
item.icon_filename = "add.svg";
-#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Add...")) + " [" + GUI::shortkey_ctrl_prefix() + "I]";
item.sprite_id = 0;
- item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); };
+ item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); };
if (!m_toolbar.add_item(item))
return false;
item.name = "delete";
-#if ENABLE_SVG_ICONS
item.icon_filename = "remove.svg";
-#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Delete")) + " [Del]";
item.sprite_id = 1;
- item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); };
- item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_delete(); };
+ item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); };
+ item.enabling_callback = []()->bool { return wxGetApp().plater()->can_delete(); };
if (!m_toolbar.add_item(item))
return false;
item.name = "deleteall";
-#if ENABLE_SVG_ICONS
item.icon_filename = "delete_all.svg";
-#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Delete all")) + " [" + GUI::shortkey_ctrl_prefix() + "Del]";
item.sprite_id = 2;
- item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); };
- item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_delete_all(); };
+ item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); };
+ item.enabling_callback = []()->bool { return wxGetApp().plater()->can_delete_all(); };
if (!m_toolbar.add_item(item))
return false;
item.name = "arrange";
-#if ENABLE_SVG_ICONS
item.icon_filename = "arrange.svg";
-#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Arrange")) + " [A]";
item.sprite_id = 3;
- item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); };
- item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_arrange(); };
+ item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); };
+ item.enabling_callback = []()->bool { return wxGetApp().plater()->can_arrange(); };
if (!m_toolbar.add_item(item))
return false;
@@ -3468,24 +3557,20 @@ bool GLCanvas3D::_init_toolbar()
return false;
item.name = "copy";
-#if ENABLE_SVG_ICONS
item.icon_filename = "copy.svg";
-#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Copy")) + " [" + GUI::shortkey_ctrl_prefix() + "C]";
item.sprite_id = 4;
- item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); };
- item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_copy_to_clipboard(); };
+ item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); };
+ item.enabling_callback = []()->bool { return wxGetApp().plater()->can_copy_to_clipboard(); };
if (!m_toolbar.add_item(item))
return false;
item.name = "paste";
-#if ENABLE_SVG_ICONS
item.icon_filename = "paste.svg";
-#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Paste")) + " [" + GUI::shortkey_ctrl_prefix() + "V]";
item.sprite_id = 5;
- item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); };
- item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_paste_from_clipboard(); };
+ item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); };
+ item.enabling_callback = []()->bool { return wxGetApp().plater()->can_paste_from_clipboard(); };
if (!m_toolbar.add_item(item))
return false;
@@ -3493,26 +3578,23 @@ bool GLCanvas3D::_init_toolbar()
return false;
item.name = "more";
-#if ENABLE_SVG_ICONS
item.icon_filename = "instance_add.svg";
-#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Add instance")) + " [+]";
item.sprite_id = 6;
- item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); };
+ item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); };
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
- item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_increase_instances(); };
+ item.enabling_callback = []()->bool { return wxGetApp().plater()->can_increase_instances(); };
+
if (!m_toolbar.add_item(item))
return false;
item.name = "fewer";
-#if ENABLE_SVG_ICONS
item.icon_filename = "instance_remove.svg";
-#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Remove instance")) + " [-]";
item.sprite_id = 7;
- item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); };
+ item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); };
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
- item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_decrease_instances(); };
+ item.enabling_callback = []()->bool { return wxGetApp().plater()->can_decrease_instances(); };
if (!m_toolbar.add_item(item))
return false;
@@ -3520,26 +3602,22 @@ bool GLCanvas3D::_init_toolbar()
return false;
item.name = "splitobjects";
-#if ENABLE_SVG_ICONS
item.icon_filename = "split_objects.svg";
-#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Split to objects"));
item.sprite_id = 8;
- item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); };
+ item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); };
item.visibility_callback = GLToolbarItem::Default_Visibility_Callback;
- item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); };
+ item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); };
if (!m_toolbar.add_item(item))
return false;
item.name = "splitvolumes";
-#if ENABLE_SVG_ICONS
item.icon_filename = "split_parts.svg";
-#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Split to parts"));
item.sprite_id = 9;
- item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); };
+ item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); };
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
- item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); };
+ item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); };
if (!m_toolbar.add_item(item))
return false;
@@ -3547,15 +3625,41 @@ bool GLCanvas3D::_init_toolbar()
return false;
item.name = "layersediting";
-#if ENABLE_SVG_ICONS
item.icon_filename = "layers_white.svg";
-#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Layers editing"));
item.sprite_id = 10;
- item.is_toggable = true;
- item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
+ item.left.toggable = true;
+ item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
item.visibility_callback = [this]()->bool { return m_process->current_printer_technology() == ptFFF; };
- item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); };
+ item.enabling_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); };
+ if (!m_toolbar.add_item(item))
+ return false;
+
+ if (!m_toolbar.add_separator())
+ return false;
+
+ item.name = "undo";
+ item.icon_filename = "undo_toolbar.svg";
+ item.tooltip = _utf8(L("Undo")) + " [" + GUI::shortkey_ctrl_prefix() + "Z]";
+ item.sprite_id = 11;
+ item.left.toggable = false;
+ item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_UNDO)); };
+ item.right.toggable = true;
+ item.right.action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; };
+ item.right.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(true, 0.5f * (left + right)); };
+ item.visibility_callback = []()->bool { return true; };
+ item.enabling_callback = [this]()->bool { return wxGetApp().plater()->can_undo(); };
+ if (!m_toolbar.add_item(item))
+ return false;
+
+ item.name = "redo";
+ item.icon_filename = "redo_toolbar.svg";
+ item.tooltip = _utf8(L("Redo")) + " [" + GUI::shortkey_ctrl_prefix() + "Y]";
+ item.sprite_id = 12;
+ item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_REDO)); };
+ item.right.action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; };
+ item.right.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(false, 0.5f * (left + right)); };
+ item.enabling_callback = [this]()->bool { return wxGetApp().plater()->can_redo(); };
if (!m_toolbar.add_item(item))
return false;
@@ -3648,7 +3752,7 @@ void GLCanvas3D::_picking_pass() const
if (m_camera_clipping_plane.is_active())
::glDisable(GL_CLIP_PLANE0);
- m_gizmos.render_current_gizmo_for_picking_pass(m_selection);
+ m_gizmos.render_current_gizmo_for_picking_pass();
if (m_multisample_allowed)
glsafe(::glEnable(GL_MULTISAMPLE));
@@ -3791,7 +3895,11 @@ void GLCanvas3D::_render_bed(float theta) const
#if ENABLE_RETINA_GL
scale_factor = m_retina_helper->get_scale_factor();
#endif // ENABLE_RETINA_GL
- m_bed.render(const_cast<GLCanvas3D*>(this), theta, m_use_VBOs, scale_factor);
+#if ENABLE_TEXTURES_FROM_SVG
+ m_bed.render(const_cast<GLCanvas3D*>(this), theta, scale_factor);
+#else
+ m_bed.render(theta, scale_factor);
+#endif // ENABLE_TEXTURES_FROM_SVG
}
void GLCanvas3D::_render_axes() const
@@ -3799,8 +3907,6 @@ void GLCanvas3D::_render_axes() const
m_bed.render_axes();
}
-
-
void GLCanvas3D::_render_objects() const
{
if (m_volumes.empty())
@@ -3811,75 +3917,44 @@ void GLCanvas3D::_render_objects() const
m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane();
- if (m_use_VBOs)
+ if (m_picking_enabled)
{
- if (m_picking_enabled)
- {
- // Update the layer editing selection to the first object selected, update the current object maximum Z.
- const_cast<LayersEditing&>(m_layers_editing).select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1);
-
- if (m_config != nullptr)
- {
- const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(false);
- m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height"));
- m_volumes.check_outside_state(m_config, nullptr);
- }
- }
+ // Update the layer editing selection to the first object selected, update the current object maximum Z.
+ const_cast<LayersEditing&>(m_layers_editing).select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1);
- if (m_use_clipping_planes)
- m_volumes.set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]);
- else
- m_volumes.set_z_range(-FLT_MAX, FLT_MAX);
-
- m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
-
- m_shader.start_using();
- if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) {
- int object_id = m_layers_editing.last_object_id;
- m_volumes.render_VBOs(GLVolumeCollection::Opaque, false, m_camera.get_view_matrix(), [object_id](const GLVolume &volume) {
- // Which volume to paint without the layer height profile shader?
- return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
- });
- // Let LayersEditing handle rendering of the active object using the layer height profile shader.
- m_layers_editing.render_volumes(*this, this->m_volumes);
- } else {
- // do not cull backfaces to show broken geometry, if any
- m_volumes.render_VBOs(GLVolumeCollection::Opaque, m_picking_enabled, m_camera.get_view_matrix(), [this](const GLVolume& volume) {
- return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
- });
+ if (m_config != nullptr)
+ {
+ const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(false);
+ m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height"));
+ m_volumes.check_outside_state(m_config, nullptr);
}
- m_volumes.render_VBOs(GLVolumeCollection::Transparent, false, m_camera.get_view_matrix());
- m_shader.stop_using();
}
+
+ if (m_use_clipping_planes)
+ m_volumes.set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]);
else
- {
- ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data());
- ::glEnable(GL_CLIP_PLANE0);
+ m_volumes.set_z_range(-FLT_MAX, FLT_MAX);
- if (m_use_clipping_planes)
- {
- glsafe(::glClipPlane(GL_CLIP_PLANE1, (GLdouble*)m_clipping_planes[0].get_data()));
- glsafe(::glEnable(GL_CLIP_PLANE1));
- glsafe(::glClipPlane(GL_CLIP_PLANE2, (GLdouble*)m_clipping_planes[1].get_data()));
- glsafe(::glEnable(GL_CLIP_PLANE2));
- }
-
+ m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
+ m_shader.start_using();
+ if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) {
+ int object_id = m_layers_editing.last_object_id;
+ m_volumes.render(GLVolumeCollection::Opaque, false, m_camera.get_view_matrix(), [object_id](const GLVolume& volume) {
+ // Which volume to paint without the layer height profile shader?
+ return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
+ });
+ // Let LayersEditing handle rendering of the active object using the layer height profile shader.
+ m_layers_editing.render_volumes(*this, this->m_volumes);
+ } else {
// do not cull backfaces to show broken geometry, if any
- m_volumes.render_legacy(GLVolumeCollection::Opaque, m_picking_enabled, m_camera.get_view_matrix(), [this](const GLVolume& volume) {
+ m_volumes.render(GLVolumeCollection::Opaque, m_picking_enabled, m_camera.get_view_matrix(), [this](const GLVolume& volume) {
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
});
- m_volumes.render_legacy(GLVolumeCollection::Transparent, false, m_camera.get_view_matrix());
-
- ::glDisable(GL_CLIP_PLANE0);
-
- if (m_use_clipping_planes)
- {
- glsafe(::glDisable(GL_CLIP_PLANE1));
- glsafe(::glDisable(GL_CLIP_PLANE2));
- }
}
-
+ m_volumes.render(GLVolumeCollection::Transparent, false, m_camera.get_view_matrix());
+ m_shader.stop_using();
+
m_camera_clipping_plane = ClippingPlane::ClipsNothing();
glsafe(::glDisable(GL_LIGHTING));
}
@@ -3916,9 +3991,6 @@ void GLCanvas3D::_render_overlays() const
_render_gizmos_overlay();
_render_warning_texture();
_render_legend_texture();
-#if !ENABLE_SVG_ICONS
- _resize_toolbars();
-#endif // !ENABLE_SVG_ICONS
_render_toolbar();
_render_view_toolbar();
@@ -3990,7 +4062,7 @@ void GLCanvas3D::_render_volumes_for_picking() const
void GLCanvas3D::_render_current_gizmo() const
{
- m_gizmos.render_current_gizmo(m_selection);
+ m_gizmos.render_current_gizmo();
}
void GLCanvas3D::_render_gizmos_overlay() const
@@ -4006,12 +4078,11 @@ void GLCanvas3D::_render_gizmos_overlay() const
m_gizmos.set_overlay_icon_size(size); //! #ys_FIXME_experiment
#endif /* __WXMSW__ */
- m_gizmos.render_overlay(*this, m_selection);
+ m_gizmos.render_overlay();
}
void GLCanvas3D::_render_toolbar() const
{
-#if ENABLE_SVG_ICONS
#if ENABLE_RETINA_GL
// m_toolbar.set_scale(m_retina_helper->get_scale_factor());
const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(true);
@@ -4066,20 +4137,12 @@ void GLCanvas3D::_render_toolbar() const
}
}
m_toolbar.set_position(top, left);
-#else
-#if ENABLE_RETINA_GL
- m_toolbar.set_icons_scale(m_retina_helper->get_scale_factor());
-#else
- m_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor());
-#endif /* __WXMSW__ */
-#endif // ENABLE_SVG_ICONS
m_toolbar.render(*this);
}
void GLCanvas3D::_render_view_toolbar() const
{
-#if ENABLE_SVG_ICONS
#if ENABLE_RETINA_GL
// m_view_toolbar.set_scale(m_retina_helper->get_scale_factor());
const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale();
@@ -4099,13 +4162,6 @@ void GLCanvas3D::_render_view_toolbar() const
float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom;
float left = -0.5f * (float)cnv_size.get_width() * inv_zoom;
m_view_toolbar.set_position(top, left);
-#else
-#if ENABLE_RETINA_GL
- m_view_toolbar.set_icons_scale(m_retina_helper->get_scale_factor());
-#else
- m_view_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor());
-#endif /* __WXMSW__ */
-#endif // ENABLE_SVG_ICONS
m_view_toolbar.render(*this);
}
@@ -4258,16 +4314,9 @@ void GLCanvas3D::_render_sla_slices() const
void GLCanvas3D::_render_selection_sidebar_hints() const
{
- if (m_use_VBOs)
- m_shader.start_using();
-
- m_selection.render_sidebar_hints(m_sidebar_field);
-
- if (m_use_VBOs)
- m_shader.stop_using();
+ m_selection.render_sidebar_hints(m_sidebar_field, m_shader);
}
-
void GLCanvas3D::_update_volumes_hover_state() const
{
for (GLVolume* v : m_volumes.volumes)
@@ -4476,8 +4525,6 @@ void GLCanvas3D::_load_print_toolpaths()
_3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), volume);
}
- volume.bounding_box = volume.indexed_vertex_array.bounding_box();
- volume.indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
}
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<double>& color_print_values)
@@ -4642,19 +4689,13 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
// Copy the content back to the old GLVolume.
vol.indexed_vertex_array = vol_new.indexed_vertex_array;
- // Finalize a bounding box of the old GLVolume.
- vol.bounding_box = vol.indexed_vertex_array.bounding_box();
- // Clear the buffers, but keep them pre-allocated.
+ // Clear the buffers, but keep them pre-allocated.
vol_new.indexed_vertex_array.clear();
// Just make sure that clear did not clear the reserved memory.
vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
}
}
}
- for (GLVolume *vol : vols) {
- vol->bounding_box = vol->indexed_vertex_array.bounding_box();
- vol->indexed_vertex_array.shrink_to_fit();
- }
});
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results";
@@ -4663,8 +4704,6 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
[](const GLVolume *volume) { return volume->empty(); }),
m_volumes.volumes.end());
- for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
- m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end";
}
@@ -4684,7 +4723,7 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
{
const Print *print;
const std::vector<float> *tool_colors;
- WipeTower::xy wipe_tower_pos;
+ Vec2f wipe_tower_pos;
float wipe_tower_angle;
// Number of vertices (each vertex is 6x4=24 bytes long)
@@ -4715,12 +4754,13 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
ctxt.print = print;
ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
if (print->wipe_tower_data().priming && print->config().single_extruder_multi_material_priming)
- ctxt.priming.emplace_back(*print->wipe_tower_data().priming.get());
+ for (int i=0; i<print->wipe_tower_data().priming.get()->size(); ++i)
+ ctxt.priming.emplace_back(print->wipe_tower_data().priming.get()->at(i));
if (print->wipe_tower_data().final_purge)
ctxt.final.emplace_back(*print->wipe_tower_data().final_purge.get());
ctxt.wipe_tower_angle = ctxt.print->config().wipe_tower_rotation_angle.value/180.f * PI;
- ctxt.wipe_tower_pos = WipeTower::xy(ctxt.print->config().wipe_tower_x.value, ctxt.print->config().wipe_tower_y.value);
+ ctxt.wipe_tower_pos = Vec2f(ctxt.print->config().wipe_tower_x.value, ctxt.print->config().wipe_tower_y.value);
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
@@ -4782,19 +4822,19 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
WipeTower::Extrusion e_prev = extrusions.extrusions[i-1];
if (!extrusions.priming) { // wipe tower extrusions describe the wipe tower at the origin with no rotation
- e_prev.pos.rotate(ctxt.wipe_tower_angle);
- e_prev.pos.translate(ctxt.wipe_tower_pos);
+ e_prev.pos = Eigen::Rotation2Df(ctxt.wipe_tower_angle) * e_prev.pos;
+ e_prev.pos += ctxt.wipe_tower_pos;
}
for (; i < j; ++i) {
WipeTower::Extrusion e = extrusions.extrusions[i];
assert(e.width > 0.f);
if (!extrusions.priming) {
- e.pos.rotate(ctxt.wipe_tower_angle);
- e.pos.translate(ctxt.wipe_tower_pos);
+ e.pos = Eigen::Rotation2Df(ctxt.wipe_tower_angle) * e.pos;
+ e.pos += ctxt.wipe_tower_pos;
}
- lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y));
+ lines.emplace_back(Point::new_scale(e_prev.pos.x(), e_prev.pos.y()), Point::new_scale(e.pos.x(), e.pos.y()));
widths.emplace_back(e.width);
e_prev = e;
@@ -4814,18 +4854,12 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
// Copy the content back to the old GLVolume.
vol.indexed_vertex_array = vol_new.indexed_vertex_array;
- // Finalize a bounding box of the old GLVolume.
- vol.bounding_box = vol.indexed_vertex_array.bounding_box();
// Clear the buffers, but keep them pre-allocated.
vol_new.indexed_vertex_array.clear();
// Just make sure that clear did not clear the reserved memory.
vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
}
}
- for (GLVolume *vol : vols) {
- vol->bounding_box = vol->indexed_vertex_array.bounding_box();
- vol->indexed_vertex_array.shrink_to_fit();
- }
});
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results";
@@ -4834,8 +4868,6 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
[](const GLVolume *volume) { return volume->empty(); }),
m_volumes.volumes.end());
- for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
- m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end";
}
@@ -5012,17 +5044,6 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
}
}
}
-
- // finalize volumes and sends geometry to gpu
- if (m_volumes.volumes.size() > initial_volumes_count)
- {
- for (size_t i = initial_volumes_count; i < m_volumes.volumes.size(); ++i)
- {
- GLVolume* volume = m_volumes.volumes[i];
- volume->bounding_box = volume->indexed_vertex_array.bounding_box();
- volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
- }
- }
}
void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
@@ -5067,17 +5088,6 @@ void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data,
return;
}
-
- // finalize volumes and sends geometry to gpu
- if (m_volumes.volumes.size() > initial_volumes_count)
- {
- for (size_t i = initial_volumes_count; i < m_volumes.volumes.size(); ++i)
- {
- GLVolume* volume = m_volumes.volumes[i];
- volume->bounding_box = volume->indexed_vertex_array.bounding_box();
- volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
- }
- }
}
bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data)
@@ -5306,10 +5316,6 @@ void GLCanvas3D::_load_gcode_retractions(const GCodePreviewData& preview_data)
_3DScene::point3_to_verts(position.position, position.width, position.height, *volume);
}
-
- // finalize volumes and sends geometry to gpu
- volume->bounding_box = volume->indexed_vertex_array.bounding_box();
- volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
}
}
@@ -5337,10 +5343,6 @@ void GLCanvas3D::_load_gcode_unretractions(const GCodePreviewData& preview_data)
_3DScene::point3_to_verts(position.position, position.width, position.height, *volume);
}
-
- // finalize volumes and sends geometry to gpu
- volume->bounding_box = volume->indexed_vertex_array.bounding_box();
- volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
}
}
@@ -5366,7 +5368,7 @@ void GLCanvas3D::_load_fff_shells()
instance_ids[i] = i;
}
- m_volumes.load_object(model_obj, object_id, instance_ids, "object", m_use_VBOs && m_initialized);
+ m_volumes.load_object(model_obj, object_id, instance_ids, "object");
++object_id;
}
@@ -5376,7 +5378,7 @@ void GLCanvas3D::_load_fff_shells()
double max_z = print->objects()[0]->model_object()->get_model()->bounding_box().max(2);
const PrintConfig& config = print->config();
unsigned int extruders_count = config.nozzle_diameter.size();
- if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) {
+ if ((extruders_count > 1) && config.wipe_tower && !config.complete_objects) {
float depth = print->get_wipe_tower_depth();
// Calculate wipe tower brim spacing.
@@ -5386,9 +5388,9 @@ void GLCanvas3D::_load_fff_shells()
float brim_spacing = print->config().nozzle_diameter.values[0] * 1.25f - first_layer_height * (1. - M_PI_4);
if (!print->is_step_done(psWipeTower))
- depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1) ;
+ depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1);
m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
- m_use_VBOs && m_initialized, !print->is_step_done(psWipeTower), brim_spacing * 4.5f);
+ !print->is_step_done(psWipeTower), brim_spacing * 4.5f);
}
}
}
@@ -5406,8 +5408,8 @@ void GLCanvas3D::_load_sla_shells()
const TriangleMesh &mesh, const float color[4], bool outside_printer_detection_enabled) {
m_volumes.volumes.emplace_back(new GLVolume(color));
GLVolume& v = *m_volumes.volumes.back();
- v.indexed_vertex_array.load_mesh(mesh, m_use_VBOs);
- v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled;
+ v.indexed_vertex_array.load_mesh(mesh);
+ v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled;
v.composite_id.volume_id = volume_id;
v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0));
v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation));
@@ -5432,9 +5434,6 @@ void GLCanvas3D::_load_sla_shells()
double shift_z = obj->get_current_elevation();
for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) {
GLVolume& v = *m_volumes.volumes[i];
- // finalize volumes and sends geometry to gpu
- v.bounding_box = v.indexed_vertex_array.bounding_box();
- v.indexed_vertex_array.finalize_geometry(m_use_VBOs);
// apply shift z
v.set_sla_shift_z(shift_z);
}
@@ -5525,7 +5524,7 @@ void GLCanvas3D::_update_toolpath_volumes_outside_state()
for (GLVolume* volume : m_volumes.volumes)
{
- volume->is_outside = ((print_volume.radius() > 0.0) && volume->is_extrusion_path) ? !print_volume.contains(volume->bounding_box) : false;
+ volume->is_outside = ((print_volume.radius() > 0.0) && volume->is_extrusion_path) ? !print_volume.contains(volume->bounding_box()) : false;
}
}
@@ -5606,76 +5605,6 @@ bool GLCanvas3D::_is_any_volume_outside() const
return false;
}
-#if !ENABLE_SVG_ICONS
-void GLCanvas3D::_resize_toolbars() const
-{
- Size cnv_size = get_canvas_size();
- float zoom = get_camera_zoom();
- float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
-
-#if ENABLE_RETINA_GL
- m_toolbar.set_icons_scale(m_retina_helper->get_scale_factor());
-#else
- m_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor());
-#endif /* __WXMSW__ */
-
- GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation();
-
- switch (m_toolbar.get_layout_type())
- {
- default:
- case GLToolbar::Layout::Horizontal:
- {
- // centers the toolbar on the top edge of the 3d scene
- float top, left;
- if (orientation == GLToolbar::Layout::Top)
- {
- top = 0.5f * (float)cnv_size.get_height() * inv_zoom;
- left = -0.5f * m_toolbar.get_width() * inv_zoom;
- }
- else
- {
- top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom;
- left = -0.5f * m_toolbar.get_width() * inv_zoom;
- }
- m_toolbar.set_position(top, left);
- break;
- }
- case GLToolbar::Layout::Vertical:
- {
- // centers the toolbar on the right edge of the 3d scene
- float top, left;
- if (orientation == GLToolbar::Layout::Left)
- {
- top = 0.5f * m_toolbar.get_height() * inv_zoom;
- left = (-0.5f * (float)cnv_size.get_width()) * inv_zoom;
- }
- else
- {
- top = 0.5f * m_toolbar.get_height() * inv_zoom;
- left = (0.5f * (float)cnv_size.get_width() - m_toolbar.get_width()) * inv_zoom;
- }
- m_toolbar.set_position(top, left);
- break;
- }
- }
-
- if (m_view_toolbar != nullptr)
- {
-#if ENABLE_RETINA_GL
- m_view_toolbar.set_icons_scale(m_retina_helper->get_scale_factor());
-#else
- m_view_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor());
-#endif /* __WXMSW__ */
-
- // places the toolbar on the bottom-left corner of the 3d scene
- float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom;
- float left = -0.5f * (float)cnv_size.get_width() * inv_zoom;
- m_view_toolbar.set_position(top, left);
- }
-}
-#endif // !ENABLE_SVG_ICONS
-
void GLCanvas3D::_update_selection_from_hover()
{
bool ctrl_pressed = wxGetKeyState(WXK_CONTROL);
@@ -5722,13 +5651,29 @@ void GLCanvas3D::_update_selection_from_hover()
if (m_selection.is_empty())
m_gizmos.reset_all_states();
else
- m_gizmos.refresh_on_off_state(m_selection);
+ m_gizmos.refresh_on_off_state();
- m_gizmos.update_data(*this);
+ m_gizmos.update_data();
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
m_dirty = true;
}
+bool GLCanvas3D::_deactivate_undo_redo_toolbar_items()
+{
+ if (m_toolbar.is_item_pressed("undo"))
+ {
+ m_toolbar.force_right_action(m_toolbar.get_item_id("undo"), *this);
+ return true;
+ }
+ else if (m_toolbar.is_item_pressed("redo"))
+ {
+ m_toolbar.force_right_action(m_toolbar.get_item_id("redo"), *this);
+ return true;
+ }
+
+ return false;
+}
+
const Print* GLCanvas3D::fff_print() const
{
return (m_process == nullptr) ? nullptr : m_process->fff_print();
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 36d16035e..fdf7cf38e 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -11,6 +11,7 @@
#include "Camera.hpp"
#include "Selection.hpp"
#include "Gizmos/GLGizmosManager.hpp"
+#include "GUI_ObjectLayers.hpp"
#include <float.h>
@@ -125,6 +126,8 @@ wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
+wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
+wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
class GLCanvas3D
{
@@ -451,7 +454,6 @@ private:
// Screen is only refreshed from the OnIdle handler if it is dirty.
bool m_dirty;
bool m_initialized;
- bool m_use_VBOs;
bool m_apply_zoom_to_volumes_filter;
mutable std::vector<int> m_hover_volume_idxs;
bool m_warning_texture_enabled;
@@ -476,10 +478,16 @@ private:
GCodePreviewVolumeIndex m_gcode_preview_volume_index;
+#if ENABLE_RENDER_PICKING_PASS
+ bool m_show_picking_texture;
+#endif // ENABLE_RENDER_PICKING_PASS
+
#if ENABLE_RENDER_STATISTICS
RenderStats m_render_stats;
#endif // ENABLE_RENDER_STATISTICS
+ int m_imgui_undo_redo_hovered_pos{ -1 };
+
public:
GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar);
~GLCanvas3D();
@@ -489,7 +497,7 @@ public:
wxGLCanvas* get_wxglcanvas() { return m_canvas; }
const wxGLCanvas* get_wxglcanvas() const { return m_canvas; }
- bool init(bool useVBOs);
+ bool init();
void post_event(wxEvent &&event);
void set_as_dirty();
@@ -508,6 +516,9 @@ public:
const Selection& get_selection() const { return m_selection; }
Selection& get_selection() { return m_selection; }
+ const GLGizmosManager& get_gizmos_manager() const { return m_gizmos; }
+ GLGizmosManager& get_gizmos_manager() { return m_gizmos; }
+
void bed_shape_changed();
void set_clipping_plane(unsigned int id, const ClippingPlane& plane)
@@ -591,11 +602,12 @@ public:
void set_tooltip(const std::string& tooltip) const;
- void do_move();
- void do_rotate();
- void do_scale();
- void do_flatten();
- void do_mirror();
+ // the following methods add a snapshot to the undo/redo stack, unless the given string is empty
+ void do_move(const std::string& snapshot_type);
+ void do_rotate(const std::string& snapshot_type);
+ void do_scale(const std::string& snapshot_type);
+ void do_flatten(const Vec3d& normal, const std::string& snapshot_type);
+ void do_mirror(const std::string& snapshot_type);
void set_camera_zoom(double zoom);
@@ -603,6 +615,7 @@ public:
void reset_all_gizmos() { m_gizmos.reset_all_states(); }
void handle_sidebar_focus_event(const std::string& opt_key, bool focus_on);
+ void handle_layers_data_focus_event(const t_layer_height_range range, const EditorType type);
void update_ui_from_settings();
@@ -686,6 +699,7 @@ private:
#endif // ENABLE_SHOW_CAMERA_TARGET
void _render_sla_slices() const;
void _render_selection_sidebar_hints() const;
+ void _render_undo_redo_stack(const bool is_undo, float pos_x);
void _update_volumes_hover_state() const;
@@ -741,13 +755,11 @@ private:
bool _is_any_volume_outside() const;
-#if !ENABLE_SVG_ICONS
- void _resize_toolbars() const;
-#endif // !ENABLE_SVG_ICONS
-
// updates the selection from the content of m_hover_volume_idxs
void _update_selection_from_hover();
+ bool _deactivate_undo_redo_toolbar_items();
+
static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
public:
diff --git a/src/slic3r/GUI/GLCanvas3DManager.cpp b/src/slic3r/GUI/GLCanvas3DManager.cpp
index a1430ef22..b2a3161e8 100644
--- a/src/slic3r/GUI/GLCanvas3DManager.cpp
+++ b/src/slic3r/GUI/GLCanvas3DManager.cpp
@@ -192,7 +192,6 @@ GLCanvas3DManager::GLInfo GLCanvas3DManager::s_gl_info;
GLCanvas3DManager::GLCanvas3DManager()
: m_context(nullptr)
, m_gl_initialized(false)
- , m_use_VBOs(false)
{
}
@@ -266,8 +265,6 @@ void GLCanvas3DManager::init_gl()
if (!m_gl_initialized)
{
glewInit();
- const AppConfig* config = GUI::get_app_config();
- m_use_VBOs = s_gl_info.is_version_greater_or_equal_to(2, 0);
m_gl_initialized = true;
if (GLEW_EXT_texture_compression_s3tc)
s_compressed_textures_supported = true;
@@ -323,7 +320,7 @@ bool GLCanvas3DManager::init(GLCanvas3D& canvas)
if (!m_gl_initialized)
init_gl();
- return canvas.init(m_use_VBOs);
+ return canvas.init();
}
void GLCanvas3DManager::detect_multisample(int* attribList)
diff --git a/src/slic3r/GUI/GLCanvas3DManager.hpp b/src/slic3r/GUI/GLCanvas3DManager.hpp
index 7a600dcbd..c0e0df622 100644
--- a/src/slic3r/GUI/GLCanvas3DManager.hpp
+++ b/src/slic3r/GUI/GLCanvas3DManager.hpp
@@ -75,7 +75,6 @@ private:
wxGLContext* m_context;
static GLInfo s_gl_info;
bool m_gl_initialized;
- bool m_use_VBOs;
static EMultisampleState s_multisample;
static bool s_compressed_textures_supported;
diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp
index f8082ad7e..0002eda2d 100644
--- a/src/slic3r/GUI/GLToolbar.cpp
+++ b/src/slic3r/GUI/GLToolbar.cpp
@@ -34,20 +34,24 @@ wxDEFINE_EVENT(EVT_GLVIEWTOOLBAR_PREVIEW, SimpleEvent);
const GLToolbarItem::ActionCallback GLToolbarItem::Default_Action_Callback = [](){};
const GLToolbarItem::VisibilityCallback GLToolbarItem::Default_Visibility_Callback = []()->bool { return true; };
-const GLToolbarItem::EnabledStateCallback GLToolbarItem::Default_Enabled_State_Callback = []()->bool { return true; };
+const GLToolbarItem::EnablingCallback GLToolbarItem::Default_Enabling_Callback = []()->bool { return true; };
+const GLToolbarItem::RenderCallback GLToolbarItem::Default_Render_Callback = [](float, float, float, float){};
+
+GLToolbarItem::Data::Option::Option()
+ : toggable(false)
+ , action_callback(Default_Action_Callback)
+ , render_callback(nullptr)
+{
+}
GLToolbarItem::Data::Data()
: name("")
-#if ENABLE_SVG_ICONS
, icon_filename("")
-#endif // ENABLE_SVG_ICONS
, tooltip("")
, sprite_id(-1)
- , is_toggable(false)
, visible(true)
- , action_callback(Default_Action_Callback)
, visibility_callback(Default_Visibility_Callback)
- , enabled_state_callback(Default_Enabled_State_Callback)
+ , enabling_callback(Default_Enabling_Callback)
{
}
@@ -55,6 +59,7 @@ GLToolbarItem::GLToolbarItem(GLToolbarItem::EType type, const GLToolbarItem::Dat
: m_type(type)
, m_state(Normal)
, m_data(data)
+ , m_last_action_type(Undefined)
{
}
@@ -70,7 +75,7 @@ bool GLToolbarItem::update_visibility()
bool GLToolbarItem::update_enabled_state()
{
- bool enabled = m_data.enabled_state_callback();
+ bool enabled = m_data.enabling_callback();
bool ret = (is_enabled() != enabled);
if (ret)
m_state = enabled ? GLToolbarItem::Normal : GLToolbarItem::Disabled;
@@ -81,6 +86,14 @@ bool GLToolbarItem::update_enabled_state()
void GLToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const
{
GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(tex_width, tex_height, icon_size));
+
+ if (is_pressed())
+ {
+ if ((m_last_action_type == Left) && m_data.left.can_render())
+ m_data.left.render_callback(left, right, bottom, top);
+ else if ((m_last_action_type == Right) && m_data.right.can_render())
+ m_data.right.render_callback(left, right, bottom, top);
+ }
}
GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const
@@ -105,14 +118,6 @@ GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int
return uvs;
}
-#if !ENABLE_SVG_ICONS
-ItemsIconsTexture::Metadata::Metadata()
- : filename("")
- , icon_size(0)
-{
-}
-#endif // !ENABLE_SVG_ICONS
-
BackgroundTexture::Metadata::Metadata()
: filename("")
, left(0)
@@ -122,9 +127,7 @@ BackgroundTexture::Metadata::Metadata()
{
}
-#if ENABLE_SVG_ICONS
const float GLToolbar::Default_Icons_Size = 40.0f;
-#endif // ENABLE_SVG_ICONS
GLToolbar::Layout::Layout()
: type(Horizontal)
@@ -134,32 +137,21 @@ GLToolbar::Layout::Layout()
, border(0.0f)
, separator_size(0.0f)
, gap_size(0.0f)
-#if ENABLE_SVG_ICONS
, icons_size(Default_Icons_Size)
, scale(1.0f)
-#else
- , icons_scale(1.0f)
-#endif // ENABLE_SVG_ICONS
, width(0.0f)
, height(0.0f)
, dirty(true)
{
}
-#if ENABLE_SVG_ICONS
GLToolbar::GLToolbar(GLToolbar::EType type, const std::string& name)
-#else
-GLToolbar::GLToolbar(GLToolbar::EType type)
-#endif // ENABLE_SVG_ICONS
: m_type(type)
-#if ENABLE_SVG_ICONS
, m_name(name)
-#endif // ENABLE_SVG_ICONS
, m_enabled(false)
-#if ENABLE_SVG_ICONS
, m_icons_texture_dirty(true)
-#endif // ENABLE_SVG_ICONS
, m_tooltip("")
+ , m_pressed_toggable_id(-1)
{
}
@@ -171,27 +163,13 @@ GLToolbar::~GLToolbar()
}
}
-#if ENABLE_SVG_ICONS
bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture)
-#else
-bool GLToolbar::init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture)
-#endif // ENABLE_SVG_ICONS
{
-#if ENABLE_SVG_ICONS
if (m_background_texture.texture.get_id() != 0)
return true;
std::string path = resources_dir() + "/icons/";
bool res = false;
-#else
- if (m_icons_texture.texture.get_id() != 0)
- return true;
-
- std::string path = resources_dir() + "/icons/";
- bool res = !icons_texture.filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture.filename, false);
- if (res)
- m_icons_texture.metadata = icons_texture;
-#endif // ENABLE_SVG_ICONS
if (!background_texture.filename.empty())
res = m_background_texture.texture.load_from_file(path + background_texture.filename, false, true);
@@ -247,7 +225,6 @@ void GLToolbar::set_gap_size(float size)
m_layout.dirty = true;
}
-#if ENABLE_SVG_ICONS
void GLToolbar::set_icons_size(float size)
{
if (m_layout.icons_size != size)
@@ -267,13 +244,6 @@ void GLToolbar::set_scale(float scale)
m_icons_texture_dirty = true;
}
}
-#else
-void GLToolbar::set_icons_scale(float scale)
-{
- m_layout.icons_scale = scale;
- m_layout.dirty = true;
-}
-#endif // ENABLE_SVG_ICONS
bool GLToolbar::is_enabled() const
{
@@ -341,7 +311,7 @@ void GLToolbar::select_item(const std::string& name)
bool GLToolbar::is_item_pressed(const std::string& name) const
{
- for (GLToolbarItem* item : m_items)
+ for (const GLToolbarItem* item : m_items)
{
if (item->get_name() == name)
return item->is_pressed();
@@ -352,7 +322,7 @@ bool GLToolbar::is_item_pressed(const std::string& name) const
bool GLToolbar::is_item_disabled(const std::string& name) const
{
- for (GLToolbarItem* item : m_items)
+ for (const GLToolbarItem* item : m_items)
{
if (item->get_name() == name)
return item->is_disabled();
@@ -363,7 +333,7 @@ bool GLToolbar::is_item_disabled(const std::string& name) const
bool GLToolbar::is_item_visible(const std::string& name) const
{
- for (GLToolbarItem* item : m_items)
+ for (const GLToolbarItem* item : m_items)
{
if (item->get_name() == name)
return item->is_visible();
@@ -372,11 +342,46 @@ bool GLToolbar::is_item_visible(const std::string& name) const
return false;
}
+bool GLToolbar::is_any_item_pressed() const
+{
+ for (const GLToolbarItem* item : m_items)
+ {
+ if (item->is_pressed())
+ return true;
+ }
+
+ return false;
+}
+
+unsigned int GLToolbar::get_item_id(const std::string& name) const
+{
+ for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
+ {
+ if (m_items[i]->get_name() == name)
+ return i;
+ }
+
+ return -1;
+}
+
+void GLToolbar::force_left_action(unsigned int item_id, GLCanvas3D& parent)
+{
+ do_action(GLToolbarItem::Left, item_id, parent, false);
+}
+
+void GLToolbar::force_right_action(unsigned int item_id, GLCanvas3D& parent)
+{
+ do_action(GLToolbarItem::Right, item_id, parent, false);
+}
+
bool GLToolbar::update_items_state()
{
bool ret = false;
ret |= update_items_visibility();
ret |= update_items_enabled_state();
+ if (!is_any_item_pressed())
+ m_pressed_toggable_id = -1;
+
return ret;
}
@@ -385,10 +390,8 @@ void GLToolbar::render(const GLCanvas3D& parent) const
if (!m_enabled || m_items.empty())
return;
-#if ENABLE_SVG_ICONS
if (m_icons_texture_dirty)
generate_icons_texture();
-#endif // ENABLE_SVG_ICONS
switch (m_layout.type)
{
@@ -440,10 +443,11 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
m_mouse_capture.left = true;
m_mouse_capture.parent = &parent;
processed = true;
- if ((item_id != -2) && !m_items[item_id]->is_separator())
+ if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Left)))
{
// mouse is inside an icon
- do_action((unsigned int)item_id, parent);
+ do_action(GLToolbarItem::Left, (unsigned int)item_id, parent, true);
+ parent.set_as_dirty();
}
}
else if (evt.MiddleDown())
@@ -455,6 +459,13 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
{
m_mouse_capture.right = true;
m_mouse_capture.parent = &parent;
+ processed = true;
+ if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Right)))
+ {
+ // mouse is inside an icon
+ do_action(GLToolbarItem::Right, (unsigned int)item_id, parent, true);
+ parent.set_as_dirty();
+ }
}
else if (evt.LeftUp())
processed = true;
@@ -492,20 +503,12 @@ float GLToolbar::get_width_horizontal() const
float GLToolbar::get_width_vertical() const
{
-#if ENABLE_SVG_ICONS
return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale;
-#else
- return 2.0f * m_layout.border * m_layout.icons_scale + m_icons_texture.metadata.icon_size * m_layout.icons_scale;
-#endif // ENABLE_SVG_ICONS
}
float GLToolbar::get_height_horizontal() const
{
-#if ENABLE_SVG_ICONS
return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale;
-#else
- return 2.0f * m_layout.border * m_layout.icons_scale + m_icons_texture.metadata.icon_size * m_layout.icons_scale;
-#endif // ENABLE_SVG_ICONS
}
float GLToolbar::get_height_vertical() const
@@ -515,7 +518,6 @@ float GLToolbar::get_height_vertical() const
float GLToolbar::get_main_size() const
{
-#if ENABLE_SVG_ICONS
float size = 2.0f * m_layout.border;
for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
{
@@ -531,59 +533,64 @@ float GLToolbar::get_main_size() const
if (m_items.size() > 1)
size += ((float)m_items.size() - 1.0f) * m_layout.gap_size;
- size *= m_layout.scale;
-#else
- float size = 2.0f * m_layout.border * m_layout.icons_scale;
- for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
- {
- if (!m_items[i]->is_visible())
- continue;
-
- if (m_items[i]->is_separator())
- size += m_layout.separator_size * m_layout.icons_scale;
- else
- size += (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale;
- }
-
- if (m_items.size() > 1)
- size += ((float)m_items.size() - 1.0f) * m_layout.gap_size * m_layout.icons_scale;
-#endif // ENABLE_SVG_ICONS
-
- return size;
+ return size * m_layout.scale;
}
-void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent)
+void GLToolbar::do_action(GLToolbarItem::EActionType type, unsigned int item_id, GLCanvas3D& parent, bool check_hover)
{
- if (item_id < (unsigned int)m_items.size())
+ if ((m_pressed_toggable_id == -1) || (m_pressed_toggable_id == item_id))
{
- GLToolbarItem* item = m_items[item_id];
- if ((item != nullptr) && !item->is_separator() && item->is_hovered())
+ if (item_id < (unsigned int)m_items.size())
{
- if (item->is_toggable())
+ GLToolbarItem* item = m_items[item_id];
+ if ((item != nullptr) && !item->is_separator() && (!check_hover || item->is_hovered()))
{
- GLToolbarItem::EState state = item->get_state();
- if (state == GLToolbarItem::Hover)
- item->set_state(GLToolbarItem::HoverPressed);
- else if (state == GLToolbarItem::HoverPressed)
- item->set_state(GLToolbarItem::Hover);
+ if (((type == GLToolbarItem::Right) && item->is_right_toggable()) ||
+ ((type == GLToolbarItem::Left) && item->is_left_toggable()))
+ {
+ GLToolbarItem::EState state = item->get_state();
+ if (state == GLToolbarItem::Hover)
+ item->set_state(GLToolbarItem::HoverPressed);
+ else if (state == GLToolbarItem::HoverPressed)
+ item->set_state(GLToolbarItem::Hover);
+ else if (state == GLToolbarItem::Pressed)
+ item->set_state(GLToolbarItem::Normal);
+ else if (state == GLToolbarItem::Normal)
+ item->set_state(GLToolbarItem::Pressed);
+
+ m_pressed_toggable_id = item->is_pressed() ? item_id : -1;
+ item->reset_last_action_type();
- parent.render();
- item->do_action();
- }
- else
- {
- if (m_type == Radio)
- select_item(item->get_name());
+ parent.render();
+ switch (type)
+ {
+ default:
+ case GLToolbarItem::Left: { item->do_left_action(); break; }
+ case GLToolbarItem::Right: { item->do_right_action(); break; }
+ }
+ }
else
- item->set_state(GLToolbarItem::HoverPressed);
-
- parent.render();
- item->do_action();
- if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled))
{
- // the item may get disabled during the action, if not, set it back to hover state
- item->set_state(GLToolbarItem::Hover);
+ if (m_type == Radio)
+ select_item(item->get_name());
+ else
+ item->set_state(item->is_hovered() ? GLToolbarItem::HoverPressed : GLToolbarItem::Pressed);
+
+ item->reset_last_action_type();
parent.render();
+ switch (type)
+ {
+ default:
+ case GLToolbarItem::Left: { item->do_left_action(); break; }
+ case GLToolbarItem::Right: { item->do_right_action(); break; }
+ }
+
+ if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled))
+ {
+ // the item may get disabled during the action, if not, set it back to hover state
+ item->set_state(GLToolbarItem::Hover);
+ parent.render();
+ }
}
}
}
@@ -609,20 +616,12 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
-#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
-#else
- float factor = m_layout.icons_scale * inv_zoom;
-#endif // ENABLE_SVG_ICONS
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
-#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
-#else
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
-#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@@ -714,20 +713,12 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
-#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
-#else
- float factor = m_layout.icons_scale * inv_zoom;
-#endif // ENABLE_SVG_ICONS
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
-#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
-#else
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
-#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@@ -831,20 +822,12 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
-#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
-#else
- float factor = m_layout.icons_scale * inv_zoom;
-#endif // ENABLE_SVG_ICONS
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
-#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
-#else
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
-#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@@ -914,20 +897,12 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D&
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
-#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
-#else
- float factor = m_layout.icons_scale * inv_zoom;
-#endif // ENABLE_SVG_ICONS
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
-#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
-#else
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
-#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@@ -993,34 +968,15 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D&
void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
{
-#if ENABLE_SVG_ICONS
unsigned int tex_id = m_icons_texture.get_id();
int tex_width = m_icons_texture.get_width();
int tex_height = m_icons_texture.get_height();
-#else
- unsigned int tex_id = m_icons_texture.texture.get_id();
- int tex_width = m_icons_texture.texture.get_width();
- int tex_height = m_icons_texture.texture.get_height();
-#endif // ENABLE_SVG_ICONS
-
-#if !ENABLE_SVG_ICONS
- if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
- return;
-#endif // !ENABLE_SVG_ICONS
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
-#if ENABLE_SVG_ICONS
float factor = inv_zoom * m_layout.scale;
-#else
- float factor = inv_zoom * m_layout.icons_scale;
-#endif // ENABLE_SVG_ICONS
-#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
-#else
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
-#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@@ -1121,10 +1077,8 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
left += scaled_border;
top -= scaled_border;
-#if ENABLE_SVG_ICONS
if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
-#endif // ENABLE_SVG_ICONS
// renders icons
for (const GLToolbarItem* item : m_items)
@@ -1136,11 +1090,7 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
left += separator_stride;
else
{
-#if ENABLE_SVG_ICONS
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
-#else
- item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, m_icons_texture.metadata.icon_size);
-#endif // ENABLE_SVG_ICONS
left += icon_stride;
}
}
@@ -1148,34 +1098,15 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
void GLToolbar::render_vertical(const GLCanvas3D& parent) const
{
-#if ENABLE_SVG_ICONS
unsigned int tex_id = m_icons_texture.get_id();
int tex_width = m_icons_texture.get_width();
int tex_height = m_icons_texture.get_height();
-#else
- unsigned int tex_id = m_icons_texture.texture.get_id();
- int tex_width = m_icons_texture.texture.get_width();
- int tex_height = m_icons_texture.texture.get_height();
-#endif // ENABLE_SVG_ICONS
-
-#if !ENABLE_SVG_ICONS
- if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
- return;
-#endif // !ENABLE_SVG_ICONS
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
-#if ENABLE_SVG_ICONS
float factor = inv_zoom * m_layout.scale;
-#else
- float factor = inv_zoom * m_layout.icons_scale;
-#endif // ENABLE_SVG_ICONS
-#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
-#else
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * factor;
-#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@@ -1276,10 +1207,8 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const
left += scaled_border;
top -= scaled_border;
-#if ENABLE_SVG_ICONS
if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
-#endif // ENABLE_SVG_ICONS
// renders icons
for (const GLToolbarItem* item : m_items)
@@ -1291,17 +1220,12 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const
top -= separator_stride;
else
{
-#if ENABLE_SVG_ICONS
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
-#else
- item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, m_icons_texture.metadata.icon_size);
-#endif // ENABLE_SVG_ICONS
top -= icon_stride;
}
}
}
-#if ENABLE_SVG_ICONS
bool GLToolbar::generate_icons_texture() const
{
std::string path = resources_dir() + "/icons/";
@@ -1337,7 +1261,6 @@ bool GLToolbar::generate_icons_texture() const
return res;
}
-#endif // ENABLE_SVG_ICONS
bool GLToolbar::update_items_visibility()
{
@@ -1371,9 +1294,15 @@ bool GLToolbar::update_items_enabled_state()
{
bool ret = false;
- for (GLToolbarItem* item : m_items)
+ for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
{
+ GLToolbarItem* item = m_items[i];
ret |= item->update_enabled_state();
+ if (item->is_enabled() && (m_pressed_toggable_id != -1) && (m_pressed_toggable_id != i))
+ {
+ ret = true;
+ item->set_state(GLToolbarItem::Disabled);
+ }
}
if (ret)
diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp
index 24314d60f..875b2f9f6 100644
--- a/src/slic3r/GUI/GLToolbar.hpp
+++ b/src/slic3r/GUI/GLToolbar.hpp
@@ -36,7 +36,8 @@ class GLToolbarItem
public:
typedef std::function<void()> ActionCallback;
typedef std::function<bool()> VisibilityCallback;
- typedef std::function<bool()> EnabledStateCallback;
+ typedef std::function<bool()> EnablingCallback;
+ typedef std::function<void(float, float, float, float)> RenderCallback;
enum EType : unsigned char
{
@@ -45,6 +46,14 @@ public:
Num_Types
};
+ enum EActionType : unsigned char
+ {
+ Undefined,
+ Left,
+ Right,
+ Num_Action_Types
+ };
+
enum EState : unsigned char
{
Normal,
@@ -57,29 +66,42 @@ public:
struct Data
{
+ struct Option
+ {
+ bool toggable;
+ ActionCallback action_callback;
+ RenderCallback render_callback;
+
+ Option();
+
+ bool can_render() const { return toggable && (render_callback != nullptr); }
+ };
+
std::string name;
-#if ENABLE_SVG_ICONS
std::string icon_filename;
-#endif // ENABLE_SVG_ICONS
std::string tooltip;
unsigned int sprite_id;
- bool is_toggable;
+ // mouse left click
+ Option left;
+ // mouse right click
+ Option right;
bool visible;
- ActionCallback action_callback;
VisibilityCallback visibility_callback;
- EnabledStateCallback enabled_state_callback;
+ EnablingCallback enabling_callback;
Data();
};
static const ActionCallback Default_Action_Callback;
static const VisibilityCallback Default_Visibility_Callback;
- static const EnabledStateCallback Default_Enabled_State_Callback;
+ static const EnablingCallback Default_Enabling_Callback;
+ static const RenderCallback Default_Render_Callback;
private:
EType m_type;
EState m_state;
Data m_data;
+ EActionType m_last_action_type;
public:
GLToolbarItem(EType type, const Data& data);
@@ -88,22 +110,28 @@ public:
void set_state(EState state) { m_state = state; }
const std::string& get_name() const { return m_data.name; }
-#if ENABLE_SVG_ICONS
const std::string& get_icon_filename() const { return m_data.icon_filename; }
-#endif // ENABLE_SVG_ICONS
const std::string& get_tooltip() const { return m_data.tooltip; }
- void do_action() { m_data.action_callback(); }
+ void do_left_action() { m_last_action_type = Left; m_data.left.action_callback(); }
+ void do_right_action() { m_last_action_type = Right; m_data.right.action_callback(); }
bool is_enabled() const { return m_state != Disabled; }
bool is_disabled() const { return m_state == Disabled; }
bool is_hovered() const { return (m_state == Hover) || (m_state == HoverPressed); }
bool is_pressed() const { return (m_state == Pressed) || (m_state == HoverPressed); }
-
- bool is_toggable() const { return m_data.is_toggable; }
bool is_visible() const { return m_data.visible; }
bool is_separator() const { return m_type == Separator; }
+ bool is_left_toggable() const { return m_data.left.toggable; }
+ bool is_right_toggable() const { return m_data.right.toggable; }
+
+ bool has_left_render_callback() const { return m_data.left.render_callback != nullptr; }
+ bool has_right_render_callback() const { return m_data.right.render_callback != nullptr; }
+
+ EActionType get_last_action_type() const { return m_last_action_type; }
+ void reset_last_action_type() { m_last_action_type = Undefined; }
+
// returns true if the state changes
bool update_visibility();
// returns true if the state changes
@@ -118,27 +146,6 @@ private:
friend class GLToolbar;
};
-#if !ENABLE_SVG_ICONS
-// items icon textures are assumed to be square and all with the same size in pixels, no internal check is done
-// icons are layed-out into the texture starting from the top-left corner in the same order as enum GLToolbarItem::EState
-// from left to right
-struct ItemsIconsTexture
-{
- struct Metadata
- {
- // path of the file containing the icons' texture
- std::string filename;
- // size of the square icons, in pixels
- unsigned int icon_size;
-
- Metadata();
- };
-
- GLTexture texture;
- Metadata metadata;
-};
-#endif // !ENABLE_SVG_ICONS
-
struct BackgroundTexture
{
struct Metadata
@@ -164,9 +171,7 @@ struct BackgroundTexture
class GLToolbar
{
public:
-#if ENABLE_SVG_ICONS
static const float Default_Icons_Size;
-#endif // ENABLE_SVG_ICONS
enum EType : unsigned char
{
@@ -201,12 +206,8 @@ public:
float border;
float separator_size;
float gap_size;
-#if ENABLE_SVG_ICONS
float icons_size;
float scale;
-#else
- float icons_scale;
-#endif // ENABLE_SVG_ICONS
float width;
float height;
@@ -219,16 +220,10 @@ private:
typedef std::vector<GLToolbarItem*> ItemsList;
EType m_type;
-#if ENABLE_SVG_ICONS
std::string m_name;
-#endif // ENABLE_SVG_ICONS
bool m_enabled;
-#if ENABLE_SVG_ICONS
mutable GLTexture m_icons_texture;
mutable bool m_icons_texture_dirty;
-#else
- ItemsIconsTexture m_icons_texture;
-#endif // ENABLE_SVG_ICONS
BackgroundTexture m_background_texture;
mutable Layout m_layout;
@@ -249,20 +244,13 @@ private:
MouseCapture m_mouse_capture;
std::string m_tooltip;
+ unsigned int m_pressed_toggable_id;
public:
-#if ENABLE_SVG_ICONS
GLToolbar(EType type, const std::string& name);
-#else
- explicit GLToolbar(EType type);
-#endif // ENABLE_SVG_ICONS
~GLToolbar();
-#if ENABLE_SVG_ICONS
bool init(const BackgroundTexture::Metadata& background_texture);
-#else
- bool init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture);
-#endif // ENABLE_SVG_ICONS
Layout::EType get_layout_type() const;
void set_layout_type(Layout::EType type);
@@ -273,12 +261,8 @@ public:
void set_border(float border);
void set_separator_size(float size);
void set_gap_size(float size);
-#if ENABLE_SVG_ICONS
void set_icons_size(float size);
void set_scale(float scale);
-#else
- void set_icons_scale(float scale);
-#endif // ENABLE_SVG_ICONS
bool is_enabled() const;
void set_enabled(bool enable);
@@ -295,8 +279,14 @@ public:
bool is_item_disabled(const std::string& name) const;
bool is_item_visible(const std::string& name) const;
- const std::string& get_tooltip() const { return m_tooltip; }
+ bool is_any_item_pressed() const;
+ unsigned int get_item_id(const std::string& name) const;
+
+ void force_left_action(unsigned int item_id, GLCanvas3D& parent);
+ void force_right_action(unsigned int item_id, GLCanvas3D& parent);
+
+ const std::string& get_tooltip() const { return m_tooltip; }
// returns true if any item changed its state
bool update_items_state();
@@ -312,7 +302,7 @@ private:
float get_height_horizontal() const;
float get_height_vertical() const;
float get_main_size() const;
- void do_action(unsigned int item_id, GLCanvas3D& parent);
+ void do_action(GLToolbarItem::EActionType type, unsigned int item_id, GLCanvas3D& parent, bool check_hover);
std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent);
std::string update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent);
std::string update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent);
@@ -324,9 +314,7 @@ private:
void render_horizontal(const GLCanvas3D& parent) const;
void render_vertical(const GLCanvas3D& parent) const;
-#if ENABLE_SVG_ICONS
bool generate_icons_texture() const;
-#endif // ENABLE_SVG_ICONS
// returns true if any item changed its state
bool update_items_visibility();
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 4f1c3adc8..8a376c3a3 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -141,6 +141,18 @@ GUI_App::GUI_App()
, m_imgui(new ImGuiWrapper())
{}
+GUI_App::~GUI_App()
+{
+ if (app_config != nullptr)
+ delete app_config;
+
+ if (preset_bundle != nullptr)
+ delete preset_bundle;
+
+ if (preset_updater != nullptr)
+ delete preset_updater;
+}
+
bool GUI_App::OnInit()
{
try {
@@ -922,6 +934,11 @@ ObjectList* GUI_App::obj_list()
return sidebar().obj_list();
}
+ObjectLayers* GUI_App::obj_layers()
+{
+ return sidebar().obj_layers();
+}
+
Plater* GUI_App::plater()
{
return plater_;
diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp
index 3f8b23e2d..e69503ff8 100644
--- a/src/slic3r/GUI/GUI_App.hpp
+++ b/src/slic3r/GUI/GUI_App.hpp
@@ -95,6 +95,7 @@ public:
bool initialized() const { return m_initialized; }
GUI_App();
+ ~GUI_App();
static unsigned get_colour_approx_luma(const wxColour &colour);
static bool dark_mode();
@@ -155,6 +156,7 @@ public:
ObjectManipulation* obj_manipul();
ObjectSettings* obj_settings();
ObjectList* obj_list();
+ ObjectLayers* obj_layers();
Plater* plater();
std::vector<ModelObject*> *model_objects();
diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp
new file mode 100644
index 000000000..b30d3ecd3
--- /dev/null
+++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp
@@ -0,0 +1,341 @@
+#include "GUI_ObjectLayers.hpp"
+#include "GUI_ObjectList.hpp"
+
+#include "OptionsGroup.hpp"
+#include "PresetBundle.hpp"
+#include "libslic3r/Model.hpp"
+#include "GLCanvas3D.hpp"
+
+#include <boost/algorithm/string.hpp>
+
+#include "I18N.hpp"
+
+#include <wx/wupdlock.h>
+
+namespace Slic3r
+{
+namespace GUI
+{
+
+ObjectLayers::ObjectLayers(wxWindow* parent) :
+ OG_Settings(parent, true)
+{
+ m_grid_sizer = new wxFlexGridSizer(3, 5, 5); // "Min Z", "Max Z", "Layer height" & buttons sizer
+ m_grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
+
+ // Legend for object layers
+ for (const std::string col : { "Min Z", "Max Z", "Layer height" }) {
+ auto temp = new wxStaticText(m_parent, wxID_ANY, _(L(col)), wxDefaultPosition, /*size*/wxDefaultSize, wxST_ELLIPSIZE_MIDDLE);
+ temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
+ temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
+ temp->SetFont(wxGetApp().bold_font());
+
+ m_grid_sizer->Add(temp);
+ }
+
+ m_og->sizer->Clear(true);
+ m_og->sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX ? 0 : 5);
+
+ m_bmp_delete = ScalableBitmap(parent, "remove_copies"/*"cross"*/);
+ m_bmp_add = ScalableBitmap(parent, "add_copies");
+}
+
+void ObjectLayers::select_editor(LayerRangeEditor* editor, const bool is_last_edited_range)
+{
+ if (is_last_edited_range && m_selection_type == editor->type()) {
+ /* Workaround! Under OSX we should use CallAfter() for SetFocus() after LayerEditors "reorganizations",
+ * because of selected control's strange behavior:
+ * cursor is set to the control, but blue border - doesn't.
+ * And as a result we couldn't edit this control.
+ * */
+#ifdef __WXOSX__
+ wxTheApp->CallAfter([editor]() {
+#endif
+ editor->SetFocus();
+ editor->SetInsertionPointEnd();
+#ifdef __WXOSX__
+ });
+#endif
+ }
+}
+
+wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range)
+{
+ const bool is_last_edited_range = range == m_selectable_range;
+
+ auto set_focus_data = [range, this](const EditorType type)
+ {
+ m_selectable_range = range;
+ m_selection_type = type;
+ };
+
+ auto update_focus_data = [range, this](const t_layer_height_range& new_range, EditorType type, bool enter_pressed)
+ {
+ // change selectable range for new one, if enter was pressed or if same range was selected
+ if (enter_pressed || m_selectable_range == range)
+ m_selectable_range = new_range;
+ if (enter_pressed)
+ m_selection_type = type;
+ };
+
+ // Add control for the "Min Z"
+
+ auto editor = new LayerRangeEditor(this, double_to_string(range.first), etMinZ,
+ set_focus_data, [range, update_focus_data, this](coordf_t min_z, bool enter_pressed)
+ {
+ if (fabs(min_z - range.first) < EPSILON) {
+ m_selection_type = etUndef;
+ return false;
+ }
+
+ // data for next focusing
+ coordf_t max_z = min_z < range.second ? range.second : min_z + 0.5;
+ const t_layer_height_range& new_range = { min_z, max_z };
+ update_focus_data(new_range, etMinZ, enter_pressed);
+
+ return wxGetApp().obj_list()->edit_layer_range(range, new_range);
+ });
+
+ select_editor(editor, is_last_edited_range);
+ m_grid_sizer->Add(editor);
+
+ // Add control for the "Max Z"
+
+ editor = new LayerRangeEditor(this, double_to_string(range.second), etMaxZ,
+ set_focus_data, [range, update_focus_data, this](coordf_t max_z, bool enter_pressed)
+ {
+ if (fabs(max_z - range.second) < EPSILON || range.first > max_z) {
+ m_selection_type = etUndef;
+ return false; // LayersList would not be updated/recreated
+ }
+
+ // data for next focusing
+ const t_layer_height_range& new_range = { range.first, max_z };
+ update_focus_data(new_range, etMaxZ, enter_pressed);
+
+ return wxGetApp().obj_list()->edit_layer_range(range, new_range);
+ });
+
+ select_editor(editor, is_last_edited_range);
+ m_grid_sizer->Add(editor);
+
+ // Add control for the "Layer height"
+
+ editor = new LayerRangeEditor(this,
+ double_to_string(m_object->layer_config_ranges[range].option("layer_height")->getFloat()),
+ etLayerHeight, set_focus_data, [range, this](coordf_t layer_height, bool)
+ {
+ return wxGetApp().obj_list()->edit_layer_range(range, layer_height);
+ });
+
+ select_editor(editor, is_last_edited_range);
+
+ auto sizer = new wxBoxSizer(wxHORIZONTAL);
+ sizer->Add(editor);
+ m_grid_sizer->Add(sizer);
+
+ return sizer;
+}
+
+void ObjectLayers::create_layers_list()
+{
+ for (const auto layer : m_object->layer_config_ranges)
+ {
+ const t_layer_height_range& range = layer.first;
+ auto sizer = create_layer(range);
+
+ auto del_btn = new ScalableButton(m_parent, wxID_ANY, m_bmp_delete);
+ del_btn->SetToolTip(_(L("Remove layer")));
+
+ sizer->Add(del_btn, 0, wxRIGHT | wxLEFT, em_unit(m_parent));
+
+ del_btn->Bind(wxEVT_BUTTON, [this, range](wxEvent &event) {
+ wxGetApp().obj_list()->del_layer_range(range);
+ });
+
+ auto add_btn = new ScalableButton(m_parent, wxID_ANY, m_bmp_add);
+ add_btn->SetToolTip(_(L("Add layer")));
+
+ sizer->Add(add_btn, 0, wxRIGHT, em_unit(m_parent));
+
+ add_btn->Bind(wxEVT_BUTTON, [this, range](wxEvent &event) {
+ wxGetApp().obj_list()->add_layer_range_after_current(range);
+ });
+ }
+}
+
+void ObjectLayers::update_layers_list()
+{
+ ObjectList* objects_ctrl = wxGetApp().obj_list();
+ if (objects_ctrl->multiple_selection()) return;
+
+ const auto item = objects_ctrl->GetSelection();
+ if (!item) return;
+
+ const int obj_idx = objects_ctrl->get_selected_obj_idx();
+ if (obj_idx < 0) return;
+
+ const ItemType type = objects_ctrl->GetModel()->GetItemType(item);
+ if (!(type & (itLayerRoot | itLayer))) return;
+
+ m_object = objects_ctrl->object(obj_idx);
+ if (!m_object || m_object->layer_config_ranges.empty()) return;
+
+ // Delete all controls from options group except of the legends
+
+ const int cols = m_grid_sizer->GetEffectiveColsCount();
+ const int rows = m_grid_sizer->GetEffectiveRowsCount();
+ for (int idx = cols*rows-1; idx >= cols; idx--) {
+ wxSizerItem* t = m_grid_sizer->GetItem(idx);
+ if (t->IsSizer())
+ t->GetSizer()->Clear(true);
+ else
+ t->DeleteWindows();
+ m_grid_sizer->Remove(idx);
+ }
+
+ // Add new control according to the selected item
+
+ if (type & itLayerRoot)
+ create_layers_list();
+ else
+ create_layer(objects_ctrl->GetModel()->GetLayerRangeByItem(item));
+
+ m_parent->Layout();
+}
+
+void ObjectLayers::update_scene_from_editor_selection() const
+{
+ // needed to show the visual hints in 3D scene
+ wxGetApp().plater()->canvas3D()->handle_layers_data_focus_event(m_selectable_range, m_selection_type);
+}
+
+void ObjectLayers::UpdateAndShow(const bool show)
+{
+ if (show)
+ update_layers_list();
+
+ OG_Settings::UpdateAndShow(show);
+}
+
+void ObjectLayers::msw_rescale()
+{
+ m_bmp_delete.msw_rescale();
+ m_bmp_add.msw_rescale();
+}
+
+void ObjectLayers::reset_selection()
+{
+ m_selectable_range = { 0.0, 0.0 };
+ m_selection_type = etLayerHeight;
+}
+
+LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
+ const wxString& value,
+ EditorType type,
+ std::function<void(EditorType)> set_focus_data_fn,
+ std::function<bool(coordf_t, bool enter_pressed)> edit_fn
+ ) :
+ m_valid_value(value),
+ m_type(type),
+ m_set_focus_data(set_focus_data_fn),
+ wxTextCtrl(parent->m_parent, wxID_ANY, value, wxDefaultPosition,
+ wxSize(8 * em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER)
+{
+ this->SetFont(wxGetApp().normal_font());
+
+ this->Bind(wxEVT_TEXT_ENTER, [this, edit_fn](wxEvent&)
+ {
+ m_enter_pressed = true;
+ // If LayersList wasn't updated/recreated, we can call wxEVT_KILL_FOCUS.Skip()
+ if (m_type&etLayerHeight) {
+ if (!edit_fn(get_value(), true))
+ SetValue(m_valid_value);
+ else
+ m_valid_value = double_to_string(get_value());
+ m_call_kill_focus = true;
+ }
+ else if (!edit_fn(get_value(), true)) {
+ SetValue(m_valid_value);
+ m_call_kill_focus = true;
+ }
+ }, this->GetId());
+
+ this->Bind(wxEVT_KILL_FOCUS, [this, edit_fn](wxFocusEvent& e)
+ {
+ if (!m_enter_pressed) {
+#ifndef __WXGTK__
+ /* Update data for next editor selection.
+ * But under GTK it lucks like there is no information about selected control at e.GetWindow(),
+ * so we'll take it from wxEVT_LEFT_DOWN event
+ * */
+ LayerRangeEditor* new_editor = dynamic_cast<LayerRangeEditor*>(e.GetWindow());
+ if (new_editor)
+ new_editor->set_focus_data();
+#endif // not __WXGTK__
+ // If LayersList wasn't updated/recreated, we should call e.Skip()
+ if (m_type & etLayerHeight) {
+ if (!edit_fn(get_value(), false))
+ SetValue(m_valid_value);
+ else
+ m_valid_value = double_to_string(get_value());
+ e.Skip();
+ }
+ else if (!edit_fn(get_value(), false)) {
+ SetValue(m_valid_value);
+ e.Skip();
+ }
+ }
+ else if (m_call_kill_focus) {
+ m_call_kill_focus = false;
+ e.Skip();
+ }
+ }, this->GetId());
+
+ this->Bind(wxEVT_SET_FOCUS, [this, parent](wxFocusEvent& e)
+ {
+ set_focus_data();
+ parent->update_scene_from_editor_selection();
+ e.Skip();
+ }, this->GetId());
+
+#ifdef __WXGTK__ // Workaround! To take information about selectable range
+ this->Bind(wxEVT_LEFT_DOWN, [this](wxEvent& e)
+ {
+ set_focus_data();
+ e.Skip();
+ }, this->GetId());
+#endif //__WXGTK__
+
+ this->Bind(wxEVT_CHAR, ([this](wxKeyEvent& event)
+ {
+ // select all text using Ctrl+A
+ if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL))
+ this->SetSelection(-1, -1); //select all
+ event.Skip();
+ }));
+}
+
+coordf_t LayerRangeEditor::get_value()
+{
+ wxString str = GetValue();
+
+ coordf_t layer_height;
+ // Replace the first occurence of comma in decimal number.
+ str.Replace(",", ".", false);
+ if (str == ".")
+ layer_height = 0.0;
+ else
+ {
+ if (!str.ToCDouble(&layer_height) || layer_height < 0.0f)
+ {
+ show_error(m_parent, _(L("Invalid numeric input.")));
+ SetValue(double_to_string(layer_height));
+ }
+ }
+
+ return layer_height;
+}
+
+} //namespace GUI
+} //namespace Slic3r \ No newline at end of file
diff --git a/src/slic3r/GUI/GUI_ObjectLayers.hpp b/src/slic3r/GUI/GUI_ObjectLayers.hpp
new file mode 100644
index 000000000..f274183e2
--- /dev/null
+++ b/src/slic3r/GUI/GUI_ObjectLayers.hpp
@@ -0,0 +1,88 @@
+#ifndef slic3r_GUI_ObjectLayers_hpp_
+#define slic3r_GUI_ObjectLayers_hpp_
+
+#include "GUI_ObjectSettings.hpp"
+#include "wxExtensions.hpp"
+
+#ifdef __WXOSX__
+#include "../libslic3r/PrintConfig.hpp"
+#endif
+
+class wxBoxSizer;
+
+namespace Slic3r {
+class ModelObject;
+
+namespace GUI {
+class ConfigOptionsGroup;
+
+typedef double coordf_t;
+typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
+
+class ObjectLayers;
+
+enum EditorType
+{
+ etUndef = 0,
+ etMinZ = 1,
+ etMaxZ = 2,
+ etLayerHeight = 4,
+};
+
+class LayerRangeEditor : public wxTextCtrl
+{
+ bool m_enter_pressed { false };
+ bool m_call_kill_focus { false };
+ wxString m_valid_value;
+ EditorType m_type;
+
+ std::function<void(EditorType)> m_set_focus_data;
+
+public:
+ LayerRangeEditor( ObjectLayers* parent,
+ const wxString& value = wxEmptyString,
+ EditorType type = etUndef,
+ std::function<void(EditorType)> set_focus_data_fn = [](EditorType) {;},
+ std::function<bool(coordf_t, bool)> edit_fn = [](coordf_t, bool) {return false; }
+ );
+ ~LayerRangeEditor() {}
+
+ EditorType type() const {return m_type;}
+ void set_focus_data() const { m_set_focus_data(m_type);}
+
+private:
+ coordf_t get_value();
+};
+
+class ObjectLayers : public OG_Settings
+{
+ ScalableBitmap m_bmp_delete;
+ ScalableBitmap m_bmp_add;
+ ModelObject* m_object {nullptr};
+
+ wxFlexGridSizer* m_grid_sizer;
+ t_layer_height_range m_selectable_range;
+ EditorType m_selection_type {etUndef};
+
+public:
+ ObjectLayers(wxWindow* parent);
+ ~ObjectLayers() {}
+
+ void select_editor(LayerRangeEditor* editor, const bool is_last_edited_range);
+ wxSizer* create_layer(const t_layer_height_range& range); // without_buttons
+ void create_layers_list();
+ void update_layers_list();
+
+ void update_scene_from_editor_selection() const;
+
+ void UpdateAndShow(const bool show) override;
+ void msw_rescale();
+ void reset_selection();
+ void set_selectable_range(const t_layer_height_range& range) { m_selectable_range = range; }
+
+ friend class LayerRangeEditor;
+};
+
+}}
+
+#endif // slic3r_GUI_ObjectLayers_hpp_
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index e926dcf97..9e681257c 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -1,6 +1,7 @@
#include "libslic3r/libslic3r.h"
#include "GUI_ObjectList.hpp"
#include "GUI_ObjectManipulation.hpp"
+#include "GUI_ObjectLayers.hpp"
#include "GUI_App.hpp"
#include "I18N.hpp"
@@ -24,7 +25,7 @@ namespace GUI
wxDEFINE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
// pt_FFF
-FreqSettingsBundle FREQ_SETTINGS_BUNDLE_FFF =
+SettingsBundle FREQ_SETTINGS_BUNDLE_FFF =
{
{ L("Layers and Perimeters"), { "layer_height" , "perimeters", "top_solid_layers", "bottom_solid_layers" } },
{ L("Infill") , { "fill_density", "fill_pattern" } },
@@ -35,7 +36,7 @@ FreqSettingsBundle FREQ_SETTINGS_BUNDLE_FFF =
};
// pt_SLA
-FreqSettingsBundle FREQ_SETTINGS_BUNDLE_SLA =
+SettingsBundle FREQ_SETTINGS_BUNDLE_SLA =
{
{ L("Pad and Support") , { "supports_enable", "pad_enable" } }
};
@@ -65,6 +66,14 @@ static int extruders_count()
return wxGetApp().extruders_cnt();
}
+static void take_snapshot(const wxString& snapshot_name)
+{
+ wxGetApp().plater()->take_snapshot(snapshot_name);
+}
+
+static void suppress_snapshots(){ wxGetApp().plater()->suppress_snapshots(); }
+static void allow_snapshots() { wxGetApp().plater()->allow_snapshots(); }
+
ObjectList::ObjectList(wxWindow* parent) :
wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE),
m_parent(parent)
@@ -137,20 +146,24 @@ ObjectList::ObjectList(wxWindow* parent) :
// Bind(wxEVT_KEY_DOWN, &ObjectList::OnChar, this);
{
// Accelerators
- wxAcceleratorEntry entries[6];
+ wxAcceleratorEntry entries[8];
entries[0].Set(wxACCEL_CTRL, (int) 'C', wxID_COPY);
entries[1].Set(wxACCEL_CTRL, (int) 'X', wxID_CUT);
entries[2].Set(wxACCEL_CTRL, (int) 'V', wxID_PASTE);
entries[3].Set(wxACCEL_CTRL, (int) 'A', wxID_SELECTALL);
- entries[4].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE);
- entries[5].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE);
- wxAcceleratorTable accel(6, entries);
+ entries[4].Set(wxACCEL_CTRL, (int) 'Z', wxID_UNDO);
+ entries[5].Set(wxACCEL_CTRL, (int) 'Y', wxID_REDO);
+ entries[6].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE);
+ entries[7].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE);
+ wxAcceleratorTable accel(8, entries);
SetAcceleratorTable(accel);
- this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY)); }, wxID_COPY);
- this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE)); }, wxID_PASTE);
- this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->select_item_all_children(); }, wxID_SELECTALL);
- this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->remove(); }, wxID_DELETE);
+ this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->copy(); }, wxID_COPY);
+ this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->paste(); }, wxID_PASTE);
+ this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->select_item_all_children(); }, wxID_SELECTALL);
+ this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->remove(); }, wxID_DELETE);
+ this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->undo(); }, wxID_UNDO);
+ this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->redo(); }, wxID_REDO);
}
#else __WXOSX__
Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX
@@ -350,12 +363,13 @@ DynamicPrintConfig& ObjectList::get_item_config(const wxDataViewItem& item) cons
const ItemType type = m_objects_model->GetItemType(item);
const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) :
- m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
+ m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
const int vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1;
assert(obj_idx >= 0 || ((type & itVolume) && vol_idx >=0));
return type & itVolume ?(*m_objects)[obj_idx]->volumes[vol_idx]->config :
+ type & itLayer ?(*m_objects)[obj_idx]->layer_config_ranges[m_objects_model->GetLayerRangeByItem(item)] :
(*m_objects)[obj_idx]->config;
}
@@ -441,16 +455,23 @@ void ObjectList::update_extruder_in_config(const wxDataViewItem& item)
{
if (m_prevent_update_extruder_in_config)
return;
- if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
+
+ const ItemType item_type = m_objects_model->GetItemType(item);
+ if (item_type & itObject) {
const int obj_idx = m_objects_model->GetIdByItem(item);
m_config = &(*m_objects)[obj_idx]->config;
}
else {
- const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item));
+ const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
+ if (item_type & itVolume)
+ {
const int volume_id = m_objects_model->GetVolumeIdByItem(item);
if (obj_idx < 0 || volume_id < 0)
return;
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
+ }
+ else if (item_type & itLayer)
+ m_config = &get_item_config(item);
}
wxVariant variant;
@@ -569,9 +590,75 @@ void ObjectList::selection_changed()
wxPostEvent(this, event);
}
+ if (const wxDataViewItem item = GetSelection())
+ {
+ const ItemType type = m_objects_model->GetItemType(item);
+ // to correct visual hints for layers editing on the Scene
+ if (type & (itLayer|itLayerRoot)) {
+ wxGetApp().obj_layers()->reset_selection();
+
+ if (type & itLayerRoot)
+ wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
+ else {
+ wxGetApp().obj_layers()->set_selectable_range(m_objects_model->GetLayerRangeByItem(item));
+ wxGetApp().obj_layers()->update_scene_from_editor_selection();
+ }
+ }
+ }
+
part_selection_changed();
}
+void ObjectList::fill_layer_config_ranges_cache()
+{
+ wxDataViewItemArray sel_layers;
+ GetSelections(sel_layers);
+
+ const int obj_idx = m_objects_model->GetObjectIdByItem(sel_layers[0]);
+ if (obj_idx < 0 || (int)m_objects->size() <= obj_idx)
+ return;
+
+ const t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
+ m_layer_config_ranges_cache.clear();
+
+ for (const auto layer_item : sel_layers)
+ if (m_objects_model->GetItemType(layer_item) & itLayer) {
+ auto range = m_objects_model->GetLayerRangeByItem(layer_item);
+ auto it = ranges.find(range);
+ if (it != ranges.end())
+ m_layer_config_ranges_cache[it->first] = it->second;
+ }
+}
+
+void ObjectList::paste_layers_into_list()
+{
+ const int obj_idx = m_objects_model->GetObjectIdByItem(GetSelection());
+
+ if (obj_idx < 0 || (int)m_objects->size() <= obj_idx ||
+ m_layer_config_ranges_cache.empty() || printer_technology() == ptSLA)
+ return;
+
+ const wxDataViewItem object_item = m_objects_model->GetItemById(obj_idx);
+ wxDataViewItem layers_item = m_objects_model->GetLayerRootItem(object_item);
+ if (layers_item)
+ m_objects_model->Delete(layers_item);
+
+ t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
+
+ // and create Layer item(s) according to the layer_config_ranges
+ for (const auto range : m_layer_config_ranges_cache)
+ ranges.emplace(range);
+
+ layers_item = add_layer_root_item(object_item);
+
+ changed_object(obj_idx);
+
+ select_item(layers_item);
+#ifndef __WXOSX__
+ selection_changed();
+#endif //no __WXOSX__
+}
+
void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes)
{
if ((obj_idx < 0) || ((int)m_objects->size() <= obj_idx))
@@ -580,7 +667,6 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol
if (volumes.empty())
return;
- ModelObject& model_object = *(*m_objects)[obj_idx];
const auto object_item = m_objects_model->GetItemById(obj_idx);
wxDataViewItemArray items;
@@ -590,10 +676,7 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(object_item, wxString::FromUTF8(volume->name.c_str()), volume->type(),
volume->get_mesh_errors_count()>0 ,
volume->config.has("extruder") ? volume->config.option<ConfigOptionInt>("extruder")->value : 0);
- auto opt_keys = volume->config.keys();
- if (!opt_keys.empty() && !((opt_keys.size() == 1) && (opt_keys[0] == "extruder")))
- select_item(m_objects_model->AddSettingsChild(vol_item));
-
+ add_settings_item(vol_item, &volume->config);
items.Add(vol_item);
}
@@ -653,7 +736,7 @@ void ObjectList::OnContextMenu(wxDataViewEvent&)
const wxPoint pt = get_mouse_position_in_control();
HitTest(pt, item, col);
if (!item)
-#ifdef __WXOSX__ // #ys_FIXME temporary workaround for OSX
+#ifdef __WXOSX__ // temporary workaround for OSX
// after Yosemite OS X version, HitTest return undefined item
item = GetSelection();
if (item)
@@ -699,10 +782,11 @@ void ObjectList::show_context_menu()
if (item)
{
const ItemType type = m_objects_model->GetItemType(item);
- if (!(type & (itObject | itVolume | itInstance)))
+ if (!(type & (itObject | itVolume | itLayer | itInstance)))
return;
wxMenu* menu = type & itInstance ? &m_menu_instance :
+ type & itLayer ? &m_menu_layer :
m_objects_model->GetParent(item) != wxDataViewItem(0) ? &m_menu_part :
printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object;
@@ -713,6 +797,32 @@ void ObjectList::show_context_menu()
}
}
+void ObjectList::copy()
+{
+ if (m_selection_mode & smLayer)
+ fill_layer_config_ranges_cache();
+ else
+ wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY));
+}
+
+void ObjectList::paste()
+{
+ if (!m_layer_config_ranges_cache.empty())
+ paste_layers_into_list();
+ else
+ wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE));
+}
+
+void ObjectList::undo()
+{
+ wxGetApp().plater()->undo();
+}
+
+void ObjectList::redo()
+{
+ wxGetApp().plater()->redo();
+}
+
#ifndef __WXOSX__
void ObjectList::key_event(wxKeyEvent& event)
{
@@ -727,10 +837,14 @@ void ObjectList::key_event(wxKeyEvent& event)
}
else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/))
select_item_all_children();
- else if (wxGetKeyState(wxKeyCode('C')) && wxGetKeyState(WXK_CONTROL))
- wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY));
+ else if (wxGetKeyState(wxKeyCode('C')) && wxGetKeyState(WXK_CONTROL))
+ copy();
else if (wxGetKeyState(wxKeyCode('V')) && wxGetKeyState(WXK_CONTROL))
- wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE));
+ paste();
+ else if (wxGetKeyState(wxKeyCode('Y')) && wxGetKeyState(WXK_CONTROL))
+ redo();
+ else if (wxGetKeyState(wxKeyCode('Z')) && wxGetKeyState(WXK_CONTROL))
+ undo();
else
event.Skip();
}
@@ -816,6 +930,7 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
if (m_dragged_data.type() == itInstance)
{
+ take_snapshot(_(L("Instances to Separated Objects")));
instances_to_separated_object(m_dragged_data.obj_idx(), m_dragged_data.inst_idxs());
m_dragged_data.clear();
return;
@@ -833,6 +948,8 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
// if (to_volume_id > from_volume_id) to_volume_id--;
// #endif // __WXGTK__
+ take_snapshot(_(L("Remov Volume(s)")));
+
auto& volumes = (*m_objects)[m_dragged_data.obj_idx()]->volumes;
auto delta = to_volume_id < from_volume_id ? -1 : 1;
int cnt = 0;
@@ -871,7 +988,7 @@ std::vector<std::string> ObjectList::get_options(const bool is_part)
const std::vector<std::string>& ObjectList::get_options_for_bundle(const wxString& bundle_name)
{
- const FreqSettingsBundle& bundle = printer_technology() == ptSLA ?
+ const SettingsBundle& bundle = printer_technology() == ptSLA ?
FREQ_SETTINGS_BUNDLE_SLA : FREQ_SETTINGS_BUNDLE_FFF;
for (auto& it : bundle)
@@ -881,7 +998,7 @@ const std::vector<std::string>& ObjectList::get_options_for_bundle(const wxStrin
}
#if 0
// if "Quick menu" is selected
- FreqSettingsBundle& bundle_quick = printer_technology() == ptSLA ?
+ SettingsBundle& bundle_quick = printer_technology() == ptSLA ?
m_freq_settings_sla: m_freq_settings_fff;
for (auto& it : bundle_quick)
@@ -957,7 +1074,7 @@ void ObjectList::get_settings_choice(const wxString& category_name)
if (selection_cnt > 0)
{
// Add selected items to the "Quick menu"
- FreqSettingsBundle& freq_settings = printer_technology() == ptSLA ?
+ SettingsBundle& freq_settings = printer_technology() == ptSLA ?
m_freq_settings_sla : m_freq_settings_fff;
bool changed_existing = false;
@@ -998,6 +1115,8 @@ void ObjectList::get_settings_choice(const wxString& category_name)
}
#endif
+ take_snapshot(wxString::Format(_(L("Add Settings for %s")), is_part ? _(L("Sub-object")) : _(L("Object"))));
+
std::vector <std::string> selected_options;
selected_options.reserve(selection_cnt);
for (auto sel : selections)
@@ -1027,17 +1146,29 @@ void ObjectList::get_settings_choice(const wxString& category_name)
}
- // Add settings item for object
- update_settings_item();
+ // Add settings item for object/sub-object and show them
+ show_settings(add_settings_item(GetSelection(), m_config));
}
void ObjectList::get_freq_settings_choice(const wxString& bundle_name)
{
- const std::vector<std::string>& options = get_options_for_bundle(bundle_name);
+ std::vector<std::string> options = get_options_for_bundle(bundle_name);
+
+ /* Because of we couldn't edited layer_height for ItVolume from settings list,
+ * correct options according to the selected item type :
+ * remove "layer_height" option
+ */
+ if ((m_objects_model->GetItemType(GetSelection()) & itVolume) && bundle_name == _("Layers and Perimeters")) {
+ const auto layer_height_it = std::find(options.begin(), options.end(), "layer_height");
+ if (layer_height_it != options.end())
+ options.erase(layer_height_it);
+ }
assert(m_config);
auto opt_keys = m_config->keys();
+ take_snapshot(wxString::Format(_(L("Add Settings Bundle for %s")), m_objects_model->GetItemType(GetSelection()) & itObject ? _(L("Object")) : _(L("Sub-object"))));
+
const DynamicPrintConfig& from_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
for (auto& opt_key : options)
{
@@ -1052,13 +1183,21 @@ void ObjectList::get_freq_settings_choice(const wxString& bundle_name)
}
}
- // Add settings item for object
- update_settings_item();
+ // Add settings item for object/sub-object and show them
+ show_settings(add_settings_item(GetSelection(), m_config));
}
-void ObjectList::update_settings_item()
+void ObjectList::show_settings(const wxDataViewItem settings_item)
{
- auto item = GetSelection();
+ if (!settings_item)
+ return;
+
+ select_item(settings_item);
+
+ // update object selection on Plater
+ if (!m_prevent_canvas_selection_update)
+ update_selections_on_canvas();
+/* auto item = GetSelection();
if (item) {
if (m_objects_model->GetItemType(item) == itInstance)
item = m_objects_model->GetTopParent(item);
@@ -1070,12 +1209,14 @@ void ObjectList::update_settings_item()
if (!m_prevent_canvas_selection_update)
update_selections_on_canvas();
}
- else {
+ else {
+ //# ys_FIXME ??? use case ???
auto panel = wxGetApp().sidebar().scrolled_panel();
panel->Freeze();
wxGetApp().obj_settings()->UpdateAndShow(true);
panel->Thaw();
}
+ */
}
wxMenu* ObjectList::append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type) {
@@ -1137,6 +1278,12 @@ wxMenuItem* ObjectList::append_menu_item_split(wxMenu* menu)
[this]() { return is_splittable(); }, wxGetApp().plater());
}
+wxMenuItem* ObjectList::append_menu_item_layers_editing(wxMenu* menu)
+{
+ return append_menu_item(menu, wxID_ANY, _(L("Edit Layers")), "",
+ [this](wxCommandEvent&) { layers_editing(); }, "layers", menu);
+}
+
wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_)
{
MenuWithSeparators* menu = dynamic_cast<MenuWithSeparators*>(menu_);
@@ -1301,7 +1448,11 @@ void ObjectList::create_object_popupmenu(wxMenu *menu)
append_menu_item_scale_selection_to_fit_print_volume(menu);
// Split object to parts
- m_menu_item_split = append_menu_item_split(menu);
+ append_menu_item_split(menu);
+ menu->AppendSeparator();
+
+ // Layers Editing for object
+ append_menu_item_layers_editing(menu);
menu->AppendSeparator();
// rest of a object_menu will be added later in:
@@ -1330,7 +1481,7 @@ void ObjectList::create_part_popupmenu(wxMenu *menu)
append_menu_item_fix_through_netfabb(menu);
append_menu_item_export_stl(menu);
- m_menu_item_split_part = append_menu_item_split(menu);
+ append_menu_item_split(menu);
// Append change part type
menu->AppendSeparator();
@@ -1376,7 +1527,7 @@ wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
void ObjectList::create_freq_settings_popupmenu(wxMenu *menu)
{
// Add default settings bundles
- const FreqSettingsBundle& bundle = printer_technology() == ptFFF ?
+ const SettingsBundle& bundle = printer_technology() == ptFFF ?
FREQ_SETTINGS_BUNDLE_FFF : FREQ_SETTINGS_BUNDLE_SLA;
const int extruders_cnt = extruders_count();
@@ -1391,7 +1542,7 @@ void ObjectList::create_freq_settings_popupmenu(wxMenu *menu)
}
#if 0
// Add "Quick" settings bundles
- const FreqSettingsBundle& bundle_quick = printer_technology() == ptFFF ?
+ const SettingsBundle& bundle_quick = printer_technology() == ptFFF ?
m_freq_settings_fff : m_freq_settings_sla;
for (auto& it : bundle_quick) {
@@ -1427,6 +1578,8 @@ void ObjectList::load_subobject(ModelVolumeType type)
if (m_objects_model->GetItemType(item)&itInstance)
item = m_objects_model->GetItemById(obj_idx);
+ take_snapshot(_(L("Load Part")));
+
std::vector<std::pair<wxString, bool>> volumes_info;
load_part((*m_objects)[obj_idx], volumes_info, type);
@@ -1502,6 +1655,8 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
if (instance_idx == -1)
return;
+ take_snapshot(_(L("Add Generic Subobject")));
+
// Selected object
ModelObject &model_object = *(*m_objects)[obj_idx];
// Bounding box of the selected instance in world coordinate system including the translation, without modifiers.
@@ -1576,38 +1731,54 @@ void ObjectList::del_subobject_item(wxDataViewItem& item)
ItemType type;
m_objects_model->GetItemInfo(item, type, obj_idx, idx);
- if (type == itUndef)
+ if (type & itUndef)
return;
- if (type == itSettings)
- del_settings_from_config();
- else if (type == itInstanceRoot && obj_idx != -1)
+ if (type & itSettings)
+ del_settings_from_config(m_objects_model->GetParent(item));
+ else if (type & itInstanceRoot && obj_idx != -1)
del_instances_from_object(obj_idx);
+ else if (type & itLayerRoot && obj_idx != -1)
+ del_layers_from_object(obj_idx);
+ else if (type & itLayer && obj_idx != -1)
+ del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item));
else if (idx == -1)
return;
else if (!del_subobject_from_object(obj_idx, idx, type))
return;
// If last volume item with warning was deleted, unmark object item
- if (type == itVolume && (*m_objects)[obj_idx]->get_mesh_errors_count() == 0)
+ if (type & itVolume && (*m_objects)[obj_idx]->get_mesh_errors_count() == 0)
m_objects_model->DeleteWarningIcon(m_objects_model->GetParent(item));
m_objects_model->Delete(item);
}
-void ObjectList::del_settings_from_config()
+void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item)
{
- auto opt_keys = m_config->keys();
- if (opt_keys.size() == 1 && opt_keys[0] == "extruder")
+ const bool is_layer_settings = m_objects_model->GetItemType(parent_item) == itLayer;
+
+ const int opt_cnt = m_config->keys().size();
+ if (opt_cnt == 1 && m_config->has("extruder") ||
+ is_layer_settings && opt_cnt == 2 && m_config->has("extruder") && m_config->has("layer_height"))
return;
+
+ take_snapshot(_(L("Delete Settings")));
+
int extruder = -1;
if (m_config->has("extruder"))
extruder = m_config->option<ConfigOptionInt>("extruder")->value;
+ coordf_t layer_height = 0.0;
+ if (is_layer_settings)
+ layer_height = m_config->opt_float("layer_height");
+
m_config->clear();
if (extruder >= 0)
m_config->set_key_value("extruder", new ConfigOptionInt(extruder));
+ if (is_layer_settings)
+ m_config->set_key_value("layer_height", new ConfigOptionFloat(layer_height));
}
void ObjectList::del_instances_from_object(const int obj_idx)
@@ -1616,6 +1787,8 @@ void ObjectList::del_instances_from_object(const int obj_idx)
if (instances.size() <= 1)
return;
+ take_snapshot(_(L("Delete All Instances from Object")));
+
while ( instances.size()> 1)
instances.pop_back();
@@ -1624,6 +1797,26 @@ void ObjectList::del_instances_from_object(const int obj_idx)
changed_object(obj_idx);
}
+void ObjectList::del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range)
+{
+ const auto del_range = object(obj_idx)->layer_config_ranges.find(layer_range);
+ if (del_range == object(obj_idx)->layer_config_ranges.end())
+ return;
+
+ take_snapshot(_(L("Delete Layers Range")));
+
+ object(obj_idx)->layer_config_ranges.erase(del_range);
+
+ changed_object(obj_idx);
+}
+
+void ObjectList::del_layers_from_object(const int obj_idx)
+{
+ object(obj_idx)->layer_config_ranges.clear();
+
+ changed_object(obj_idx);
+}
+
bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, const int type)
{
if (obj_idx == 1000)
@@ -1645,6 +1838,8 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
return false;
}
+ take_snapshot(_(L("Delete Subobject")));
+
object->delete_volume(idx);
if (object->volumes.size() == 1)
@@ -1661,6 +1856,8 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last intance from object.")));
return false;
}
+
+ take_snapshot(_(L("Delete Instance")));
object->delete_instance(idx);
}
else
@@ -1688,6 +1885,8 @@ void ObjectList::split()
return;
}
+ take_snapshot(_(L("Split to Parts")));
+
wxBusyCursor wait;
auto model_object = (*m_objects)[obj_idx];
@@ -1706,11 +1905,7 @@ void ObjectList::split()
volume->config.option<ConfigOptionInt>("extruder")->value : 0,
false);
// add settings to the part, if it has those
- auto opt_keys = volume->config.keys();
- if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) {
- select_item(m_objects_model->AddSettingsChild(vol_item));
- Expand(vol_item);
- }
+ add_settings_item(vol_item, &volume->config);
}
if (parent == item)
@@ -1719,6 +1914,73 @@ void ObjectList::split()
changed_object(obj_idx);
}
+void ObjectList::layers_editing()
+{
+ const auto item = GetSelection();
+ const int obj_idx = get_selected_obj_idx();
+ if (!item || obj_idx < 0)
+ return;
+
+ const wxDataViewItem obj_item = m_objects_model->GetTopParent(item);
+ wxDataViewItem layers_item = m_objects_model->GetLayerRootItem(obj_item);
+
+ // if it doesn't exist now
+ if (!layers_item.IsOk())
+ {
+ t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
+
+ // set some default value
+ if (ranges.empty()) {
+ take_snapshot(_(L("Add Layers")));
+ ranges[{ 0.0f, 2.0f }] = get_default_layer_config(obj_idx);
+ }
+
+ // create layer root item
+ layers_item = add_layer_root_item(obj_item);
+ }
+ if (!layers_item.IsOk())
+ return;
+
+ // to correct visual hints for layers editing on the Scene, reset previous selection
+ wxGetApp().obj_layers()->reset_selection();
+ wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
+
+ // select LayerRoor item and expand
+ select_item(layers_item);
+ Expand(layers_item);
+}
+
+wxDataViewItem ObjectList::add_layer_root_item(const wxDataViewItem obj_item)
+{
+ const int obj_idx = m_objects_model->GetIdByItem(obj_item);
+ if (obj_idx < 0 ||
+ object(obj_idx)->layer_config_ranges.empty() ||
+ printer_technology() == ptSLA)
+ return wxDataViewItem(0);
+
+ // create LayerRoot item
+ wxDataViewItem layers_item = m_objects_model->AddLayersRoot(obj_item);
+
+ // and create Layer item(s) according to the layer_config_ranges
+ for (const auto range : object(obj_idx)->layer_config_ranges)
+ add_layer_item(range.first, layers_item);
+
+ Expand(layers_item);
+ return layers_item;
+}
+
+DynamicPrintConfig ObjectList::get_default_layer_config(const int obj_idx)
+{
+ DynamicPrintConfig config;
+ coordf_t layer_height = object(obj_idx)->config.has("layer_height") ?
+ object(obj_idx)->config.opt_float("layer_height") :
+ wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_float("layer_height");
+ config.set_key_value("layer_height",new ConfigOptionFloat(layer_height));
+ config.set_key_value("extruder", new ConfigOptionInt(0));
+
+ return config;
+}
+
bool ObjectList::get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume)
{
auto obj_idx = get_selected_obj_idx();
@@ -1788,6 +2050,7 @@ void ObjectList::part_selection_changed()
bool update_and_show_manipulations = false;
bool update_and_show_settings = false;
+ bool update_and_show_layers = false;
const auto item = GetSelection();
@@ -1810,36 +2073,47 @@ void ObjectList::part_selection_changed()
update_and_show_manipulations = true;
}
else {
- auto parent = m_objects_model->GetParent(item);
- // Take ID of the parent object to "inform" perl-side which object have to be selected on the scene
- obj_idx = m_objects_model->GetIdByItem(parent);
- if (m_objects_model->GetItemType(item) == itSettings) {
- if (m_objects_model->GetParent(parent) == wxDataViewItem(0)) {
+ obj_idx = m_objects_model->GetObjectIdByItem(item);
+
+ const ItemType type = m_objects_model->GetItemType(item);
+ if (type & itSettings) {
+ const auto parent = m_objects_model->GetParent(item);
+ const ItemType parent_type = m_objects_model->GetItemType(parent);
+
+ if (parent_type & itObject) {
og_name = _(L("Object Settings to modify"));
m_config = &(*m_objects)[obj_idx]->config;
}
- else {
+ else if (parent_type & itVolume) {
og_name = _(L("Part Settings to modify"));
- auto main_parent = m_objects_model->GetParent(parent);
- obj_idx = m_objects_model->GetIdByItem(main_parent);
- const auto volume_id = m_objects_model->GetVolumeIdByItem(parent);
+ volume_id = m_objects_model->GetVolumeIdByItem(parent);
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
}
+ else if (parent_type & itLayer) {
+ og_name = _(L("Layer range Settings to modify"));
+ m_config = &get_item_config(parent);
+ }
update_and_show_settings = true;
}
- else if (m_objects_model->GetItemType(item) == itVolume) {
+ else if (type & itVolume) {
og_name = _(L("Part manipulation"));
volume_id = m_objects_model->GetVolumeIdByItem(item);
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
update_and_show_manipulations = true;
}
- else if (m_objects_model->GetItemType(item) == itInstance) {
+ else if (type & itInstance) {
og_name = _(L("Instance manipulation"));
update_and_show_manipulations = true;
// fill m_config by object's values
- const int obj_idx_ = m_objects_model->GetObjectIdByItem(item);
- m_config = &(*m_objects)[obj_idx_]->config;
+ m_config = &(*m_objects)[obj_idx]->config;
+ }
+ else if (type & (itLayerRoot|itLayer)) {
+ og_name = type & itLayerRoot ? _(L("Layers Editing")) : _(L("Layer Editing"));
+ update_and_show_layers = true;
+
+ if (type & itLayer)
+ m_config = &get_item_config(item);
}
}
}
@@ -1859,18 +2133,97 @@ void ObjectList::part_selection_changed()
if (update_and_show_settings)
wxGetApp().obj_settings()->get_og()->set_name(" " + og_name + " ");
+ if (printer_technology() == ptSLA)
+ update_and_show_layers = false;
+ else if (update_and_show_layers)
+ wxGetApp().obj_layers()->get_og()->set_name(" " + og_name + " ");
+
Sidebar& panel = wxGetApp().sidebar();
panel.Freeze();
+ wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
wxGetApp().obj_manipul() ->UpdateAndShow(update_and_show_manipulations);
wxGetApp().obj_settings()->UpdateAndShow(update_and_show_settings);
+ wxGetApp().obj_layers() ->UpdateAndShow(update_and_show_layers);
wxGetApp().sidebar().show_info_sizer();
panel.Layout();
panel.Thaw();
}
-void ObjectList::add_object_to_list(size_t obj_idx)
+SettingsBundle ObjectList::get_item_settings_bundle(const DynamicPrintConfig* config, const bool is_layers_range_settings)
+{
+ auto opt_keys = config->keys();
+ if (opt_keys.empty())
+ return SettingsBundle();
+
+ update_opt_keys(opt_keys); // update options list according to print technology
+
+ if (opt_keys.size() == 1 && opt_keys[0] == "extruder" ||
+ is_layers_range_settings && opt_keys.size() == 2)
+ return SettingsBundle();
+
+ const int extruders_cnt = wxGetApp().extruders_edited_cnt();
+
+ SettingsBundle bundle;
+ for (auto& opt_key : opt_keys)
+ {
+ auto category = config->def()->get(opt_key)->category;
+ if (category.empty() || (category == "Extruders" && extruders_cnt == 1))
+ continue;
+
+ std::vector< std::string > new_category;
+
+ auto& cat_opt = bundle.find(category) == bundle.end() ? new_category : bundle.at(category);
+ cat_opt.push_back(opt_key);
+ if (cat_opt.size() == 1)
+ bundle[category] = cat_opt;
+ }
+
+ return bundle;
+}
+
+// Add new SettingsItem for parent_item if it doesn't exist, or just update a digest according to new config
+wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const DynamicPrintConfig* config)
+{
+ wxDataViewItem ret = wxDataViewItem(0);
+
+ if (!parent_item)
+ return ret;
+
+ const bool is_layers_range_settings = m_objects_model->GetItemType(parent_item) == itLayer;
+ SettingsBundle cat_options = get_item_settings_bundle(config, is_layers_range_settings);
+ if (cat_options.empty())
+ return ret;
+
+ std::vector<std::string> categories;
+ categories.reserve(cat_options.size());
+ for (auto& cat : cat_options)
+ {
+ if (cat.second.size() == 1 &&
+ (cat.second[0] == "extruder" || is_layers_range_settings && cat.second[0] == "layer_height"))
+ continue;
+
+ categories.push_back(cat.first);
+ }
+
+ if (categories.empty())
+ return ret;
+
+ if (m_objects_model->GetItemType(parent_item) & itInstance)
+ parent_item = m_objects_model->GetTopParent(parent_item);
+
+ ret = m_objects_model->IsSettingsItem(parent_item) ? parent_item : m_objects_model->GetSettingsItem(parent_item);
+
+ if (!ret) ret = m_objects_model->AddSettingsChild(parent_item);
+
+ m_objects_model->UpdateSettingsDigest(ret, categories);
+ Expand(parent_item);
+
+ return ret;
+}
+
+void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
{
auto model_object = (*m_objects)[obj_idx];
const wxString& item_name = from_u8(model_object->name);
@@ -1889,11 +2242,7 @@ void ObjectList::add_object_to_list(size_t obj_idx)
!volume->config.has("extruder") ? 0 :
volume->config.option<ConfigOptionInt>("extruder")->value,
false);
- auto opt_keys = volume->config.keys();
- if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) {
- select_item(m_objects_model->AddSettingsChild(vol_item));
- Expand(vol_item);
- }
+ add_settings_item(vol_item, &volume->config);
}
Expand(item);
}
@@ -1903,14 +2252,14 @@ void ObjectList::add_object_to_list(size_t obj_idx)
increase_object_instances(obj_idx, model_object->instances.size());
// add settings to the object, if it has those
- auto opt_keys = model_object->config.keys();
- if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) {
- select_item(m_objects_model->AddSettingsChild(item));
- Expand(item);
- }
+ add_settings_item(item, &model_object->config);
+
+ // Add layers if it has
+ add_layer_root_item(item);
#ifndef __WXOSX__
- selection_changed();
+ if (call_selection_changed)
+ selection_changed();
#endif //__WXMSW__
}
@@ -1945,6 +2294,8 @@ void ObjectList::delete_from_model_and_list(const ItemType type, const int obj_i
if ( !(type&(itObject|itVolume|itInstance)) )
return;
+ take_snapshot(_(L("Delete Selected Item")));
+
if (type&itObject) {
del_object(obj_idx);
delete_object_from_list(obj_idx);
@@ -2053,16 +2404,209 @@ void ObjectList::remove()
wxDataViewItemArray sels;
GetSelections(sels);
+ wxDataViewItem parent = wxDataViewItem(0);
+
+ take_snapshot(_(L("Delete Selected")));
+ suppress_snapshots();
+
for (auto& item : sels)
{
if (m_objects_model->GetParent(item) == wxDataViewItem(0))
delete_from_model_and_list(itObject, m_objects_model->GetIdByItem(item), -1);
else {
- if (sels.size() == 1)
+ if (m_objects_model->GetItemType(item) & itLayer) {
+ parent = m_objects_model->GetParent(item);
+ wxDataViewItemArray children;
+ if (m_objects_model->GetChildren(parent, children) == 1)
+ parent = m_objects_model->GetTopParent(item);
+ }
+ else if (sels.size() == 1)
select_item(m_objects_model->GetParent(item));
+
del_subobject_item(item);
}
}
+
+ if (parent)
+ select_item(parent);
+
+ allow_snapshots();
+}
+
+void ObjectList::del_layer_range(const t_layer_height_range& range)
+{
+ const int obj_idx = get_selected_obj_idx();
+ if (obj_idx < 0) return;
+
+ t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
+
+ wxDataViewItem selectable_item = GetSelection();
+
+ if (ranges.size() == 1)
+ selectable_item = m_objects_model->GetParent(selectable_item);
+
+ wxDataViewItem layer_item = m_objects_model->GetItemByLayerRange(obj_idx, range);
+ del_subobject_item(layer_item);
+
+ select_item(selectable_item);
+}
+
+double get_min_layer_height(const int extruder_idx)
+{
+ const DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
+ return config.opt_float("min_layer_height", extruder_idx <= 0 ? 0 : extruder_idx-1);
+}
+
+double get_max_layer_height(const int extruder_idx)
+{
+ const DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
+ return config.opt_float("max_layer_height", extruder_idx <= 0 ? 0 : extruder_idx-1);
+}
+
+void ObjectList::add_layer_range_after_current(const t_layer_height_range& current_range)
+{
+ const int obj_idx = get_selected_obj_idx();
+ if (obj_idx < 0) return;
+
+ const wxDataViewItem layers_item = GetSelection();
+
+ t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
+
+ const t_layer_height_range& last_range = (--ranges.end())->first;
+
+ if (current_range == last_range)
+ {
+ take_snapshot(_(L("Add New Layers Range")));
+
+ const t_layer_height_range& new_range = { last_range.second, last_range.second + 2.0f };
+ ranges[new_range] = get_default_layer_config(obj_idx);
+ add_layer_item(new_range, layers_item);
+ }
+ else
+ {
+ const t_layer_height_range& next_range = (++ranges.find(current_range))->first;
+
+ if (current_range.second > next_range.first)
+ return; // range division has no sense
+
+ const int layer_idx = m_objects_model->GetItemIdByLayerRange(obj_idx, next_range);
+ if (layer_idx < 0)
+ return;
+
+ if (current_range.second == next_range.first)
+ {
+ const auto old_config = ranges.at(next_range);
+
+ const coordf_t delta = (next_range.second - next_range.first);
+ if (delta < get_min_layer_height(old_config.opt_int("extruder"))/*0.05f*/) // next range division has no sense
+ return;
+
+ const coordf_t midl_layer = next_range.first + 0.5f * delta;
+
+ t_layer_height_range new_range = { midl_layer, next_range.second };
+
+ take_snapshot(_(L("Add New Layers Range")));
+ suppress_snapshots();
+
+ // create new 2 layers instead of deleted one
+
+ // delete old layer
+
+ wxDataViewItem layer_item = m_objects_model->GetItemByLayerRange(obj_idx, next_range);
+ del_subobject_item(layer_item);
+
+ ranges[new_range] = old_config;
+ add_layer_item(new_range, layers_item, layer_idx);
+
+ new_range = { current_range.second, midl_layer };
+ ranges[new_range] = get_default_layer_config(obj_idx);
+ add_layer_item(new_range, layers_item, layer_idx);
+ allow_snapshots();
+ }
+ else
+ {
+ take_snapshot(_(L("Add New Layers Range")));
+
+ const t_layer_height_range new_range = { current_range.second, next_range.first };
+ ranges[new_range] = get_default_layer_config(obj_idx);
+ add_layer_item(new_range, layers_item, layer_idx);
+ }
+ }
+
+ changed_object(obj_idx);
+
+ // select item to update layers sizer
+ select_item(layers_item);
+}
+
+void ObjectList::add_layer_item(const t_layer_height_range& range,
+ const wxDataViewItem layers_item,
+ const int layer_idx /* = -1*/)
+{
+ const int obj_idx = m_objects_model->GetObjectIdByItem(layers_item);
+ if (obj_idx < 0) return;
+
+ const DynamicPrintConfig& config = object(obj_idx)->layer_config_ranges[range];
+ if (!config.has("extruder"))
+ return;
+
+ const auto layer_item = m_objects_model->AddLayersChild(layers_item,
+ range,
+ config.opt_int("extruder"),
+ layer_idx);
+ add_settings_item(layer_item, &config);
+}
+
+bool ObjectList::edit_layer_range(const t_layer_height_range& range, coordf_t layer_height)
+{
+ const int obj_idx = get_selected_obj_idx();
+ if (obj_idx < 0)
+ return false;
+
+ DynamicPrintConfig* config = &object(obj_idx)->layer_config_ranges[range];
+ if (fabs(layer_height - config->opt_float("layer_height")) < EPSILON)
+ return false;
+
+ const int extruder_idx = config->opt_int("extruder");
+
+ if (layer_height >= get_min_layer_height(extruder_idx) &&
+ layer_height <= get_max_layer_height(extruder_idx))
+ {
+ config->set_key_value("layer_height", new ConfigOptionFloat(layer_height));
+ return true;
+ }
+
+ return false;
+}
+
+bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_layer_height_range& new_range)
+{
+ const int obj_idx = get_selected_obj_idx();
+ if (obj_idx < 0) return false;
+
+ take_snapshot(_(L("Edit Layers Range")));
+
+ const ItemType sel_type = m_objects_model->GetItemType(GetSelection());
+
+ t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
+
+ const DynamicPrintConfig config = ranges[range];
+
+ ranges.erase(range);
+ ranges[new_range] = config;
+
+ wxDataViewItem root_item = m_objects_model->GetLayerRootItem(m_objects_model->GetItemById(obj_idx));
+ m_objects_model->DeleteChildren(root_item);
+
+ if (root_item.IsOk())
+ // create Layer item(s) according to the layer_config_ranges
+ for (const auto r : ranges)
+ add_layer_item(r.first, root_item);
+
+ select_item(sel_type&itLayer ? m_objects_model->GetItemByLayerRange(obj_idx, new_range) : root_item);
+ Expand(root_item);
+
+ return true;
}
void ObjectList::init_objects()
@@ -2092,11 +2636,12 @@ void ObjectList::update_selections()
m_selection_mode = smInstance;
// We doesn't update selection if SettingsItem for the current object/part is selected
- if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings )
+// if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings )
+ if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) & (itSettings | itLayerRoot | itLayer))
{
const auto item = GetSelection();
if (selection.is_single_full_object()) {
- if ( m_objects_model->GetIdByItem(m_objects_model->GetParent(item)) == selection.get_object_idx())
+ if (m_objects_model->GetObjectIdByItem(item) == selection.get_object_idx())
return;
sels.Add(m_objects_model->GetItemById(selection.get_object_idx()));
}
@@ -2217,22 +2762,18 @@ void ObjectList::update_selections_on_canvas()
auto add_to_selection = [this](const wxDataViewItem& item, Selection& selection, int instance_idx, bool as_single_selection)
{
const ItemType& type = m_objects_model->GetItemType(item);
- if ( type == itInstanceRoot || m_objects_model->GetParent(item) == wxDataViewItem(0) ) {
- wxDataViewItem obj_item = type == itInstanceRoot ? m_objects_model->GetParent(item) : item;
- selection.add_object(m_objects_model->GetIdByItem(obj_item), as_single_selection);
- return;
- }
+ const int obj_idx = m_objects_model->GetObjectIdByItem(item);
if (type == itVolume) {
- const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item));
const int vol_idx = m_objects_model->GetVolumeIdByItem(item);
selection.add_volume(obj_idx, vol_idx, std::max(instance_idx, 0), as_single_selection);
}
else if (type == itInstance) {
- const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
const int inst_idx = m_objects_model->GetInstanceIdByItem(item);
selection.add_instance(obj_idx, inst_idx, as_single_selection);
}
+ else
+ selection.add_object(obj_idx, as_single_selection);
};
// stores current instance idx before to clear the selection
@@ -2240,7 +2781,7 @@ void ObjectList::update_selections_on_canvas()
if (sel_cnt == 1) {
wxDataViewItem item = GetSelection();
- if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot))
+ if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer))
add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, true);
else
add_to_selection(item, selection, instance_idx, true);
@@ -2303,11 +2844,13 @@ void ObjectList::select_item_all_children()
}
else {
const auto item = GetSelection();
- // Some volume(instance) is selected => select all volumes(instances) inside the current object
- if (m_objects_model->GetItemType(item) & (itVolume | itInstance))
+ const ItemType item_type = m_objects_model->GetItemType(item);
+ // Some volume/layer/instance is selected => select all volumes/layers/instances inside the current object
+ if (item_type & (itVolume | itInstance | itLayer))
m_objects_model->GetChildren(m_objects_model->GetParent(item), sels);
- m_selection_mode = m_objects_model->GetItemType(item)&itVolume ? smVolume : smInstance;
+ m_selection_mode = item_type&itVolume ? smVolume :
+ item_type&itLayer ? smLayer : smInstance;
}
SetSelections(sels);
@@ -2326,8 +2869,9 @@ void ObjectList::update_selection_mode()
}
const ItemType type = m_objects_model->GetItemType(GetSelection());
- m_selection_mode = type&itSettings ? smUndef :
- type&itVolume ? smVolume : smInstance;
+ m_selection_mode = type & itSettings ? smUndef :
+ type & itLayer ? smLayer :
+ type & itVolume ? smVolume : smInstance;
}
// check last selected item. If is it possible to select it
@@ -2338,33 +2882,37 @@ bool ObjectList::check_last_selection(wxString& msg_str)
const bool is_shift_pressed = wxGetKeyState(WXK_SHIFT);
- /* We can't mix Parts and Objects/Instances.
+ /* We can't mix Volumes, Layers and Objects/Instances.
* So, show information about it
*/
const ItemType type = m_objects_model->GetItemType(m_last_selected_item);
- // check a case of a selection of the Parts from different Objects
- bool impossible_multipart_selection = false;
- if (type & itVolume && m_selection_mode == smVolume)
- {
+ // check a case of a selection of the same type items from different Objects
+ auto impossible_multi_selection = [type, this](const ItemType item_type, const SELECTION_MODE selection_mode) {
+ if (!(type & item_type && m_selection_mode & selection_mode))
+ return false;
+
wxDataViewItemArray sels;
GetSelections(sels);
- for (const auto& sel: sels)
- if (sel != m_last_selected_item &&
- m_objects_model->GetParent(sel) != m_objects_model->GetParent(m_last_selected_item))
- {
- impossible_multipart_selection = true;
- break;
- }
- }
+ for (const auto& sel : sels)
+ if (sel != m_last_selected_item &&
+ m_objects_model->GetTopParent(sel) != m_objects_model->GetTopParent(m_last_selected_item))
+ return true;
+
+ return false;
+ };
- if (impossible_multipart_selection ||
+ if (impossible_multi_selection(itVolume, smVolume) ||
+ impossible_multi_selection(itLayer, smLayer ) ||
type & itSettings ||
- type & itVolume && m_selection_mode == smInstance ||
- !(type & itVolume) && m_selection_mode == smVolume)
+ type & itVolume && !(m_selection_mode & smVolume ) ||
+ type & itLayer && !(m_selection_mode & smLayer ) ||
+ type & itInstance && !(m_selection_mode & smInstance)
+ )
{
// Inform user why selection isn't complited
- const wxString item_type = m_selection_mode == smInstance ? _(L("Object or Instance")) : _(L("Part"));
+ const wxString item_type = m_selection_mode & smInstance ? _(L("Object or Instance")) :
+ m_selection_mode & smVolume ? _(L("Part")) : _(L("Layer"));
msg_str = wxString::Format( _(L("Unsupported selection")) + "\n\n" +
_(L("You started your selection with %s Item.")) + "\n" +
@@ -2401,7 +2949,7 @@ void ObjectList::fix_multiselection_conflicts()
wxDataViewItemArray sels;
GetSelections(sels);
- if (m_selection_mode == smVolume)
+ if (m_selection_mode & (smVolume|smLayer))
{
// identify correct parent of the initial selected item
const wxDataViewItem& parent = m_objects_model->GetParent(m_last_selected_item == sels.front() ? sels.back() : sels.front());
@@ -2410,8 +2958,10 @@ void ObjectList::fix_multiselection_conflicts()
wxDataViewItemArray children; // selected volumes from current parent
m_objects_model->GetChildren(parent, children);
+ const ItemType item_type = m_selection_mode & smVolume ? itVolume : itLayer;
+
for (const auto child : children)
- if (IsSelected(child) && m_objects_model->GetItemType(child)&itVolume)
+ if (IsSelected(child) && m_objects_model->GetItemType(child) & item_type)
sels.Add(child);
// If some part is selected, unselect all items except of selected parts of the current object
@@ -2502,6 +3052,8 @@ void ObjectList::change_part_type()
if (new_type == type || new_type == ModelVolumeType::INVALID)
return;
+ take_snapshot(_(L("Paste from Clipboard")));
+
const auto item = GetSelection();
volume->set_type(new_type);
m_objects_model->SetVolumeType(item, new_type);
@@ -2517,18 +3069,17 @@ void ObjectList::change_part_type()
}
else if (!settings_item &&
(new_type == ModelVolumeType::MODEL_PART || new_type == ModelVolumeType::PARAMETER_MODIFIER)) {
- select_item(m_objects_model->AddSettingsChild(item));
+ add_settings_item(item, &volume->config);
}
}
void ObjectList::last_volume_is_deleted(const int obj_idx)
{
- if (obj_idx < 0 || m_objects->empty() ||
- obj_idx <= m_objects->size() ||
- (*m_objects)[obj_idx]->volumes.empty())
+ if (obj_idx < 0 || obj_idx >= m_objects->size() || (*m_objects)[obj_idx]->volumes.empty())
return;
- auto volume = (*m_objects)[obj_idx]->volumes[0];
+
+ auto volume = (*m_objects)[obj_idx]->volumes.front();
// clear volume's config values
volume->config.clear();
@@ -2550,6 +3101,7 @@ bool ObjectList::has_multi_part_objects()
return false;
}
+/* #lm_FIXME_delete_after_testing
void ObjectList::update_settings_items()
{
m_prevent_canvas_selection_update = true;
@@ -2575,6 +3127,117 @@ void ObjectList::update_settings_items()
SetSelections(sel);
m_prevent_canvas_selection_update = false;
}
+*/
+void ObjectList::update_and_show_object_settings_item()
+{
+ const wxDataViewItem item = GetSelection();
+ if (!item) return;
+
+ const wxDataViewItem& obj_item = m_objects_model->IsSettingsItem(item) ? m_objects_model->GetParent(item) : item;
+ select_item(add_settings_item(obj_item, &get_item_config(obj_item)));
+}
+
+// Update settings item for item had it
+void ObjectList::update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections)
+{
+ const wxDataViewItem old_settings_item = m_objects_model->GetSettingsItem(item);
+ const wxDataViewItem new_settings_item = add_settings_item(item, &get_item_config(item));
+
+ if (!new_settings_item && old_settings_item)
+ m_objects_model->Delete(old_settings_item);
+
+ // if ols settings item was is selected area
+ if (selections.Index(old_settings_item) != wxNOT_FOUND)
+ {
+ // If settings item was just updated
+ if (old_settings_item == new_settings_item)
+ {
+ Sidebar& panel = wxGetApp().sidebar();
+ panel.Freeze();
+
+ // update settings list
+ wxGetApp().obj_settings()->UpdateAndShow(true);
+
+ panel.Layout();
+ panel.Thaw();
+ }
+ else
+ // If settings item was deleted from the list,
+ // it's need to be deleted from selection array, if it was there
+ {
+ selections.Remove(old_settings_item);
+
+ // Select item, if settings_item doesn't exist for item anymore, but was selected
+ if (selections.Index(item) == wxNOT_FOUND) {
+ selections.Add(item);
+ select_item(item); // to correct update of the SettingsList and ManipulationPanel sizers
+ }
+ }
+ }
+}
+
+void ObjectList::update_object_list_by_printer_technology()
+{
+ m_prevent_canvas_selection_update = true;
+ wxDataViewItemArray sel;
+ GetSelections(sel); // stash selection
+
+ wxDataViewItemArray object_items;
+ m_objects_model->GetChildren(wxDataViewItem(0), object_items);
+
+ for (auto& object_item : object_items) {
+ // Update Settings Item for object
+ update_settings_item_and_selection(object_item, sel);
+
+ // Update settings for Volumes
+ wxDataViewItemArray all_object_subitems;
+ m_objects_model->GetChildren(object_item, all_object_subitems);
+ for (auto item : all_object_subitems)
+ if (m_objects_model->GetItemType(item) & itVolume)
+ // update settings for volume
+ update_settings_item_and_selection(item, sel);
+
+ // Update Layers Items
+ wxDataViewItem layers_item = m_objects_model->GetLayerRootItem(object_item);
+ if (!layers_item)
+ layers_item = add_layer_root_item(object_item);
+ else if (printer_technology() == ptSLA) {
+ // If layers root item will be deleted from the list, so
+ // it's need to be deleted from selection array, if it was there
+ wxDataViewItemArray del_items;
+ bool some_layers_was_selected = false;
+ m_objects_model->GetAllChildren(layers_item, del_items);
+ for (auto& del_item:del_items)
+ if (sel.Index(del_item) != wxNOT_FOUND) {
+ some_layers_was_selected = true;
+ sel.Remove(del_item);
+ }
+ if (sel.Index(layers_item) != wxNOT_FOUND) {
+ some_layers_was_selected = true;
+ sel.Remove(layers_item);
+ }
+
+ // delete all "layers" items
+ m_objects_model->Delete(layers_item);
+
+ // Select object_item, if layers_item doesn't exist for item anymore, but was some of layer items was/were selected
+ if (some_layers_was_selected)
+ sel.Add(object_item);
+ }
+ else {
+ wxDataViewItemArray all_obj_layers;
+ m_objects_model->GetChildren(layers_item, all_obj_layers);
+
+ for (auto item : all_obj_layers)
+ // update settings for layer
+ update_settings_item_and_selection(item, sel);
+ }
+ }
+
+ // restore selection:
+ SetSelections(sel);
+ m_prevent_canvas_selection_update = false;
+}
void ObjectList::update_object_menu()
{
@@ -2749,7 +3412,8 @@ void ObjectList::msw_rescale()
for (MenuWithSeparators* menu : { &m_menu_object,
&m_menu_part,
&m_menu_sla_object,
- &m_menu_instance })
+ &m_menu_instance,
+ &m_menu_layer })
msw_rescale_menu(menu);
Layout();
@@ -2862,5 +3526,42 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const
wxGetApp().plater()->update();
}
+void ObjectList::update_after_undo_redo()
+{
+ m_prevent_list_events = true;
+ m_prevent_canvas_selection_update = true;
+
+ suppress_snapshots();
+
+ // Unselect all objects before deleting them, so that no change of selection is emitted during deletion.
+ this->UnselectAll();
+ m_objects_model->DeleteAll();
+
+ size_t obj_idx = 0;
+ while (obj_idx < m_objects->size()) {
+ add_object_to_list(obj_idx, false);
+ ++obj_idx;
+ }
+
+ allow_snapshots();
+
+#ifndef __WXOSX__
+ selection_changed();
+#endif /* __WXOSX__ */
+
+ update_selections();
+
+ m_prevent_canvas_selection_update = false;
+ m_prevent_list_events = false;
+}
+
+ModelObject* ObjectList::object(const int obj_idx) const
+{
+ if (obj_idx < 0)
+ return nullptr;
+
+ return (*m_objects)[obj_idx];
+}
+
} //namespace GUI
} //namespace Slic3r \ No newline at end of file
diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp
index 166606e2e..2d7e9f5f1 100644
--- a/src/slic3r/GUI/GUI_ObjectList.hpp
+++ b/src/slic3r/GUI/GUI_ObjectList.hpp
@@ -26,13 +26,17 @@ enum class ModelVolumeType : int;
// FIXME: broken build on mac os because of this is missing:
typedef std::vector<std::string> t_config_option_keys;
-typedef std::map<std::string, std::vector<std::string>> FreqSettingsBundle;
+typedef std::map<std::string, std::vector<std::string>> SettingsBundle;
// category -> vector ( option ; label )
typedef std::map< std::string, std::vector< std::pair<std::string, std::string> > > settings_menu_hierarchy;
typedef std::vector<ModelVolume*> ModelVolumePtrs;
+typedef double coordf_t;
+typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
+typedef std::map<t_layer_height_range, DynamicPrintConfig> t_layer_config_ranges;
+
namespace GUI {
wxDECLARE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
@@ -64,9 +68,10 @@ class ObjectList : public wxDataViewCtrl
{
enum SELECTION_MODE
{
- smUndef,
- smVolume,
- smInstance
+ smUndef = 0,
+ smVolume = 1,
+ smInstance = 2,
+ smLayer = 4
} m_selection_mode {smUndef};
struct dragged_item_data
@@ -119,12 +124,17 @@ class ObjectList : public wxDataViewCtrl
MenuWithSeparators m_menu_part;
MenuWithSeparators m_menu_sla_object;
MenuWithSeparators m_menu_instance;
- wxMenuItem* m_menu_item_split { nullptr };
- wxMenuItem* m_menu_item_split_part { nullptr };
+ MenuWithSeparators m_menu_layer;
wxMenuItem* m_menu_item_settings { nullptr };
wxMenuItem* m_menu_item_split_instances { nullptr };
- std::vector<wxBitmap*> m_bmp_vector;
+ ObjectDataViewModel *m_objects_model{ nullptr };
+ DynamicPrintConfig *m_config {nullptr};
+ std::vector<ModelObject*> *m_objects{ nullptr };
+
+ std::vector<wxBitmap*> m_bmp_vector;
+
+ t_layer_config_ranges m_layer_config_ranges_cache;
int m_selected_object_id = -1;
bool m_prevent_list_events = false; // We use this flag to avoid circular event handling Select()
@@ -142,8 +152,8 @@ class ObjectList : public wxDataViewCtrl
wxDataViewItem m_last_selected_item {nullptr};
#if 0
- FreqSettingsBundle m_freq_settings_fff;
- FreqSettingsBundle m_freq_settings_sla;
+ SettingsBundle m_freq_settings_fff;
+ SettingsBundle m_freq_settings_sla;
#endif
public:
@@ -153,11 +163,11 @@ public:
std::map<std::string, wxBitmap> CATEGORY_ICON;
- ObjectDataViewModel *m_objects_model{ nullptr };
- DynamicPrintConfig *m_config {nullptr};
-
- std::vector<ModelObject*> *m_objects{ nullptr };
+ ObjectDataViewModel* GetModel() const { return m_objects_model; }
+ DynamicPrintConfig* config() const { return m_config; }
+ std::vector<ModelObject*>* objects() const { return m_objects; }
+ ModelObject* object(const int obj_idx) const ;
void create_objects_ctrl();
void create_popup_menus();
@@ -192,13 +202,19 @@ public:
void key_event(wxKeyEvent& event);
#endif /* __WXOSX__ */
+ void copy();
+ void paste();
+ void undo();
+ void redo();
+
void get_settings_choice(const wxString& category_name);
void get_freq_settings_choice(const wxString& bundle_name);
- void update_settings_item();
+ void show_settings(const wxDataViewItem settings_item);
wxMenu* append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type);
void append_menu_items_add_volume(wxMenu* menu);
wxMenuItem* append_menu_item_split(wxMenu* menu);
+ wxMenuItem* append_menu_item_layers_editing(wxMenu* menu);
wxMenuItem* append_menu_item_settings(wxMenu* menu);
wxMenuItem* append_menu_item_change_type(wxMenu* menu);
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu, wxWindow* parent);
@@ -222,10 +238,18 @@ public:
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
void del_object(const int obj_idx);
void del_subobject_item(wxDataViewItem& item);
- void del_settings_from_config();
+ void del_settings_from_config(const wxDataViewItem& parent_item);
void del_instances_from_object(const int obj_idx);
+ void del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range);
+ void del_layers_from_object(const int obj_idx);
bool del_subobject_from_object(const int obj_idx, const int idx, const int type);
void split();
+ void layers_editing();
+
+ wxDataViewItem add_layer_root_item(const wxDataViewItem obj_item);
+ wxDataViewItem add_settings_item(wxDataViewItem parent_item, const DynamicPrintConfig* config);
+
+ DynamicPrintConfig get_default_layer_config(const int obj_idx);
bool get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume);
bool is_splittable();
bool selected_instances_of_same_object();
@@ -235,12 +259,13 @@ public:
wxBoxSizer* get_sizer() {return m_sizer;}
int get_selected_obj_idx() const;
DynamicPrintConfig& get_item_config(const wxDataViewItem& item) const;
+ SettingsBundle get_item_settings_bundle(const DynamicPrintConfig* config, const bool is_layers_range_settings);
void changed_object(const int obj_idx = -1) const;
void part_selection_changed();
// Add object to the list
- void add_object_to_list(size_t obj_idx);
+ void add_object_to_list(size_t obj_idx, bool call_selection_changed = true);
// Delete object from the list
void delete_object_from_list();
void delete_object_from_list(const size_t obj_idx);
@@ -265,6 +290,14 @@ public:
// Remove objects/sub-object from the list
void remove();
+ void del_layer_range(const t_layer_height_range& range);
+ void add_layer_range_after_current(const t_layer_height_range& current_range);
+ void add_layer_item (const t_layer_height_range& range,
+ const wxDataViewItem layers_item,
+ const int layer_idx = -1);
+ bool edit_layer_range(const t_layer_height_range& range, coordf_t layer_height);
+ bool edit_layer_range(const t_layer_height_range& range,
+ const t_layer_height_range& new_range);
void init_objects();
bool multiple_selection() const ;
@@ -286,6 +319,9 @@ public:
void last_volume_is_deleted(const int obj_idx);
bool has_multi_part_objects();
void update_settings_items();
+ void update_and_show_object_settings_item();
+ void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections);
+ void update_object_list_by_printer_technology();
void update_object_menu();
void instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idx);
@@ -295,11 +331,15 @@ public:
void fix_through_netfabb();
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
+ void fill_layer_config_ranges_cache();
+ void paste_layers_into_list();
void paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes);
void paste_objects_into_list(const std::vector<size_t>& object_idxs);
void msw_rescale();
+ void update_after_undo_redo();
+
private:
#ifdef __WXOSX__
// void OnChar(wxKeyEvent& event);
diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp
index 372cd79ef..fcb047139 100644
--- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp
+++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp
@@ -17,6 +17,28 @@ namespace Slic3r
namespace GUI
{
+
+// Helper function to be used by drop to bed button. Returns lowest point of this
+// volume in world coordinate system.
+static double get_volume_min_z(const GLVolume* volume)
+{
+ const Transform3f& world_matrix = volume->world_matrix().cast<float>();
+
+ // need to get the ModelVolume pointer
+ const ModelObject* mo = wxGetApp().model_objects()->at(volume->composite_id.object_id);
+ const ModelVolume* mv = mo->volumes[volume->composite_id.volume_id];
+ const TriangleMesh& hull = mv->get_convex_hull();
+
+ float min_z = std::numeric_limits<float>::max();
+ for (const stl_facet& facet : hull.stl.facet_start) {
+ for (int i = 0; i < 3; ++ i)
+ min_z = std::min(min_z, Vec3f::UnitZ().dot(world_matrix * facet.vertex[i]));
+ }
+ return min_z;
+}
+
+
+
static wxBitmapComboBox* create_word_local_combo(wxWindow *parent)
{
wxSize size(15 * wxGetApp().em_unit(), -1);
@@ -185,7 +207,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
unsigned int axis_idx = (axis[0] - 'x'); // 0, 1 or 2
// We will add a button to toggle mirroring to each axis:
- auto mirror_button = [=](wxWindow* parent) {
+ auto mirror_button = [this, mirror_btn_width, axis_idx, &label](wxWindow* parent) {
wxSize btn_size(em_unit(parent) * mirror_btn_width, em_unit(parent) * mirror_btn_width);
auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off.png", wxEmptyString, btn_size, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW);
btn->SetToolTip(wxString::Format(_(L("Toggle %s axis mirroring")), label));
@@ -195,7 +217,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
- btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
+ btn->Bind(wxEVT_BUTTON, [this, axis_idx](wxCommandEvent &e) {
Axis axis = (Axis)(axis_idx + X);
if (m_mirror_buttons[axis_idx].second == mbHidden)
return;
@@ -220,8 +242,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL);
selection.synchronize_unselected_volumes();
// Copy mirroring values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing.
- canvas->do_mirror();
- canvas->set_as_dirty();
+ canvas->do_mirror(L("Set Mirror"));
UpdateAndShow(true);
});
return sizer;
@@ -258,13 +279,13 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
return btn;
};
// Add reset scale button
- auto reset_scale_button = [=](wxWindow* parent) {
+ auto reset_scale_button = [this](wxWindow* parent) {
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo"));
btn->SetToolTip(_(L("Reset scale")));
m_reset_scale_button = btn;
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn, wxBU_EXACTFIT);
- btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
+ btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {
change_scale_value(0, 100.);
change_scale_value(1, 100.);
change_scale_value(2, 100.);
@@ -275,13 +296,13 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
}
else if (option_name == "Rotation") {
// Add reset rotation button
- auto reset_rotation_button = [=](wxWindow* parent) {
+ auto reset_rotation_button = [this](wxWindow* parent) {
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo"));
btn->SetToolTip(_(L("Reset rotation")));
m_reset_rotation_button = btn;
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn, wxBU_EXACTFIT);
- btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
+ btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
Selection& selection = canvas->get_selection();
@@ -302,7 +323,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL);
selection.synchronize_unselected_volumes();
// Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing.
- canvas->do_rotate();
+ canvas->do_rotate(L("Set Rotation"));
UpdateAndShow(true);
});
@@ -310,6 +331,34 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
};
line.append_widget(reset_rotation_button);
}
+ else if (option_name == "Position") {
+ // Add drop to bed button
+ auto drop_to_bed_button = [=](wxWindow* parent) {
+ auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "drop_to_bed.png"));
+ btn->SetToolTip(_(L("Drop to bed")));
+ m_drop_to_bed_button = btn;
+ auto sizer = new wxBoxSizer(wxHORIZONTAL);
+ sizer->Add(btn, wxBU_EXACTFIT);
+ btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
+ // ???
+ GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
+ Selection& selection = canvas->get_selection();
+
+ if (selection.is_single_volume() || selection.is_single_modifier()) {
+ const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
+
+ const Geometry::Transformation& instance_trafo = volume->get_instance_transformation();
+ Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(volume));
+
+ change_position_value(0, diff.x());
+ change_position_value(1, diff.y());
+ change_position_value(2, diff.z());
+ }
+ });
+ return sizer;
+ };
+ line.append_widget(drop_to_bed_button);
+ }
// Add empty bmp (Its size have to be equal to PrusaLockButton) in front of "Size" option to label alignment
else if (option_name == "Size") {
line.near_label_widget = [this](wxWindow* parent) {
@@ -441,7 +490,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
m_new_position = volume->get_volume_offset();
m_new_rotation = volume->get_volume_rotation() * (180. / M_PI);
m_new_scale = volume->get_volume_scaling_factor() * 100.;
- m_new_size = volume->get_volume_transformation().get_scaling_factor().cwiseProduct(volume->bounding_box.size());
+ m_new_size = volume->get_volume_transformation().get_scaling_factor().cwiseProduct(volume->bounding_box().size());
m_new_enabled = true;
}
else if (obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot))
@@ -536,11 +585,13 @@ void ObjectManipulation::update_reset_buttons_visibility()
bool show_rotation = false;
bool show_scale = false;
+ bool show_drop_to_bed = false;
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
Vec3d rotation;
Vec3d scale;
+ double min_z = 0.;
if (selection.is_single_full_instance()) {
rotation = volume->get_instance_rotation();
@@ -549,14 +600,17 @@ void ObjectManipulation::update_reset_buttons_visibility()
else {
rotation = volume->get_volume_rotation();
scale = volume->get_volume_scaling_factor();
+ min_z = get_volume_min_z(volume);
}
show_rotation = !rotation.isApprox(Vec3d::Zero());
show_scale = !scale.isApprox(Vec3d::Ones());
+ show_drop_to_bed = (std::abs(min_z) > EPSILON);
}
- wxGetApp().CallAfter([this, show_rotation, show_scale]{
+ wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed]{
m_reset_rotation_button->Show(show_rotation);
m_reset_scale_button->Show(show_scale);
+ m_drop_to_bed_button->Show(show_drop_to_bed);
});
}
@@ -655,7 +709,7 @@ void ObjectManipulation::change_position_value(int axis, double value)
Selection& selection = canvas->get_selection();
selection.start_dragging();
selection.translate(position - m_cache.position, selection.requires_local_axes());
- canvas->do_move();
+ canvas->do_move(L("Set Position"));
m_cache.position = position;
m_cache.position_rounded(axis) = DBL_MAX;
@@ -686,7 +740,7 @@ void ObjectManipulation::change_rotation_value(int axis, double value)
selection.rotate(
(M_PI / 180.0) * (transformation_type.absolute() ? rotation : rotation - m_cache.rotation),
transformation_type);
- canvas->do_rotate();
+ canvas->do_rotate(L("Set Orientation"));
m_cache.rotation = rotation;
m_cache.rotation_rounded(axis) = DBL_MAX;
@@ -721,8 +775,8 @@ void ObjectManipulation::change_size_value(int axis, double value)
Vec3d ref_size = m_cache.size;
if (selection.is_single_volume() || selection.is_single_modifier())
- ref_size = selection.get_volume(*selection.get_volume_idxs().begin())->bounding_box.size();
- else if (selection.is_single_full_instance())
+ ref_size = selection.get_volume(*selection.get_volume_idxs().begin())->bounding_box().size();
+ else if (selection.is_single_full_instance())
ref_size = m_world_coordinates ?
selection.get_unscaled_instance_bounding_box().size() :
(*wxGetApp().model_objects())[selection.get_volume(*selection.get_volume_idxs().begin())->object_idx()]->raw_mesh_bounding_box().size();
@@ -751,7 +805,7 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const
selection.start_dragging();
selection.scale(scaling_factor * 0.01, transformation_type);
- wxGetApp().plater()->canvas3D()->do_scale();
+ wxGetApp().plater()->canvas3D()->do_scale(L("Set Scale"));
}
void ObjectManipulation::on_change(t_config_option_key opt_key, const boost::any& value)
@@ -869,6 +923,7 @@ void ObjectManipulation::msw_rescale()
m_mirror_bitmap_hidden.msw_rescale();
m_reset_scale_button->msw_rescale();
m_reset_rotation_button->msw_rescale();
+ m_drop_to_bed_button->msw_rescale();
get_og()->msw_rescale();
}
diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp
index cc2154514..e4e190b5b 100644
--- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp
+++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp
@@ -56,6 +56,7 @@ class ObjectManipulation : public OG_Settings
// Non-owning pointers to the reset buttons, so we can hide and show them.
ScalableButton* m_reset_scale_button = nullptr;
ScalableButton* m_reset_rotation_button = nullptr;
+ ScalableButton* m_drop_to_bed_button = nullptr;
// Mirroring buttons and their current state
enum MirrorButtonState {
diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp
index 4107e872a..16c64360a 100644
--- a/src/slic3r/GUI/GUI_ObjectSettings.cpp
+++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp
@@ -63,18 +63,28 @@ ObjectSettings::ObjectSettings(wxWindow* parent) :
m_bmp_delete = ScalableBitmap(parent, "cross");
}
-void ObjectSettings::update_settings_list()
+bool ObjectSettings::update_settings_list()
{
m_settings_list_sizer->Clear(true);
+ m_og_settings.resize(0);
auto objects_ctrl = wxGetApp().obj_list();
- auto objects_model = wxGetApp().obj_list()->m_objects_model;
- auto config = wxGetApp().obj_list()->m_config;
+ auto objects_model = wxGetApp().obj_list()->GetModel();
+ auto config = wxGetApp().obj_list()->config();
const auto item = objects_ctrl->GetSelection();
- if (item && !objects_ctrl->multiple_selection() &&
- config && objects_model->IsSettingsItem(item))
- {
+
+ if (!item || !objects_model->IsSettingsItem(item) || !config || objects_ctrl->multiple_selection())
+ return false;
+
+ const bool is_layers_range_settings = objects_model->GetItemType(objects_model->GetParent(item)) == itLayer;
+ SettingsBundle cat_options = objects_ctrl->get_item_settings_bundle(config, is_layers_range_settings);
+
+ if (!cat_options.empty())
+ {
+ std::vector<std::string> categories;
+ categories.reserve(cat_options.size());
+
auto extra_column = [config, this](wxWindow* parent, const Line& line)
{
auto opt_key = (line.get_options())[0].opt_id; //we assume that we have one option per line
@@ -94,85 +104,61 @@ void ObjectSettings::update_settings_list()
return btn;
};
- std::map<std::string, std::vector<std::string>> cat_options;
- auto opt_keys = config->keys();
- objects_ctrl->update_opt_keys(opt_keys); // update options list according to print technology
-
- m_og_settings.resize(0);
- std::vector<std::string> categories;
- if (!(opt_keys.size() == 1 && opt_keys[0] == "extruder"))// return;
+ for (auto& cat : cat_options)
{
- const int extruders_cnt = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
- wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
-
- for (auto& opt_key : opt_keys) {
- auto category = config->def()->get(opt_key)->category;
- if (category.empty() ||
- (category == "Extruders" && extruders_cnt == 1)) continue;
-
- std::vector< std::string > new_category;
-
- auto& cat_opt = cat_options.find(category) == cat_options.end() ? new_category : cat_options.at(category);
- cat_opt.push_back(opt_key);
- if (cat_opt.size() == 1)
- cat_options[category] = cat_opt;
- }
-
- for (auto& cat : cat_options) {
- if (cat.second.size() == 1 && cat.second[0] == "extruder")
+ if (cat.second.size() == 1 &&
+ (cat.second[0] == "extruder" || is_layers_range_settings && cat.second[0] == "layer_height"))
+ continue;
+
+ categories.push_back(cat.first);
+
+ auto optgroup = std::make_shared<ConfigOptionsGroup>(m_og->ctrl_parent(), _(cat.first), config, false, extra_column);
+ optgroup->label_width = 15;
+ optgroup->sidetext_width = 5.5;
+
+ optgroup->m_on_change = [](const t_config_option_key& opt_id, const boost::any& value) {
+ wxGetApp().obj_list()->changed_object(); };
+
+ // call back for rescaling of the extracolumn control
+ optgroup->rescale_extra_column_item = [this](wxWindow* win) {
+ auto *ctrl = dynamic_cast<ScalableButton*>(win);
+ if (ctrl == nullptr)
+ return;
+ ctrl->SetBitmap_(m_bmp_delete);
+ };
+
+ const bool is_extruders_cat = cat.first == "Extruders";
+ for (auto& opt : cat.second)
+ {
+ if (opt == "extruder" || is_layers_range_settings && opt == "layer_height")
continue;
-
- auto optgroup = std::make_shared<ConfigOptionsGroup>(m_og->ctrl_parent(), _(cat.first), config, false, extra_column);
- optgroup->label_width = 15;
- optgroup->sidetext_width = 5.5;
-
- optgroup->m_on_change = [](const t_config_option_key& opt_id, const boost::any& value) {
- wxGetApp().obj_list()->changed_object(); };
-
- const bool is_extriders_cat = cat.first == "Extruders";
- for (auto& opt : cat.second)
- {
- if (opt == "extruder")
- continue;
- Option option = optgroup->get_option(opt);
- option.opt.width = 12;
- if (is_extriders_cat)
- option.opt.max = wxGetApp().extruders_cnt();
- optgroup->append_single_option_line(option);
- }
- optgroup->reload_config();
- m_settings_list_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0);
-
- // call back for rescaling of the extracolumn control
- optgroup->rescale_extra_column_item = [this](wxWindow* win) {
- auto *ctrl = dynamic_cast<ScalableButton*>(win);
- if (ctrl == nullptr)
- return;
- ctrl->SetBitmap_(m_bmp_delete);
- };
-
- m_og_settings.push_back(optgroup);
-
- categories.push_back(cat.first);
+ Option option = optgroup->get_option(opt);
+ option.opt.width = 12;
+ if (is_extruders_cat)
+ option.opt.max = wxGetApp().extruders_edited_cnt();
+ optgroup->append_single_option_line(option);
}
- }
+ optgroup->reload_config();
- if (m_og_settings.empty()) {
- objects_ctrl->select_item(objects_model->Delete(item));
+ m_settings_list_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0);
+ m_og_settings.push_back(optgroup);
}
- else {
- if (!categories.empty())
- objects_model->UpdateSettingsDigest(item, categories);
- }
- }
+
+ if (!categories.empty())
+ objects_model->UpdateSettingsDigest(item, categories);
+ }
+ else
+ {
+ objects_ctrl->select_item(objects_model->Delete(item));
+ return false;
+ }
+
+ return true;
}
void ObjectSettings::UpdateAndShow(const bool show)
{
- if (show)
- update_settings_list();
-
- OG_Settings::UpdateAndShow(show);
+ OG_Settings::UpdateAndShow(show ? update_settings_list() : false);
}
void ObjectSettings::msw_rescale()
diff --git a/src/slic3r/GUI/GUI_ObjectSettings.hpp b/src/slic3r/GUI/GUI_ObjectSettings.hpp
index 3d49f13b7..01daa5622 100644
--- a/src/slic3r/GUI/GUI_ObjectSettings.hpp
+++ b/src/slic3r/GUI/GUI_ObjectSettings.hpp
@@ -44,7 +44,7 @@ public:
ObjectSettings(wxWindow* parent);
~ObjectSettings() {}
- void update_settings_list();
+ bool update_settings_list();
void UpdateAndShow(const bool show) override;
void msw_rescale();
};
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index 671c49eef..0ba447c1b 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -541,6 +541,26 @@ void Preview::on_checkbox_shells(wxCommandEvent& evt)
refresh_print();
}
+void Preview::update_view_type()
+{
+ const DynamicPrintConfig& config = wxGetApp().preset_bundle->project_config;
+
+ const wxString& choice = !config.option<ConfigOptionFloats>("colorprint_heights")->values.empty() &&
+ wxGetApp().extruders_edited_cnt()==1 ?
+ _(L("Color Print")) :
+ config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values.size() > 1 ?
+ _(L("Tool")) :
+ _(L("Feature type"));
+
+ int type = m_choice_view_type->FindString(choice);
+ if (m_choice_view_type->GetSelection() != type) {
+ m_choice_view_type->SetSelection(type);
+ if (0 <= type && type < (int)GCodePreviewData::Extrusion::Num_View_Types)
+ m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type;
+ m_preferred_color_mode = "feature";
+ }
+}
+
void Preview::create_double_slider()
{
m_slider = new DoubleSlider(this, wxID_ANY, 0, 0, 0, 100);
@@ -553,23 +573,13 @@ void Preview::create_double_slider()
Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) {
- auto& config = wxGetApp().preset_bundle->project_config;
- ((config.option<ConfigOptionFloats>("colorprint_heights"))->values) = (m_slider->GetTicksValues());
- m_schedule_background_process();
-
- const wxString& choise = !config.option<ConfigOptionFloats>("colorprint_heights")->values.empty() ? _(L("Color Print")) :
- config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values.size() > 1 ?
- _(L("Tool")) : _(L("Feature type"));
-
- int type = m_choice_view_type->FindString(choise);
- if (m_choice_view_type->GetSelection() != type) {
- m_choice_view_type->SetSelection(type);
- if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types))
- m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type;
- m_preferred_color_mode = "feature";
- }
- reload_print();
- });
+ wxGetApp().preset_bundle->project_config.option<ConfigOptionFloats>("colorprint_heights")->values = m_slider->GetTicksValues();
+ m_schedule_background_process();
+
+ update_view_type();
+
+ reload_print();
+ });
}
// Find an index of a value in a sorted vector, which is in <z-eps, z+eps>.
@@ -787,9 +797,14 @@ void Preview::load_print_as_fff(bool keep_z_range)
// Load the real G-code preview.
m_canvas->load_gcode_preview(*m_gcode_preview_data, colors);
m_loaded = true;
- } else
+ } else {
+ // disable color change information for multi-material presets
+ if (wxGetApp().extruders_edited_cnt() > 1)
+ color_print_values.clear();
+
// Load the initial preview based on slices, not the final G-code.
m_canvas->load_preview(colors, color_print_values);
+ }
show_hide_ui_elements(gcode_preview_data_valid ? "full" : "simple");
// recalculates zs and update sliders accordingly
std::vector<double> zs = m_canvas->get_current_print_zs(true);
diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp
index 993e260e4..e86d0e430 100644
--- a/src/slic3r/GUI/GUI_Preview.hpp
+++ b/src/slic3r/GUI/GUI_Preview.hpp
@@ -127,6 +127,8 @@ public:
void move_double_slider(wxKeyEvent& evt);
void edit_double_slider(wxKeyEvent& evt);
+ void update_view_type();
+
private:
bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model);
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
index 030bd0146..4b975e87e 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
@@ -132,18 +132,13 @@ void GLGizmoBase::Grabber::render_face(float half_size) const
glsafe(::glEnd());
}
-#if ENABLE_SVG_ICONS
+
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
-#else
-GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id)
-#endif // ENABLE_SVG_ICONS
: m_parent(parent)
, m_group_id(-1)
, m_state(Off)
, m_shortcut_key(0)
-#if ENABLE_SVG_ICONS
, m_icon_filename(icon_filename)
-#endif // ENABLE_SVG_ICONS
, m_sprite_id(sprite_id)
, m_hover_id(-1)
, m_dragging(false)
@@ -185,7 +180,7 @@ void GLGizmoBase::disable_grabber(unsigned int id)
on_disable_grabber(id);
}
-void GLGizmoBase::start_dragging(const Selection& selection)
+void GLGizmoBase::start_dragging()
{
m_dragging = true;
@@ -194,7 +189,7 @@ void GLGizmoBase::start_dragging(const Selection& selection)
m_grabbers[i].dragging = (m_hover_id == i);
}
- on_start_dragging(selection);
+ on_start_dragging();
}
void GLGizmoBase::stop_dragging()
@@ -209,10 +204,10 @@ void GLGizmoBase::stop_dragging()
on_stop_dragging();
}
-void GLGizmoBase::update(const UpdateData& data, const Selection& selection)
+void GLGizmoBase::update(const UpdateData& data)
{
if (m_hover_id != -1)
- on_update(data, selection);
+ on_update(data);
}
std::array<float, 3> GLGizmoBase::picking_color_component(unsigned int id) const
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
index a29aaed3f..b84442b94 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
@@ -6,6 +6,7 @@
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/Selection.hpp"
+#include <cereal/archives/binary.hpp>
class wxWindow;
class GLUquadric;
@@ -75,10 +76,10 @@ public:
struct UpdateData
{
- const Linef3 mouse_ray;
- const Point* mouse_pos;
+ const Linef3& mouse_ray;
+ const Point& mouse_pos;
- UpdateData(const Linef3& mouse_ray, const Point* mouse_pos = nullptr)
+ UpdateData(const Linef3& mouse_ray, const Point& mouse_pos)
: mouse_ray(mouse_ray), mouse_pos(mouse_pos)
{}
};
@@ -89,9 +90,7 @@ protected:
int m_group_id;
EState m_state;
int m_shortcut_key;
-#if ENABLE_SVG_ICONS
std::string m_icon_filename;
-#endif // ENABLE_SVG_ICONS
unsigned int m_sprite_id;
int m_hover_id;
bool m_dragging;
@@ -102,15 +101,14 @@ protected:
ImGuiWrapper* m_imgui;
public:
-#if ENABLE_SVG_ICONS
GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
-#else
- GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id);
-#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoBase() {}
bool init() { return on_init(); }
+ void load(cereal::BinaryInputArchive& ar) { m_state = On; on_load(ar); }
+ void save(cereal::BinaryOutputArchive& ar) const { on_save(ar); }
+
std::string get_name() const { return on_get_name(); }
int get_group_id() const { return m_group_id; }
@@ -122,11 +120,9 @@ public:
int get_shortcut_key() const { return m_shortcut_key; }
void set_shortcut_key(int key) { m_shortcut_key = key; }
-#if ENABLE_SVG_ICONS
const std::string& get_icon_filename() const { return m_icon_filename; }
-#endif // ENABLE_SVG_ICONS
- bool is_activable(const Selection& selection) const { return on_is_activable(selection); }
+ bool is_activable() const { return on_is_activable(); }
bool is_selectable() const { return on_is_selectable(); }
unsigned int get_sprite_id() const { return m_sprite_id; }
@@ -139,32 +135,34 @@ public:
void enable_grabber(unsigned int id);
void disable_grabber(unsigned int id);
- void start_dragging(const Selection& selection);
+ void start_dragging();
void stop_dragging();
bool is_dragging() const { return m_dragging; }
- void update(const UpdateData& data, const Selection& selection);
+ void update(const UpdateData& data);
- void render(const Selection& selection) const { on_render(selection); }
- void render_for_picking(const Selection& selection) const { on_render_for_picking(selection); }
- void render_input_window(float x, float y, float bottom_limit, const Selection& selection) { on_render_input_window(x, y, bottom_limit, selection); }
+ void render() const { on_render(); }
+ void render_for_picking() const { on_render_for_picking(); }
+ void render_input_window(float x, float y, float bottom_limit) { on_render_input_window(x, y, bottom_limit); }
protected:
virtual bool on_init() = 0;
+ virtual void on_load(cereal::BinaryInputArchive& ar) {}
+ virtual void on_save(cereal::BinaryOutputArchive& ar) const {}
virtual std::string on_get_name() const = 0;
virtual void on_set_state() {}
virtual void on_set_hover_id() {}
- virtual bool on_is_activable(const Selection& selection) const { return true; }
+ virtual bool on_is_activable() const { return true; }
virtual bool on_is_selectable() const { return true; }
virtual void on_enable_grabber(unsigned int id) {}
virtual void on_disable_grabber(unsigned int id) {}
- virtual void on_start_dragging(const Selection& selection) {}
+ virtual void on_start_dragging() {}
virtual void on_stop_dragging() {}
- virtual void on_update(const UpdateData& data, const Selection& selection) = 0;
- virtual void on_render(const Selection& selection) const = 0;
- virtual void on_render_for_picking(const Selection& selection) const = 0;
- virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) {}
+ virtual void on_update(const UpdateData& data) {}
+ virtual void on_render() const = 0;
+ virtual void on_render_for_picking() const = 0;
+ virtual void on_render_input_window(float x, float y, float bottom_limit) {}
// Returns the picking color for the given id, based on the BASE_ID constant
// No check is made for clashing with other picking color (i.e. GLVolumes)
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
index 8934bc52b..39399fc0d 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
@@ -15,59 +15,12 @@
namespace Slic3r {
namespace GUI {
-
-class GLGizmoCutPanel : public wxPanel
-{
-public:
- GLGizmoCutPanel(wxWindow *parent);
-
- void display(bool display);
-private:
- bool m_active;
- wxCheckBox *m_cb_rotate;
- wxButton *m_btn_cut;
- wxButton *m_btn_cancel;
-};
-
-GLGizmoCutPanel::GLGizmoCutPanel(wxWindow *parent)
- : wxPanel(parent)
- , m_active(false)
- , m_cb_rotate(new wxCheckBox(this, wxID_ANY, _(L("Rotate lower part upwards"))))
- , m_btn_cut(new wxButton(this, wxID_OK, _(L("Perform cut"))))
- , m_btn_cancel(new wxButton(this, wxID_CANCEL, _(L("Cancel"))))
-{
- enum { MARGIN = 5 };
-
- auto *sizer = new wxBoxSizer(wxHORIZONTAL);
-
- auto *label = new wxStaticText(this, wxID_ANY, _(L("Cut object:")));
- sizer->Add(label, 0, wxALL | wxALIGN_CENTER, MARGIN);
- sizer->Add(m_cb_rotate, 0, wxALL | wxALIGN_CENTER, MARGIN);
- sizer->AddStretchSpacer();
- sizer->Add(m_btn_cut, 0, wxALL | wxALIGN_CENTER, MARGIN);
- sizer->Add(m_btn_cancel, 0, wxALL | wxALIGN_CENTER, MARGIN);
-
- SetSizer(sizer);
-}
-
-void GLGizmoCutPanel::display(bool display)
-{
- Show(display);
- GetParent()->Layout();
-}
-
-
const double GLGizmoCut::Offset = 10.0;
const double GLGizmoCut::Margin = 20.0;
const std::array<float, 3> GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0 };
-#if ENABLE_SVG_ICONS
GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
-#else
-GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id)
- : GLGizmoBase(parent, sprite_id)
-#endif // ENABLE_SVG_ICONS
, m_cut_z(0.0)
, m_max_z(0.0)
, m_keep_upper(true)
@@ -75,7 +28,6 @@ GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id)
, m_rotate_lower(false)
{}
-
bool GLGizmoCut::on_init()
{
m_grabbers.emplace_back();
@@ -96,15 +48,18 @@ void GLGizmoCut::on_set_state()
}
}
-bool GLGizmoCut::on_is_activable(const Selection& selection) const
+bool GLGizmoCut::on_is_activable() const
{
+ const Selection& selection = m_parent.get_selection();
return selection.is_single_full_instance() && !selection.is_wipe_tower();
}
-void GLGizmoCut::on_start_dragging(const Selection& selection)
+void GLGizmoCut::on_start_dragging()
{
- if (m_hover_id == -1) { return; }
+ if (m_hover_id == -1)
+ return;
+ const Selection& selection = m_parent.get_selection();
const BoundingBoxf3& box = selection.get_bounding_box();
m_start_z = m_cut_z;
update_max_z(selection);
@@ -113,19 +68,21 @@ void GLGizmoCut::on_start_dragging(const Selection& selection)
m_drag_center(2) = m_cut_z;
}
-void GLGizmoCut::on_update(const UpdateData& data, const Selection& selection)
+void GLGizmoCut::on_update(const UpdateData& data)
{
if (m_hover_id != -1) {
set_cut_z(m_start_z + calc_projection(data.mouse_ray));
}
}
-void GLGizmoCut::on_render(const Selection& selection) const
+void GLGizmoCut::on_render() const
{
if (m_grabbers[0].dragging) {
set_tooltip("Z: " + format(m_cut_z, 2));
}
+ const Selection& selection = m_parent.get_selection();
+
update_max_z(selection);
const BoundingBoxf3& box = selection.get_bounding_box();
@@ -171,14 +128,13 @@ void GLGizmoCut::on_render(const Selection& selection) const
m_grabbers[0].render(m_hover_id == 0, (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0));
}
-void GLGizmoCut::on_render_for_picking(const Selection& selection) const
+void GLGizmoCut::on_render_for_picking() const
{
glsafe(::glDisable(GL_DEPTH_TEST));
-
- render_grabbers_for_picking(selection.get_bounding_box());
+ render_grabbers_for_picking(m_parent.get_selection().get_bounding_box());
}
-void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection)
+void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit)
{
const float approx_height = m_imgui->scaled(11.0f);
y = std::min(y, bottom_limit - approx_height);
@@ -188,7 +144,7 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, co
m_imgui->begin(_(L("Cut")), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
ImGui::PushItemWidth(m_imgui->scaled(5.0f));
- bool _value_changed = ImGui::InputDouble("Z", &m_cut_z, 0.0f, 0.0f, "%.2f");
+ ImGui::InputDouble("Z", &m_cut_z, 0.0f, 0.0f, "%.2f");
m_imgui->checkbox(_(L("Keep upper part")), m_keep_upper);
m_imgui->checkbox(_(L("Keep lower part")), m_keep_lower);
@@ -201,7 +157,7 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, co
m_imgui->end();
if (cut_clicked && (m_keep_upper || m_keep_lower)) {
- perform_cut(selection);
+ perform_cut(m_parent.get_selection());
}
}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
index fd4e8d8dc..5bfeda526 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
@@ -23,22 +23,20 @@ class GLGizmoCut : public GLGizmoBase
bool m_rotate_lower;
public:
-#if ENABLE_SVG_ICONS
GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
-#else
- GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id);
-#endif // ENABLE_SVG_ICONS
protected:
virtual bool on_init();
+ virtual void on_load(cereal::BinaryInputArchive& ar) { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); }
+ virtual void on_save(cereal::BinaryOutputArchive& ar) const { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); }
virtual std::string on_get_name() const;
virtual void on_set_state();
- virtual bool on_is_activable(const Selection& selection) const;
- virtual void on_start_dragging(const Selection& selection);
- virtual void on_update(const UpdateData& data, const Selection& selection);
- virtual void on_render(const Selection& selection) const;
- virtual void on_render_for_picking(const Selection& selection) const;
- virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection);
+ virtual bool on_is_activable() const;
+ virtual void on_start_dragging();
+ virtual void on_update(const UpdateData& data);
+ virtual void on_render() const;
+ virtual void on_render_for_picking() const;
+ virtual void on_render_input_window(float x, float y, float bottom_limit);
private:
void update_max_z(const Selection& selection) const;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
index ec991a241..cb996a104 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
@@ -1,5 +1,7 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoFlatten.hpp"
+#include "slic3r/GUI/GLCanvas3D.hpp"
+#include "slic3r/GUI/GUI_App.hpp"
#include <numeric>
@@ -9,13 +11,8 @@ namespace Slic3r {
namespace GUI {
-#if ENABLE_SVG_ICONS
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
-#else
-GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id)
- : GLGizmoBase(parent, sprite_id)
-#endif // ENABLE_SVG_ICONS
, m_normal(Vec3d::Zero())
, m_starting_center(Vec3d::Zero())
{
@@ -27,28 +24,46 @@ bool GLGizmoFlatten::on_init()
return true;
}
+void GLGizmoFlatten::on_set_state()
+{
+ // m_model_object pointer can be invalid (for instance because of undo/redo action),
+ // we should recover it from the object id
+ m_model_object = nullptr;
+ for (const auto mo : *wxGetApp().model_objects()) {
+ if (mo->id() == m_model_object_id) {
+ m_model_object = mo;
+ break;
+ }
+ }
+
+ if (m_state == On && is_plane_update_necessary())
+ update_planes();
+}
+
std::string GLGizmoFlatten::on_get_name() const
{
return (_(L("Place on face")) + " [F]").ToUTF8().data();
}
-bool GLGizmoFlatten::on_is_activable(const Selection& selection) const
+bool GLGizmoFlatten::on_is_activable() const
{
- return selection.is_single_full_instance();
+ return m_parent.get_selection().is_single_full_instance();
}
-void GLGizmoFlatten::on_start_dragging(const Selection& selection)
+void GLGizmoFlatten::on_start_dragging()
{
if (m_hover_id != -1)
{
assert(m_planes_valid);
m_normal = m_planes[m_hover_id].normal;
- m_starting_center = selection.get_bounding_box().center();
+ m_starting_center = m_parent.get_selection().get_bounding_box().center();
}
}
-void GLGizmoFlatten::on_render(const Selection& selection) const
+void GLGizmoFlatten::on_render() const
{
+ const Selection& selection = m_parent.get_selection();
+
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
glsafe(::glEnable(GL_DEPTH_TEST));
@@ -83,8 +98,10 @@ void GLGizmoFlatten::on_render(const Selection& selection) const
glsafe(::glDisable(GL_BLEND));
}
-void GLGizmoFlatten::on_render_for_picking(const Selection& selection) const
+void GLGizmoFlatten::on_render_for_picking() const
{
+ const Selection& selection = m_parent.get_selection();
+
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glDisable(GL_BLEND));
@@ -120,6 +137,7 @@ void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
m_planes_valid = false;
}
m_model_object = model_object;
+ m_model_object_id = model_object ? model_object->id() : 0;
}
void GLGizmoFlatten::update_planes()
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp
index 1bd17e5ef..c69d64134 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp
@@ -31,17 +31,14 @@ private:
bool m_planes_valid = false;
mutable Vec3d m_starting_center;
const ModelObject* m_model_object = nullptr;
+ ObjectID m_model_object_id = 0;
std::vector<const Transform3d*> instances_matrices;
void update_planes();
bool is_plane_update_necessary() const;
public:
-#if ENABLE_SVG_ICONS
GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
-#else
- GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id);
-#endif // ENABLE_SVG_ICONS
void set_flattening_data(const ModelObject* model_object);
Vec3d get_flattening_normal() const;
@@ -49,16 +46,11 @@ public:
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
- virtual bool on_is_activable(const Selection& selection) const;
- virtual void on_start_dragging(const Selection& selection);
- virtual void on_update(const UpdateData& data, const Selection& selection) {}
- virtual void on_render(const Selection& selection) const;
- virtual void on_render_for_picking(const Selection& selection) const;
- virtual void on_set_state()
- {
- if (m_state == On && is_plane_update_necessary())
- update_planes();
- }
+ virtual bool on_is_activable() const;
+ virtual void on_start_dragging();
+ virtual void on_render() const;
+ virtual void on_render_for_picking() const;
+ virtual void on_set_state() override;
};
} // namespace GUI
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
index a2ea738f5..11bdcd4f8 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
@@ -1,5 +1,6 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoMove.hpp"
+#include "slic3r/GUI/GLCanvas3D.hpp"
#include <GL/glew.h>
@@ -10,13 +11,8 @@ namespace GUI {
const double GLGizmoMove3D::Offset = 10.0;
-#if ENABLE_SVG_ICONS
GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
-#else
-GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id)
- : GLGizmoBase(parent, sprite_id)
-#endif // ENABLE_SVG_ICONS
, m_displacement(Vec3d::Zero())
, m_snap_step(1.0)
, m_starting_drag_position(Vec3d::Zero())
@@ -52,12 +48,12 @@ std::string GLGizmoMove3D::on_get_name() const
return (_(L("Move")) + " [M]").ToUTF8().data();
}
-void GLGizmoMove3D::on_start_dragging(const Selection& selection)
+void GLGizmoMove3D::on_start_dragging()
{
if (m_hover_id != -1)
{
m_displacement = Vec3d::Zero();
- const BoundingBoxf3& box = selection.get_bounding_box();
+ const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box();
m_starting_drag_position = m_grabbers[m_hover_id].center;
m_starting_box_center = box.center();
m_starting_box_bottom_center = box.center();
@@ -70,7 +66,7 @@ void GLGizmoMove3D::on_stop_dragging()
m_displacement = Vec3d::Zero();
}
-void GLGizmoMove3D::on_update(const UpdateData& data, const Selection& selection)
+void GLGizmoMove3D::on_update(const UpdateData& data)
{
if (m_hover_id == 0)
m_displacement(0) = calc_projection(data);
@@ -80,8 +76,10 @@ void GLGizmoMove3D::on_update(const UpdateData& data, const Selection& selection
m_displacement(2) = calc_projection(data);
}
-void GLGizmoMove3D::on_render(const Selection& selection) const
+void GLGizmoMove3D::on_render() const
{
+ const Selection& selection = m_parent.get_selection();
+
bool show_position = selection.is_single_full_instance();
const Vec3d& position = selection.get_bounding_box().center();
@@ -157,20 +155,21 @@ void GLGizmoMove3D::on_render(const Selection& selection) const
}
}
-void GLGizmoMove3D::on_render_for_picking(const Selection& selection) const
+void GLGizmoMove3D::on_render_for_picking() const
{
glsafe(::glDisable(GL_DEPTH_TEST));
- const BoundingBoxf3& box = selection.get_bounding_box();
+ const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box();
render_grabbers_for_picking(box);
render_grabber_extension(X, box, true);
render_grabber_extension(Y, box, true);
render_grabber_extension(Z, box, true);
}
-void GLGizmoMove3D::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection)
-{
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
+void GLGizmoMove3D::on_render_input_window(float x, float y, float bottom_limit)
+{
+ const Selection& selection = m_parent.get_selection();
bool show_position = selection.is_single_full_instance();
const Vec3d& position = selection.get_bounding_box().center();
@@ -183,8 +182,8 @@ void GLGizmoMove3D::on_render_input_window(float x, float y, float bottom_limit,
m_imgui->input_vec3("", displacement, 100.0f, "%.2f");
m_imgui->end();
-#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
}
+#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
double GLGizmoMove3D::calc_projection(const UpdateData& data) const
{
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp
index ddab2b777..21b1d397b 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp
@@ -22,11 +22,7 @@ class GLGizmoMove3D : public GLGizmoBase
GLUquadricObj* m_quadric;
public:
-#if ENABLE_SVG_ICONS
GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
-#else
- GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id);
-#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoMove3D();
double get_snap_step(double step) const { return m_snap_step; }
@@ -37,12 +33,14 @@ public:
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
- virtual void on_start_dragging(const Selection& selection);
+ virtual void on_start_dragging();
virtual void on_stop_dragging();
- virtual void on_update(const UpdateData& data, const Selection& selection);
- virtual void on_render(const Selection& selection) const;
- virtual void on_render_for_picking(const Selection& selection) const;
- virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection);
+ virtual void on_update(const UpdateData& data);
+ virtual void on_render() const;
+ virtual void on_render_for_picking() const;
+#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
+ virtual void on_render_input_window(float x, float y, float bottom_limit);
+#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
private:
double calc_projection(const UpdateData& data) const;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
index 68b9a8c33..f481bb5d7 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
@@ -1,5 +1,6 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoRotate.hpp"
+#include "slic3r/GUI/GLCanvas3D.hpp"
#include <GL/glew.h>
@@ -19,11 +20,7 @@ const unsigned int GLGizmoRotate::SnapRegionsCount = 8;
const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius
GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis)
-#if ENABLE_SVG_ICONS
: GLGizmoBase(parent, "", -1)
-#else
- : GLGizmoBase(parent, -1)
-#endif // ENABLE_SVG_ICONS
, m_axis(axis)
, m_angle(0.0)
, m_quadric(nullptr)
@@ -40,11 +37,7 @@ GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis)
}
GLGizmoRotate::GLGizmoRotate(const GLGizmoRotate& other)
-#if ENABLE_SVG_ICONS
: GLGizmoBase(other.m_parent, other.m_icon_filename, other.m_sprite_id)
-#else
- : GLGizmoBase(other.m_parent, other.m_sprite_id)
-#endif // ENABLE_SVG_ICONS
, m_axis(other.m_axis)
, m_angle(other.m_angle)
, m_quadric(nullptr)
@@ -80,9 +73,9 @@ bool GLGizmoRotate::on_init()
return true;
}
-void GLGizmoRotate::on_start_dragging(const Selection& selection)
+void GLGizmoRotate::on_start_dragging()
{
- const BoundingBoxf3& box = selection.get_bounding_box();
+ const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box();
m_center = box.center();
m_radius = Offset + box.radius();
m_snap_coarse_in_radius = m_radius / 3.0f;
@@ -91,9 +84,9 @@ void GLGizmoRotate::on_start_dragging(const Selection& selection)
m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth;
}
-void GLGizmoRotate::on_update(const UpdateData& data, const Selection& selection)
+void GLGizmoRotate::on_update(const UpdateData& data)
{
- Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, selection));
+ Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, m_parent.get_selection()));
Vec2d orig_dir = Vec2d::UnitX();
Vec2d new_dir = mouse_pos.normalized();
@@ -126,11 +119,12 @@ void GLGizmoRotate::on_update(const UpdateData& data, const Selection& selection
m_angle = theta;
}
-void GLGizmoRotate::on_render(const Selection& selection) const
+void GLGizmoRotate::on_render() const
{
if (!m_grabbers[0].enabled)
return;
+ const Selection& selection = m_parent.get_selection();
const BoundingBoxf3& box = selection.get_bounding_box();
std::string axis;
@@ -183,8 +177,10 @@ void GLGizmoRotate::on_render(const Selection& selection) const
glsafe(::glPopMatrix());
}
-void GLGizmoRotate::on_render_for_picking(const Selection& selection) const
+void GLGizmoRotate::on_render_for_picking() const
{
+ const Selection& selection = m_parent.get_selection();
+
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glPushMatrix());
@@ -417,13 +413,8 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, cons
return transform(mouse_ray, m).intersect_plane(0.0);
}
-#if ENABLE_SVG_ICONS
GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
-#else
-GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id)
- : GLGizmoBase(parent, sprite_id)
-#endif // ENABLE_SVG_ICONS
{
m_gizmos.emplace_back(parent, GLGizmoRotate::X);
m_gizmos.emplace_back(parent, GLGizmoRotate::Y);
@@ -458,10 +449,10 @@ std::string GLGizmoRotate3D::on_get_name() const
return (_(L("Rotate")) + " [R]").ToUTF8().data();
}
-void GLGizmoRotate3D::on_start_dragging(const Selection& selection)
+void GLGizmoRotate3D::on_start_dragging()
{
if ((0 <= m_hover_id) && (m_hover_id < 3))
- m_gizmos[m_hover_id].start_dragging(selection);
+ m_gizmos[m_hover_id].start_dragging();
}
void GLGizmoRotate3D::on_stop_dragging()
@@ -470,23 +461,23 @@ void GLGizmoRotate3D::on_stop_dragging()
m_gizmos[m_hover_id].stop_dragging();
}
-void GLGizmoRotate3D::on_render(const Selection& selection) const
+void GLGizmoRotate3D::on_render() const
{
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
if ((m_hover_id == -1) || (m_hover_id == 0))
- m_gizmos[X].render(selection);
+ m_gizmos[X].render();
if ((m_hover_id == -1) || (m_hover_id == 1))
- m_gizmos[Y].render(selection);
+ m_gizmos[Y].render();
if ((m_hover_id == -1) || (m_hover_id == 2))
- m_gizmos[Z].render(selection);
+ m_gizmos[Z].render();
}
-void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection)
-{
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
+void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit)
+{
Vec3d rotation(Geometry::rad2deg(m_gizmos[0].get_angle()), Geometry::rad2deg(m_gizmos[1].get_angle()), Geometry::rad2deg(m_gizmos[2].get_angle()));
wxString label = _(L("Rotation (deg)"));
@@ -495,8 +486,8 @@ void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limi
m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
m_imgui->input_vec3("", rotation, 100.0f, "%.2f");
m_imgui->end();
-#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
}
+#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp
index c65dee4d8..7846edb22 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp
@@ -52,10 +52,10 @@ public:
protected:
virtual bool on_init();
virtual std::string on_get_name() const { return ""; }
- virtual void on_start_dragging(const Selection& selection);
- virtual void on_update(const UpdateData& data, const Selection& selection);
- virtual void on_render(const Selection& selection) const;
- virtual void on_render_for_picking(const Selection& selection) const;
+ virtual void on_start_dragging();
+ virtual void on_update(const UpdateData& data);
+ virtual void on_render() const;
+ virtual void on_render_for_picking() const;
private:
void render_circle() const;
@@ -76,11 +76,7 @@ class GLGizmoRotate3D : public GLGizmoBase
std::vector<GLGizmoRotate> m_gizmos;
public:
-#if ENABLE_SVG_ICONS
GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
-#else
- GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id);
-#endif // ENABLE_SVG_ICONS
Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); }
void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); }
@@ -102,7 +98,6 @@ protected:
m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1);
}
}
- virtual bool on_is_activable(const Selection& selection) const { return true; }
virtual void on_enable_grabber(unsigned int id)
{
if ((0 <= id) && (id < 3))
@@ -113,25 +108,26 @@ protected:
if ((0 <= id) && (id < 3))
m_gizmos[id].disable_grabber(0);
}
- virtual void on_start_dragging(const Selection& selection);
+ virtual void on_start_dragging();
virtual void on_stop_dragging();
- virtual void on_update(const UpdateData& data, const Selection& selection)
+ virtual void on_update(const UpdateData& data)
{
for (GLGizmoRotate& g : m_gizmos)
{
- g.update(data, selection);
+ g.update(data);
}
}
- virtual void on_render(const Selection& selection) const;
- virtual void on_render_for_picking(const Selection& selection) const
+ virtual void on_render() const;
+ virtual void on_render_for_picking() const
{
for (const GLGizmoRotate& g : m_gizmos)
{
- g.render_for_picking(selection);
+ g.render_for_picking();
}
}
-
- virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection);
+#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
+ virtual void on_render_input_window(float x, float y, float bottom_limit);
+#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
};
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
index e8027c871..7dc38b801 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
@@ -1,7 +1,6 @@
-
-
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoScale.hpp"
+#include "slic3r/GUI/GLCanvas3D.hpp"
#include <GL/glew.h>
@@ -13,13 +12,8 @@ namespace GUI {
const float GLGizmoScale3D::Offset = 5.0f;
-#if ENABLE_SVG_ICONS
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
-#else
-GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id)
- : GLGizmoBase(parent, sprite_id)
-#endif // ENABLE_SVG_ICONS
, m_scale(Vec3d::Ones())
, m_offset(Vec3d::Zero())
, m_snap_step(0.05)
@@ -53,13 +47,18 @@ std::string GLGizmoScale3D::on_get_name() const
return (_(L("Scale")) + " [S]").ToUTF8().data();
}
-void GLGizmoScale3D::on_start_dragging(const Selection& selection)
+bool GLGizmoScale3D::on_is_activable() const
+{
+ return !m_parent.get_selection().is_wipe_tower();
+}
+
+void GLGizmoScale3D::on_start_dragging()
{
if (m_hover_id != -1)
{
m_starting.drag_position = m_grabbers[m_hover_id].center;
m_starting.ctrl_down = wxGetKeyState(WXK_CONTROL);
- m_starting.box = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_box : selection.get_bounding_box();
+ m_starting.box = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_box : m_parent.get_selection().get_bounding_box();
const Vec3d& center = m_starting.box.center();
m_starting.pivots[0] = m_transform * Vec3d(m_starting.box.max(0), center(1), center(2));
@@ -71,7 +70,7 @@ void GLGizmoScale3D::on_start_dragging(const Selection& selection)
}
}
-void GLGizmoScale3D::on_update(const UpdateData& data, const Selection& selection)
+void GLGizmoScale3D::on_update(const UpdateData& data)
{
if ((m_hover_id == 0) || (m_hover_id == 1))
do_scale_along_axis(X, data);
@@ -83,8 +82,10 @@ void GLGizmoScale3D::on_update(const UpdateData& data, const Selection& selectio
do_scale_uniform(data);
}
-void GLGizmoScale3D::on_render(const Selection& selection) const
+void GLGizmoScale3D::on_render() const
{
+ const Selection& selection = m_parent.get_selection();
+
bool single_instance = selection.is_single_full_instance();
bool single_volume = selection.is_single_modifier() || selection.is_single_volume();
bool single_selection = single_instance || single_volume;
@@ -136,7 +137,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
for (unsigned int idx : idxs)
{
const GLVolume* vol = selection.get_volume(idx);
- m_box.merge(vol->bounding_box.transformed(vol->get_volume_transformation().get_matrix()));
+ m_box.merge(vol->bounding_box().transformed(vol->get_volume_transformation().get_matrix()));
}
// gets transform from first selected volume
@@ -151,7 +152,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
else if (single_volume)
{
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
- m_box = v->bounding_box;
+ m_box = v->bounding_box();
m_transform = v->world_matrix();
angles = Geometry::extract_euler_angles(m_transform);
// consider rotation+mirror only components of the transform for offsets
@@ -277,16 +278,16 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
}
}
-void GLGizmoScale3D::on_render_for_picking(const Selection& selection) const
+void GLGizmoScale3D::on_render_for_picking() const
{
glsafe(::glDisable(GL_DEPTH_TEST));
-
- render_grabbers_for_picking(selection.get_bounding_box());
+ render_grabbers_for_picking(m_parent.get_selection().get_bounding_box());
}
-void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection)
-{
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
+void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit)
+{
+ const Selection& selection = m_parent.get_selection();
bool single_instance = selection.is_single_full_instance();
wxString label = _(L("Scale (%)"));
@@ -295,8 +296,8 @@ void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit
m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
m_imgui->input_vec3("", m_scale * 100.f, 100.0f, "%.2f");
m_imgui->end();
-#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
}
+#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int id_2) const
{
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp
index 3b0717f04..7b77fe559 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp
@@ -32,11 +32,7 @@ class GLGizmoScale3D : public GLGizmoBase
StartingData m_starting;
public:
-#if ENABLE_SVG_ICONS
GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
-#else
- GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id);
-#endif // ENABLE_SVG_ICONS
double get_snap_step(double step) const { return m_snap_step; }
void set_snap_step(double step) { m_snap_step = step; }
@@ -49,12 +45,14 @@ public:
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
- virtual bool on_is_activable(const Selection& selection) const { return !selection.is_wipe_tower(); }
- virtual void on_start_dragging(const Selection& selection);
- virtual void on_update(const UpdateData& data, const Selection& selection);
- virtual void on_render(const Selection& selection) const;
- virtual void on_render_for_picking(const Selection& selection) const;
- virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection);
+ virtual bool on_is_activable() const;
+ virtual void on_start_dragging();
+ virtual void on_update(const UpdateData& data);
+ virtual void on_render() const;
+ virtual void on_render_for_picking() const;
+#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
+ virtual void on_render_input_window(float x, float y, float bottom_limit);
+#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
private:
void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index cfe07a61c..833ba3ee2 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -19,13 +19,8 @@
namespace Slic3r {
namespace GUI {
-#if ENABLE_SVG_ICONS
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
-#else
-GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id)
- : GLGizmoBase(parent, sprite_id)
-#endif // ENABLE_SVG_ICONS
, m_quadric(nullptr)
, m_its(nullptr)
{
@@ -99,8 +94,10 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S
}
}
-void GLGizmoSlaSupports::on_render(const Selection& selection) const
+void GLGizmoSlaSupports::on_render() const
{
+ const Selection& selection = m_parent.get_selection();
+
// If current m_model_object does not match selection, ask GLCanvas3D to turn us off
if (m_state == On
&& (m_model_object != selection.get_model()->objects[selection.get_object_idx()]
@@ -109,6 +106,9 @@ void GLGizmoSlaSupports::on_render(const Selection& selection) const
return;
}
+ if (! m_its || ! m_mesh)
+ const_cast<GLGizmoSlaSupports*>(this)->update_mesh();
+
glsafe(::glEnable(GL_BLEND));
glsafe(::glEnable(GL_DEPTH_TEST));
@@ -257,8 +257,13 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
}
-void GLGizmoSlaSupports::on_render_for_picking(const Selection& selection) const
+void GLGizmoSlaSupports::on_render_for_picking() const
{
+ const Selection& selection = m_parent.get_selection();
+#if ENABLE_RENDER_PICKING_PASS
+ m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z();
+#endif
+
glsafe(::glEnable(GL_DEPTH_TEST));
render_points(selection, true);
}
@@ -380,23 +385,31 @@ bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const
bool GLGizmoSlaSupports::is_mesh_update_necessary() const
{
return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty())
- && ((m_model_object->id() != m_current_mesh_model_id) || m_its == nullptr);
+ && ((m_model_object->id() != m_current_mesh_object_id) || m_its == nullptr);
}
void GLGizmoSlaSupports::update_mesh()
{
+ if (! m_model_object)
+ return;
+
wxBusyCursor wait;
// this way we can use that mesh directly.
// This mesh does not account for the possible Z up SLA offset.
m_mesh = &m_model_object->volumes.front()->mesh();
m_its = &m_mesh->its;
- m_current_mesh_model_id = m_model_object->id();
- m_editing_mode = false;
- m_AABB.deinit();
- m_AABB.init(
- MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
- MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3));
+ // If this is different mesh than last time or if the AABB tree is uninitialized, recalculate it.
+ if (m_current_mesh_object_id != m_model_object->id() || (m_AABB.m_left == NULL && m_AABB.m_right == NULL))
+ {
+ m_AABB.deinit();
+ m_AABB.init(
+ MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
+ MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3));
+ }
+
+ m_current_mesh_object_id = m_model_object->id();
+ m_editing_mode = false;
}
// Unprojects the mouse position on the mesh and return the hit point and normal of the facet.
@@ -703,12 +716,12 @@ void GLGizmoSlaSupports::delete_selected_points(bool force)
//m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
-void GLGizmoSlaSupports::on_update(const UpdateData& data, const Selection& selection)
+void GLGizmoSlaSupports::on_update(const UpdateData& data)
{
- if (m_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_editing_mode_cache[m_hover_id].support_point.is_new_island || !m_lock_unique_islands)) {
+ if (m_editing_mode && m_hover_id != -1 && (!m_editing_mode_cache[m_hover_id].support_point.is_new_island || !m_lock_unique_islands)) {
std::pair<Vec3f, Vec3f> pos_and_normal;
try {
- pos_and_normal = unproject_on_mesh(Vec2d((*data.mouse_pos)(0), (*data.mouse_pos)(1)));
+ pos_and_normal = unproject_on_mesh(data.mouse_pos.cast<double>());
}
catch (...) { return; }
m_editing_mode_cache[m_hover_id].support_point.pos = pos_and_normal.first;
@@ -823,7 +836,7 @@ void GLGizmoSlaSupports::make_line_segments() const
*/
-void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection)
+void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit)
{
if (!m_model_object)
return;
@@ -924,10 +937,12 @@ RENDER_AGAIN:
}
if (value_changed) { // Update side panel
- wxTheApp->CallAfter([]() {
- wxGetApp().obj_settings()->UpdateAndShow(true);
- wxGetApp().obj_list()->update_settings_items();
- });
+/* wxTheApp->CallAfter([]() {
+ * wxGetApp().obj_settings()->UpdateAndShow(true);
+ * wxGetApp().obj_list()->update_settings_items();
+ * });
+ * #lm_FIXME_delete_after_testing */
+ wxGetApp().obj_list()->update_and_show_object_settings_item();
}
bool generate = m_imgui->button(m_desc.at("auto_generate"));
@@ -1001,11 +1016,13 @@ RENDER_AGAIN:
m_parent.set_as_dirty();
}
-bool GLGizmoSlaSupports::on_is_activable(const Selection& selection) const
+bool GLGizmoSlaSupports::on_is_activable() const
{
+ const Selection& selection = m_parent.get_selection();
+
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA
|| !selection.is_from_single_instance())
- return false;
+ return false;
// Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside.
const Selection::IndicesList& list = selection.get_volume_idxs();
@@ -1028,53 +1045,72 @@ std::string GLGizmoSlaSupports::on_get_name() const
void GLGizmoSlaSupports::on_set_state()
{
- if (m_state == On && m_old_state != On) { // the gizmo was just turned on
- if (is_mesh_update_necessary())
- update_mesh();
+ // m_model_object pointer can be invalid (for instance because of undo/redo action),
+ // we should recover it from the object id
+ const ModelObject* old_model_object = m_model_object;
+ m_model_object = nullptr;
+ for (const auto mo : *wxGetApp().model_objects()) {
+ if (mo->id() == m_current_mesh_object_id) {
+ m_model_object = mo;
+ break;
+ }
+ }
- // we'll now reload support points:
- if (m_model_object)
- editing_mode_reload_cache();
+ // If ModelObject pointer really changed, invalidate mesh and do everything
+ // as if the gizmo was switched from Off state
+ if (m_model_object == nullptr || old_model_object != m_model_object) {
+ m_mesh = nullptr;
+ m_its = nullptr;
+ m_old_state = Off;
+ }
- m_parent.toggle_model_objects_visibility(false);
- if (m_model_object)
- m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
+ if (m_state == On && m_old_state != On) { // the gizmo was just turned on
+ if (is_mesh_update_necessary())
+ update_mesh();
- // Set default head diameter from config.
- const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
- m_new_point_head_diameter = static_cast<const ConfigOptionFloat*>(cfg.option("support_head_front_diameter"))->value;
- }
- if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
- wxGetApp().CallAfter([this]() {
- // Following is called through CallAfter, because otherwise there was a problem
- // on OSX with the wxMessageDialog being shown several times when clicked into.
- if (m_model_object) {
- if (m_unsaved_changes) {
- wxMessageDialog dlg(GUI::wxGetApp().mainframe, _(L("Do you want to save your manually edited support points?")) + "\n",
- _(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
- if (dlg.ShowModal() == wxID_YES)
- editing_mode_apply_changes();
- else
- editing_mode_discard_changes();
- }
+ // we'll now reload support points:
+ if (m_model_object)
+ editing_mode_reload_cache();
+
+ m_parent.toggle_model_objects_visibility(false);
+ if (m_model_object)
+ m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
+
+ // Set default head diameter from config.
+ const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
+ m_new_point_head_diameter = static_cast<const ConfigOptionFloat*>(cfg.option("support_head_front_diameter"))->value;
+ }
+ if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
+ wxGetApp().CallAfter([this]() {
+ // Following is called through CallAfter, because otherwise there was a problem
+ // on OSX with the wxMessageDialog being shown several times when clicked into.
+ if (m_model_object) {
+ if (m_unsaved_changes) {
+ wxMessageDialog dlg(GUI::wxGetApp().mainframe, _(L("Do you want to save your manually edited support points?")) + "\n",
+ _(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
+ if (dlg.ShowModal() == wxID_YES)
+ editing_mode_apply_changes();
+ else
+ editing_mode_discard_changes();
}
- m_parent.toggle_model_objects_visibility(true);
- m_editing_mode = false; // so it is not active next time the gizmo opens
- m_editing_mode_cache.clear();
- m_clipping_plane_distance = 0.f;
- // Release triangle mesh slicer and the AABB spatial search structure.
- m_AABB.deinit();
- m_its = nullptr;
- m_tms.reset();
- m_supports_tms.reset();
- });
- }
- m_old_state = m_state;
+ }
+ m_parent.toggle_model_objects_visibility(true);
+ m_editing_mode = false; // so it is not active next time the gizmo opens
+ m_editing_mode_cache.clear();
+ m_clipping_plane_distance = 0.f;
+ // Release triangle mesh slicer and the AABB spatial search structure.
+ m_AABB.deinit();
+ m_its = nullptr;
+ m_tms.reset();
+ m_supports_tms.reset();
+ });
+ }
+ m_old_state = m_state;
}
-void GLGizmoSlaSupports::on_start_dragging(const Selection& selection)
+void GLGizmoSlaSupports::on_start_dragging()
{
if (m_hover_id != -1) {
select_point(NoPoints);
@@ -1084,6 +1120,26 @@ void GLGizmoSlaSupports::on_start_dragging(const Selection& selection)
+void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar)
+{
+ ar(m_clipping_plane_distance,
+ m_clipping_plane_normal,
+ m_current_mesh_object_id
+ );
+}
+
+
+
+void GLGizmoSlaSupports::on_save(cereal::BinaryOutputArchive& ar) const
+{
+ ar(m_clipping_plane_distance,
+ m_clipping_plane_normal,
+ m_current_mesh_object_id
+ );
+}
+
+
+
void GLGizmoSlaSupports::select_point(int i)
{
if (i == AllPoints || i == NoPoints) {
@@ -1140,6 +1196,7 @@ void GLGizmoSlaSupports::editing_mode_apply_changes()
// If there are no changes, don't touch the front-end. The data in the cache could have been
// taken from the backend and copying them to ModelObject would needlessly invalidate them.
if (m_unsaved_changes) {
+ wxGetApp().plater()->take_snapshot(_(L("Support points edit")));
m_model_object->sla_points_status = sla::PointsStatus::UserModified;
m_model_object->sla_support_points.clear();
for (const CacheEntry& cache_entry : m_editing_mode_cache)
@@ -1198,6 +1255,7 @@ void GLGizmoSlaSupports::auto_generate()
)), _(L("Warning")), wxICON_WARNING | wxYES | wxNO);
if (m_model_object->sla_points_status != sla::PointsStatus::UserModified || m_editing_mode_cache.empty() || dlg.ShowModal() == wxID_YES) {
+ wxGetApp().plater()->take_snapshot(_(L("Autogenerate support points")));
m_model_object->sla_support_points.clear();
m_model_object->sla_points_status = sla::PointsStatus::Generating;
m_editing_mode_cache.clear();
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
index 30238cc9d..fbc438f1d 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
@@ -14,6 +14,8 @@
#include "libslic3r/SLAPrint.hpp"
#include <wx/dialog.h>
+#include <cereal/types/vector.hpp>
+
namespace Slic3r {
namespace GUI {
@@ -26,7 +28,7 @@ class GLGizmoSlaSupports : public GLGizmoBase
{
private:
ModelObject* m_model_object = nullptr;
- ModelID m_current_mesh_model_id = 0;
+ ObjectID m_current_mesh_object_id = 0;
int m_active_instance = -1;
float m_active_instance_bb_radius; // to cache the bb
mutable float m_z_shift = 0.f;
@@ -49,20 +51,25 @@ private:
class CacheEntry {
public:
+ CacheEntry() :
+ support_point(sla::SupportPoint()), selected(false), normal(Vec3f::Zero()) {}
+
CacheEntry(const sla::SupportPoint& point, bool sel, const Vec3f& norm = Vec3f::Zero()) :
support_point(point), selected(sel), normal(norm) {}
sla::SupportPoint support_point;
bool selected; // whether the point is selected
Vec3f normal;
+
+ template<class Archive>
+ void serialize(Archive & ar)
+ {
+ ar(support_point, selected, normal);
+ }
};
public:
-#if ENABLE_SVG_ICONS
GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
-#else
- GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id);
-#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoSlaSupports();
void set_sla_support_data(ModelObject* model_object, const Selection& selection);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
@@ -74,9 +81,9 @@ public:
private:
bool on_init();
- void on_update(const UpdateData& data, const Selection& selection);
- virtual void on_render(const Selection& selection) const;
- virtual void on_render_for_picking(const Selection& selection) const;
+ void on_update(const UpdateData& data);
+ virtual void on_render() const;
+ virtual void on_render_for_picking() const;
//void render_selection_rectangle() const;
void render_points(const Selection& selection, bool picking = false) const;
@@ -137,12 +144,14 @@ protected:
if ((int)m_editing_mode_cache.size() <= m_hover_id)
m_hover_id = -1;
}
- void on_start_dragging(const Selection& selection) override;
- virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) override;
+ void on_start_dragging() override;
+ virtual void on_render_input_window(float x, float y, float bottom_limit) override;
virtual std::string on_get_name() const;
- virtual bool on_is_activable(const Selection& selection) const;
+ virtual bool on_is_activable() const;
virtual bool on_is_selectable() const;
+ virtual void on_load(cereal::BinaryInputArchive& ar) override;
+ virtual void on_save(cereal::BinaryOutputArchive& ar) const override;
};
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
index c7435636d..df53b40aa 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
@@ -12,51 +12,29 @@
namespace Slic3r {
namespace GUI {
-#if ENABLE_SVG_ICONS
- const float GLGizmosManager::Default_Icons_Size = 64;
-#endif // ENABLE_SVG_ICONS
+const float GLGizmosManager::Default_Icons_Size = 64;
-GLGizmosManager::GLGizmosManager()
- : m_enabled(false)
-#if ENABLE_SVG_ICONS
+GLGizmosManager::GLGizmosManager(GLCanvas3D& parent)
+ : m_parent(parent)
+ , m_enabled(false)
, m_icons_texture_dirty(true)
-#endif // ENABLE_SVG_ICONS
, m_current(Undefined)
-#if ENABLE_SVG_ICONS
, m_overlay_icons_size(Default_Icons_Size)
, m_overlay_scale(1.0f)
, m_overlay_border(5.0f)
, m_overlay_gap_y(5.0f)
, m_tooltip("")
+ , m_serializing(false)
{
}
-#else
-{
- set_overlay_scale(1.0);
-}
-#endif // ENABLE_SVG_ICONS
GLGizmosManager::~GLGizmosManager()
{
reset();
}
-bool GLGizmosManager::init(GLCanvas3D& parent)
+bool GLGizmosManager::init()
{
-#if !ENABLE_SVG_ICONS
- m_icons_texture.metadata.filename = "gizmos.png";
- m_icons_texture.metadata.icon_size = 64;
-
- if (!m_icons_texture.metadata.filename.empty())
- {
- if (!m_icons_texture.texture.load_from_file(resources_dir() + "/icons/" + m_icons_texture.metadata.filename, false))
- {
- reset();
- return false;
- }
- }
-#endif // !ENABLE_SVG_ICONS
-
m_background_texture.metadata.filename = "toolbar_background.png";
m_background_texture.metadata.left = 16;
m_background_texture.metadata.top = 16;
@@ -72,11 +50,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
}
}
-#if ENABLE_SVG_ICONS
- GLGizmoBase* gizmo = new GLGizmoMove3D(parent, "move.svg", 0);
-#else
- GLGizmoBase* gizmo = new GLGizmoMove3D(parent, 0);
-#endif // ENABLE_SVG_ICONS
+ GLGizmoBase* gizmo = new GLGizmoMove3D(m_parent, "move.svg", 0);
if (gizmo == nullptr)
return false;
@@ -85,11 +59,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
m_gizmos.insert(GizmosMap::value_type(Move, gizmo));
-#if ENABLE_SVG_ICONS
- gizmo = new GLGizmoScale3D(parent, "scale.svg", 1);
-#else
- gizmo = new GLGizmoScale3D(parent, 1);
-#endif // ENABLE_SVG_ICONS
+ gizmo = new GLGizmoScale3D(m_parent, "scale.svg", 1);
if (gizmo == nullptr)
return false;
@@ -98,11 +68,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
m_gizmos.insert(GizmosMap::value_type(Scale, gizmo));
-#if ENABLE_SVG_ICONS
- gizmo = new GLGizmoRotate3D(parent, "rotate.svg", 2);
-#else
- gizmo = new GLGizmoRotate3D(parent, 2);
-#endif // ENABLE_SVG_ICONS
+ gizmo = new GLGizmoRotate3D(m_parent, "rotate.svg", 2);
if (gizmo == nullptr)
{
reset();
@@ -117,11 +83,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo));
-#if ENABLE_SVG_ICONS
- gizmo = new GLGizmoFlatten(parent, "place.svg", 3);
-#else
- gizmo = new GLGizmoFlatten(parent, 3);
-#endif // ENABLE_SVG_ICONS
+ gizmo = new GLGizmoFlatten(m_parent, "place.svg", 3);
if (gizmo == nullptr)
return false;
@@ -132,11 +94,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo));
-#if ENABLE_SVG_ICONS
- gizmo = new GLGizmoCut(parent, "cut.svg", 4);
-#else
- gizmo = new GLGizmoCut(parent, 4);
-#endif // ENABLE_SVG_ICONS
+ gizmo = new GLGizmoCut(m_parent, "cut.svg", 4);
if (gizmo == nullptr)
return false;
@@ -147,11 +105,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
m_gizmos.insert(GizmosMap::value_type(Cut, gizmo));
-#if ENABLE_SVG_ICONS
- gizmo = new GLGizmoSlaSupports(parent, "sla_supports.svg", 5);
-#else
- gizmo = new GLGizmoSlaSupports(parent, 5);
-#endif // ENABLE_SVG_ICONS
+ gizmo = new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 5);
if (gizmo == nullptr)
return false;
@@ -165,7 +119,6 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
return true;
}
-#if ENABLE_SVG_ICONS
void GLGizmosManager::set_overlay_icon_size(float size)
{
if (m_overlay_icons_size != size)
@@ -174,29 +127,25 @@ void GLGizmosManager::set_overlay_icon_size(float size)
m_icons_texture_dirty = true;
}
}
-#endif // ENABLE_SVG_ICONS
void GLGizmosManager::set_overlay_scale(float scale)
{
-#if ENABLE_SVG_ICONS
if (m_overlay_scale != scale)
{
m_overlay_scale = scale;
m_icons_texture_dirty = true;
}
-#else
- m_overlay_icons_scale = scale;
- m_overlay_border = 5.0f * scale;
- m_overlay_gap_y = 5.0f * scale;
-#endif // ENABLE_SVG_ICONS
}
-void GLGizmosManager::refresh_on_off_state(const Selection& selection)
+void GLGizmosManager::refresh_on_off_state()
{
+ if (m_serializing)
+ return;
+
GizmosMap::iterator it = m_gizmos.find(m_current);
if ((it != m_gizmos.end()) && (it->second != nullptr))
{
- if (!it->second->is_activable(selection))
+ if (!it->second->is_activable())
{
it->second->set_state(GLGizmoBase::Off);
m_current = Undefined;
@@ -209,6 +158,9 @@ void GLGizmosManager::reset_all_states()
if (!m_enabled)
return;
+ if (m_serializing)
+ return;
+
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
{
if (it->second != nullptr)
@@ -248,22 +200,22 @@ void GLGizmosManager::enable_grabber(EType type, unsigned int id, bool enable)
}
}
-void GLGizmosManager::update(const Linef3& mouse_ray, const Selection& selection, const Point* mouse_pos)
+void GLGizmosManager::update(const Linef3& mouse_ray, const Point& mouse_pos)
{
if (!m_enabled)
return;
GLGizmoBase* curr = get_current();
if (curr != nullptr)
- curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos), selection);
+ curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos));
}
-void GLGizmosManager::update_data(GLCanvas3D& canvas)
+void GLGizmosManager::update_data()
{
if (!m_enabled)
return;
- const Selection& selection = canvas.get_selection();
+ const Selection& selection = m_parent.get_selection();
bool is_wipe_tower = selection.is_wipe_tower();
enable_grabber(Move, 2, !is_wipe_tower);
@@ -284,7 +236,7 @@ void GLGizmosManager::update_data(GLCanvas3D& canvas)
set_rotation(Vec3d::Zero());
ModelObject* model_object = selection.get_model()->objects[selection.get_object_idx()];
set_flattening_data(model_object);
- set_sla_support_data(model_object, selection);
+ set_sla_support_data(model_object);
}
else if (selection.is_single_volume() || selection.is_single_modifier())
{
@@ -292,7 +244,7 @@ void GLGizmosManager::update_data(GLCanvas3D& canvas)
set_scale(volume->get_volume_scaling_factor());
set_rotation(Vec3d::Zero());
set_flattening_data(nullptr);
- set_sla_support_data(nullptr, selection);
+ set_sla_support_data(nullptr);
}
else if (is_wipe_tower)
{
@@ -300,14 +252,14 @@ void GLGizmosManager::update_data(GLCanvas3D& canvas)
set_scale(Vec3d::Ones());
set_rotation(Vec3d(0., 0., (M_PI/180.) * dynamic_cast<const ConfigOptionFloat*>(config.option("wipe_tower_rotation_angle"))->value));
set_flattening_data(nullptr);
- set_sla_support_data(nullptr, selection);
+ set_sla_support_data(nullptr);
}
else
{
set_scale(Vec3d::Ones());
set_rotation(Vec3d::Zero());
set_flattening_data(selection.is_from_single_object() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr);
- set_sla_support_data(nullptr, selection);
+ set_sla_support_data(nullptr);
}
}
@@ -320,9 +272,12 @@ bool GLGizmosManager::is_running() const
return (curr != nullptr) ? (curr->get_state() == GLGizmoBase::On) : false;
}
-bool GLGizmosManager::handle_shortcut(int key, const Selection& selection)
+bool GLGizmosManager::handle_shortcut(int key)
{
- if (!m_enabled || selection.is_empty())
+ if (!m_enabled)
+ return false;
+
+ if (m_parent.get_selection().is_empty())
return false;
EType old_current = m_current;
@@ -334,7 +289,7 @@ bool GLGizmosManager::handle_shortcut(int key, const Selection& selection)
int it_key = it->second->get_shortcut_key();
- if (it->second->is_activable(selection) && ((it_key == key - 64) || (it_key == key - 96)))
+ if (it->second->is_activable() && ((it_key == key - 64) || (it_key == key - 96)))
{
if ((it->second->get_state() == GLGizmoBase::On))
{
@@ -370,14 +325,14 @@ bool GLGizmosManager::is_dragging() const
return (curr != nullptr) ? curr->is_dragging() : false;
}
-void GLGizmosManager::start_dragging(const Selection& selection)
+void GLGizmosManager::start_dragging()
{
if (!m_enabled)
return;
GLGizmoBase* curr = get_current();
if (curr != nullptr)
- curr->start_dragging(selection);
+ curr->start_dragging();
}
void GLGizmosManager::stop_dragging()
@@ -465,14 +420,14 @@ void GLGizmosManager::set_flattening_data(const ModelObject* model_object)
reinterpret_cast<GLGizmoFlatten*>(it->second)->set_flattening_data(model_object);
}
-void GLGizmosManager::set_sla_support_data(ModelObject* model_object, const Selection& selection)
+void GLGizmosManager::set_sla_support_data(ModelObject* model_object)
{
if (!m_enabled)
return;
GizmosMap::const_iterator it = m_gizmos.find(SlaSupports);
if (it != m_gizmos.end())
- reinterpret_cast<GLGizmoSlaSupports*>(it->second)->set_sla_support_data(model_object, selection);
+ reinterpret_cast<GLGizmoSlaSupports*>(it->second)->set_sla_support_data(model_object, m_parent.get_selection());
}
// Returns true if the gizmo used the event to do something, false otherwise.
@@ -501,40 +456,38 @@ ClippingPlane GLGizmosManager::get_sla_clipping_plane() const
}
-void GLGizmosManager::render_current_gizmo(const Selection& selection) const
+void GLGizmosManager::render_current_gizmo() const
{
if (!m_enabled)
return;
GLGizmoBase* curr = get_current();
if (curr != nullptr)
- curr->render(selection);
+ curr->render();
}
-void GLGizmosManager::render_current_gizmo_for_picking_pass(const Selection& selection) const
+void GLGizmosManager::render_current_gizmo_for_picking_pass() const
{
if (!m_enabled)
return;
GLGizmoBase* curr = get_current();
if (curr != nullptr)
- curr->render_for_picking(selection);
+ curr->render_for_picking();
}
-void GLGizmosManager::render_overlay(const GLCanvas3D& canvas, const Selection& selection) const
+void GLGizmosManager::render_overlay() const
{
if (!m_enabled)
return;
-#if ENABLE_SVG_ICONS
if (m_icons_texture_dirty)
generate_icons_texture();
-#endif // ENABLE_SVG_ICONS
- do_render_overlay(canvas, selection);
+ do_render_overlay();
}
-bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt, GLCanvas3D& canvas)
+bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt)
{
bool processed = false;
@@ -547,14 +500,12 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt, GLCanvas3D& canvas)
return processed;
}
-
-
-bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
+bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
{
Point pos(evt.GetX(), evt.GetY());
Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY());
- Selection& selection = canvas.get_selection();
+ Selection& selection = m_parent.get_selection();
int selected_object_idx = selection.get_object_idx();
bool processed = false;
@@ -570,7 +521,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
// mouse anywhere
if (evt.Moving())
- m_tooltip = update_hover_state(canvas, mouse_pos);
+ m_tooltip = update_hover_state(mouse_pos);
else if (evt.LeftUp())
m_mouse_capture.left = false;
else if (evt.MiddleUp())
@@ -581,7 +532,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
// if the button down was done on this toolbar, prevent from dragging into the scene
processed = true;
- if (!overlay_contains_mouse(canvas, mouse_pos))
+ if (!overlay_contains_mouse(mouse_pos))
{
// mouse is outside the toolbar
m_tooltip = "";
@@ -593,41 +544,40 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
processed = true;
else if (!selection.is_empty() && grabber_contains_mouse())
{
- update_data(canvas);
+ update_data();
selection.start_dragging();
- start_dragging(selection);
+ start_dragging();
if (m_current == Flatten)
{
// Rotate the object so the normal points downward:
- selection.flattening_rotate(get_flattening_normal());
- canvas.do_flatten();
+ m_parent.do_flatten(get_flattening_normal(), L("Gizmo-Place on Face"));
wxGetApp().obj_manipul()->set_dirty();
}
- canvas.set_as_dirty();
+ m_parent.set_as_dirty();
processed = true;
}
}
else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::RightDown))
// event was taken care of by the SlaSupports gizmo
processed = true;
- else if (evt.Dragging() && (canvas.get_move_volume_id() != -1) && (m_current == SlaSupports))
- // don't allow dragging objects with the Sla gizmo on
+ else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) && (m_current == SlaSupports))
+ // don't allow dragging objects with the Sla gizmo on
processed = true;
else if (evt.Dragging() && (m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()))
{
// the gizmo got the event and took some action, no need to do anything more here
- canvas.set_as_dirty();
+ m_parent.set_as_dirty();
processed = true;
}
else if (evt.Dragging() && is_dragging())
{
- if (!canvas.get_wxglcanvas()->HasCapture())
- canvas.get_wxglcanvas()->CaptureMouse();
+ if (!m_parent.get_wxglcanvas()->HasCapture())
+ m_parent.get_wxglcanvas()->CaptureMouse();
- canvas.set_mouse_as_dragging();
- update(canvas.mouse_ray(pos), selection, &pos);
+ m_parent.set_mouse_as_dragging();
+ update(m_parent.mouse_ray(pos), pos);
switch (m_current)
{
@@ -664,7 +614,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
break;
}
- canvas.set_as_dirty();
+ m_parent.set_as_dirty();
processed = true;
}
else if (evt.LeftUp() && is_dragging())
@@ -673,18 +623,18 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
{
case Move:
{
- canvas.disable_regenerate_volumes();
- canvas.do_move();
+ m_parent.disable_regenerate_volumes();
+ m_parent.do_move(L("Gizmo-Move"));
break;
}
case Scale:
{
- canvas.do_scale();
+ m_parent.do_scale(L("Gizmo-Scale"));
break;
}
case Rotate:
{
- canvas.do_rotate();
+ m_parent.do_rotate(L("Gizmo-Rotate"));
break;
}
default:
@@ -692,25 +642,25 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
}
stop_dragging();
- update_data(canvas);
+ update_data();
wxGetApp().obj_manipul()->set_dirty();
// Let the platter know that the dragging finished, so a delayed refresh
// of the scene with the background processing data should be performed.
- canvas.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
+ m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
// updates camera target constraints
- canvas.refresh_camera_scene_box();
+ m_parent.refresh_camera_scene_box();
processed = true;
}
- else if (evt.LeftUp() && (m_current == SlaSupports) && !canvas.is_mouse_dragging())
+ else if (evt.LeftUp() && (m_current == SlaSupports) && !m_parent.is_mouse_dragging())
{
// in case SLA gizmo is selected, we just pass the LeftUp event and stop processing - neither
// object moving or selecting is suppressed in that case
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown());
processed = true;
}
- else if (evt.LeftUp() && (m_current == Flatten) && ((canvas.get_first_hover_volume_idx() != -1) || grabber_contains_mouse()))
+ else if (evt.LeftUp() && (m_current == Flatten) && ((m_parent.get_first_hover_volume_idx() != -1) || grabber_contains_mouse()))
{
// to avoid to loose the selection when user clicks an object while the Flatten gizmo is active
processed = true;
@@ -722,24 +672,24 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
if (evt.LeftDown() || evt.LeftDClick())
{
m_mouse_capture.left = true;
- m_mouse_capture.parent = &canvas;
+ m_mouse_capture.parent = &m_parent;
processed = true;
if (!selection.is_empty())
{
- update_on_off_state(canvas, mouse_pos, selection);
- update_data(canvas);
- canvas.set_as_dirty();
+ update_on_off_state(mouse_pos);
+ update_data();
+ m_parent.set_as_dirty();
}
}
else if (evt.MiddleDown())
{
m_mouse_capture.middle = true;
- m_mouse_capture.parent = &canvas;
+ m_mouse_capture.parent = &m_parent;
}
else if (evt.RightDown())
{
m_mouse_capture.right = true;
- m_mouse_capture.parent = &canvas;
+ m_mouse_capture.parent = &m_parent;
}
else if (evt.LeftUp())
processed = true;
@@ -748,7 +698,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
return processed;
}
-bool GLGizmosManager::on_char(wxKeyEvent& evt, GLCanvas3D& canvas)
+bool GLGizmosManager::on_char(wxKeyEvent& evt)
{
// see include/wx/defs.h enum wxKeyCode
int keyCode = evt.GetKeyCode();
@@ -856,20 +806,20 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt, GLCanvas3D& canvas)
if (!processed && !evt.HasModifiers())
{
- if (handle_shortcut(keyCode, canvas.get_selection()))
+ if (handle_shortcut(keyCode))
{
- update_data(canvas);
+ update_data();
processed = true;
}
}
if (processed)
- canvas.set_as_dirty();
+ m_parent.set_as_dirty();
return processed;
}
-bool GLGizmosManager::on_key(wxKeyEvent& evt, GLCanvas3D& canvas)
+bool GLGizmosManager::on_key(wxKeyEvent& evt)
{
const int keyCode = evt.GetKeyCode();
bool processed = false;
@@ -895,23 +845,29 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt, GLCanvas3D& canvas)
}
// if (processed)
-// canvas.set_cursor(GLCanvas3D::Standard);
+// m_parent.set_cursor(GLCanvas3D::Standard);
}
else if (evt.GetEventType() == wxEVT_KEY_DOWN)
{
if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)) && reinterpret_cast<GLGizmoSlaSupports*>(get_current())->is_in_editing_mode())
{
-// canvas.set_cursor(GLCanvas3D::Cross);
+// m_parent.set_cursor(GLCanvas3D::Cross);
processed = true;
}
}
if (processed)
- canvas.set_as_dirty();
+ m_parent.set_as_dirty();
return processed;
}
+void GLGizmosManager::update_after_undo_redo()
+{
+ update_data();
+ m_serializing = false;
+}
+
void GLGizmosManager::reset()
{
for (GizmosMap::value_type& gizmo : m_gizmos)
@@ -923,23 +879,19 @@ void GLGizmosManager::reset()
m_gizmos.clear();
}
-void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selection& selection) const
+void GLGizmosManager::do_render_overlay() const
{
if (m_gizmos.empty())
return;
- float cnv_w = (float)canvas.get_canvas_size().get_width();
- float cnv_h = (float)canvas.get_canvas_size().get_height();
- float zoom = (float)canvas.get_camera().get_zoom();
+ float cnv_w = (float)m_parent.get_canvas_size().get_width();
+ float cnv_h = (float)m_parent.get_canvas_size().get_height();
+ float zoom = (float)m_parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float height = get_total_overlay_height();
float width = get_total_overlay_width();
-#if ENABLE_SVG_ICONS
float scaled_border = m_overlay_border * m_overlay_scale * inv_zoom;
-#else
- float scaled_border = m_overlay_border * inv_zoom;
-#endif // ENABLE_SVG_ICONS
float top_x = (-0.5f * cnv_w) * inv_zoom;
float top_y = (0.5f * height) * inv_zoom;
@@ -1015,7 +967,6 @@ void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selectio
}
}
-#if ENABLE_SVG_ICONS
top_x += scaled_border;
top_y -= scaled_border;
float scaled_gap_y = m_overlay_gap_y * m_overlay_scale * inv_zoom;
@@ -1027,21 +978,9 @@ void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selectio
unsigned int tex_height = m_icons_texture.get_height();
float inv_tex_width = (tex_width != 0) ? 1.0f / (float)tex_width : 0.0f;
float inv_tex_height = (tex_height != 0) ? 1.0f / (float)tex_height : 0.0f;
-#else
- top_x += m_overlay_border * inv_zoom;
- top_y -= m_overlay_border * inv_zoom;
- float scaled_gap_y = m_overlay_gap_y * inv_zoom;
-
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale * inv_zoom;
- unsigned int icons_texture_id = m_icons_texture.texture.get_id();
- unsigned int texture_size = m_icons_texture.texture.get_width();
- float inv_texture_size = (texture_size != 0) ? 1.0f / (float)texture_size : 0.0f;
-#endif // ENABLE_SVG_ICONS
-
-#if ENABLE_SVG_ICONS
+
if ((icons_texture_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
-#endif // ENABLE_SVG_ICONS
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
{
@@ -1051,78 +990,44 @@ void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selectio
unsigned int sprite_id = it->second->get_sprite_id();
GLGizmoBase::EState state = it->second->get_state();
-#if ENABLE_SVG_ICONS
float u_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_width;
float v_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_height;
float v_top = sprite_id * v_icon_size;
float u_left = state * u_icon_size;
float v_bottom = v_top + v_icon_size;
float u_right = u_left + u_icon_size;
-#else
- float uv_icon_size = (float)m_icons_texture.metadata.icon_size * inv_texture_size;
- float v_top = sprite_id * uv_icon_size;
- float u_left = state * uv_icon_size;
- float v_bottom = v_top + uv_icon_size;
- float u_right = u_left + uv_icon_size;
-#endif // ENABLE_SVG_ICONS
GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } });
if (it->second->get_state() == GLGizmoBase::On) {
- float toolbar_top = (float)cnv_h - canvas.get_view_toolbar_height();
-#if ENABLE_SVG_ICONS
- it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection);
-#else
- it->second->render_input_window(2.0f * m_overlay_border + icon_size * zoom, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection);
-#endif // ENABLE_SVG_ICONS
+ float toolbar_top = (float)cnv_h - m_parent.get_view_toolbar_height();
+ it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top);
}
-#if ENABLE_SVG_ICONS
top_y -= scaled_stride_y;
-#else
- top_y -= (scaled_icons_size + scaled_gap_y);
-#endif // ENABLE_SVG_ICONS
}
}
float GLGizmosManager::get_total_overlay_height() const
{
-#if ENABLE_SVG_ICONS
float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
float scaled_border = m_overlay_border * m_overlay_scale;
float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
float scaled_stride_y = scaled_icons_size + scaled_gap_y;
float height = 2.0f * scaled_border;
-#else
- float height = 2.0f * m_overlay_border;
-
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
-#endif // ENABLE_SVG_ICONS
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
{
if ((it->second == nullptr) || !it->second->is_selectable())
continue;
-#if ENABLE_SVG_ICONS
height += scaled_stride_y;
-#else
- height += (scaled_icons_size + m_overlay_gap_y);
-#endif // ENABLE_SVG_ICONS
}
-#if ENABLE_SVG_ICONS
return height - scaled_gap_y;
-#else
- return height - m_overlay_gap_y;
-#endif // ENABLE_SVG_ICONS
}
float GLGizmosManager::get_total_overlay_width() const
{
-#if ENABLE_SVG_ICONS
return (2.0f * m_overlay_border + m_overlay_icons_size) * m_overlay_scale;
-#else
- return (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale + 2.0f * m_overlay_border;
-#endif // ENABLE_SVG_ICONS
}
GLGizmoBase* GLGizmosManager::get_current() const
@@ -1131,7 +1036,6 @@ GLGizmoBase* GLGizmosManager::get_current() const
return (it != m_gizmos.end()) ? it->second : nullptr;
}
-#if ENABLE_SVG_ICONS
bool GLGizmosManager::generate_icons_texture() const
{
std::string path = resources_dir() + "/icons/";
@@ -1157,38 +1061,28 @@ bool GLGizmosManager::generate_icons_texture() const
return res;
}
-#endif // ENABLE_SVG_ICONS
-void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection)
+void GLGizmosManager::update_on_off_state(const Vec2d& mouse_pos)
{
if (!m_enabled)
return;
- float cnv_h = (float)canvas.get_canvas_size().get_height();
+ float cnv_h = (float)m_parent.get_canvas_size().get_height();
float height = get_total_overlay_height();
-#if ENABLE_SVG_ICONS
float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
float scaled_border = m_overlay_border * m_overlay_scale;
float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
float scaled_stride_y = scaled_icons_size + scaled_gap_y;
float top_y = 0.5f * (cnv_h - height) + scaled_border;
-#else
- float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
-#endif // ENABLE_SVG_ICONS
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
{
if ((it->second == nullptr) || !it->second->is_selectable())
continue;
-#if ENABLE_SVG_ICONS
bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
-#else
- bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
-#endif // ENABLE_SVG_ICONS
- if (it->second->is_activable(selection) && inside)
+ if (it->second->is_activable() && inside)
{
if ((it->second->get_state() == GLGizmoBase::On))
{
@@ -1204,11 +1098,7 @@ void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d&
else
it->second->set_state(GLGizmoBase::Off);
-#if ENABLE_SVG_ICONS
top_y += scaled_stride_y;
-#else
- top_y += (scaled_icons_size + m_overlay_gap_y);
-#endif // ENABLE_SVG_ICONS
}
GizmosMap::iterator it = m_gizmos.find(m_current);
@@ -1216,90 +1106,62 @@ void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d&
it->second->set_state(GLGizmoBase::On);
}
-std::string GLGizmosManager::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos)
+std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos)
{
std::string name = "";
if (!m_enabled)
return name;
- const Selection& selection = canvas.get_selection();
-
- float cnv_h = (float)canvas.get_canvas_size().get_height();
+ float cnv_h = (float)m_parent.get_canvas_size().get_height();
float height = get_total_overlay_height();
-#if ENABLE_SVG_ICONS
float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
float scaled_border = m_overlay_border * m_overlay_scale;
float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
float scaled_stride_y = scaled_icons_size + scaled_gap_y;
float top_y = 0.5f * (cnv_h - height) + scaled_border;
-#else
- float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
-#endif // ENABLE_SVG_ICONS
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
{
if ((it->second == nullptr) || !it->second->is_selectable())
continue;
-#if ENABLE_SVG_ICONS
bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
-#else
- bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
-#endif // ENABLE_SVG_ICONS
if (inside)
name = it->second->get_name();
- if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On))
+ if (it->second->is_activable() && (it->second->get_state() != GLGizmoBase::On))
it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off);
-#if ENABLE_SVG_ICONS
top_y += scaled_stride_y;
-#else
- top_y += (scaled_icons_size + m_overlay_gap_y);
-#endif // ENABLE_SVG_ICONS
}
return name;
}
-bool GLGizmosManager::overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const
+bool GLGizmosManager::overlay_contains_mouse(const Vec2d& mouse_pos) const
{
if (!m_enabled)
return false;
- float cnv_h = (float)canvas.get_canvas_size().get_height();
+ float cnv_h = (float)m_parent.get_canvas_size().get_height();
float height = get_total_overlay_height();
-#if ENABLE_SVG_ICONS
float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
float scaled_border = m_overlay_border * m_overlay_scale;
float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
float scaled_stride_y = scaled_icons_size + scaled_gap_y;
float top_y = 0.5f * (cnv_h - height) + scaled_border;
-#else
- float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
- float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
-#endif // ENABLE_SVG_ICONS
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
{
if ((it->second == nullptr) || !it->second->is_selectable())
continue;
-#if ENABLE_SVG_ICONS
if ((scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size))
-#else
- if ((m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size))
-#endif // ENABLE_SVG_ICONS
return true;
-#if ENABLE_SVG_ICONS
top_y += scaled_stride_y;
-#else
- top_y += (scaled_icons_size + m_overlay_gap_y);
-#endif // ENABLE_SVG_ICONS
}
return false;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
index 1e42a29e6..803613ec7 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
@@ -4,14 +4,13 @@
#include "slic3r/GUI/GLTexture.hpp"
#include "slic3r/GUI/GLToolbar.hpp"
#include "slic3r/GUI/Gizmos/GLGizmos.hpp"
+#include "libslic3r/ObjectID.hpp"
#include <map>
namespace Slic3r {
namespace GUI {
-class Selection;
-class GLGizmoBase;
class GLCanvas3D;
class ClippingPlane;
@@ -43,12 +42,10 @@ public:
float get_height() const { return m_top - m_bottom; }
};
-class GLGizmosManager
+class GLGizmosManager : public Slic3r::ObjectBase
{
public:
-#if ENABLE_SVG_ICONS
static const float Default_Icons_Size;
-#endif // ENABLE_SVG_ICONS
enum EType : unsigned char
{
@@ -63,24 +60,17 @@ public:
};
private:
+ GLCanvas3D& m_parent;
bool m_enabled;
typedef std::map<EType, GLGizmoBase*> GizmosMap;
GizmosMap m_gizmos;
-#if ENABLE_SVG_ICONS
mutable GLTexture m_icons_texture;
mutable bool m_icons_texture_dirty;
-#else
- ItemsIconsTexture m_icons_texture;
-#endif // ENABLE_SVG_ICONS
BackgroundTexture m_background_texture;
EType m_current;
-#if ENABLE_SVG_ICONS
float m_overlay_icons_size;
float m_overlay_scale;
-#else
- float m_overlay_icons_scale;
-#endif // ENABLE_SVG_ICONS
float m_overlay_border;
float m_overlay_gap_y;
@@ -99,38 +89,67 @@ private:
MouseCapture m_mouse_capture;
std::string m_tooltip;
+ bool m_serializing;
public:
- GLGizmosManager();
+ explicit GLGizmosManager(GLCanvas3D& parent);
~GLGizmosManager();
- bool init(GLCanvas3D& parent);
+ bool init();
+
+ template<class Archive>
+ void load(Archive& ar)
+ {
+ if (!m_enabled)
+ return;
+
+ m_serializing = true;
+
+ ar(m_current);
+
+ GLGizmoBase* curr = get_current();
+ if (curr != nullptr)
+ {
+ curr->set_state(GLGizmoBase::On);
+ curr->load(ar);
+ }
+ }
+
+ template<class Archive>
+ void save(Archive& ar) const
+ {
+ if (!m_enabled)
+ return;
+
+ ar(m_current);
+
+ GLGizmoBase* curr = get_current();
+ if (curr != nullptr)
+ curr->save(ar);
+ }
bool is_enabled() const { return m_enabled; }
void set_enabled(bool enable) { m_enabled = enable; }
-#if ENABLE_SVG_ICONS
void set_overlay_icon_size(float size);
-#endif // ENABLE_SVG_ICONS
void set_overlay_scale(float scale);
- void refresh_on_off_state(const Selection& selection);
+ void refresh_on_off_state();
void reset_all_states();
void set_hover_id(int id);
void enable_grabber(EType type, unsigned int id, bool enable);
- void update(const Linef3& mouse_ray, const Selection& selection, const Point* mouse_pos = nullptr);
- void update_data(GLCanvas3D& canvas);
+ void update(const Linef3& mouse_ray, const Point& mouse_pos);
+ void update_data();
- Rect get_reset_rect_viewport(const GLCanvas3D& canvas) const;
EType get_current_type() const { return m_current; }
bool is_running() const;
- bool handle_shortcut(int key, const Selection& selection);
+ bool handle_shortcut(int key);
bool is_dragging() const;
- void start_dragging(const Selection& selection);
+ void start_dragging();
void stop_dragging();
Vec3d get_displacement() const;
@@ -147,43 +166,48 @@ public:
void set_flattening_data(const ModelObject* model_object);
- void set_sla_support_data(ModelObject* model_object, const Selection& selection);
+ void set_sla_support_data(ModelObject* model_object);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false, bool alt_down = false, bool control_down = false);
ClippingPlane get_sla_clipping_plane() const;
- void render_current_gizmo(const Selection& selection) const;
- void render_current_gizmo_for_picking_pass(const Selection& selection) const;
+ void render_current_gizmo() const;
+ void render_current_gizmo_for_picking_pass() const;
- void render_overlay(const GLCanvas3D& canvas, const Selection& selection) const;
+ void render_overlay() const;
const std::string& get_tooltip() const { return m_tooltip; }
- bool on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas);
- bool on_mouse_wheel(wxMouseEvent& evt, GLCanvas3D& canvas);
- bool on_char(wxKeyEvent& evt, GLCanvas3D& canvas);
- bool on_key(wxKeyEvent& evt, GLCanvas3D& canvas);
+ bool on_mouse(wxMouseEvent& evt);
+ bool on_mouse_wheel(wxMouseEvent& evt);
+ bool on_char(wxKeyEvent& evt);
+ bool on_key(wxKeyEvent& evt);
+
+ void update_after_undo_redo();
private:
void reset();
- void do_render_overlay(const GLCanvas3D& canvas, const Selection& selection) const;
+ void do_render_overlay() const;
float get_total_overlay_height() const;
float get_total_overlay_width() const;
GLGizmoBase* get_current() const;
-#if ENABLE_SVG_ICONS
bool generate_icons_texture() const;
-#endif // ENABLE_SVG_ICONS
- void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection);
- std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos);
- bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const;
+ void update_on_off_state(const Vec2d& mouse_pos);
+ std::string update_hover_state(const Vec2d& mouse_pos);
+ bool overlay_contains_mouse(const Vec2d& mouse_pos) const;
bool grabber_contains_mouse() const;
};
} // namespace GUI
} // namespace Slic3r
+namespace cereal
+{
+ template <class Archive> struct specialize<Archive, Slic3r::GUI::GLGizmosManager, cereal::specialization::member_load_save> {};
+}
+
#endif // slic3r_GUI_GLGizmosManager_hpp_
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index ca1538bf7..f58266a5d 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -233,9 +233,9 @@ ImVec2 ImGuiWrapper::calc_text_size(const wxString &text)
return size;
}
-void ImGuiWrapper::set_next_window_pos(float x, float y, int flag)
+void ImGuiWrapper::set_next_window_pos(float x, float y, int flag, float pivot_x, float pivot_y)
{
- ImGui::SetNextWindowPos(ImVec2(x, y), (ImGuiCond)flag);
+ ImGui::SetNextWindowPos(ImVec2(x, y), (ImGuiCond)flag, ImVec2(pivot_x, pivot_y));
ImGui::SetNextWindowSize(ImVec2(0.0, 0.0));
}
@@ -326,9 +326,9 @@ bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>&
int selection_out = -1;
bool res = false;
- const char *selection_str = selection < options.size() ? options[selection].c_str() : "";
+ const char *selection_str = selection < (int)options.size() ? options[selection].c_str() : "";
if (ImGui::BeginCombo("", selection_str)) {
- for (int i = 0; i < options.size(); i++) {
+ for (int i = 0; i < (int)options.size(); i++) {
if (ImGui::Selectable(options[i].c_str(), i == selection)) {
selection_out = i;
}
@@ -342,6 +342,32 @@ bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>&
return res;
}
+bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool , int , const char**), int& hovered, int& selected)
+{
+ bool is_hovered = false;
+ ImGui::ListBoxHeader("", size);
+
+ int i=0;
+ const char* item_text;
+ while (items_getter(is_undo, i, &item_text))
+ {
+ ImGui::Selectable(item_text, i < hovered);
+
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip(item_text);
+ hovered = i;
+ is_hovered = true;
+ }
+
+ if (ImGui::IsItemClicked())
+ selected = i;
+ i++;
+ }
+
+ ImGui::ListBoxFooter();
+ return is_hovered;
+}
+
void ImGuiWrapper::disabled_begin(bool disabled)
{
wxCHECK_RET(!m_disabled, "ImGUI: Unbalanced disabled_begin() call");
diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp
index 0479e4743..c6550351e 100644
--- a/src/slic3r/GUI/ImGuiWrapper.hpp
+++ b/src/slic3r/GUI/ImGuiWrapper.hpp
@@ -51,7 +51,7 @@ public:
ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); }
ImVec2 calc_text_size(const wxString &text);
- void set_next_window_pos(float x, float y, int flag);
+ void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f);
void set_next_window_bg_alpha(float alpha);
bool begin(const std::string &name, int flags = 0);
@@ -67,6 +67,7 @@ public:
void text(const std::string &label);
void text(const wxString &label);
bool combo(const wxString& label, const std::vector<std::string>& options, int& selection); // Use -1 to not mark any option as selected
+ bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected);
void disabled_begin(bool disabled);
void disabled_end();
diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp
index 1af658ed3..955c4b60b 100644
--- a/src/slic3r/GUI/KBShortcutsDialog.cpp
+++ b/src/slic3r/GUI/KBShortcutsDialog.cpp
@@ -154,6 +154,9 @@ void KBShortcutsDialog::fill_shortcuts()
plater_shortcuts.push_back(Shortcut("I", L("Zoom in")));
plater_shortcuts.push_back(Shortcut("O", L("Zoom out")));
plater_shortcuts.push_back(Shortcut("ESC", L("Unselect gizmo / Clear selection")));
+#if ENABLE_RENDER_PICKING_PASS
+ plater_shortcuts.push_back(Shortcut("T", L("Toggle picking pass texture rendering on/off")));
+#endif // ENABLE_RENDER_PICKING_PASS
m_full_shortcuts.push_back(std::make_pair(_(L("Plater Shortcuts")), std::make_pair(plater_shortcuts, szRight)));
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index d800f6f38..b08df1690 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -35,6 +35,7 @@ namespace GUI {
MainFrame::MainFrame() :
DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"),
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
+ , m_recent_projects(9)
{
// Fonts were created by the DPIFrame constructor for the monitor, on which the window opened.
wxGetApp().update_fonts(this);
@@ -383,6 +384,40 @@ void MainFrame::init_menubar()
append_menu_item(fileMenu, wxID_ANY, _(L("&Open Project")) + dots + "\tCtrl+O", _(L("Open a project file")),
[this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, menu_icon("open"), nullptr,
[this](){return m_plater != nullptr; }, this);
+
+ wxMenu* recent_projects_menu = new wxMenu();
+ wxMenuItem* recent_projects_submenu = append_submenu(fileMenu, recent_projects_menu, wxID_ANY, _(L("Recent projects")), "");
+ m_recent_projects.UseMenu(recent_projects_menu);
+ Bind(wxEVT_MENU, [this](wxCommandEvent& evt) {
+ size_t file_id = evt.GetId() - wxID_FILE1;
+ wxString filename = m_recent_projects.GetHistoryFile(file_id);
+ if (wxFileExists(filename))
+ m_plater->load_project(filename);
+ else
+ {
+ wxMessageDialog msg(this, _(L("The selected project is no more available")), _(L("Error")));
+ msg.ShowModal();
+
+ m_recent_projects.RemoveFileFromHistory(file_id);
+ std::vector<std::string> recent_projects;
+ size_t count = m_recent_projects.GetCount();
+ for (size_t i = 0; i < count; ++i)
+ {
+ recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
+ }
+ wxGetApp().app_config->set_recent_projects(recent_projects);
+ wxGetApp().app_config->save();
+ }
+ }, wxID_FILE1, wxID_FILE9);
+
+ std::vector<std::string> recent_projects = wxGetApp().app_config->get_recent_projects();
+ for (const std::string& project : recent_projects)
+ {
+ m_recent_projects.AddFileToHistory(from_u8(project));
+ }
+
+ Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_recent_projects.GetCount() > 0); }, recent_projects_submenu->GetId());
+
append_menu_item(fileMenu, wxID_ANY, _(L("&Save Project")) + "\tCtrl+S", _(L("Save current project file")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, menu_icon("save"), nullptr,
[this](){return m_plater != nullptr && can_save(); }, this);
@@ -503,6 +538,14 @@ void MainFrame::init_menubar()
menu_icon("delete_all_menu"), nullptr, [this](){return can_delete_all(); }, this);
editMenu->AppendSeparator();
+ append_menu_item(editMenu, wxID_ANY, _(L("&Undo")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Z",
+ _(L("Undo")), [this](wxCommandEvent&) { m_plater->undo(); },
+ "undo", nullptr, [this](){return m_plater->can_undo(); }, this);
+ append_menu_item(editMenu, wxID_ANY, _(L("&Redo")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Y",
+ _(L("Redo")), [this](wxCommandEvent&) { m_plater->redo(); },
+ "redo", nullptr, [this](){return m_plater->can_redo(); }, this);
+
+ editMenu->AppendSeparator();
append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "C",
_(L("Copy selection to clipboard")), [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); },
menu_icon("copy_menu"), nullptr, [this](){return m_plater->can_copy_to_clipboard(); }, this);
@@ -1046,6 +1089,23 @@ void MainFrame::on_config_changed(DynamicPrintConfig* config) const
m_plater->on_config_change(*config); // propagate config change events to the plater
}
+void MainFrame::add_to_recent_projects(const wxString& filename)
+{
+ if (wxFileExists(filename))
+ {
+ m_recent_projects.AddFileToHistory(filename);
+ std::vector<std::string> recent_projects;
+ size_t count = m_recent_projects.GetCount();
+ for (size_t i = 0; i < count; ++i)
+ {
+ recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
+ }
+ wxGetApp().app_config->set_recent_projects(recent_projects);
+ wxGetApp().app_config->save();
+ }
+}
+
+//
// Called after the Preferences dialog is closed and the program settings are saved.
// Update the UI based on the current preferences.
void MainFrame::update_ui_from_settings()
diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp
index 805b663bb..a41f33824 100644
--- a/src/slic3r/GUI/MainFrame.hpp
+++ b/src/slic3r/GUI/MainFrame.hpp
@@ -6,6 +6,7 @@
#include <wx/frame.h>
#include <wx/settings.h>
#include <wx/string.h>
+#include <wx/filehistory.h>
#include <string>
#include <map>
@@ -84,6 +85,8 @@ class MainFrame : public DPIFrame
// vector of a MenuBar items changeable in respect to printer technology
std::vector<wxMenuItem*> m_changeable_menu_items;
+ wxFileHistory m_recent_projects;
+
protected:
virtual void on_dpi_changed(const wxRect &suggested_rect);
@@ -121,6 +124,8 @@ public:
// Propagate changed configuration from the Tab to the Platter and save changes to the AppConfig
void on_config_changed(DynamicPrintConfig* cfg) const ;
+ void add_to_recent_projects(const wxString& filename);
+
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
Plater* m_plater { nullptr };
diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp
index 014932900..9feca2f3d 100644
--- a/src/slic3r/GUI/OptionsGroup.cpp
+++ b/src/slic3r/GUI/OptionsGroup.cpp
@@ -266,7 +266,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
is_sizer_field(field) ?
v_sizer->Add(field->getSizer(), 0, wxEXPAND) :
v_sizer->Add(field->getWindow(), 0, wxEXPAND);
- return;
+ break;//return;
}
is_sizer_field(field) ?
@@ -300,7 +300,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
{
// extra widget for non-staticbox option group (like for the frequently used parameters on the sidebar) should be wxALIGN_RIGHT
const auto v_sizer = new wxBoxSizer(wxVERTICAL);
- sizer->Add(v_sizer, 1, wxEXPAND);
+ sizer->Add(v_sizer, option_set.size() == 1 ? 0 : 1, wxEXPAND);
v_sizer->Add(extra_widget(this->ctrl_parent()), 0, wxALIGN_RIGHT);
return;
}
@@ -320,6 +320,17 @@ Line OptionsGroup::create_single_option_line(const Option& option) const {
return retval;
}
+void OptionsGroup::clear_fields_except_of(const std::vector<std::string> left_fields)
+{
+ auto it = m_fields.begin();
+ while (it != m_fields.end()) {
+ if (std::find(left_fields.begin(), left_fields.end(), it->first) == left_fields.end())
+ it = m_fields.erase(it);
+ else
+ it++;
+ }
+}
+
void OptionsGroup::on_set_focus(const std::string& opt_key)
{
if (m_set_focus != nullptr)
diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp
index 73b2c5110..d720787b6 100644
--- a/src/slic3r/GUI/OptionsGroup.hpp
+++ b/src/slic3r/GUI/OptionsGroup.hpp
@@ -160,6 +160,14 @@ public:
m_show_modified_btns = show;
}
+ void clear_fields_except_of(const std::vector<std::string> left_fields);
+
+ void hide_labels() {
+ label_width = 0;
+ m_grid_sizer->SetCols(m_grid_sizer->GetEffectiveColsCount()-1);
+ static_cast<wxFlexGridSizer*>(m_grid_sizer)->AddGrowableCol(!extra_column ? 0 : 1);
+ }
+
OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false,
column_t extra_clmn = nullptr) :
m_parent(_parent), title(title),
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 151642613..0c7a8a3ee 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -50,6 +50,7 @@
#include "GUI_App.hpp"
#include "GUI_ObjectList.hpp"
#include "GUI_ObjectManipulation.hpp"
+#include "GUI_ObjectLayers.hpp"
#include "GUI_Utils.hpp"
#include "wxExtensions.hpp"
#include "MainFrame.hpp"
@@ -69,6 +70,7 @@
#include "../Utils/ASCIIFolding.hpp"
#include "../Utils/PrintHost.hpp"
#include "../Utils/FixModelByWin10.hpp"
+#include "../Utils/UndoRedo.hpp"
#include <wx/glcanvas.h> // Needs to be last because reasons :-/
#include "WipeTowerDialog.hpp"
@@ -244,6 +246,7 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 *
last_selected(wxNOT_FOUND),
m_em_unit(wxGetApp().em_unit())
{
+ SetFont(wxGetApp().normal_font());
Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) {
auto selected_item = this->GetSelection();
@@ -371,24 +374,36 @@ class FreqChangedParams : public OG_Settings
wxSizer* m_sizer {nullptr};
std::shared_ptr<ConfigOptionsGroup> m_og_sla;
+ std::vector<ScalableButton*> m_empty_buttons;
public:
- FreqChangedParams(wxWindow* parent, const int label_width);
+ FreqChangedParams(wxWindow* parent);
~FreqChangedParams() {}
wxButton* get_wiping_dialog_button() { return m_wiping_dialog_button; }
wxSizer* get_sizer() override;
ConfigOptionsGroup* get_og(const bool is_fff);
void Show(const bool is_fff);
+
+ void msw_rescale();
};
-FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
+void FreqChangedParams::msw_rescale()
+{
+ m_og->msw_rescale();
+ m_og_sla->msw_rescale();
+
+ for (auto btn: m_empty_buttons)
+ btn->msw_rescale();
+}
+
+FreqChangedParams::FreqChangedParams(wxWindow* parent) :
OG_Settings(parent, false)
{
DynamicPrintConfig* config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
// Frequently changed parameters for FFF_technology
m_og->set_config(config);
- m_og->label_width = label_width == 0 ? 1 : label_width;
+ m_og->hide_labels();
m_og->m_on_change = [config, this](t_config_option_key opt_key, boost::any value) {
Tab* tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT);
@@ -460,6 +475,20 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
Option option = Option(support_def, "support");
option.opt.full_width = true;
line.append_option(option);
+
+ /* Not a best solution, but
+ * Temporary workaround for right border alignment
+ */
+ auto empty_widget = [this] (wxWindow* parent) {
+ auto sizer = new wxBoxSizer(wxHORIZONTAL);
+ auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent.png", wxEmptyString,
+ wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW);
+ sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, int(0.3 * wxGetApp().em_unit()));
+ m_empty_buttons.push_back(btn);
+ return sizer;
+ };
+ line.append_widget(empty_widget);
+
m_og->append_line(line);
@@ -468,7 +497,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
option = m_og->get_option("fill_density");
option.opt.label = L("Infill");
option.opt.width = 7/*6*/;
- option.opt.sidetext = " ";
+ option.opt.sidetext = " ";
line.append_option(option);
m_brim_width = config->opt_float("brim_width");
@@ -479,13 +508,14 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
def.gui_type = "";
def.set_default_value(new ConfigOptionBool{ m_brim_width > 0.0 ? true : false });
option = Option(def, "brim");
- option.opt.sidetext = " ";
+ option.opt.sidetext = "";
line.append_option(option);
auto wiping_dialog_btn = [config, this](wxWindow* parent) {
m_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
+ m_wiping_dialog_button->SetFont(wxGetApp().normal_font());
auto sizer = new wxBoxSizer(wxHORIZONTAL);
- sizer->Add(m_wiping_dialog_button);
+ sizer->Add(m_wiping_dialog_button, 0, wxALIGN_CENTER_VERTICAL);
m_wiping_dialog_button->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent& e)
{
auto &config = wxGetApp().preset_bundle->project_config;
@@ -502,6 +532,13 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
wxPostEvent(parent, SimpleEvent(EVT_SCHEDULE_BACKGROUND_PROCESS, parent));
}
}));
+
+ auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent.png", wxEmptyString,
+ wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW);
+ sizer->Add(btn , 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT,
+ int(0.3 * wxGetApp().em_unit()));
+ m_empty_buttons.push_back(btn);
+
return sizer;
};
line.append_widget(wiping_dialog_btn);
@@ -511,9 +548,9 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
// Frequently changed parameters for SLA_technology
m_og_sla = std::make_shared<ConfigOptionsGroup>(parent, "");
+ m_og_sla->hide_labels();
DynamicPrintConfig* config_sla = &wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
m_og_sla->set_config(config_sla);
- m_og_sla->label_width = label_width == 0 ? 1 : label_width;
m_og_sla->m_on_change = [config_sla, this](t_config_option_key opt_key, boost::any value) {
Tab* tab = wxGetApp().get_tab(Preset::TYPE_SLA_PRINT);
@@ -551,7 +588,8 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
support_def_sla.enum_labels.erase(support_def_sla.enum_labels.begin() + 2);
option = Option(support_def_sla, "support");
option.opt.full_width = true;
- line.append_option(option);
+ line.append_option(option);
+ line.append_widget(empty_widget);
m_og_sla->append_line(line);
line = Line{ "", "" };
@@ -614,10 +652,11 @@ struct Sidebar::priv
PresetComboBox *combo_printer;
wxBoxSizer *sizer_params;
- FreqChangedParams *frequently_changed_parameters;
- ObjectList *object_list;
- ObjectManipulation *object_manipulation;
- ObjectSettings *object_settings;
+ FreqChangedParams *frequently_changed_parameters{ nullptr };
+ ObjectList *object_list{ nullptr };
+ ObjectManipulation *object_manipulation{ nullptr };
+ ObjectSettings *object_settings{ nullptr };
+ ObjectLayers *object_layers{ nullptr };
ObjectInfo *object_info;
SlicedInfo *sliced_info;
@@ -626,10 +665,26 @@ struct Sidebar::priv
wxButton *btn_send_gcode;
priv(Plater *plater) : plater(plater) {}
+ ~priv();
void show_preset_comboboxes();
};
+Sidebar::priv::~priv()
+{
+ if (object_manipulation != nullptr)
+ delete object_manipulation;
+
+ if (object_settings != nullptr)
+ delete object_settings;
+
+ if (frequently_changed_parameters != nullptr)
+ delete frequently_changed_parameters;
+
+ if (object_layers != nullptr)
+ delete object_layers;
+}
+
void Sidebar::priv::show_preset_comboboxes()
{
const bool showSLA = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA;
@@ -715,13 +770,12 @@ Sidebar::Sidebar(Plater *parent)
init_combo(&p->combo_printer, _(L("Printer")), Preset::TYPE_PRINTER, false);
const int margin_5 = int(0.5*wxGetApp().em_unit());// 5;
- const int margin_10 = 10;//int(1.5*wxGetApp().em_unit());// 15;
p->sizer_params = new wxBoxSizer(wxVERTICAL);
// Frequently changed parameters
- p->frequently_changed_parameters = new FreqChangedParams(p->scrolled, 0/*label_width*/);
- p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxTOP | wxBOTTOM, margin_10);
+ p->frequently_changed_parameters = new FreqChangedParams(p->scrolled);
+ p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxTOP | wxBOTTOM, wxOSX ? 1 : margin_5);
// Object List
p->object_list = new ObjectList(p->scrolled);
@@ -736,6 +790,11 @@ Sidebar::Sidebar(Plater *parent)
p->object_settings = new ObjectSettings(p->scrolled);
p->object_settings->Hide();
p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxTOP, margin_5);
+
+ // Object Layers
+ p->object_layers = new ObjectLayers(p->scrolled);
+ p->object_layers->Hide();
+ p->sizer_params->Add(p->object_layers->get_sizer(), 0, wxEXPAND | wxTOP, margin_5);
// Info boxes
p->object_info = new ObjectInfo(p->scrolled);
@@ -923,12 +982,11 @@ void Sidebar::msw_rescale()
// ... then refill them and set min size to correct layout of the sidebar
update_all_preset_comboboxes();
- p->frequently_changed_parameters->get_og(true)->msw_rescale();
- p->frequently_changed_parameters->get_og(false)->msw_rescale();
-
+ p->frequently_changed_parameters->msw_rescale();
p->object_list->msw_rescale();
p->object_manipulation->msw_rescale();
p->object_settings->msw_rescale();
+ p->object_layers->msw_rescale();
p->object_info->msw_rescale();
@@ -950,6 +1008,11 @@ ObjectSettings* Sidebar::obj_settings()
return p->object_settings;
}
+ObjectLayers* Sidebar::obj_layers()
+{
+ return p->object_layers;
+}
+
wxScrolledWindow* Sidebar::scrolled_panel()
{
return p->scrolled;
@@ -1103,10 +1166,20 @@ void Sidebar::show_sliced_info_sizer(const bool show)
if (ps.estimated_normal_print_time != "N/A") {
new_label += wxString::Format("\n - %s", _(L("normal mode")));
info_text += wxString::Format("\n%s", ps.estimated_normal_print_time);
+ for (int i = (int)ps.estimated_normal_color_print_times.size() - 1; i >= 0; --i)
+ {
+ new_label += wxString::Format("\n - %s%d", _(L("Color ")), i + 1);
+ info_text += wxString::Format("\n%s", ps.estimated_normal_color_print_times[i]);
+ }
}
if (ps.estimated_silent_print_time != "N/A") {
new_label += wxString::Format("\n - %s", _(L("stealth mode")));
info_text += wxString::Format("\n%s", ps.estimated_silent_print_time);
+ for (int i = (int)ps.estimated_normal_color_print_times.size() - 1; i >= 0; --i)
+ {
+ new_label += wxString::Format("\n - %s%d", _(L("Color ")), i + 1);
+ info_text += wxString::Format("\n%s", ps.estimated_normal_color_print_times[i]);
+ }
}
p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label);
}
@@ -1182,6 +1255,8 @@ const std::regex PlaterDropTarget::pattern_drop(".*[.](stl|obj|amf|3mf|prusa)",
bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames)
{
+ plater->take_snapshot(_(L("Load Files")));
+
std::vector<fs::path> paths;
for (const auto &filename : filenames) {
@@ -1247,7 +1322,12 @@ struct Plater::priv
Slic3r::Model model;
PrinterTechnology printer_technology = ptFFF;
Slic3r::GCodePreviewData gcode_preview_data;
-
+ Slic3r::UndoRedo::Stack undo_redo_stack;
+ int m_prevent_snapshots = 0; /* Used for avoid of excess "snapshoting".
+ * Like for "delete selected" or "set numbers of copies"
+ * we should call tack_snapshot just ones
+ * instead of calls for each action separately
+ * */
// GUI elements
wxSizer* panel_sizer{ nullptr };
wxPanel* current_panel{ nullptr };
@@ -1633,6 +1713,7 @@ struct Plater::priv
static const std::regex pattern_prusa;
priv(Plater *q, MainFrame *main_frame);
+ ~priv();
void update(bool force_full_scene_refresh = false);
void select_view(const std::string& direction);
@@ -1669,6 +1750,23 @@ struct Plater::priv
void split_object();
void split_volume();
void scale_selection_to_fit_print_volume();
+
+ void take_snapshot(const std::string& snapshot_name)
+ {
+ if (this->m_prevent_snapshots > 0)
+ return;
+ assert(this->m_prevent_snapshots >= 0);
+ this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager());
+ }
+ void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); }
+ int get_active_snapshot_index();
+ void undo();
+ void redo();
+ void undo_to(size_t time_to_load);
+ void redo_to(size_t time_to_load);
+ void suppress_snapshots() { this->m_prevent_snapshots++; }
+ void allow_snapshots() { this->m_prevent_snapshots--; }
+
bool background_processing_enabled() const { return this->get_config("background_processing") == "1"; }
void update_print_volume_state();
void schedule_background_process();
@@ -1759,6 +1857,7 @@ private:
void update_fff_scene();
void update_sla_scene();
+ void update_after_undo_redo();
// path to project file stored with no extension
wxString m_project_filename;
@@ -1786,11 +1885,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
}))
, sidebar(new Sidebar(q))
, delayed_scene_refresh(false)
-#if ENABLE_SVG_ICONS
, view_toolbar(GLToolbar::Radio, "View")
-#else
- , view_toolbar(GLToolbar::Radio)
-#endif // ENABLE_SVG_ICONS
, m_project_filename(wxEmptyString)
{
this->q->SetFont(Slic3r::GUI::wxGetApp().normal_font());
@@ -1865,6 +1960,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
view3D_canvas->Bind(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, &priv::on_3dcanvas_mouse_dragging_finished, this);
view3D_canvas->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); });
// 3DScene/Toolbar:
view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this);
@@ -1903,6 +2000,15 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
// updates camera type from .ini file
camera.set_type(get_config("use_perspective_camera"));
+
+ // Initialize the Undo / Redo stack with a first snapshot.
+ this->take_snapshot(_(L("New Project")));
+}
+
+Plater::priv::~priv()
+{
+ if (config != nullptr)
+ delete config;
}
void Plater::priv::update(bool force_full_scene_refresh)
@@ -2267,7 +2373,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
// the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
// so scale down the mesh
double inv = 1. / max_ratio;
- object->scale_mesh(Vec3d(inv, inv, inv));
+ object->scale_mesh_after_creation(Vec3d(inv, inv, inv));
object->origin_translation = Vec3d::Zero();
object->center_around_origin();
scaled_down = true;
@@ -2280,9 +2386,6 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
}
object->ensure_on_bed();
-
- // print.auto_assign_extruders(object);
- // print.add_model_object(object);
}
#ifdef AUTOPLACEMENT_ON_LOAD
@@ -2449,12 +2552,15 @@ void Plater::priv::object_list_changed()
void Plater::priv::select_all()
{
+// this->take_snapshot(_(L("Select All")));
+
view3D->select_all();
this->sidebar->obj_list()->update_selections();
}
void Plater::priv::deselect_all()
{
+// this->take_snapshot(_(L("Deselect All")));
view3D->deselect_all();
}
@@ -2475,7 +2581,8 @@ void Plater::priv::remove(size_t obj_idx)
void Plater::priv::delete_object_from_model(size_t obj_idx)
-{
+{
+ this->take_snapshot(_(L("Delete Object")));
model.delete_object(obj_idx);
update();
object_list_changed();
@@ -2483,6 +2590,8 @@ void Plater::priv::delete_object_from_model(size_t obj_idx)
void Plater::priv::reset()
{
+ this->take_snapshot(_(L("Reset Project")));
+
set_project_filename(wxEmptyString);
// Prevent toolpaths preview from rendering while we modify the Print object
@@ -2508,17 +2617,20 @@ void Plater::priv::reset()
void Plater::priv::mirror(Axis axis)
{
+ this->take_snapshot(_(L("Mirror")));
view3D->mirror_selection(axis);
}
void Plater::priv::arrange()
{
+ this->take_snapshot(_(L("Arrange")));
m_ui_jobs.start(Jobs::Arrange);
}
// This method will find an optimal orientation for the currently selected item
// Very similar in nature to the arrange method above...
void Plater::priv::sla_optimize_rotation() {
+ this->take_snapshot(_(L("Optimize Rotation")));
m_ui_jobs.start(Jobs::Rotoptimize);
}
@@ -2671,6 +2783,8 @@ void Plater::priv::split_object()
Slic3r::GUI::warning_catcher(q, _(L("The selected object couldn't be split because it contains only one part.")));
else
{
+ this->take_snapshot(_(L("Split to Objects")));
+
unsigned int counter = 1;
for (ModelObject* m : new_objects)
m->name = current_model_object->name + "_" + std::to_string(counter++);
@@ -2909,6 +3023,8 @@ void Plater::priv::update_sla_scene()
void Plater::priv::reload_from_disk()
{
+ this->take_snapshot(_(L("Reload from Disk")));
+
const auto &selection = get_selection();
const auto obj_orig_idx = selection.get_object_idx();
if (selection.is_wipe_tower() || obj_orig_idx == -1) { return; }
@@ -2942,6 +3058,9 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* =
{
if (obj_idx < 0)
return;
+
+ this->take_snapshot(_(L("Fix Throught NetFabb")));
+
fix_model_by_win10_sdk_gui(*model.objects[obj_idx], vol_idx);
this->update();
this->object_list_changed();
@@ -3049,8 +3168,14 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
// update plater with new config
wxGetApp().plater()->on_config_change(wxGetApp().preset_bundle->full_config());
+ /* Settings list can be changed after printer preset changing, so
+ * update all settings items for all item had it.
+ * Furthermore, Layers editing is implemented only for FFF printers
+ * and for SLA presets they should be deleted
+ */
if (preset_type == Preset::TYPE_PRINTER)
- wxGetApp().obj_list()->update_settings_items();
+// wxGetApp().obj_list()->update_settings_items();
+ wxGetApp().obj_list()->update_object_list_by_printer_technology();
}
void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
@@ -3181,6 +3306,8 @@ void Plater::priv::on_action_layersediting(SimpleEvent&)
void Plater::priv::on_object_select(SimpleEvent& evt)
{
+// this->take_snapshot(_(L("Object Selection")));
+
wxGetApp().obj_list()->update_selections();
selection_changed();
}
@@ -3319,6 +3446,9 @@ void Plater::priv::set_project_filename(const wxString& filename)
m_project_filename = from_path(full_path);
wxGetApp().mainframe->update_title();
+
+ if (!filename.empty())
+ wxGetApp().mainframe->add_to_recent_projects(filename);
}
bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/)
@@ -3395,6 +3525,10 @@ bool Plater::priv::complit_init_object_menu()
[this]() { return can_split() && wxGetApp().get_mode() > comSimple; }, q);
object_menu.AppendSeparator();
+ // Layers Editing for object
+ sidebar->obj_list()->append_menu_item_layers_editing(&object_menu);
+ object_menu.AppendSeparator();
+
// "Add (volumes)" popupmenu will be added later in append_menu_items_add_volume()
return true;
@@ -3429,12 +3563,6 @@ bool Plater::priv::complit_init_part_menu()
void Plater::priv::init_view_toolbar()
{
-#if !ENABLE_SVG_ICONS
- ItemsIconsTexture::Metadata icons_data;
- icons_data.filename = "view_toolbar.png";
- icons_data.icon_size = 64;
-#endif // !ENABLE_SVG_ICONS
-
BackgroundTexture::Metadata background_data;
background_data.filename = "toolbar_background.png";
background_data.left = 16;
@@ -3442,11 +3570,7 @@ void Plater::priv::init_view_toolbar()
background_data.right = 16;
background_data.bottom = 16;
-#if ENABLE_SVG_ICONS
if (!view_toolbar.init(background_data))
-#else
- if (!view_toolbar.init(icons_data, background_data))
-#endif // ENABLE_SVG_ICONS
return;
view_toolbar.set_layout_orientation(GLToolbar::Layout::Bottom);
@@ -3456,24 +3580,18 @@ void Plater::priv::init_view_toolbar()
GLToolbarItem::Data item;
item.name = "3D";
-#if ENABLE_SVG_ICONS
item.icon_filename = "editor.svg";
-#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("3D editor view")) + " [" + GUI::shortkey_ctrl_prefix() + "5]";
item.sprite_id = 0;
- item.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); };
- item.is_toggable = false;
+ item.left.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); };
if (!view_toolbar.add_item(item))
return;
item.name = "Preview";
-#if ENABLE_SVG_ICONS
item.icon_filename = "preview.svg";
-#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Preview")) + " [" + GUI::shortkey_ctrl_prefix() + "6]";
item.sprite_id = 1;
- item.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); };
- item.is_toggable = false;
+ item.left.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); };
if (!view_toolbar.add_item(item))
return;
@@ -3603,6 +3721,52 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
}
}
+int Plater::priv::get_active_snapshot_index()
+{
+ const size_t& active_snapshot_time = this->undo_redo_stack.active_snapshot_time();
+ const std::vector<UndoRedo::Snapshot>& ss_stack = this->undo_redo_stack.snapshots();
+ const auto it = std::lower_bound(ss_stack.begin(), ss_stack.end(), UndoRedo::Snapshot(active_snapshot_time));
+ return it - ss_stack.begin();
+}
+
+void Plater::priv::undo()
+{
+ if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), this->view3D->get_canvas3d()->get_gizmos_manager()))
+ this->update_after_undo_redo();
+}
+
+void Plater::priv::redo()
+{
+ if (this->undo_redo_stack.redo(model, this->view3D->get_canvas3d()->get_gizmos_manager()))
+ this->update_after_undo_redo();
+}
+
+void Plater::priv::undo_to(size_t time_to_load)
+{
+ if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), this->view3D->get_canvas3d()->get_gizmos_manager(), time_to_load))
+ this->update_after_undo_redo();
+}
+
+void Plater::priv::redo_to(size_t time_to_load)
+{
+ if (this->undo_redo_stack.redo(model, this->view3D->get_canvas3d()->get_gizmos_manager(), time_to_load))
+ this->update_after_undo_redo();
+}
+
+void Plater::priv::update_after_undo_redo()
+{
+ this->view3D->get_canvas3d()->get_selection().clear();
+ this->update(false); // update volumes from the deserializd model
+ //YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) (no selections at this point of time)
+ this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack.selection_deserialized().mode), this->undo_redo_stack.selection_deserialized().volumes_and_instances);
+ this->view3D->get_canvas3d()->get_gizmos_manager().update_after_undo_redo();
+
+ wxGetApp().obj_list()->update_after_undo_redo();
+
+ //FIXME what about the state of the manipulators?
+ //FIXME what about the focus? Cursor in the side panel?
+}
+
void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& label) const
{
switch (btn_type)
@@ -3640,17 +3804,23 @@ void Plater::new_project()
void Plater::load_project()
{
+ this->take_snapshot(_(L("Load Project")));
+
wxString input_file;
wxGetApp().load_project(this, input_file);
+ load_project(input_file);
+}
- if (input_file.empty())
+void Plater::load_project(const wxString& filename)
+{
+ if (filename.empty())
return;
p->reset();
- p->set_project_filename(input_file);
+ p->set_project_filename(filename);
std::vector<fs::path> input_paths;
- input_paths.push_back(into_path(input_file));
+ input_paths.push_back(into_path(filename));
load_files(input_paths);
}
@@ -3661,6 +3831,8 @@ void Plater::add_model()
if (input_files.empty())
return;
+ this->take_snapshot(_(L("Add object(s)")));
+
std::vector<fs::path> input_paths;
for (const auto &file : input_files) {
input_paths.push_back(into_path(file));
@@ -3718,13 +3890,18 @@ void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_mo
void Plater::remove_selected()
{
+ this->take_snapshot(_(L("Delete Selected Objects")));
+ this->suppress_snapshots();
this->p->view3D->delete_selected();
+ this->allow_snapshots();
}
void Plater::increase_instances(size_t num)
{
if (! can_increase_instances()) { return; }
+ this->take_snapshot(_(L("Increase Instances")));
+
int obj_idx = p->get_selected_object_idx();
ModelObject* model_object = p->model.objects[obj_idx];
@@ -3759,6 +3936,8 @@ void Plater::decrease_instances(size_t num)
{
if (! can_decrease_instances()) { return; }
+ this->take_snapshot(_(L("Decrease Instances")));
+
int obj_idx = p->get_selected_object_idx();
ModelObject* model_object = p->model.objects[obj_idx];
@@ -3793,11 +3972,16 @@ void Plater::set_number_of_copies(/*size_t num*/)
if (num < 0)
return;
+ this->take_snapshot(wxString::Format(_(L("Set numbers of copies to %d")), num));
+ this->suppress_snapshots();
+
int diff = (int)num - (int)model_object->instances.size();
if (diff > 0)
increase_instances(diff);
else if (diff < 0)
decrease_instances(-diff);
+
+ this->allow_snapshots();
}
bool Plater::is_selection_empty() const
@@ -3821,6 +4005,8 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe
return;
}
+ this->take_snapshot(_(L("Gizmo-Cut")));
+
wxBusyCursor wait;
const auto new_objects = object->cut(instance_idx, z, keep_upper, keep_lower, rotate_lower);
@@ -4047,6 +4233,9 @@ void Plater::reslice()
}
else if (!p->background_process.empty() && !p->background_process.idle())
p->show_action_buttons(true);
+
+ // update type of preview
+ p->preview->update_view_type();
}
void Plater::reslice_SLA_supports(const ModelObject &object)
@@ -4107,6 +4296,45 @@ void Plater::send_gcode()
}
}
+void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot(snapshot_name); }
+void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); }
+void Plater::suppress_snapshots() { p->suppress_snapshots(); }
+void Plater::allow_snapshots() { p->allow_snapshots(); }
+void Plater::undo() { p->undo(); }
+void Plater::redo() { p->redo(); }
+void Plater::undo_to(int selection)
+{
+ if (selection == 0) {
+ p->undo();
+ return;
+ }
+
+ const int idx = p->get_active_snapshot_index() - selection - 1;
+ p->undo_to(p->undo_redo_stack.snapshots()[idx].timestamp);
+}
+void Plater::redo_to(int selection)
+{
+ if (selection == 0) {
+ p->redo();
+ return;
+ }
+
+ const int idx = p->get_active_snapshot_index() + selection + 1;
+ p->redo_to(p->undo_redo_stack.snapshots()[idx].timestamp);
+}
+bool Plater::undo_redo_string_getter(const bool is_undo, int idx, const char** out_text)
+{
+ const std::vector<UndoRedo::Snapshot>& ss_stack = p->undo_redo_stack.snapshots();
+ const int idx_in_ss_stack = p->get_active_snapshot_index() + (is_undo ? -(++idx) : idx);
+
+ if (0 < idx_in_ss_stack && idx_in_ss_stack < ss_stack.size() - 1) {
+ *out_text = ss_stack[idx_in_ss_stack].name.c_str();
+ return true;
+ }
+
+ return false;
+}
+
void Plater::on_extruders_change(int num_extruders)
{
auto& choices = sidebar().combos_filament();
@@ -4332,8 +4560,11 @@ void Plater::copy_selection_to_clipboard()
void Plater::paste_from_clipboard()
{
- if (can_paste_from_clipboard())
- p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
+ if (!can_paste_from_clipboard())
+ return;
+
+ this->take_snapshot(_(L("Paste From Clipboard")));
+ p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
}
void Plater::msw_rescale()
@@ -4350,6 +4581,11 @@ void Plater::msw_rescale()
GetParent()->Layout();
}
+const Camera& Plater::get_camera() const
+{
+ return p->camera;
+}
+
bool Plater::can_delete() const { return p->can_delete(); }
bool Plater::can_delete_all() const { return p->can_delete_all(); }
bool Plater::can_increase_instances() const { return p->can_increase_instances(); }
@@ -4393,6 +4629,16 @@ bool Plater::can_copy_to_clipboard() const
return true;
}
+bool Plater::can_undo() const
+{
+ return p->undo_redo_stack.has_undo_snapshot();
+}
+
+bool Plater::can_redo() const
+{
+ return p->undo_redo_stack.has_redo_snapshot();
+}
+
SuppressBackgroundProcessingUpdate::SuppressBackgroundProcessingUpdate() :
m_was_running(wxGetApp().plater()->is_background_process_running())
{
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 5b1347891..e73f88ef3 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -33,6 +33,7 @@ class MainFrame;
class ConfigOptionsGroup;
class ObjectManipulation;
class ObjectSettings;
+class ObjectLayers;
class ObjectList;
class GLCanvas3D;
@@ -93,6 +94,7 @@ public:
ObjectManipulation* obj_manipul();
ObjectList* obj_list();
ObjectSettings* obj_settings();
+ ObjectLayers* obj_layers();
wxScrolledWindow* scrolled_panel();
wxPanel* presets_panel();
@@ -136,6 +138,7 @@ public:
void new_project();
void load_project();
+ void load_project(const wxString& filename);
void add_model();
void extract_config_from_project();
@@ -181,6 +184,16 @@ public:
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
void send_gcode();
+ void take_snapshot(const std::string &snapshot_name);
+ void take_snapshot(const wxString &snapshot_name);
+ void suppress_snapshots();
+ void allow_snapshots();
+ void undo();
+ void redo();
+ void undo_to(int selection);
+ void redo_to(int selection);
+ bool undo_redo_string_getter(const bool is_undo, int idx, const char** out_text);
+
void on_extruders_change(int extruders_count);
void on_config_change(const DynamicPrintConfig &config);
// On activating the parent window.
@@ -215,9 +228,13 @@ public:
bool can_layers_editing() const;
bool can_paste_from_clipboard() const;
bool can_copy_to_clipboard() const;
+ bool can_undo() const;
+ bool can_redo() const;
void msw_rescale();
+ const Camera& get_camera() const;
+
private:
struct priv;
std::unique_ptr<priv> p;
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index 7192d485c..3d467d7f8 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -461,6 +461,7 @@ const std::vector<std::string>& Preset::sla_print_options()
"support_pillar_widening_factor",
"support_base_diameter",
"support_base_height",
+ "support_base_safety_distance",
"support_critical_angle",
"support_max_bridge_length",
"support_max_pillar_link_distance",
@@ -474,6 +475,10 @@ const std::vector<std::string>& Preset::sla_print_options()
"pad_max_merge_distance",
"pad_edge_radius",
"pad_wall_slope",
+ "pad_object_gap",
+ "pad_object_connector_stride",
+ "pad_object_connector_width",
+ "pad_object_connector_penetration",
"output_filename_format",
"default_sla_print_profile",
"compatible_printers",
@@ -824,11 +829,25 @@ const Preset* PresetCollection::get_selected_preset_parent() const
if (this->get_selected_idx() == -1)
// This preset collection has no preset activated yet. Only the get_edited_preset() is valid.
return nullptr;
- const std::string &inherits = this->get_edited_preset().inherits();
+// const std::string &inherits = this->get_edited_preset().inherits();
+// if (inherits.empty())
+// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
+
+ std::string inherits = this->get_edited_preset().inherits();
if (inherits.empty())
- return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
+ {
+ if (this->get_selected_preset().is_system || this->get_selected_preset().is_default)
+ return &this->get_selected_preset();
+ if (this->get_selected_preset().is_external)
+ return nullptr;
+
+ inherits = m_type != Preset::Type::TYPE_PRINTER ? "- default -" :
+ this->get_edited_preset().printer_technology() == ptFFF ?
+ "- default FFF -" : "- default SLA -" ;
+ }
+
const Preset* preset = this->find_preset(inherits, false);
- return (preset == nullptr || preset->is_default || preset->is_external) ? nullptr : preset;
+ return (preset == nullptr/* || preset->is_default*/ || preset->is_external) ? nullptr : preset;
}
const Preset* PresetCollection::get_preset_parent(const Preset& child) const
diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp
index b28cb2eda..00c1f8168 100644
--- a/src/slic3r/GUI/PresetBundle.cpp
+++ b/src/slic3r/GUI/PresetBundle.cpp
@@ -1366,7 +1366,7 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst
continue;
c << std::endl << "[" << presets->section_name() << ":" << preset.name << "]" << std::endl;
for (const std::string &opt_key : preset.config.keys())
- c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl;
+ c << opt_key << " = " << preset.config.opt_serialize(opt_key) << std::endl;
}
}
diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp
index 97168ee04..b990f28b8 100644
--- a/src/slic3r/GUI/Selection.cpp
+++ b/src/slic3r/GUI/Selection.cpp
@@ -6,7 +6,8 @@
#include "GUI_ObjectManipulation.hpp"
#include "GUI_ObjectList.hpp"
#include "Gizmos/GLGizmoBase.hpp"
-#include "slic3r/GUI/3DScene.hpp"
+#include "3DScene.hpp"
+#include "Camera.hpp"
#include <GL/glew.h>
@@ -99,14 +100,14 @@ void Selection::set_volumes(GLVolumePtrs* volumes)
update_valid();
}
-bool Selection::init(bool useVBOs)
+bool Selection::init()
{
- if (!m_arrow.init(useVBOs))
+ if (!m_arrow.init())
return false;
m_arrow.set_scale(5.0 * Vec3d::Ones());
- if (!m_curved_arrow.init(useVBOs))
+ if (!m_curved_arrow.init())
return false;
m_curved_arrow.set_scale(5.0 * Vec3d::Ones());
@@ -311,6 +312,22 @@ void Selection::add_all()
this->set_bounding_boxes_dirty();
}
+void Selection::set_deserialized(EMode mode, const std::vector<std::pair<size_t, size_t>> &volumes_and_instances)
+{
+ if (! m_valid)
+ return;
+
+ m_mode = mode;
+ for (unsigned int i : m_list)
+ (*m_volumes)[i]->selected = false;
+ m_list.clear();
+ for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++ i)
+ if (std::binary_search(volumes_and_instances.begin(), volumes_and_instances.end(), (*m_volumes)[i]->geometry_id))
+ this->do_add_volume(i);
+ update_type();
+ this->set_bounding_boxes_dirty();
+}
+
void Selection::clear()
{
if (!m_valid)
@@ -331,6 +348,9 @@ void Selection::clear()
// resets the cache in the sidebar
wxGetApp().obj_manipul()->reset_cache();
+
+ // #et_FIXME fake KillFocus from sidebar
+ wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
}
// Update the selection based on the new instance IDs.
@@ -786,6 +806,8 @@ void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
double s = std::min(sx, std::min(sy, sz));
if (s != 1.0)
{
+ wxGetApp().plater()->take_snapshot(_(L("Scale To Fit")));
+
TransformationType type;
type.set_world();
type.set_relative();
@@ -794,12 +816,12 @@ void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
// apply scale
start_dragging();
scale(s * Vec3d::Ones(), type);
- wxGetApp().plater()->canvas3D()->do_scale();
+ wxGetApp().plater()->canvas3D()->do_scale(L("")); // avoid storing another snapshot
// center selection on print bed
start_dragging();
translate(print_volume.center() - get_bounding_box().center());
- wxGetApp().plater()->canvas3D()->do_move();
+ wxGetApp().plater()->canvas3D()->do_move(L("")); // avoid storing another snapshot
wxGetApp().obj_manipul()->set_dirty();
}
@@ -1070,62 +1092,69 @@ void Selection::render_center(bool gizmo_is_dragging) const
}
#endif // ENABLE_RENDER_SELECTION_CENTER
-void Selection::render_sidebar_hints(const std::string& sidebar_field) const
+void Selection::render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const
{
if (sidebar_field.empty())
return;
- glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
- glsafe(::glEnable(GL_DEPTH_TEST));
+ if (!boost::starts_with(sidebar_field, "layer"))
+ {
+ shader.start_using();
+ glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
+ glsafe(::glEnable(GL_LIGHTING));
+ }
- glsafe(::glEnable(GL_LIGHTING));
+ glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glPushMatrix());
- const Vec3d& center = get_bounding_box().center();
-
- if (is_single_full_instance() && ! wxGetApp().obj_manipul()->get_world_coordinates())
+ if (!boost::starts_with(sidebar_field, "layer"))
{
- glsafe(::glTranslated(center(0), center(1), center(2)));
- if (!boost::starts_with(sidebar_field, "position"))
+ const Vec3d& center = get_bounding_box().center();
+
+ if (is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates())
{
- Transform3d orient_matrix = Transform3d::Identity();
- if (boost::starts_with(sidebar_field, "scale"))
- orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
- else if (boost::starts_with(sidebar_field, "rotation"))
+ glsafe(::glTranslated(center(0), center(1), center(2)));
+ if (!boost::starts_with(sidebar_field, "position"))
{
- if (boost::ends_with(sidebar_field, "x"))
+ Transform3d orient_matrix = Transform3d::Identity();
+ if (boost::starts_with(sidebar_field, "scale"))
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
- else if (boost::ends_with(sidebar_field, "y"))
+ else if (boost::starts_with(sidebar_field, "rotation"))
{
- const Vec3d& rotation = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation();
- if (rotation(0) == 0.0)
+ if (boost::ends_with(sidebar_field, "x"))
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
- else
- orient_matrix.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ()));
+ else if (boost::ends_with(sidebar_field, "y"))
+ {
+ const Vec3d& rotation = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation();
+ if (rotation(0) == 0.0)
+ orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
+ else
+ orient_matrix.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ()));
+ }
}
- }
- glsafe(::glMultMatrixd(orient_matrix.data()));
+ glsafe(::glMultMatrixd(orient_matrix.data()));
+ }
}
- }
- else if (is_single_volume() || is_single_modifier())
- {
- glsafe(::glTranslated(center(0), center(1), center(2)));
- Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
- if (!boost::starts_with(sidebar_field, "position"))
- orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true);
-
- glsafe(::glMultMatrixd(orient_matrix.data()));
- }
- else
- {
- glsafe(::glTranslated(center(0), center(1), center(2)));
- if (requires_local_axes())
+ else if (is_single_volume() || is_single_modifier())
{
+ glsafe(::glTranslated(center(0), center(1), center(2)));
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
+ if (!boost::starts_with(sidebar_field, "position"))
+ orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true);
+
glsafe(::glMultMatrixd(orient_matrix.data()));
}
+ else
+ {
+ glsafe(::glTranslated(center(0), center(1), center(2)));
+ if (requires_local_axes())
+ {
+ Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
+ glsafe(::glMultMatrixd(orient_matrix.data()));
+ }
+ }
}
if (boost::starts_with(sidebar_field, "position"))
@@ -1136,10 +1165,16 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const
render_sidebar_scale_hints(sidebar_field);
else if (boost::starts_with(sidebar_field, "size"))
render_sidebar_size_hints(sidebar_field);
+ else if (boost::starts_with(sidebar_field, "layer"))
+ render_sidebar_layers_hints(sidebar_field);
glsafe(::glPopMatrix());
- glsafe(::glDisable(GL_LIGHTING));
+ if (!boost::starts_with(sidebar_field, "layer"))
+ {
+ glsafe(::glDisable(GL_LIGHTING));
+ shader.stop_using();
+ }
}
bool Selection::requires_local_axes() const
@@ -1160,10 +1195,10 @@ void Selection::copy_to_clipboard()
ModelObject* dst_object = m_clipboard.add_object();
dst_object->name = src_object->name;
dst_object->input_file = src_object->input_file;
- dst_object->config = src_object->config;
+ static_cast<DynamicPrintConfig&>(dst_object->config) = static_cast<const DynamicPrintConfig&>(src_object->config);
dst_object->sla_support_points = src_object->sla_support_points;
dst_object->sla_points_status = src_object->sla_points_status;
- dst_object->layer_height_ranges = src_object->layer_height_ranges;
+ dst_object->layer_config_ranges = src_object->layer_config_ranges; // #ys_FIXME_experiment
dst_object->layer_height_profile = src_object->layer_height_profile;
dst_object->origin_translation = src_object->origin_translation;
@@ -1709,6 +1744,78 @@ void Selection::render_sidebar_size_hints(const std::string& sidebar_field) cons
render_sidebar_scale_hints(sidebar_field);
}
+void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) const
+{
+ static const double Margin = 10.0;
+
+ std::string field = sidebar_field;
+
+ // extract max_z
+ std::string::size_type pos = field.rfind("_");
+ if (pos == std::string::npos)
+ return;
+
+ double max_z = std::stod(field.substr(pos + 1));
+
+ // extract min_z
+ field = field.substr(0, pos);
+ pos = field.rfind("_");
+ if (pos == std::string::npos)
+ return;
+
+ double min_z = std::stod(field.substr(pos + 1));
+
+ // extract type
+ field = field.substr(0, pos);
+ pos = field.rfind("_");
+ if (pos == std::string::npos)
+ return;
+
+ int type = std::stoi(field.substr(pos + 1));
+
+ const BoundingBoxf3& box = get_bounding_box();
+
+ const float min_x = box.min(0) - Margin;
+ const float max_x = box.max(0) + Margin;
+ const float min_y = box.min(1) - Margin;
+ const float max_y = box.max(1) + Margin;
+
+ // view dependend order of rendering to keep correct transparency
+ bool camera_on_top = wxGetApp().plater()->get_camera().get_theta() <= 90.0f;
+ float z1 = camera_on_top ? min_z : max_z;
+ float z2 = camera_on_top ? max_z : min_z;
+
+ glsafe(::glEnable(GL_DEPTH_TEST));
+ glsafe(::glDisable(GL_CULL_FACE));
+ glsafe(::glEnable(GL_BLEND));
+ glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
+
+ ::glBegin(GL_QUADS);
+ if ((camera_on_top && (type == 1)) || (!camera_on_top && (type == 2)))
+ ::glColor4f(1.0f, 0.38f, 0.0f, 1.0f);
+ else
+ ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
+ ::glVertex3f(min_x, min_y, z1);
+ ::glVertex3f(max_x, min_y, z1);
+ ::glVertex3f(max_x, max_y, z1);
+ ::glVertex3f(min_x, max_y, z1);
+ glsafe(::glEnd());
+
+ ::glBegin(GL_QUADS);
+ if ((camera_on_top && (type == 2)) || (!camera_on_top && (type == 1)))
+ ::glColor4f(1.0f, 0.38f, 0.0f, 1.0f);
+ else
+ ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
+ ::glVertex3f(min_x, min_y, z2);
+ ::glVertex3f(max_x, min_y, z2);
+ ::glVertex3f(max_x, max_y, z2);
+ ::glVertex3f(min_x, max_y, z2);
+ glsafe(::glEnd());
+
+ glsafe(::glEnable(GL_CULL_FACE));
+ glsafe(::glDisable(GL_BLEND));
+}
+
void Selection::render_sidebar_position_hint(Axis axis) const
{
m_arrow.set_color(AXES_COLOR[axis], 3);
@@ -1927,6 +2034,10 @@ bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const
void Selection::paste_volumes_from_clipboard()
{
+#ifdef _DEBUG
+ check_model_ids_validity(*m_model);
+#endif /* _DEBUG */
+
int dst_obj_idx = get_object_idx();
if ((dst_obj_idx < 0) || ((int)m_model->objects.size() <= dst_obj_idx))
return;
@@ -1968,6 +2079,9 @@ void Selection::paste_volumes_from_clipboard()
}
volumes.push_back(dst_volume);
+#ifdef _DEBUG
+ check_model_ids_validity(*m_model);
+#endif /* _DEBUG */
}
// keeps relative position of multivolume selections
@@ -1981,10 +2095,18 @@ void Selection::paste_volumes_from_clipboard()
wxGetApp().obj_list()->paste_volumes_into_list(dst_obj_idx, volumes);
}
+
+#ifdef _DEBUG
+ check_model_ids_validity(*m_model);
+#endif /* _DEBUG */
}
void Selection::paste_objects_from_clipboard()
{
+#ifdef _DEBUG
+ check_model_ids_validity(*m_model);
+#endif /* _DEBUG */
+
std::vector<size_t> object_idxs;
const ModelObjectPtrs& src_objects = m_clipboard.get_objects();
for (const ModelObject* src_object : src_objects)
@@ -1998,9 +2120,16 @@ void Selection::paste_objects_from_clipboard()
}
object_idxs.push_back(m_model->objects.size() - 1);
+#ifdef _DEBUG
+ check_model_ids_validity(*m_model);
+#endif /* _DEBUG */
}
wxGetApp().obj_list()->paste_objects_into_list(object_idxs);
+
+#ifdef _DEBUG
+ check_model_ids_validity(*m_model);
+#endif /* _DEBUG */
}
} // namespace GUI
diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp
index 802f8d284..915a8c855 100644
--- a/src/slic3r/GUI/Selection.hpp
+++ b/src/slic3r/GUI/Selection.hpp
@@ -11,8 +11,8 @@ typedef class GLUquadric GLUquadricObj;
#endif // ENABLE_RENDER_SELECTION_CENTER
namespace Slic3r {
+class Shader;
namespace GUI {
-
class TransformationType
{
public:
@@ -171,7 +171,7 @@ private:
Vec3d dragging_center;
// Map from indices of ModelObject instances in Model::objects
// to a set of indices of ModelVolume instances in ModelObject::instances
- // Here the index means a position inside the respective std::vector, not ModelID.
+ // Here the index means a position inside the respective std::vector, not ObjectID.
ObjectIdxsToInstanceIdxsMap content;
};
@@ -212,7 +212,7 @@ public:
#endif // ENABLE_RENDER_SELECTION_CENTER
void set_volumes(GLVolumePtrs* volumes);
- bool init(bool useVBOs);
+ bool init();
bool is_enabled() const { return m_enabled; }
void set_enabled(bool enable) { m_enabled = enable; }
@@ -237,6 +237,9 @@ public:
void add_all();
+ // To be called after Undo or Redo once the volumes are updated.
+ void set_deserialized(EMode mode, const std::vector<std::pair<size_t, size_t>> &volumes_and_instances);
+
// Update the selection based on the new instance IDs.
void instances_changed(const std::vector<size_t> &instance_ids_selected);
// Update the selection based on the map from old indices to new indices after m_volumes changed.
@@ -302,7 +305,7 @@ public:
#if ENABLE_RENDER_SELECTION_CENTER
void render_center(bool gizmo_is_dragging) const;
#endif // ENABLE_RENDER_SELECTION_CENTER
- void render_sidebar_hints(const std::string& sidebar_field) const;
+ void render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const;
bool requires_local_axes() const;
@@ -332,6 +335,7 @@ private:
void render_sidebar_rotation_hints(const std::string& sidebar_field) const;
void render_sidebar_scale_hints(const std::string& sidebar_field) const;
void render_sidebar_size_hints(const std::string& sidebar_field) const;
+ void render_sidebar_layers_hints(const std::string& sidebar_field) const;
void render_sidebar_position_hint(Axis axis) const;
void render_sidebar_rotation_hint(Axis axis) const;
void render_sidebar_scale_hint(Axis axis) const;
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 6e8b8a471..7ab564beb 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -423,7 +423,7 @@ void Tab::update_changed_ui()
const ScalableBitmap *sys_icon = &m_bmp_value_lock;
const ScalableBitmap *icon = &m_bmp_value_revert;
- const wxColour *color = &m_sys_label_clr;
+ const wxColour *color = m_is_default_preset ? &m_default_text_clr : &m_sys_label_clr;
const wxString *sys_tt = &m_tt_value_lock;
const wxString *tt = &m_tt_value_revert;
@@ -590,7 +590,7 @@ void Tab::update_changed_tree_ui()
}
}
- const wxColor *clr = sys_page ? &m_sys_label_clr :
+ const wxColor *clr = sys_page ? (m_is_default_preset ? &m_default_text_clr : &m_sys_label_clr) :
modified_page ? &m_modified_label_clr :
&m_default_text_clr;
@@ -874,11 +874,10 @@ void Tab::update_wiping_button_visibility() {
return; // ys_FIXME
bool wipe_tower_enabled = dynamic_cast<ConfigOptionBool*>( (m_preset_bundle->prints.get_edited_preset().config ).option("wipe_tower"))->value;
bool multiple_extruders = dynamic_cast<ConfigOptionFloats*>((m_preset_bundle->printers.get_edited_preset().config).option("nozzle_diameter"))->values.size() > 1;
- bool single_extruder_mm = dynamic_cast<ConfigOptionBool*>( (m_preset_bundle->printers.get_edited_preset().config).option("single_extruder_multi_material"))->value;
auto wiping_dialog_button = wxGetApp().sidebar().get_wiping_dialog_button();
if (wiping_dialog_button) {
- wiping_dialog_button->Show(wipe_tower_enabled && multiple_extruders && single_extruder_mm);
+ wiping_dialog_button->Show(wipe_tower_enabled && multiple_extruders);
wiping_dialog_button->GetParent()->Layout();
}
}
@@ -1557,6 +1556,9 @@ void TabFilament::build()
};
optgroup->append_line(line);
+ optgroup = page->new_optgroup(_(L("Wipe tower parameters")));
+ optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower");
+
optgroup = page->new_optgroup(_(L("Toolchange parameters with single extruder MM printers")));
optgroup->append_single_option_line("filament_loading_speed_start");
optgroup->append_single_option_line("filament_loading_speed");
@@ -1568,7 +1570,6 @@ void TabFilament::build()
optgroup->append_single_option_line("filament_cooling_moves");
optgroup->append_single_option_line("filament_cooling_initial_speed");
optgroup->append_single_option_line("filament_cooling_final_speed");
- optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower");
line = optgroup->create_single_option_line("filament_ramming_parameters");// { _(L("Ramming")), "" };
line.widget = [this](wxWindow* parent) {
@@ -2584,11 +2585,14 @@ void Tab::load_current_preset()
// Reload preset pages with the new configuration values.
reload_config();
- m_bmp_non_system = m_presets->get_selected_preset_parent() ? &m_bmp_value_unlock : &m_bmp_white_bullet;
- m_ttg_non_system = m_presets->get_selected_preset_parent() ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns;
- m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
+ const Preset* selected_preset_parent = m_presets->get_selected_preset_parent();
+ m_is_default_preset = selected_preset_parent != nullptr && selected_preset_parent->is_default;
+
+ m_bmp_non_system = selected_preset_parent ? &m_bmp_value_unlock : &m_bmp_white_bullet;
+ m_ttg_non_system = selected_preset_parent ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns;
+ m_tt_non_system = selected_preset_parent ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
- m_undo_to_sys_btn->Enable(!preset.is_default);
+// m_undo_to_sys_btn->Enable(!preset.is_default);
#if 0
// use CallAfter because some field triggers schedule on_change calls using CallAfter,
@@ -3174,18 +3178,18 @@ void Tab::fill_icon_descriptions()
{
m_icon_descriptions.emplace_back(&m_bmp_value_lock, L("LOCKED LOCK"),
// TRN Description for "LOCKED LOCK"
- L("indicates that the settings are the same as the system values for the current option group"));
+ L("indicates that the settings are the same as the system (or default) values for the current option group"));
m_icon_descriptions.emplace_back(&m_bmp_value_unlock, L("UNLOCKED LOCK"),
// TRN Description for "UNLOCKED LOCK"
- L("indicates that some settings were changed and are not equal to the system values for "
+ L("indicates that some settings were changed and are not equal to the system (or default) values for "
"the current option group.\n"
"Click the UNLOCKED LOCK icon to reset all settings for current option group to "
- "the system values."));
+ "the system (or default) values."));
m_icon_descriptions.emplace_back(&m_bmp_white_bullet, L("WHITE BULLET"),
// TRN Description for "WHITE BULLET"
- L("for the left button: \tindicates a non-system preset,\n"
+ L("for the left button: \tindicates a non-system (or non-default) preset,\n"
"for the right button: \tindicates that the settings hasn't been modified."));
m_icon_descriptions.emplace_back(&m_bmp_value_revert, L("BACK ARROW"),
@@ -3198,29 +3202,14 @@ void Tab::fill_icon_descriptions()
void Tab::set_tooltips_text()
{
-// m_undo_to_sys_btn->SetToolTip(_(L( "LOCKED LOCK icon indicates that the settings are the same as the system values "
-// "for the current option group.\n"
-// "UNLOCKED LOCK icon indicates that some settings were changed and are not equal "
-// "to the system values for the current option group.\n"
-// "WHITE BULLET icon indicates a non system preset.\n\n"
-// "Click the UNLOCKED LOCK icon to reset all settings for current option group to "
-// "the system values.")));
-//
-// m_undo_btn->SetToolTip(_(L( "WHITE BULLET icon indicates that the settings are the same as in the last saved"
-// "preset for the current option group.\n"
-// "BACK ARROW icon indicates that the settings were changed and are not equal to "
-// "the last saved preset for the current option group.\n\n"
-// "Click the BACK ARROW icon to reset all settings for the current option group to "
-// "the last saved preset.")));
-
// --- Tooltip text for reset buttons (for whole options group)
// Text to be shown on the "Revert to system" aka "Lock to system" button next to each input field.
- m_ttg_value_lock = _(L("LOCKED LOCK icon indicates that the settings are the same as the system values "
+ m_ttg_value_lock = _(L("LOCKED LOCK icon indicates that the settings are the same as the system (or default) values "
"for the current option group"));
m_ttg_value_unlock = _(L("UNLOCKED LOCK icon indicates that some settings were changed and are not equal "
- "to the system values for the current option group.\n"
- "Click to reset all settings for current option group to the system values."));
- m_ttg_white_bullet_ns = _(L("WHITE BULLET icon indicates a non system preset."));
+ "to the system (or default) values for the current option group.\n"
+ "Click to reset all settings for current option group to the system (or default) values."));
+ m_ttg_white_bullet_ns = _(L("WHITE BULLET icon indicates a non system (or non default) preset."));
m_ttg_non_system = &m_ttg_white_bullet_ns;
// Text to be shown on the "Undo user changes" button next to each input field.
m_ttg_white_bullet = _(L("WHITE BULLET icon indicates that the settings are the same as in the last saved "
@@ -3231,10 +3220,10 @@ void Tab::set_tooltips_text()
// --- Tooltip text for reset buttons (for each option in group)
// Text to be shown on the "Revert to system" aka "Lock to system" button next to each input field.
- m_tt_value_lock = _(L("LOCKED LOCK icon indicates that the value is the same as the system value."));
+ m_tt_value_lock = _(L("LOCKED LOCK icon indicates that the value is the same as the system (or default) value."));
m_tt_value_unlock = _(L("UNLOCKED LOCK icon indicates that the value was changed and is not equal "
- "to the system value.\n"
- "Click to reset current value to the system value."));
+ "to the system (or default) value.\n"
+ "Click to reset current value to the system (or default) value."));
// m_tt_white_bullet_ns= _(L("WHITE BULLET icon indicates a non system preset."));
m_tt_non_system = &m_ttg_white_bullet_ns;
// Text to be shown on the "Undo user changes" button next to each input field.
@@ -3488,9 +3477,9 @@ void TabSLAMaterial::reload_config()
void TabSLAMaterial::update()
{
if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF)
- return; // #ys_FIXME
+ return;
-// #ys_FIXME
+// #ys_FIXME. Just a template for this function
// m_update_cnt++;
// ! something to update
// m_update_cnt--;
@@ -3527,6 +3516,7 @@ void TabSLAPrint::build()
// optgroup->append_single_option_line("support_pillar_widening_factor");
optgroup->append_single_option_line("support_base_diameter");
optgroup->append_single_option_line("support_base_height");
+ optgroup->append_single_option_line("support_base_safety_distance");
optgroup->append_single_option_line("support_object_elevation");
optgroup = page->new_optgroup(_(L("Connection of the support sticks and junctions")));
@@ -3547,7 +3537,12 @@ void TabSLAPrint::build()
// TODO: Disabling this parameter for the beta release
// optgroup->append_single_option_line("pad_edge_radius");
optgroup->append_single_option_line("pad_wall_slope");
-
+
+ optgroup->append_single_option_line("pad_object_gap");
+ optgroup->append_single_option_line("pad_object_connector_stride");
+ optgroup->append_single_option_line("pad_object_connector_width");
+ optgroup->append_single_option_line("pad_object_connector_penetration");
+
page = add_options_page(_(L("Advanced")), "wrench");
optgroup = page->new_optgroup(_(L("Slicing")));
optgroup->append_single_option_line("slice_closing_radius");
@@ -3588,41 +3583,61 @@ void TabSLAPrint::reload_config()
void TabSLAPrint::update()
{
if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF)
- return; // #ys_FIXME
+ return;
-// #ys_FIXME
m_update_cnt++;
- double head_penetration = m_config->opt_float("support_head_penetration");
- double head_width = m_config->opt_float("support_head_width");
- if(head_penetration > head_width) {
- wxString msg_text = _(L("Head penetration should not be greater than the head width."));
- auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK);
- DynamicPrintConfig new_conf = *m_config;
- if (dialog->ShowModal() == wxID_OK) {
- new_conf.set_key_value("support_head_penetration", new ConfigOptionFloat(head_width));
- }
-
- load_config(new_conf);
- }
-
- double pinhead_d = m_config->opt_float("support_head_front_diameter");
- double pillar_d = m_config->opt_float("support_pillar_diameter");
- if(pinhead_d > pillar_d) {
- wxString msg_text = _(L("Pinhead diameter should be smaller than the pillar diameter."));
- auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK);
- DynamicPrintConfig new_conf = *m_config;
- if (dialog->ShowModal() == wxID_OK) {
- new_conf.set_key_value("support_head_front_diameter", new ConfigOptionFloat(pillar_d / 2.0));
- }
-
- load_config(new_conf);
- }
-
- m_update_cnt--;
-
- if (m_update_cnt == 0)
- wxGetApp().mainframe->on_config_changed(m_config);
+ double head_penetration = m_config->opt_float("support_head_penetration");
+ double head_width = m_config->opt_float("support_head_width");
+ if (head_penetration > head_width) {
+ wxString msg_text = _(
+ L("Head penetration should not be greater than the head width."));
+
+ auto dialog = new wxMessageDialog(parent(),
+ msg_text,
+ _(L("Invalid Head penetration")),
+ wxICON_WARNING | wxOK);
+
+ DynamicPrintConfig new_conf = *m_config;
+ if (dialog->ShowModal() == wxID_OK) {
+ new_conf.set_key_value("support_head_penetration",
+ new ConfigOptionFloat(head_width));
+ }
+
+ load_config(new_conf);
+ }
+
+ double pinhead_d = m_config->opt_float("support_head_front_diameter");
+ double pillar_d = m_config->opt_float("support_pillar_diameter");
+ if (pinhead_d > pillar_d) {
+ wxString msg_text = _(L(
+ "Pinhead diameter should be smaller than the pillar diameter."));
+
+ auto dialog = new wxMessageDialog(parent(),
+ msg_text,
+ _(L("Invalid pinhead diameter")),
+ wxICON_WARNING | wxOK);
+
+ DynamicPrintConfig new_conf = *m_config;
+ if (dialog->ShowModal() == wxID_OK) {
+ new_conf.set_key_value("support_head_front_diameter",
+ new ConfigOptionFloat(pillar_d / 2.0));
+ }
+
+ load_config(new_conf);
+ }
+
+ // if(m_config->opt_float("support_object_elevation") < EPSILON &&
+ // m_config->opt_bool("pad_enable")) {
+ // // TODO: disable editding of:
+ // // pad_object_connector_stride
+ // // pad_object_connector_width
+ // // pad_object_connector_penetration
+ // }
+
+ m_update_cnt--;
+
+ if (m_update_cnt == 0) wxGetApp().mainframe->on_config_changed(m_config);
}
} // GUI
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 6bbe15f7f..73b6bb08d 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -142,6 +142,12 @@ protected:
PresetDependencies m_compatible_printers;
PresetDependencies m_compatible_prints;
+ /* Indicates, that default preset or preset inherited from default is selected
+ * This value is used for a options color updating
+ * (use green color only for options, which values are equal to system values)
+ */
+ bool m_is_default_preset {false};
+
ScalableButton* m_undo_btn;
ScalableButton* m_undo_to_sys_btn;
ScalableButton* m_question_btn;
diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp
index 4c2b2480e..894b1ee62 100644
--- a/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -172,7 +172,7 @@ void WipingPanel::format_sizer(wxSizer* sizer, wxPanel* page, wxGridSizer* grid_
wxSize text_size = GetTextExtent(info);
auto info_str = new wxStaticText(page, wxID_ANY, info ,wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
info_str->Wrap(int(0.6*text_size.x));
- sizer->Add( info_str, 0, wxALIGN_CENTER_HORIZONTAL | wxEXPAND);
+ sizer->Add( info_str, 0, wxEXPAND);
auto table_sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(table_sizer, 0, wxALIGN_CENTER | wxCENTER, table_lshift);
table_sizer->Add(new wxStaticText(page, wxID_ANY, table_title), 0, wxALIGN_CENTER | wxTOP, 50);
diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp
index aed423674..7e8a2d92d 100644
--- a/src/slic3r/GUI/wxExtensions.cpp
+++ b/src/slic3r/GUI/wxExtensions.cpp
@@ -437,27 +437,69 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent
m_type(type),
m_extruder(wxEmptyString)
{
- if (type == itSettings) {
+ if (type == itSettings)
m_name = "Settings to modified";
- }
- else if (type == itInstanceRoot) {
+ else if (type == itInstanceRoot)
m_name = _(L("Instances"));
-#ifdef __WXGTK__
- m_container = true;
-#endif //__WXGTK__
- }
- else if (type == itInstance) {
+ else if (type == itInstance)
+ {
m_idx = parent->GetChildCount();
m_name = wxString::Format(_(L("Instance %d")), m_idx + 1);
set_action_icon();
}
+ else if (type == itLayerRoot)
+ {
+ m_bmp = create_scaled_bitmap(nullptr, "layers"); // FIXME: pass window ptr
+ m_name = _(L("Layers"));
+ }
+
+#ifdef __WXGTK__
+ // it's necessary on GTK because of control have to know if this item will be container
+ // in another case you couldn't to add subitem for this item
+ // it will be produce "segmentation fault"
+ if (type & (itInstanceRoot | itLayerRoot))
+ m_container = true;
+#endif //__WXGTK__
+}
+
+ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
+ const t_layer_height_range& layer_range,
+ const int idx /*= -1 */,
+ const wxString& extruder) :
+ m_parent(parent),
+ m_type(itLayer),
+ m_idx(idx),
+ m_layer_range(layer_range),
+ m_extruder(extruder)
+{
+ const int children_cnt = parent->GetChildCount();
+ if (idx < 0)
+ m_idx = children_cnt;
+ else
+ {
+ // update indexes for another Laeyr Nodes
+ for (int i = m_idx; i < children_cnt; i++)
+ parent->GetNthChild(i)->SetIdx(i + 1);
+ }
+ const std::string label_range = (boost::format(" %.2f-%.2f ") % layer_range.first % layer_range.second).str();
+ m_name = _(L("Range")) + label_range + "(" + _(L("mm")) + ")";
+ m_bmp = create_scaled_bitmap(nullptr, "layers_white"); // FIXME: pass window ptr
+
+#ifdef __WXGTK__
+ // it's necessary on GTK because of control have to know if this item will be container
+ // in another case you couldn't to add subitem for this item
+ // it will be produce "segmentation fault"
+ m_container = true;
+#endif //__WXGTK__
+
+ set_action_icon();
}
void ObjectDataViewModelNode::set_action_icon()
{
- m_action_icon_name = m_type == itObject ? "advanced_plus" :
- m_type == itVolume ? "cog" : "set_separate_obj";
+ m_action_icon_name = m_type & itObject ? "advanced_plus" :
+ m_type & (itVolume | itLayer) ? "cog" : /*m_type & itInstance*/ "set_separate_obj";
m_action_icon = create_scaled_bitmap(nullptr, m_action_icon_name); // FIXME: pass window ptr
}
@@ -523,6 +565,22 @@ void ObjectDataViewModelNode::SetIdx(const int& idx)
// ObjectDataViewModel
// ----------------------------------------------------------------------------
+static int get_root_idx(ObjectDataViewModelNode *parent_node, const ItemType root_type)
+{
+ // because of istance_root and layers_root are at the end of the list, so
+ // start locking from the end
+ for (int root_idx = parent_node->GetChildCount() - 1; root_idx >= 0; root_idx--)
+ {
+ // if there is SettingsItem or VolumeItem, then RootItems don't exist in current ObjectItem
+ if (parent_node->GetNthChild(root_idx)->GetType() & (itSettings | itVolume))
+ break;
+ if (parent_node->GetNthChild(root_idx)->GetType() & root_type)
+ return root_idx;
+ }
+
+ return -1;
+}
+
ObjectDataViewModel::ObjectDataViewModel()
{
m_bitmap_cache = new Slic3r::GUI::BitmapCache;
@@ -567,10 +625,10 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder);
- // because of istance_root is a last item of the object
- int insert_position = root->GetChildCount() - 1;
- if (insert_position < 0 || root->GetNthChild(insert_position)->m_type != itInstanceRoot)
- insert_position = -1;
+ // get insertion position according to the existed Layers and/or Instances Items
+ int insert_position = get_root_idx(root, itLayerRoot);
+ if (insert_position < 0)
+ insert_position = get_root_idx(root, itInstanceRoot);
const bool obj_errors = root->m_bmp.IsOk();
@@ -619,15 +677,30 @@ wxDataViewItem ObjectDataViewModel::AddSettingsChild(const wxDataViewItem &paren
return child;
}
-int get_istances_root_idx(ObjectDataViewModelNode *parent_node)
+/* return values:
+ * true => root_node is created and added to the parent_root
+ * false => root node alredy exists
+*/
+static bool append_root_node(ObjectDataViewModelNode *parent_node,
+ ObjectDataViewModelNode **root_node,
+ const ItemType root_type)
{
- // because of istance_root is a last item of the object
- const int inst_root_idx = parent_node->GetChildCount()-1;
+ const int inst_root_id = get_root_idx(parent_node, root_type);
- if (inst_root_idx < 0 || parent_node->GetNthChild(inst_root_idx)->GetType() == itInstanceRoot)
- return inst_root_idx;
+ *root_node = inst_root_id < 0 ?
+ new ObjectDataViewModelNode(parent_node, root_type) :
+ parent_node->GetNthChild(inst_root_id);
- return -1;
+ if (inst_root_id < 0) {
+ if ((root_type&itInstanceRoot) ||
+ (root_type&itLayerRoot) && get_root_idx(parent_node, itInstanceRoot)<0)
+ parent_node->Append(*root_node);
+ else if (root_type&itLayerRoot)
+ parent_node->Insert(*root_node, static_cast<unsigned int>(get_root_idx(parent_node, itInstanceRoot)));
+ return true;
+ }
+
+ return false;
}
wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num)
@@ -635,20 +708,15 @@ wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem &paren
ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID();
if (!parent_node) return wxDataViewItem(0);
- // Check and create/get instances root node
- const int inst_root_id = get_istances_root_idx(parent_node);
+ // get InstanceRoot node
+ ObjectDataViewModelNode *inst_root_node { nullptr };
- ObjectDataViewModelNode *inst_root_node = inst_root_id < 0 ?
- new ObjectDataViewModelNode(parent_node, itInstanceRoot) :
- parent_node->GetNthChild(inst_root_id);
+ const bool appended = append_root_node(parent_node, &inst_root_node, itInstanceRoot);
const wxDataViewItem inst_root_item((void*)inst_root_node);
+ if (!inst_root_node) return wxDataViewItem(0);
- if (inst_root_id < 0) {
- parent_node->Append(inst_root_node);
- // notify control
- ItemAdded(parent_item, inst_root_item);
-// if (num == 1) num++;
- }
+ if (appended)
+ ItemAdded(parent_item, inst_root_item);// notify control
// Add instance nodes
ObjectDataViewModelNode *instance_node = nullptr;
@@ -665,6 +733,63 @@ wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem &paren
return wxDataViewItem((void*)instance_node);
}
+wxDataViewItem ObjectDataViewModel::AddLayersRoot(const wxDataViewItem &parent_item)
+{
+ ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID();
+ if (!parent_node) return wxDataViewItem(0);
+
+ // get LayerRoot node
+ ObjectDataViewModelNode *layer_root_node{ nullptr };
+ const bool appended = append_root_node(parent_node, &layer_root_node, itLayerRoot);
+ if (!layer_root_node) return wxDataViewItem(0);
+
+ const wxDataViewItem layer_root_item((void*)layer_root_node);
+
+ if (appended)
+ ItemAdded(parent_item, layer_root_item);// notify control
+
+ return layer_root_item;
+}
+
+wxDataViewItem ObjectDataViewModel::AddLayersChild(const wxDataViewItem &parent_item,
+ const t_layer_height_range& layer_range,
+ const int extruder/* = 0*/,
+ const int index /* = -1*/)
+{
+ ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID();
+ if (!parent_node) return wxDataViewItem(0);
+
+ wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder);
+
+ // get LayerRoot node
+ ObjectDataViewModelNode *layer_root_node;
+ wxDataViewItem layer_root_item;
+
+ if (parent_node->GetType() & itLayerRoot) {
+ layer_root_node = parent_node;
+ layer_root_item = parent_item;
+ }
+ else {
+ const int root_idx = get_root_idx(parent_node, itLayerRoot);
+ if (root_idx < 0) return wxDataViewItem(0);
+ layer_root_node = parent_node->GetNthChild(root_idx);
+ layer_root_item = wxDataViewItem((void*)layer_root_node);
+ }
+
+ // Add layer node
+ ObjectDataViewModelNode *layer_node = new ObjectDataViewModelNode(layer_root_node, layer_range, index, extruder_str);
+ if (index < 0)
+ layer_root_node->Append(layer_node);
+ else
+ layer_root_node->Insert(layer_node, index);
+
+ // notify control
+ const wxDataViewItem layer_item((void*)layer_node);
+ ItemAdded(layer_root_item, layer_item);
+
+ return layer_item;
+}
+
wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
{
auto ret_item = wxDataViewItem(0);
@@ -679,10 +804,14 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
// NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_
// thus removing the node from it doesn't result in freeing it
if (node_parent) {
- if (node->m_type == itInstanceRoot)
+ if (node->m_type & (itInstanceRoot|itLayerRoot))
{
- for (int i = node->GetChildCount() - 1; i > 0; i--)
+ // node can be deleted by the Delete, let's check its type while we safely can
+ bool is_instance_root = (node->m_type & itInstanceRoot);
+
+ for (int i = node->GetChildCount() - 1; i >= (is_instance_root ? 1 : 0); i--)
Delete(wxDataViewItem(node->GetNthChild(i)));
+
return parent;
}
@@ -690,7 +819,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
auto idx = node->GetIdx();
- if (node->m_type == itVolume) {
+ if (node->m_type & (itVolume|itLayer)) {
node_parent->m_volumes_cnt--;
DeleteSettings(item);
}
@@ -734,8 +863,24 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
return ret_item;
}
+ // if there was last layer item, delete this one and layers root item
+ if (node_parent->GetChildCount() == 0 && node_parent->m_type == itLayerRoot)
+ {
+ ObjectDataViewModelNode *obj_node = node_parent->GetParent();
+ obj_node->GetChildren().Remove(node_parent);
+ delete node_parent;
+ ret_item = wxDataViewItem(obj_node);
+
+#ifndef __WXGTK__
+ if (obj_node->GetChildCount() == 0)
+ obj_node->m_container = false;
+#endif //__WXGTK__
+ ItemDeleted(ret_item, wxDataViewItem(node_parent));
+ return ret_item;
+ }
+
// if there is last volume item after deleting, delete this last volume too
- if (node_parent->GetChildCount() <= 3)
+ if (node_parent->GetChildCount() <= 3) // 3??? #ys_FIXME
{
int vol_cnt = 0;
int vol_idx = 0;
@@ -817,7 +962,7 @@ wxDataViewItem ObjectDataViewModel::DeleteLastInstance(const wxDataViewItem &par
ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID();
if (!parent_node) return ret_item;
- const int inst_root_id = get_istances_root_idx(parent_node);
+ const int inst_root_id = get_root_idx(parent_node, itInstanceRoot);
if (inst_root_id < 0) return ret_item;
wxDataViewItemArray items;
@@ -974,28 +1119,67 @@ wxDataViewItem ObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_id
return wxDataViewItem(0);
}
+wxDataViewItem ObjectDataViewModel::GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type)
+{
+ if (obj_idx >= m_objects.size() || obj_idx < 0) {
+ printf("Error! Out of objects range.\n");
+ return wxDataViewItem(0);
+ }
+
+ auto item = GetItemByType(wxDataViewItem(m_objects[obj_idx]), parent_type);
+ if (!item)
+ return wxDataViewItem(0);
+
+ auto parent = (ObjectDataViewModelNode*)item.GetID();
+ for (size_t i = 0; i < parent->GetChildCount(); i++)
+ if (parent->GetNthChild(i)->m_idx == sub_obj_idx)
+ return wxDataViewItem(parent->GetNthChild(i));
+
+ return wxDataViewItem(0);
+}
+
wxDataViewItem ObjectDataViewModel::GetItemByInstanceId(int obj_idx, int inst_idx)
{
+ return GetItemById(obj_idx, inst_idx, itInstanceRoot);
+}
+
+wxDataViewItem ObjectDataViewModel::GetItemByLayerId(int obj_idx, int layer_idx)
+{
+ return GetItemById(obj_idx, layer_idx, itLayerRoot);
+}
+
+wxDataViewItem ObjectDataViewModel::GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range)
+{
if (obj_idx >= m_objects.size() || obj_idx < 0) {
printf("Error! Out of objects range.\n");
return wxDataViewItem(0);
}
- auto instances_item = GetInstanceRootItem(wxDataViewItem(m_objects[obj_idx]));
- if (!instances_item)
+ auto item = GetItemByType(wxDataViewItem(m_objects[obj_idx]), itLayerRoot);
+ if (!item)
return wxDataViewItem(0);
- auto parent = (ObjectDataViewModelNode*)instances_item.GetID();;
+ auto parent = (ObjectDataViewModelNode*)item.GetID();
for (size_t i = 0; i < parent->GetChildCount(); i++)
- if (parent->GetNthChild(i)->m_idx == inst_idx)
+ if (parent->GetNthChild(i)->m_layer_range == layer_range)
return wxDataViewItem(parent->GetNthChild(i));
return wxDataViewItem(0);
}
+int ObjectDataViewModel::GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range)
+{
+ wxDataViewItem item = GetItemByLayerRange(obj_idx, layer_range);
+ if (!item)
+ return -1;
+
+ return GetLayerIdByItem(item);
+}
+
int ObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) const
{
- wxASSERT(item.IsOk());
+ if(!item.IsOk())
+ return -1;
ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID();
auto it = find(m_objects.begin(), m_objects.end(), node);
@@ -1030,13 +1214,28 @@ int ObjectDataViewModel::GetInstanceIdByItem(const wxDataViewItem& item) const
return GetIdByItemAndType(item, itInstance);
}
+int ObjectDataViewModel::GetLayerIdByItem(const wxDataViewItem& item) const
+{
+ return GetIdByItemAndType(item, itLayer);
+}
+
+t_layer_height_range ObjectDataViewModel::GetLayerRangeByItem(const wxDataViewItem& item) const
+{
+ wxASSERT(item.IsOk());
+
+ ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID();
+ if (!node || node->m_type != itLayer)
+ return { 0.0f, 0.0f };
+ return node->GetLayerRange();
+}
+
void ObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx)
{
wxASSERT(item.IsOk());
type = itUndef;
ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID();
- if (!node || node->GetIdx() <-1 || node->GetIdx() ==-1 && !(node->GetType() & (itObject | itSettings | itInstanceRoot)))
+ if (!node || node->GetIdx() <-1 || node->GetIdx() == -1 && !(node->GetType() & (itObject | itSettings | itInstanceRoot | itLayerRoot/* | itLayer*/)))
return;
idx = node->GetIdx();
@@ -1044,9 +1243,10 @@ void ObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type
ObjectDataViewModelNode *parent_node = node->GetParent();
if (!parent_node) return;
- if (type == itInstance)
- parent_node = node->GetParent()->GetParent();
- if (!parent_node || parent_node->m_type != itObject) { type = itUndef; return; }
+
+ // get top parent (Object) node
+ while (parent_node->m_type != itObject)
+ parent_node = parent_node->GetParent();
auto it = find(m_objects.begin(), m_objects.end(), parent_node);
if (it != m_objects.end())
@@ -1214,10 +1414,7 @@ wxDataViewItem ObjectDataViewModel::GetTopParent(const wxDataViewItem &item) con
ObjectDataViewModelNode *parent_node = node->GetParent();
while (parent_node->m_type != itObject)
- {
- node = parent_node;
- parent_node = node->GetParent();
- }
+ parent_node = parent_node->GetParent();
return wxDataViewItem((void*)parent_node);
}
@@ -1318,6 +1515,11 @@ wxDataViewItem ObjectDataViewModel::GetInstanceRootItem(const wxDataViewItem &it
return GetItemByType(item, itInstanceRoot);
}
+wxDataViewItem ObjectDataViewModel::GetLayerRootItem(const wxDataViewItem &item) const
+{
+ return GetItemByType(item, itLayerRoot);
+}
+
bool ObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const
{
if (!item.IsOk())
@@ -2027,6 +2229,9 @@ void DoubleSlider::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord
void DoubleSlider::draw_ticks(wxDC& dc)
{
+ if (!m_is_enabled_tick_manipulation)
+ return;
+
dc.SetPen(m_is_enabled_tick_manipulation ? DARK_GREY_PEN : LIGHT_GREY_PEN );
int height, width;
get_size(&width, &height);
@@ -2044,6 +2249,9 @@ void DoubleSlider::draw_ticks(wxDC& dc)
void DoubleSlider::draw_colored_band(wxDC& dc)
{
+ if (!m_is_enabled_tick_manipulation)
+ return;
+
int height, width;
get_size(&width, &height);
@@ -2113,7 +2321,7 @@ void DoubleSlider::draw_one_layer_icon(wxDC& dc)
void DoubleSlider::draw_revert_icon(wxDC& dc)
{
- if (m_ticks.empty())
+ if (m_ticks.empty() || !m_is_enabled_tick_manipulation)
return;
int width, height;
@@ -2218,7 +2426,7 @@ void DoubleSlider::OnLeftDown(wxMouseEvent& event)
m_selection == ssLower ? correct_lower_value() : correct_higher_value();
if (!m_selection) m_selection = ssHigher;
}
- else if (is_point_in_rect(pos, m_rect_revert_icon)) {
+ else if (is_point_in_rect(pos, m_rect_revert_icon) && m_is_enabled_tick_manipulation) {
// discard all color changes
SetLowerValue(m_min_value);
SetHigherValue(m_max_value);
@@ -2647,7 +2855,7 @@ ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 10*/) :
m_mode_btns.push_back(new ModeButton(parent, wxID_ANY, button.second, button.first));;
#endif // __WXOSX__
- m_mode_btns.back()->Bind(wxEVT_BUTTON, std::bind(modebtnfn, std::placeholders::_1, m_mode_btns.size() - 1));
+ m_mode_btns.back()->Bind(wxEVT_BUTTON, std::bind(modebtnfn, std::placeholders::_1, int(m_mode_btns.size() - 1)));
Add(m_mode_btns.back());
}
}
diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp
index 081c0d48f..d0edf9760 100644
--- a/src/slic3r/GUI/wxExtensions.hpp
+++ b/src/slic3r/GUI/wxExtensions.hpp
@@ -20,6 +20,9 @@ namespace Slic3r {
enum class ModelVolumeType : int;
};
+typedef double coordf_t;
+typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
+
#ifdef __WXMSW__
void msw_rescale_menu(wxMenu* menu);
#else /* __WXMSW__ */
@@ -159,12 +162,14 @@ DECLARE_VARIANT_OBJECT(DataViewBitmapText)
// ----------------------------------------------------------------------------
enum ItemType {
- itUndef = 0,
- itObject = 1,
- itVolume = 2,
- itInstanceRoot = 4,
- itInstance = 8,
- itSettings = 16
+ itUndef = 0,
+ itObject = 1,
+ itVolume = 2,
+ itInstanceRoot = 4,
+ itInstance = 8,
+ itSettings = 16,
+ itLayerRoot = 32,
+ itLayer = 64,
};
class ObjectDataViewModelNode;
@@ -177,6 +182,7 @@ class ObjectDataViewModelNode
wxBitmap m_empty_bmp;
size_t m_volumes_cnt = 0;
std::vector< std::string > m_opt_categories;
+ t_layer_height_range m_layer_range = { 0.0f, 0.0f };
wxString m_name;
wxBitmap& m_bmp = m_empty_bmp;
@@ -229,6 +235,11 @@ public:
set_action_icon();
}
+ ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
+ const t_layer_height_range& layer_range,
+ const int idx = -1,
+ const wxString& extruder = wxEmptyString );
+
ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type);
~ObjectDataViewModelNode()
@@ -318,6 +329,7 @@ public:
ItemType GetType() const { return m_type; }
void SetIdx(const int& idx);
int GetIdx() const { return m_idx; }
+ t_layer_height_range GetLayerRange() const { return m_layer_range; }
// use this function only for childrens
void AssignAllVal(ObjectDataViewModelNode& from_node)
@@ -348,7 +360,7 @@ public:
}
// Set action icons for node
- void set_action_icon();
+ void set_action_icon();
void update_settings_digest_bitmaps();
bool update_settings_digest(const std::vector<std::string>& categories);
@@ -388,6 +400,11 @@ public:
const bool create_frst_child = true);
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num);
+ wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item);
+ wxDataViewItem AddLayersChild( const wxDataViewItem &parent_item,
+ const t_layer_height_range& layer_range,
+ const int extruder = 0,
+ const int index = -1);
wxDataViewItem Delete(const wxDataViewItem &item);
wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num);
void DeleteAll();
@@ -395,13 +412,18 @@ public:
void DeleteVolumeChildren(wxDataViewItem& parent);
void DeleteSettings(const wxDataViewItem& parent);
wxDataViewItem GetItemById(int obj_idx);
+ wxDataViewItem GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type);
wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx);
wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx);
+ wxDataViewItem GetItemByLayerId(int obj_idx, int layer_idx);
+ wxDataViewItem GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range);
+ int GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range);
int GetIdByItem(const wxDataViewItem& item) const;
int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const;
int GetObjectIdByItem(const wxDataViewItem& item) const;
int GetVolumeIdByItem(const wxDataViewItem& item) const;
int GetInstanceIdByItem(const wxDataViewItem& item) const;
+ int GetLayerIdByItem(const wxDataViewItem& item) const;
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
int GetRowByItem(const wxDataViewItem& item) const;
bool IsEmpty() { return m_objects.empty(); }
@@ -450,6 +472,7 @@ public:
ItemType type) const;
wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const;
wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const;
+ wxDataViewItem GetLayerRootItem(const wxDataViewItem &item) const;
bool IsSettingsItem(const wxDataViewItem &item) const;
void UpdateSettingsDigest( const wxDataViewItem &item,
const std::vector<std::string>& categories);
@@ -465,6 +488,7 @@ public:
wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
const bool is_marked = false);
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
+ t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const;
};
// ----------------------------------------------------------------------------
diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp
index 8c3ced31a..bc600fcad 100644
--- a/src/slic3r/Utils/PresetUpdater.cpp
+++ b/src/slic3r/Utils/PresetUpdater.cpp
@@ -402,15 +402,8 @@ Updates PresetUpdater::priv::get_config_updates() const
}
}
- copy_file_fix(idx.path(), bundle_path_idx);
-
const auto ver_current = idx.find(vp.config_version);
const bool ver_current_found = ver_current != idx.end();
- if (! ver_current_found) {
- auto message = (boost::format("Preset bundle `%1%` version not found in index: %2%") % idx.vendor() % vp.config_version.to_string()).str();
- BOOST_LOG_TRIVIAL(error) << message;
- GUI::show_error(nullptr, GUI::from_u8(message));
- }
BOOST_LOG_TRIVIAL(debug) << boost::format("Vendor: %1%, version installed: %2%%3%, version cached: %4%")
% vp.name
@@ -418,6 +411,13 @@ Updates PresetUpdater::priv::get_config_updates() const
% (ver_current_found ? "" : " (not found in index!)")
% recommended->config_version.to_string();
+ if (! ver_current_found) {
+ auto message = (boost::format("Preset bundle `%1%` version not found in index: %2%") % idx.vendor() % vp.config_version.to_string()).str();
+ BOOST_LOG_TRIVIAL(error) << message;
+ GUI::show_error(nullptr, GUI::from_u8(message));
+ continue;
+ }
+
if (ver_current_found && !ver_current->is_current_slic3r_supported()) {
BOOST_LOG_TRIVIAL(warning) << "Current Slic3r incompatible with installed bundle: " << bundle_path.string();
updates.incompats.emplace_back(std::move(bundle_path), *ver_current, vp.name);
@@ -459,10 +459,16 @@ Updates PresetUpdater::priv::get_config_updates() const
found = true;
}
}
- if (! found)
+
+ if (found) {
+ // 'Install' the index in the vendor directory. This is used to memoize
+ // offered updates and to not offer the same update again if it was cancelled by the user.
+ copy_file_fix(idx.path(), bundle_path_idx);
+ } else {
BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources")
% idx.vendor()
% recommended->config_version.to_string();
+ }
}
}
diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp
new file mode 100644
index 000000000..69d60603e
--- /dev/null
+++ b/src/slic3r/Utils/UndoRedo.cpp
@@ -0,0 +1,819 @@
+#include "UndoRedo.hpp"
+
+#include <algorithm>
+#include <iostream>
+#include <fstream>
+#include <memory>
+#include <typeinfo>
+#include <cassert>
+#include <cstddef>
+
+#include <cereal/types/polymorphic.hpp>
+#include <cereal/types/map.hpp>
+#include <cereal/types/string.hpp>
+#include <cereal/types/utility.hpp>
+#include <cereal/types/vector.hpp>
+#include <cereal/archives/binary.hpp>
+#define CEREAL_FUTURE_EXPERIMENTAL
+#include <cereal/archives/adapters.hpp>
+
+#include <boost/foreach.hpp>
+
+#ifndef NDEBUG
+// #define SLIC3R_UNDOREDO_DEBUG
+#endif /* NDEBUG */
+
+namespace Slic3r {
+namespace UndoRedo {
+
+static std::string topmost_snapsnot_name = "@@@ Topmost @@@";
+
+bool Snapshot::is_topmost() const
+{
+ return this->name == topmost_snapsnot_name;
+}
+
+// Time interval, start is closed, end is open.
+struct Interval
+{
+public:
+ Interval(size_t begin, size_t end) : m_begin(begin), m_end(end) {}
+
+ size_t begin() const { return m_begin; }
+ size_t end() const { return m_end; }
+
+ bool is_valid() const { return m_begin >= 0 && m_begin < m_end; }
+ // This interval comes strictly before the rhs interval.
+ bool strictly_before(const Interval &rhs) const { return this->is_valid() && rhs.is_valid() && m_end <= rhs.m_begin; }
+ // This interval comes strictly after the rhs interval.
+ bool strictly_after(const Interval &rhs) const { return this->is_valid() && rhs.is_valid() && rhs.m_end <= m_begin; }
+
+ bool operator<(const Interval &rhs) const { return (m_begin < rhs.m_begin) || (m_begin == rhs.m_begin && m_end < rhs.m_end); }
+ bool operator==(const Interval &rhs) const { return m_begin == rhs.m_begin && m_end == rhs.m_end; }
+
+ void trim_end(size_t new_end) { m_end = std::min(m_end, new_end); }
+ void extend_end(size_t new_end) { assert(new_end >= m_end); m_end = new_end; }
+
+private:
+ size_t m_begin;
+ size_t m_end;
+};
+
+// History of a single object tracked by the Undo / Redo stack. The object may be mutable or immutable.
+class ObjectHistoryBase
+{
+public:
+ virtual ~ObjectHistoryBase() {}
+
+ // Is the object captured by this history mutable or immutable?
+ virtual bool is_mutable() const = 0;
+ virtual bool is_immutable() const = 0;
+ virtual const void* immutable_object_ptr() const { return nullptr; }
+
+ // If the history is empty, the ObjectHistory object could be released.
+ virtual bool empty() = 0;
+
+ // Release all data after the given timestamp. For the ImmutableObjectHistory, the shared pointer is NOT released.
+ virtual void release_after_timestamp(size_t timestamp) = 0;
+
+#ifdef SLIC3R_UNDOREDO_DEBUG
+ // Human readable debug information.
+ virtual std::string format() = 0;
+#endif /* SLIC3R_UNDOREDO_DEBUG */
+
+#ifndef NDEBUG
+ virtual bool valid() = 0;
+#endif /* NDEBUG */
+};
+
+template<typename T> class ObjectHistory : public ObjectHistoryBase
+{
+public:
+ ~ObjectHistory() override {}
+
+ // If the history is empty, the ObjectHistory object could be released.
+ bool empty() override { return m_history.empty(); }
+
+ // Release all data after the given timestamp. The shared pointer is NOT released.
+ void release_after_timestamp(size_t timestamp) override {
+ assert(! m_history.empty());
+ assert(this->valid());
+ // it points to an interval which either starts with timestamp, or follows the timestamp.
+ auto it = std::lower_bound(m_history.begin(), m_history.end(), T(timestamp, timestamp));
+ if (it != m_history.begin()) {
+ auto it_prev = it;
+ -- it_prev;
+ assert(it_prev->begin() < timestamp);
+ // Trim the last interval with timestamp.
+ it_prev->trim_end(timestamp);
+ }
+ m_history.erase(it, m_history.end());
+ assert(this->valid());
+ }
+
+protected:
+ std::vector<T> m_history;
+};
+
+// Big objects (mainly the triangle meshes) are tracked by Slicer using the shared pointers
+// and they are immutable.
+// The Undo / Redo stack therefore may keep a shared pointer to these immutable objects
+// and as long as the ref counter of these objects is higher than 1 (1 reference is held
+// by the Undo / Redo stack), there is no cost associated to holding the object
+// at the Undo / Redo stack. Once the reference counter drops to 1 (only the Undo / Redo
+// stack holds the reference), the shared pointer may get serialized (and possibly compressed)
+// and the shared pointer may be released.
+// The history of a single immutable object may not be continuous, as an immutable object may
+// be removed from the scene while being kept at the Copy / Paste stack.
+template<typename T>
+class ImmutableObjectHistory : public ObjectHistory<Interval>
+{
+public:
+ ImmutableObjectHistory(std::shared_ptr<const T> shared_object) : m_shared_object(shared_object) {}
+ ~ImmutableObjectHistory() override {}
+
+ bool is_mutable() const override { return false; }
+ bool is_immutable() const override { return true; }
+ const void* immutable_object_ptr() const { return (const void*)m_shared_object.get(); }
+
+ void save(size_t active_snapshot_time, size_t current_time) {
+ assert(m_history.empty() || m_history.back().end() <= active_snapshot_time ||
+ // The snapshot of an immutable object may have already been taken from another mutable object.
+ (m_history.back().begin() <= active_snapshot_time && m_history.back().end() == current_time + 1));
+ if (m_history.empty() || m_history.back().end() < active_snapshot_time)
+ m_history.emplace_back(active_snapshot_time, current_time + 1);
+ else
+ m_history.back().extend_end(current_time + 1);
+ }
+
+ bool has_snapshot(size_t timestamp) {
+ if (m_history.empty())
+ return false;
+ auto it = std::lower_bound(m_history.begin(), m_history.end(), Interval(timestamp, timestamp));
+ if (it == m_history.end() || it->begin() > timestamp) {
+ if (it == m_history.begin())
+ return false;
+ -- it;
+ }
+ return timestamp >= it->begin() && timestamp < it->end();
+ }
+
+ bool is_serialized() const { return m_shared_object.get() == nullptr; }
+ const std::string& serialized_data() const { return m_serialized; }
+ std::shared_ptr<const T>& shared_ptr(StackImpl &stack);
+
+#ifdef SLIC3R_UNDOREDO_DEBUG
+ std::string format() override {
+ std::string out = typeid(T).name();
+ out += this->is_serialized() ?
+ std::string(" len:") + std::to_string(m_serialized.size()) :
+ std::string(" shared_ptr:") + ptr_to_string(m_shared_object.get());
+ for (const Interval &interval : m_history)
+ out += std::string(", <") + std::to_string(interval.begin()) + "," + std::to_string(interval.end()) + ")";
+ return out;
+ }
+#endif /* SLIC3R_UNDOREDO_DEBUG */
+
+#ifndef NDEBUG
+ bool valid() override;
+#endif /* NDEBUG */
+
+private:
+ // Either the source object is held by a shared pointer and the m_serialized field is empty,
+ // or the shared pointer is null and the object is being serialized into m_serialized.
+ std::shared_ptr<const T> m_shared_object;
+ std::string m_serialized;
+};
+
+struct MutableHistoryInterval
+{
+private:
+ struct Data
+ {
+ // Reference counter of this data chunk. We may have used shared_ptr, but the shared_ptr is thread safe
+ // with the associated cost of CPU cache invalidation on refcount change.
+ size_t refcnt;
+ size_t size;
+ char data[1];
+
+ bool matches(const std::string& rhs) { return this->size == rhs.size() && memcmp(this->data, rhs.data(), this->size) == 0; }
+ };
+
+ Interval m_interval;
+ Data *m_data;
+
+public:
+ MutableHistoryInterval(const Interval &interval, const std::string &input_data) : m_interval(interval), m_data(nullptr) {
+ m_data = (Data*)new char[offsetof(Data, data) + input_data.size()];
+ m_data->refcnt = 1;
+ m_data->size = input_data.size();
+ memcpy(m_data->data, input_data.data(), input_data.size());
+ }
+
+ MutableHistoryInterval(const Interval &interval, MutableHistoryInterval &other) : m_interval(interval), m_data(other.m_data) {
+ ++ m_data->refcnt;
+ }
+
+ // as a key for std::lower_bound
+ MutableHistoryInterval(const size_t begin, const size_t end) : m_interval(begin, end), m_data(nullptr) {}
+
+ MutableHistoryInterval(MutableHistoryInterval&& rhs) : m_interval(rhs.m_interval), m_data(rhs.m_data) { rhs.m_data = nullptr; }
+ MutableHistoryInterval& operator=(MutableHistoryInterval&& rhs) { m_interval = rhs.m_interval; m_data = rhs.m_data; rhs.m_data = nullptr; return *this; }
+
+ ~MutableHistoryInterval() {
+ if (m_data != nullptr && -- m_data->refcnt == 0)
+ delete[] (char*)m_data;
+ }
+
+ const Interval& interval() const { return m_interval; }
+ size_t begin() const { return m_interval.begin(); }
+ size_t end() const { return m_interval.end(); }
+ void trim_end(size_t timestamp) { m_interval.trim_end(timestamp); }
+ void extend_end(size_t timestamp) { m_interval.extend_end(timestamp); }
+
+ bool operator<(const MutableHistoryInterval& rhs) const { return m_interval < rhs.m_interval; }
+ bool operator==(const MutableHistoryInterval& rhs) const { return m_interval == rhs.m_interval; }
+
+ const char* data() const { return m_data->data; }
+ size_t size() const { return m_data->size; }
+ size_t refcnt() const { return m_data->refcnt; }
+ bool matches(const std::string& data) { return m_data->matches(data); }
+
+private:
+ MutableHistoryInterval(const MutableHistoryInterval &rhs);
+ MutableHistoryInterval& operator=(const MutableHistoryInterval &rhs);
+};
+
+static inline std::string ptr_to_string(const void* ptr)
+{
+ char buf[64];
+ sprintf(buf, "%p", ptr);
+ return buf;
+}
+
+// Smaller objects (Model, ModelObject, ModelInstance, ModelVolume, DynamicPrintConfig)
+// are mutable and there is not tracking of the changes, therefore a snapshot needs to be
+// taken every time and compared to the previous data at the Undo / Redo stack.
+// The serialized data is stored if it is different from the last value on the stack, otherwise
+// the serialized data is discarded.
+// The history of a single mutable object may not be continuous, as an mutable object may
+// be removed from the scene while being kept at the Copy / Paste stack, therefore an object snapshot
+// with the same serialized object data may be shared by multiple history intervals.
+template<typename T>
+class MutableObjectHistory : public ObjectHistory<MutableHistoryInterval>
+{
+public:
+ ~MutableObjectHistory() override {}
+
+ bool is_mutable() const override { return true; }
+ bool is_immutable() const override { return false; }
+
+ void save(size_t active_snapshot_time, size_t current_time, const std::string &data) {
+ assert(m_history.empty() || m_history.back().end() <= active_snapshot_time);
+ if (m_history.empty() || m_history.back().end() < active_snapshot_time) {
+ if (! m_history.empty() && m_history.back().matches(data))
+ // Share the previous data by reference counting.
+ m_history.emplace_back(Interval(current_time, current_time + 1), m_history.back());
+ else
+ // Allocate new data.
+ m_history.emplace_back(Interval(current_time, current_time + 1), data);
+ } else {
+ assert(! m_history.empty());
+ assert(m_history.back().end() == active_snapshot_time);
+ if (m_history.back().matches(data))
+ // Just extend the last interval using the old data.
+ m_history.back().extend_end(current_time + 1);
+ else
+ // Allocate new data time continuous with the previous data.
+ m_history.emplace_back(Interval(active_snapshot_time, current_time + 1), data);
+ }
+ }
+
+ std::string load(size_t timestamp) const {
+ assert(! m_history.empty());
+ auto it = std::lower_bound(m_history.begin(), m_history.end(), MutableHistoryInterval(timestamp, timestamp));
+ if (it == m_history.end() || it->begin() > timestamp) {
+ assert(it != m_history.begin());
+ -- it;
+ }
+ assert(timestamp >= it->begin() && timestamp < it->end());
+ return std::string(it->data(), it->data() + it->size());
+ }
+
+#ifdef SLIC3R_UNDOREDO_DEBUG
+ std::string format() override {
+ std::string out = typeid(T).name();
+ for (const MutableHistoryInterval &interval : m_history)
+ out += std::string(", ptr:") + ptr_to_string(interval.data()) + " len:" + std::to_string(interval.size()) + " <" + std::to_string(interval.begin()) + "," + std::to_string(interval.end()) + ")";
+ return out;
+ }
+#endif /* SLIC3R_UNDOREDO_DEBUG */
+
+#ifndef NDEBUG
+ bool valid() override;
+#endif /* NDEBUG */
+};
+
+#ifndef NDEBUG
+template<typename T>
+bool ImmutableObjectHistory<T>::valid()
+{
+ // The immutable object content is captured either by a shared object, or by its serialization, but not both.
+ assert(! m_shared_object == ! m_serialized.empty());
+ // Verify that the history intervals are sorted and do not overlap.
+ if (! m_history.empty())
+ for (size_t i = 1; i < m_history.size(); ++ i)
+ assert(m_history[i - 1].strictly_before(m_history[i]));
+ return true;
+}
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+template<typename T>
+bool MutableObjectHistory<T>::valid()
+{
+ // Verify that the history intervals are sorted and do not overlap, and that the data reference counters are correct.
+ if (! m_history.empty()) {
+ std::map<const char*, size_t> refcntrs;
+ assert(m_history.front().data() != nullptr);
+ ++ refcntrs[m_history.front().data()];
+ for (size_t i = 1; i < m_history.size(); ++ i) {
+ assert(m_history[i - 1].interval().strictly_before(m_history[i].interval()));
+ ++ refcntrs[m_history[i].data()];
+ }
+ for (const auto &hi : m_history) {
+ assert(hi.data() != nullptr);
+ assert(refcntrs[hi.data()] == hi.refcnt());
+ }
+ }
+ return true;
+}
+#endif /* NDEBUG */
+
+class StackImpl
+{
+public:
+ // Stack needs to be initialized. An empty stack is not valid, there must be a "New Project" status stored at the beginning.
+ StackImpl() : m_active_snapshot_time(0), m_current_time(0) {}
+
+ // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time.
+ void take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos);
+ void load_snapshot(size_t timestamp, Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos);
+
+ bool has_undo_snapshot() const;
+ bool has_redo_snapshot() const;
+ bool undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, size_t jump_to_time);
+ bool redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, size_t jump_to_time);
+
+ // Snapshot history (names with timestamps).
+ const std::vector<Snapshot>& snapshots() const { return m_snapshots; }
+ // Timestamp of the active snapshot.
+ size_t active_snapshot_time() const { return m_active_snapshot_time; }
+
+ const Selection& selection_deserialized() const { return m_selection; }
+
+//protected:
+ template<typename T, typename T_AS> ObjectID save_mutable_object(const T &object);
+ template<typename T> ObjectID save_immutable_object(std::shared_ptr<const T> &object);
+ template<typename T> T* load_mutable_object(const Slic3r::ObjectID id);
+ template<typename T> std::shared_ptr<const T> load_immutable_object(const Slic3r::ObjectID id);
+ template<typename T, typename T_AS> void load_mutable_object(const Slic3r::ObjectID id, T &target);
+
+#ifdef SLIC3R_UNDOREDO_DEBUG
+ std::string format() const {
+ std::string out = "Objects\n";
+ for (const std::pair<const ObjectID, std::unique_ptr<ObjectHistoryBase>> &kvp : m_objects)
+ out += std::string("ObjectID:") + std::to_string(kvp.first.id) + " " + kvp.second->format() + "\n";
+ out += "Snapshots\n";
+ for (const Snapshot &snapshot : m_snapshots) {
+ if (snapshot.timestamp == m_active_snapshot_time)
+ out += ">>> ";
+ out += std::string("Name: \"") + snapshot.name + "\", timestamp: " + std::to_string(snapshot.timestamp) +
+ ", Model ID:" + ((snapshot.model_id == 0) ? "Invalid" : std::to_string(snapshot.model_id)) + "\n";
+ }
+ if (m_active_snapshot_time > m_snapshots.back().timestamp)
+ out += ">>>\n";
+ out += "Current time: " + std::to_string(m_current_time) + "\n";
+ return out;
+ }
+ void print() const {
+ std::cout << "Undo / Redo stack" << std::endl;
+ std::cout << this->format() << std::endl;
+ }
+#endif /* SLIC3R_UNDOREDO_DEBUG */
+
+
+#ifndef NDEBUG
+ bool valid() const {
+ assert(! m_snapshots.empty());
+ auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time));
+ assert(it != m_snapshots.begin() && it != m_snapshots.end() && it->timestamp == m_active_snapshot_time);
+ assert(m_active_snapshot_time < m_snapshots.back().timestamp || m_snapshots.back().is_topmost());
+ for (auto it = m_objects.begin(); it != m_objects.end(); ++ it)
+ assert(it->second->valid());
+ return true;
+ }
+#endif /* NDEBUG */
+
+private:
+ template<typename T> ObjectID immutable_object_id(const std::shared_ptr<const T> &ptr) {
+ return this->immutable_object_id_impl((const void*)ptr.get());
+ }
+ ObjectID immutable_object_id_impl(const void *ptr) {
+ auto it = m_shared_ptr_to_object_id.find(ptr);
+ if (it == m_shared_ptr_to_object_id.end()) {
+ // Allocate a new temporary ObjectID for this shared pointer.
+ ObjectBase object_with_id;
+ it = m_shared_ptr_to_object_id.insert(it, std::make_pair(ptr, object_with_id.id()));
+ }
+ return it->second;
+ }
+ void collect_garbage();
+
+ // Each individual object (Model, ModelObject, ModelInstance, ModelVolume, Selection, TriangleMesh)
+ // is stored with its own history, referenced by the ObjectID. Immutable objects do not provide
+ // their own IDs, therefore there are temporary IDs generated for them and stored to m_shared_ptr_to_object_id.
+ std::map<ObjectID, std::unique_ptr<ObjectHistoryBase>> m_objects;
+ std::map<const void*, ObjectID> m_shared_ptr_to_object_id;
+ // Snapshot history (names with timestamps).
+ std::vector<Snapshot> m_snapshots;
+ // Timestamp of the active snapshot.
+ size_t m_active_snapshot_time;
+ // Logical time counter. m_current_time is being incremented with each snapshot taken.
+ size_t m_current_time;
+ // Last selection serialized or deserialized.
+ Selection m_selection;
+};
+
+using InputArchive = cereal::UserDataAdapter<StackImpl, cereal::BinaryInputArchive>;
+using OutputArchive = cereal::UserDataAdapter<StackImpl, cereal::BinaryOutputArchive>;
+
+} // namespace UndoRedo
+
+class Model;
+class ModelObject;
+class ModelVolume;
+class ModelInstance;
+class ModelMaterial;
+class ModelConfig;
+class DynamicPrintConfig;
+class TriangleMesh;
+
+} // namespace Slic3r
+
+namespace cereal
+{
+ // Let cereal know that there are load / save non-member functions declared for ModelObject*, ignore serialization of pointers triggering
+ // static assert, that cereal does not support serialization of raw pointers.
+ template <class Archive> struct specialize<Archive, Slic3r::Model*, cereal::specialization::non_member_load_save> {};
+ template <class Archive> struct specialize<Archive, Slic3r::ModelObject*, cereal::specialization::non_member_load_save> {};
+ template <class Archive> struct specialize<Archive, Slic3r::ModelVolume*, cereal::specialization::non_member_load_save> {};
+ template <class Archive> struct specialize<Archive, Slic3r::ModelInstance*, cereal::specialization::non_member_load_save> {};
+ template <class Archive> struct specialize<Archive, Slic3r::ModelMaterial*, cereal::specialization::non_member_load_save> {};
+ template <class Archive> struct specialize<Archive, Slic3r::ModelConfig, cereal::specialization::non_member_load_save> {};
+ template <class Archive> struct specialize<Archive, std::shared_ptr<Slic3r::TriangleMesh>, cereal::specialization::non_member_load_save> {};
+
+ // Store ObjectBase derived class onto the Undo / Redo stack as a separate object,
+ // store just the ObjectID to this stream.
+ template <class T> void save(BinaryOutputArchive& ar, T* const& ptr)
+ {
+ ar(cereal::get_user_data<Slic3r::UndoRedo::StackImpl>(ar).save_mutable_object<T, T>(*ptr));
+ }
+
+ // Load ObjectBase derived class from the Undo / Redo stack as a separate object
+ // based on the ObjectID loaded from this stream.
+ template <class T> void load(BinaryInputArchive& ar, T*& ptr)
+ {
+ Slic3r::UndoRedo::StackImpl& stack = cereal::get_user_data<Slic3r::UndoRedo::StackImpl>(ar);
+ size_t id;
+ ar(id);
+ ptr = stack.load_mutable_object<T>(Slic3r::ObjectID(id));
+ }
+
+ // Store ObjectBase derived class onto the Undo / Redo stack as a separate object,
+ // store just the ObjectID to this stream.
+ template <class T> void save(BinaryOutputArchive &ar, const std::unique_ptr<T> &ptr)
+ {
+ ar(cereal::get_user_data<Slic3r::UndoRedo::StackImpl>(ar).save_mutable_object<T>(*ptr.get()));
+ }
+
+ // Load ObjectBase derived class from the Undo / Redo stack as a separate object
+ // based on the ObjectID loaded from this stream.
+ template <class T> void load(BinaryInputArchive &ar, std::unique_ptr<T> &ptr)
+ {
+ Slic3r::UndoRedo::StackImpl& stack = cereal::get_user_data<Slic3r::UndoRedo::StackImpl>(ar);
+ size_t id;
+ ar(id);
+ ptr.reset(stack.load_mutable_object<T>(Slic3r::ObjectID(id)));
+ }
+
+ // Store ObjectBase derived class onto the Undo / Redo stack as a separate object,
+ // store just the ObjectID to this stream.
+ void save(BinaryOutputArchive& ar, const Slic3r::ModelConfig &cfg)
+ {
+ ar(cereal::get_user_data<Slic3r::UndoRedo::StackImpl>(ar).save_mutable_object<Slic3r::ModelConfig, Slic3r::DynamicPrintConfig>(cfg));
+ }
+
+ // Load ObjectBase derived class from the Undo / Redo stack as a separate object
+ // based on the ObjectID loaded from this stream.
+ void load(BinaryInputArchive& ar, Slic3r::ModelConfig &cfg)
+ {
+ Slic3r::UndoRedo::StackImpl& stack = cereal::get_user_data<Slic3r::UndoRedo::StackImpl>(ar);
+ size_t id;
+ ar(id);
+ stack.load_mutable_object<Slic3r::ModelConfig, Slic3r::DynamicPrintConfig>(Slic3r::ObjectID(id), cfg);
+ }
+
+ // Store ObjectBase derived class onto the Undo / Redo stack as a separate object,
+ // store just the ObjectID to this stream.
+ template <class T> void save(BinaryOutputArchive &ar, const std::shared_ptr<const T> &ptr)
+ {
+ ar(cereal::get_user_data<Slic3r::UndoRedo::StackImpl>(ar).save_immutable_object<T>(const_cast<std::shared_ptr<const T>&>(ptr)));
+ }
+
+ // Load ObjectBase derived class from the Undo / Redo stack as a separate object
+ // based on the ObjectID loaded from this stream.
+ template <class T> void load(BinaryInputArchive &ar, std::shared_ptr<const T> &ptr)
+ {
+ Slic3r::UndoRedo::StackImpl &stack = cereal::get_user_data<Slic3r::UndoRedo::StackImpl>(ar);
+ size_t id;
+ ar(id);
+ ptr = stack.load_immutable_object<T>(Slic3r::ObjectID(id));
+ }
+}
+
+#include <libslic3r/Model.hpp>
+#include <libslic3r/TriangleMesh.hpp>
+#include <slic3r/GUI/Selection.hpp>
+#include <slic3r/GUI/Gizmos/GLGizmosManager.hpp>
+
+namespace Slic3r {
+namespace UndoRedo {
+
+template<typename T> std::shared_ptr<const T>& ImmutableObjectHistory<T>::shared_ptr(StackImpl &stack)
+{
+ if (m_shared_object.get() == nullptr && ! this->m_serialized.empty()) {
+ // Deserialize the object.
+ std::istringstream iss(m_serialized);
+ {
+ Slic3r::UndoRedo::InputArchive archive(stack, iss);
+ typedef typename std::remove_const<T>::type Type;
+ std::unique_ptr<Type> mesh(new Type());
+ archive(*mesh.get());
+ m_shared_object = std::move(mesh);
+ }
+ }
+ return m_shared_object;
+}
+
+template<typename T, typename T_AS> ObjectID StackImpl::save_mutable_object(const T &object)
+{
+ // First find or allocate a history stack for the ObjectID of this object instance.
+ auto it_object_history = m_objects.find(object.id());
+ if (it_object_history == m_objects.end())
+ it_object_history = m_objects.insert(it_object_history, std::make_pair(object.id(), std::unique_ptr<MutableObjectHistory<T>>(new MutableObjectHistory<T>())));
+ auto *object_history = static_cast<MutableObjectHistory<T>*>(it_object_history->second.get());
+ // Then serialize the object into a string.
+ std::ostringstream oss;
+ {
+ Slic3r::UndoRedo::OutputArchive archive(*this, oss);
+ archive(static_cast<const T_AS&>(object));
+ }
+ object_history->save(m_active_snapshot_time, m_current_time, oss.str());
+ return object.id();
+}
+
+template<typename T> ObjectID StackImpl::save_immutable_object(std::shared_ptr<const T> &object)
+{
+ // First allocate a temporary ObjectID for this pointer.
+ ObjectID object_id = this->immutable_object_id(object);
+ // and find or allocate a history stack for the ObjectID associated to this shared_ptr.
+ auto it_object_history = m_objects.find(object_id);
+ if (it_object_history == m_objects.end())
+ it_object_history = m_objects.emplace_hint(it_object_history, object_id, std::unique_ptr<ImmutableObjectHistory<T>>(new ImmutableObjectHistory<T>(object)));
+ // Then save the interval.
+ static_cast<ImmutableObjectHistory<T>*>(it_object_history->second.get())->save(m_active_snapshot_time, m_current_time);
+ return object_id;
+}
+
+template<typename T> T* StackImpl::load_mutable_object(const Slic3r::ObjectID id)
+{
+ T *target = new T();
+ this->load_mutable_object<T, T>(id, *target);
+ return target;
+}
+
+template<typename T> std::shared_ptr<const T> StackImpl::load_immutable_object(const Slic3r::ObjectID id)
+{
+ // First find a history stack for the ObjectID of this object instance.
+ auto it_object_history = m_objects.find(id);
+ assert(it_object_history != m_objects.end());
+ auto *object_history = static_cast<ImmutableObjectHistory<T>*>(it_object_history->second.get());
+ assert(object_history->has_snapshot(m_active_snapshot_time));
+ return object_history->shared_ptr(*this);
+}
+
+template<typename T, typename T_AS> void StackImpl::load_mutable_object(const Slic3r::ObjectID id, T &target)
+{
+ // First find a history stack for the ObjectID of this object instance.
+ auto it_object_history = m_objects.find(id);
+ assert(it_object_history != m_objects.end());
+ auto *object_history = static_cast<const MutableObjectHistory<T>*>(it_object_history->second.get());
+ // Then get the data associated with the object history and m_active_snapshot_time.
+ std::istringstream iss(object_history->load(m_active_snapshot_time));
+ Slic3r::UndoRedo::InputArchive archive(*this, iss);
+ target.m_id = id;
+ archive(static_cast<T_AS&>(target));
+}
+
+// Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time.
+void StackImpl::take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos)
+{
+ // Release old snapshot data.
+ assert(m_active_snapshot_time <= m_current_time);
+ for (auto &kvp : m_objects)
+ kvp.second->release_after_timestamp(m_active_snapshot_time);
+ {
+ auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time));
+ m_snapshots.erase(it, m_snapshots.end());
+ }
+ // Take new snapshots.
+ this->save_mutable_object<Slic3r::Model, Slic3r::Model>(model);
+ m_selection.volumes_and_instances.clear();
+ m_selection.volumes_and_instances.reserve(selection.get_volume_idxs().size());
+ m_selection.mode = selection.get_mode();
+ for (unsigned int volume_idx : selection.get_volume_idxs())
+ m_selection.volumes_and_instances.emplace_back(selection.get_volume(volume_idx)->geometry_id);
+ this->save_mutable_object<Selection, Selection>(m_selection);
+ this->save_mutable_object<Slic3r::GUI::GLGizmosManager, Slic3r::GUI::GLGizmosManager>(gizmos);
+ // Save the snapshot info.
+ m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id);
+ m_active_snapshot_time = m_current_time;
+ // Save snapshot info of the last "current" aka "top most" state, that is only being serialized
+ // if undoing an action. Such a snapshot has an invalid Model ID assigned if it was not taken yet.
+ m_snapshots.emplace_back(topmost_snapsnot_name, m_active_snapshot_time, 0);
+ // Release empty objects from the history.
+ this->collect_garbage();
+ assert(this->valid());
+#ifdef SLIC3R_UNDOREDO_DEBUG
+ std::cout << "After snapshot" << std::endl;
+ this->print();
+#endif /* SLIC3R_UNDOREDO_DEBUG */
+}
+
+void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos)
+{
+ // Find the snapshot by time. It must exist.
+ const auto it_snapshot = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(timestamp));
+ if (it_snapshot == m_snapshots.end() || it_snapshot->timestamp != timestamp)
+ throw std::runtime_error((boost::format("Snapshot with timestamp %1% does not exist") % timestamp).str());
+
+ m_active_snapshot_time = timestamp;
+ model.clear_objects();
+ model.clear_materials();
+ this->load_mutable_object<Slic3r::Model, Slic3r::Model>(ObjectID(it_snapshot->model_id), model);
+ model.update_links_bottom_up_recursive();
+ m_selection.volumes_and_instances.clear();
+ this->load_mutable_object<Selection, Selection>(m_selection.id(), m_selection);
+ gizmos.reset_all_states();
+ this->load_mutable_object<Slic3r::GUI::GLGizmosManager, Slic3r::GUI::GLGizmosManager>(gizmos.id(), gizmos);
+ // Sort the volumes so that we may use binary search.
+ std::sort(m_selection.volumes_and_instances.begin(), m_selection.volumes_and_instances.end());
+ this->m_active_snapshot_time = timestamp;
+ assert(this->valid());
+}
+
+bool StackImpl::has_undo_snapshot() const
+{
+ assert(this->valid());
+ auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time));
+ return -- it != m_snapshots.begin();
+}
+
+bool StackImpl::has_redo_snapshot() const
+{
+ assert(this->valid());
+ auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time));
+ return ++ it != m_snapshots.end();
+}
+
+bool StackImpl::undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load)
+{
+ assert(this->valid());
+ if (time_to_load == SIZE_MAX) {
+ auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time));
+ if (-- it_current == m_snapshots.begin())
+ return false;
+ time_to_load = it_current->timestamp;
+ }
+ assert(time_to_load < m_active_snapshot_time);
+ assert(std::binary_search(m_snapshots.begin(), m_snapshots.end(), Snapshot(time_to_load)));
+ if (m_active_snapshot_time == m_snapshots.back().timestamp && ! m_snapshots.back().is_topmost_captured()) {
+ // The current state is temporary. The current state needs to be captured to be redoable.
+ this->take_snapshot(topmost_snapsnot_name, model, selection, gizmos);
+ // The line above entered another topmost_snapshot_name.
+ assert(m_snapshots.back().is_topmost());
+ assert(! m_snapshots.back().is_topmost_captured());
+ // Pop it back, it is not needed as there is now a captured topmost state.
+ m_snapshots.pop_back();
+ // current_time was extended, but it should not cause any harm. Resetting it back may complicate the logic unnecessarily.
+ //-- m_current_time;
+ assert(m_snapshots.back().is_topmost());
+ assert(m_snapshots.back().is_topmost_captured());
+ }
+ this->load_snapshot(time_to_load, model, gizmos);
+#ifdef SLIC3R_UNDOREDO_DEBUG
+ std::cout << "After undo" << std::endl;
+ this->print();
+#endif /* SLIC3R_UNDOREDO_DEBUG */
+ return true;
+}
+
+bool StackImpl::redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load)
+{
+ assert(this->valid());
+ if (time_to_load == SIZE_MAX) {
+ auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time));
+ if (++ it_current == m_snapshots.end())
+ return false;
+ time_to_load = it_current->timestamp;
+ }
+ assert(time_to_load > m_active_snapshot_time);
+ assert(std::binary_search(m_snapshots.begin(), m_snapshots.end(), Snapshot(time_to_load)));
+ this->load_snapshot(time_to_load, model, gizmos);
+#ifdef SLIC3R_UNDOREDO_DEBUG
+ std::cout << "After redo" << std::endl;
+ this->print();
+#endif /* SLIC3R_UNDOREDO_DEBUG */
+ return true;
+}
+
+void StackImpl::collect_garbage()
+{
+ // Purge objects with empty histories.
+ for (auto it = m_objects.begin(); it != m_objects.end();) {
+ if (it->second->empty()) {
+ if (it->second->immutable_object_ptr() != nullptr)
+ // Release the immutable object from the ptr to ObjectID map.
+ m_shared_ptr_to_object_id.erase(it->second->immutable_object_ptr());
+ it = m_objects.erase(it);
+ } else
+ ++ it;
+ }
+}
+
+// Wrappers of the private implementation.
+Stack::Stack() : pimpl(new StackImpl()) {}
+Stack::~Stack() {}
+void Stack::take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos) { pimpl->take_snapshot(snapshot_name, model, selection, gizmos); }
+bool Stack::has_undo_snapshot() const { return pimpl->has_undo_snapshot(); }
+bool Stack::has_redo_snapshot() const { return pimpl->has_redo_snapshot(); }
+bool Stack::undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load) { return pimpl->undo(model, selection, gizmos, time_to_load); }
+bool Stack::redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load) { return pimpl->redo(model, gizmos, time_to_load); }
+const Selection& Stack::selection_deserialized() const { return pimpl->selection_deserialized(); }
+
+const std::vector<Snapshot>& Stack::snapshots() const { return pimpl->snapshots(); }
+size_t Stack::active_snapshot_time() const { return pimpl->active_snapshot_time(); }
+
+} // namespace UndoRedo
+} // namespace Slic3r
+
+
+//FIXME we should have unit tests for testing serialization of basic types as DynamicPrintConfig.
+#if 0
+#include "libslic3r/Config.hpp"
+#include "libslic3r/PrintConfig.hpp"
+namespace Slic3r {
+ bool test_dynamic_print_config_serialization() {
+ FullPrintConfig full_print_config;
+ DynamicPrintConfig cfg;
+ cfg.apply(full_print_config, false);
+
+ std::string serialized;
+ try {
+ std::ostringstream ss;
+ cereal::BinaryOutputArchive oarchive(ss);
+ oarchive(cfg);
+ serialized = ss.str();
+ } catch (std::runtime_error e) {
+ e.what();
+ }
+
+ DynamicPrintConfig cfg2;
+ try {
+ std::stringstream ss(serialized);
+ cereal::BinaryInputArchive iarchive(ss);
+ iarchive(cfg2);
+ } catch (std::runtime_error e) {
+ e.what();
+ }
+
+ if (cfg == cfg2) {
+ printf("Yes!\n");
+ return true;
+ }
+ printf("No!\n");
+ return false;
+ }
+} // namespace Slic3r
+#endif
diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp
new file mode 100644
index 000000000..236970b68
--- /dev/null
+++ b/src/slic3r/Utils/UndoRedo.hpp
@@ -0,0 +1,95 @@
+#ifndef slic3r_Utils_UndoRedo_hpp_
+#define slic3r_Utils_UndoRedo_hpp_
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+#include <cassert>
+
+#include <libslic3r/ObjectID.hpp>
+
+namespace Slic3r {
+
+class Model;
+
+namespace GUI {
+ class Selection;
+ class GLGizmosManager;
+} // namespace GUI
+
+namespace UndoRedo {
+
+struct Snapshot
+{
+ Snapshot(size_t timestamp) : timestamp(timestamp) {}
+ Snapshot(const std::string &name, size_t timestamp, size_t model_id) : name(name), timestamp(timestamp), model_id(model_id) {}
+
+ std::string name;
+ size_t timestamp;
+ size_t model_id;
+
+ bool operator< (const Snapshot &rhs) const { return this->timestamp < rhs.timestamp; }
+ bool operator==(const Snapshot &rhs) const { return this->timestamp == rhs.timestamp; }
+
+ // The topmost snapshot represents the current state when going forward.
+ bool is_topmost() const;
+ // The topmost snapshot is not being serialized to the Undo / Redo stack until going back in time,
+ // when the top most state is being serialized, so we can redo back to the top most state.
+ bool is_topmost_captured() const { assert(this->is_topmost()); return model_id > 0; }
+};
+
+// Excerpt of Slic3r::GUI::Selection for serialization onto the Undo / Redo stack.
+struct Selection : public Slic3r::ObjectBase {
+ unsigned char mode;
+ std::vector<std::pair<size_t, size_t>> volumes_and_instances;
+ template<class Archive> void serialize(Archive &ar) { ar(mode, volumes_and_instances); }
+};
+
+class StackImpl;
+
+class Stack
+{
+public:
+ // Stack needs to be initialized. An empty stack is not valid, there must be a "New Project" status stored at the beginning.
+ // The first "New Project" snapshot shall not be removed.
+ Stack();
+ ~Stack();
+
+ // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time.
+ void take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos);
+
+ // To be queried to enable / disable the Undo / Redo buttons at the UI.
+ bool has_undo_snapshot() const;
+ bool has_redo_snapshot() const;
+
+ // Roll back the time. If time_to_load is SIZE_MAX, the previous snapshot is activated.
+ // Undoing an action may need to take a snapshot of the current application state, so that redo to the current state is possible.
+ bool undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load = SIZE_MAX);
+
+ // Jump forward in time. If time_to_load is SIZE_MAX, the next snapshot is activated.
+ bool redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load = SIZE_MAX);
+
+ // Snapshot history (names with timestamps).
+ // Each snapshot indicates start of an interval in which this operation is performed.
+ // There is one additional snapshot taken at the very end, which indicates the current unnamed state.
+
+ const std::vector<Snapshot>& snapshots() const;
+ // Timestamp of the active snapshot. One of the snapshots of this->snapshots() shall have Snapshot::timestamp equal to this->active_snapshot_time().
+ // The snapshot time indicates start of an operation, which is finished at the time of the following snapshot, therefore
+ // the active snapshot is the successive snapshot. The same logic applies to the time_to_load parameter of undo() and redo() operations.
+ size_t active_snapshot_time() const;
+
+ // After load_snapshot() / undo() / redo() the selection is deserialized into a list of ObjectIDs, which needs to be converted
+ // into the list of GLVolume pointers once the 3D scene is updated.
+ const Selection& selection_deserialized() const;
+
+private:
+ friend class StackImpl;
+ std::unique_ptr<StackImpl> pimpl;
+};
+
+}; // namespace UndoRedo
+}; // namespace Slic3r
+
+#endif /* slic3r_Utils_UndoRedo_hpp_ */
diff --git a/t/combineinfill.t b/t/combineinfill.t
index 8aa0ff5e3..282bf467a 100644
--- a/t/combineinfill.t
+++ b/t/combineinfill.t
@@ -89,7 +89,7 @@ plan tests => 8;
# we disable combination after infill has been generated
$config->set('infill_every_layers', 1);
- $print->apply_config_perl_tests_only($config);
+ $print->apply($print->print->model->clone, $config);
$print->process;
ok !(defined first { @{$_->get_region(0)->fill_surfaces} == 0 }
diff --git a/t/multi.t b/t/multi.t
index 75ce0c286..8e7225bec 100644
--- a/t/multi.t
+++ b/t/multi.t
@@ -25,7 +25,9 @@ use Slic3r::Test;
$config->set('extruder_offset', [ [0,0], [20,0], [0,20], [20,20] ]);
$config->set('temperature', [200, 180, 170, 160]);
$config->set('first_layer_temperature', [206, 186, 166, 156]);
- $config->set('toolchange_gcode', ';toolchange'); # test that it doesn't crash when this is supplied
+ $config->set('toolchange_gcode', 'T[next_extruder] ;toolchange'); # test that it doesn't crash when this is supplied
+ # Since July 2019, PrusaSlicer only emits automatic Tn command in case that the toolchange_gcode is empty
+ # The "T[next_extruder]" is therefore needed in this test.
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
diff --git a/t/print.t b/t/print.t
index be2db3431..2144e80c1 100644
--- a/t/print.t
+++ b/t/print.t
@@ -34,25 +34,30 @@ use Slic3r::Test;
{
# this represents the aggregate config from presets
my $config = Slic3r::Config::new_from_defaults;
+ # Define 4 extruders.
+ $config->set('nozzle_diameter', [0.4, 0.4, 0.4, 0.4]);
# user adds one object to the plater
my $print = Slic3r::Test::init_print(my $model = Slic3r::Test::model('20mm_cube'), config => $config);
# user sets a per-region option
- $print->print->objects->[0]->model_object->config->set('fill_density', 100);
- $print->print->reload_object(0);
+ my $model2 = $model->clone;
+ $model2->get_object(0)->config->set('fill_density', 100);
+ $print->apply($model2, $config);
+
is $print->print->regions->[0]->config->fill_density, 100, 'region config inherits model object config';
# user exports G-code, thus the default config is reapplied
- $print->print->apply_config_perl_tests_only($config);
-
- is $print->print->regions->[0]->config->fill_density, 100, 'apply_config() does not override per-object settings';
+ $model2->get_object(0)->config->erase('fill_density');
+ $print->apply($model2, $config);
+
+ is $print->print->regions->[0]->config->fill_density, 20, 'region config is resetted';
# user assigns object extruders
- $print->print->objects->[0]->model_object->config->set('extruder', 3);
- $print->print->objects->[0]->model_object->config->set('perimeter_extruder', 2);
- $print->print->reload_object(0);
-
+ $model2->get_object(0)->config->set('extruder', 3);
+ $model2->get_object(0)->config->set('perimeter_extruder', 2);
+ $print->apply($model2, $config);
+
is $print->print->regions->[0]->config->infill_extruder, 3, 'extruder setting is correctly expanded';
is $print->print->regions->[0]->config->perimeter_extruder, 2, 'extruder setting does not override explicitely specified extruders';
}
diff --git a/t/skirt_brim.t b/t/skirt_brim.t
index b05435784..beb3c94c5 100644
--- a/t/skirt_brim.t
+++ b/t/skirt_brim.t
@@ -91,6 +91,8 @@ use Slic3r::Test;
{
my $config = Slic3r::Config::new_from_defaults;
+ # Define 4 extruders.
+ $config->set('nozzle_diameter', [0.4, 0.4, 0.4, 0.4]);
$config->set('layer_height', 0.4);
$config->set('first_layer_height', 0.4);
$config->set('skirts', 1);
@@ -106,7 +108,7 @@ use Slic3r::Test;
# we enable support material after skirt has been generated
$config->set('support_material', 1);
- $print->apply_config_perl_tests_only($config);
+ $print->apply($print->print->model->clone, $config);
my $skirt_length = 0;
my @extrusion_points = ();
diff --git a/xs/t/15_config.t b/xs/t/15_config.t
index 397f7e479..1f9fc939b 100644
--- a/xs/t/15_config.t
+++ b/xs/t/15_config.t
@@ -9,19 +9,19 @@ use Test::More tests => 147;
foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintConfig) {
$config->set('layer_height', 0.3);
ok abs($config->get('layer_height') - 0.3) < 1e-4, 'set/get float';
- is $config->serialize('layer_height'), '0.3', 'serialize float';
+ is $config->opt_serialize('layer_height'), '0.3', 'serialize float';
$config->set('perimeters', 2);
is $config->get('perimeters'), 2, 'set/get int';
- is $config->serialize('perimeters'), '2', 'serialize int';
+ is $config->opt_serialize('perimeters'), '2', 'serialize int';
$config->set('extrusion_axis', 'A');
is $config->get('extrusion_axis'), 'A', 'set/get string';
- is $config->serialize('extrusion_axis'), 'A', 'serialize string';
+ is $config->opt_serialize('extrusion_axis'), 'A', 'serialize string';
$config->set('notes', "foo\nbar");
is $config->get('notes'), "foo\nbar", 'set/get string with newline';
- is $config->serialize('notes'), 'foo\nbar', 'serialize string with newline';
+ is $config->opt_serialize('notes'), 'foo\nbar', 'serialize string with newline';
$config->set_deserialize('notes', 'bar\nbaz');
is $config->get('notes'), "bar\nbaz", 'deserialize string with newline';
@@ -59,7 +59,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo
)
{
$config->set('filament_notes', $test_data->{values});
- is $config->serialize('filament_notes'), $test_data->{serialized}, 'serialize multi-string value ' . $test_data->{name};
+ is $config->opt_serialize('filament_notes'), $test_data->{serialized}, 'serialize multi-string value ' . $test_data->{name};
$config->set_deserialize('filament_notes', '');
is_deeply $config->get('filament_notes'), [], 'deserialize multi-string value - empty ' . $test_data->{name};
$config->set_deserialize('filament_notes', $test_data->{serialized});
@@ -68,12 +68,12 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo
$config->set('first_layer_height', 0.3);
ok abs($config->get('first_layer_height') - 0.3) < 1e-4, 'set/get absolute floatOrPercent';
- is $config->serialize('first_layer_height'), '0.3', 'serialize absolute floatOrPercent';
+ is $config->opt_serialize('first_layer_height'), '0.3', 'serialize absolute floatOrPercent';
$config->set('first_layer_height', '50%');
$config->get_abs_value('first_layer_height');
ok abs($config->get_abs_value('first_layer_height') - 0.15) < 1e-4, 'set/get relative floatOrPercent';
- is $config->serialize('first_layer_height'), '50%', 'serialize relative floatOrPercent';
+ is $config->opt_serialize('first_layer_height'), '50%', 'serialize relative floatOrPercent';
# Uh-oh, we have no point option to test at the moment
#ok $config->set('print_center', [50,80]), 'valid point coordinates';
@@ -86,11 +86,10 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo
$config->set('use_relative_e_distances', 1);
is $config->get('use_relative_e_distances'), 1, 'set/get bool';
- is $config->serialize('use_relative_e_distances'), '1', 'serialize bool';
-
+ is $config->opt_serialize('use_relative_e_distances'), '1', 'serialize bool';
$config->set('gcode_flavor', 'teacup');
is $config->get('gcode_flavor'), 'teacup', 'set/get enum';
- is $config->serialize('gcode_flavor'), 'teacup', 'serialize enum';
+ is $config->opt_serialize('gcode_flavor'), 'teacup', 'serialize enum';
$config->set_deserialize('gcode_flavor', 'mach3');
is $config->get('gcode_flavor'), 'mach3', 'deserialize enum (gcode_flavor)';
$config->set_deserialize('gcode_flavor', 'machinekit');
@@ -106,7 +105,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo
is_deeply [ map $_->pp, @{$config->get('extruder_offset')} ], [[10,20],[30,45]], 'set/get points';
$config->set('extruder_offset', [Slic3r::Pointf->new(10,20),Slic3r::Pointf->new(30,45)]);
is_deeply [ map $_->pp, @{$config->get('extruder_offset')} ], [[10,20],[30,45]], 'set/get points';
- is $config->serialize('extruder_offset'), '10x20,30x45', 'serialize points';
+ is $config->opt_serialize('extruder_offset'), '10x20,30x45', 'serialize points';
$config->set_deserialize('extruder_offset', '20x10');
is_deeply [ map $_->pp, @{$config->get('extruder_offset')} ], [[20,10]], 'deserialize points';
$config->set_deserialize('extruder_offset', '0x0');
@@ -120,7 +119,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo
# truncate ->get() to first decimal digit
$config->set('nozzle_diameter', [0.2,3]);
is_deeply [ map int($_*10)/10, @{$config->get('nozzle_diameter')} ], [0.2,3], 'set/get floats';
- is $config->serialize('nozzle_diameter'), '0.2,3', 'serialize floats';
+ is $config->opt_serialize('nozzle_diameter'), '0.2,3', 'serialize floats';
$config->set_deserialize('nozzle_diameter', '0.1,0.4');
is_deeply [ map int($_*10)/10, @{$config->get('nozzle_diameter')} ], [0.1,0.4], 'deserialize floats';
$config->set_deserialize('nozzle_diameter', '3');
@@ -133,7 +132,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo
$config->set('temperature', [180,210]);
is_deeply $config->get('temperature'), [180,210], 'set/get ints';
- is $config->serialize('temperature'), '180,210', 'serialize ints';
+ is $config->opt_serialize('temperature'), '180,210', 'serialize ints';
$config->set_deserialize('temperature', '195,220');
is_deeply $config->get('temperature'), [195,220], 'deserialize ints';
{
@@ -147,7 +146,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo
is $config->get_at('wipe', 0), 1, 'get_at bools';
is $config->get_at('wipe', 1), 0, 'get_at bools';
is $config->get_at('wipe', 9), 1, 'get_at bools';
- is $config->serialize('wipe'), '1,0', 'serialize bools';
+ is $config->opt_serialize('wipe'), '1,0', 'serialize bools';
$config->set_deserialize('wipe', '0,1,1');
is_deeply $config->get('wipe'), [0,1,1], 'deserialize bools';
$config->set_deserialize('wipe', '');
@@ -162,7 +161,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo
$config->set('post_process', ['foo','bar']);
is_deeply $config->get('post_process'), ['foo','bar'], 'set/get strings';
- is $config->serialize('post_process'), 'foo;bar', 'serialize strings';
+ is $config->opt_serialize('post_process'), 'foo;bar', 'serialize strings';
$config->set_deserialize('post_process', 'bar;baz');
is_deeply $config->get('post_process'), ['bar','baz'], 'deserialize strings';
{
diff --git a/xs/t/19_model.t b/xs/t/19_model.t
index d6f6d97a1..48a000e46 100644
--- a/xs/t/19_model.t
+++ b/xs/t/19_model.t
@@ -4,7 +4,7 @@ use strict;
use warnings;
use Slic3r::XS;
-use Test::More tests => 4;
+use Test::More tests => 3;
{
my $model = Slic3r::Model->new;
@@ -14,9 +14,9 @@ use Test::More tests => 4;
$object->origin_translation->translate(10,0,0);
is_deeply \@{$object->origin_translation}, [10,0,0], 'origin_translation is modified by ref';
- my $lhr = [ [ 5, 10, 0.1 ] ];
- $object->set_layer_height_ranges($lhr);
- is_deeply $object->layer_height_ranges, $lhr, 'layer_height_ranges roundtrip';
+# my $lhr = [ [ 5, 10, 0.1 ] ];
+# $object->set_layer_height_ranges($lhr);
+# is_deeply $object->layer_height_ranges, $lhr, 'layer_height_ranges roundtrip';
}
__END__
diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp
index 4d48a2c6f..f5e6ffb05 100644
--- a/xs/xsp/Config.xsp
+++ b/xs/xsp/Config.xsp
@@ -33,7 +33,7 @@
%code{% RETVAL = ConfigBase__set_deserialize(THIS, opt_key, str); %};
void set_ifndef(t_config_option_key opt_key, SV* value, bool deserialize = false)
%code{% ConfigBase__set_ifndef(THIS, opt_key, value, deserialize); %};
- std::string serialize(t_config_option_key opt_key);
+ std::string opt_serialize(t_config_option_key opt_key);
double get_abs_value(t_config_option_key opt_key);
%name{get_abs_value_over}
double get_abs_value(t_config_option_key opt_key, double ratio_over);
@@ -49,7 +49,7 @@
void erase(t_config_option_key opt_key);
void normalize();
%name{setenv} void setenv_();
- double min_object_distance() %code{% RETVAL = PrintConfig::min_object_distance(THIS); %};
+ double min_object_distance() %code{% PrintConfig cfg; cfg.apply(*THIS, true); RETVAL = cfg.min_object_distance(); %};
static DynamicPrintConfig* load(char *path)
%code%{
auto config = new DynamicPrintConfig();
@@ -95,7 +95,7 @@
%code{% RETVAL = ConfigBase__set_deserialize(THIS, opt_key, str); %};
void set_ifndef(t_config_option_key opt_key, SV* value, bool deserialize = false)
%code{% ConfigBase__set_ifndef(THIS, opt_key, value, deserialize); %};
- std::string serialize(t_config_option_key opt_key);
+ std::string opt_serialize(t_config_option_key opt_key);
double get_abs_value(t_config_option_key opt_key);
%name{get_abs_value_over}
double get_abs_value(t_config_option_key opt_key, double ratio_over);
diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp
index 6a2cc6080..35b1c01ce 100644
--- a/xs/xsp/Model.xsp
+++ b/xs/xsp/Model.xsp
@@ -206,16 +206,12 @@ ModelMaterial::attributes()
Ref<Model> model()
%code%{ RETVAL = THIS->get_model(); %};
- t_layer_height_ranges layer_height_ranges()
- %code%{ RETVAL = THIS->layer_height_ranges; %};
- void set_layer_height_ranges(t_layer_height_ranges ranges)
- %code%{ THIS->layer_height_ranges = ranges; %};
-
Ref<Vec3d> origin_translation()
%code%{ RETVAL = &THIS->origin_translation; %};
void set_origin_translation(Vec3d* point)
%code%{ THIS->origin_translation = *point; %};
+ void ensure_on_bed();
bool needed_repair() const;
int materials_count() const;
int facets_count();
diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp
index c35f967f8..df5f48587 100644
--- a/xs/xsp/Print.xsp
+++ b/xs/xsp/Print.xsp
@@ -70,6 +70,8 @@ _constant()
Print();
~Print();
+ Ref<Model> model()
+ %code%{ RETVAL = const_cast<Model*>(&THIS->model()); %};
Ref<StaticPrintConfig> config()
%code%{ RETVAL = const_cast<GCodeConfig*>(static_cast<const GCodeConfig*>(&THIS->config())); %};
Ref<PlaceholderParser> placeholder_parser()
@@ -100,7 +102,6 @@ _constant()
%code%{ RETVAL = const_cast<PrintObjectPtrs*>(&THIS->objects()); %};
Ref<PrintObject> get_object(int idx)
%code%{ RETVAL = THIS->objects()[idx]; %};
- void reload_object(int idx);
size_t object_count()
%code%{ RETVAL = THIS->objects().size(); %};
@@ -141,9 +142,8 @@ _constant()
}
%};
- void add_model_object(ModelObject* model_object, int idx = -1);
- bool apply_config_perl_tests_only(DynamicPrintConfig* config)
- %code%{ RETVAL = THIS->apply_config_perl_tests_only(*config); %};
+ bool apply(Model *model, DynamicPrintConfig* config)
+ %code%{ RETVAL = THIS->apply(*model, *config); %};
bool has_infinite_skirt();
std::vector<unsigned int> extruders() const;
int validate() %code%{