diff --git a/CMakeLists.txt b/CMakeLists.txt
index 90ebd1e5b0..1e6cf45bd4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -34,6 +34,8 @@ option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and
option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0)
option(SLIC3R_SYNTAXONLY "Only perform source code correctness checking, no binary output (UNIX only)" 0)
+set(SLIC3R_GTK "2" CACHE STRING "GTK version to use with wxWidgets on Linux")
+
# Proposal for C++ unit tests and sandboxes
option(SLIC3R_BUILD_SANDBOXES "Build development sandboxes" OFF)
option(SLIC3R_BUILD_TESTS "Build unit tests" OFF)
diff --git a/README.md b/README.md
index f6c0677c48..3939747fc9 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,24 @@
-_Q: Oh cool, a new RepRap slicer?_
-A: Yes.
+
+
+# Slic3r Prusa Edition
-Slic3r
-======
Prebuilt Windows, OSX and Linux binaries are available through the [git releases page](https://github.com/prusa3d/Slic3r/releases).
-
-
-Slic3r takes 3D models (STL, OBJ, AMF) and converts them into G-code instructions for
-3D printers. It's compatible with any modern printer based on the RepRap toolchain,
-including all those based on the Marlin, Sprinter and Repetier firmware. It also works
+Slic3r takes 3D models (STL, OBJ, AMF) and converts them into G-code
+instructions for FFF printers or PNG layers for mSLA 3D printers. It's
+compatible with any modern printer based on the RepRap toolchain, including all
+those based on the Marlin, Prusa, Sprinter and Repetier firmware. It also works
with Mach3, LinuxCNC and Machinekit controllers.
-See the [project homepage](http://slic3r.org/) at slic3r.org and the
-[manual](http://manual.slic3r.org/) for more information.
+See the [project homepage](https://www.prusa3d.com/slic3r-prusa-edition/) and
+the [documentation directory](doc/) for more information.
### What language is it written in?
-The core geometric algorithms and data structures are written in C++,
-and Perl is used for high-level flow abstraction, GUI and testing.
-If you're wondering why Perl, see https://xkcd.com/224/
+All user facing code is written in C++, and some legacy code as well as unit
+tests are written in Perl. Perl is not required for either development or use
+of Slic3r.
The C++ API is public and its use in other projects is encouraged.
The goal is to make Slic3r fully modular so that any part of its logic
@@ -49,34 +47,23 @@ Other major features are:
* several infill patterns including honeycomb, spirals, Hilbert curves
* support material, raft, brim, skirt
* **standby temperature** and automatic wiping for multi-extruder printing
-* customizable **G-code macros** and output filename with variable placeholders
+* [customizable **G-code macros**](https://github.com/prusa3d/Slic3r/wiki/Slic3r-Prusa-Edition-Macro-Language) and output filename with variable placeholders
* support for **post-processing scripts**
* **cooling logic** controlling fan speed and dynamic print speed
-### How to install?
+### Development
-You can download a precompiled package from [slic3r.org](http://slic3r.org/);
-it will run without the need for any dependency.
-
-If you want to compile the source yourself follow the instructions on one of these wiki pages:
-* [Linux](https://github.com/alexrj/Slic3r/wiki/Running-Slic3r-from-git-on-GNU-Linux)
-* [Windows](https://github.com/prusa3d/Slic3r/wiki/How-to-compile-Slic3r-Prusa-Edition-on-MS-Windows)
-* [Mac OSX](https://github.com/alexrj/Slic3r/wiki/Running-Slic3r-from-git-on-OS-X)
+If you want to compile the source yourself, follow the instructions on one of
+these documentation pages:
+* [Linux](doc/How%20to%20build%20-%20Linux%20et%20al.md)
+* [macOS](doc/How%20to%20build%20-%20Mac%20OS.md)
+* [Windows](doc/How%20to%20build%20-%20Windows.md)
### Can I help?
Sure! You can do the following to find things that are available to help with:
-* [Pull Request Milestone](https://github.com/alexrj/Slic3r/milestone/31)
- * Please comment in the related github issue that you are working on it so that other people know.
-* Items in the [TODO](https://github.com/alexrj/Slic3r/wiki/TODO) wiki page.
- * Please comment in the related github issue that you are working on it so that other people know.
-* Drop me a line at aar@cpan.org.
-* You can also find me (rarely) in #reprap and in #slic3r on [FreeNode](https://webchat.freenode.net) with the nickname _Sound_. Another contributor, _LoH_, is also in both channels.
-* Add an [issue](https://github.com/alexrj/Slic3r/issues) to the github tracker if it isn't already present.
-
-Before sending patches and pull requests contact me (preferably through opening a github issue or commenting on an existing, related, issue) to discuss your proposed
-changes: this way we'll ensure nobody wastes their time and no conflicts arise
-in development.
+* Add an [issue](https://github.com/prusa3d/Slic3r/issues) to the github tracker if it isn't already present.
+* Look at [issues labeled "volunteer needed"](https://github.com/prusa3d/Slic3r/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3A%22volunteer+needed%22)
### What's Slic3r license?
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index 1e025adac2..a77f8b4a02 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -32,6 +32,7 @@ if (NPROC EQUAL 0)
endif ()
set(DESTDIR "${CMAKE_CURRENT_BINARY_DIR}/destdir" CACHE PATH "Destination directory")
+
option(DEP_DEBUG "Build debug variants (only applicable on Windows)" ON)
option(DEP_WX_STABLE "Build against wxWidgets stable 3.0 as opposed to default 3.1 (Linux only)" OFF)
@@ -73,7 +74,9 @@ else ()
include("deps-linux.cmake")
endif()
-add_custom_target(deps ALL
+if (MSVC)
+
+ add_custom_target(deps ALL
DEPENDS
dep_boost
dep_tbb
@@ -81,8 +84,22 @@ add_custom_target(deps ALL
dep_wxwidgets
dep_gtest
dep_nlopt
- dep_libpng
-)
+ dep_zlib # on Windows we still need zlib
+ )
+
+else()
+
+ add_custom_target(deps ALL
+ DEPENDS
+ dep_boost
+ dep_tbb
+ dep_libcurl
+ dep_wxwidgets
+ dep_gtest
+ dep_nlopt
+ )
+
+endif()
# Note: I'm not using any of the LOG_xxx options in ExternalProject_Add() commands
# because they seem to generate bogus build files (possibly a bug in ExternalProject).
diff --git a/deps/deps-linux.cmake b/deps/deps-linux.cmake
index 125553f77e..03e8e12d57 100644
--- a/deps/deps-linux.cmake
+++ b/deps/deps-linux.cmake
@@ -3,7 +3,6 @@ set(DEP_CMAKE_OPTS "-DCMAKE_POSITION_INDEPENDENT_CODE=ON")
include("deps-unix-common.cmake")
-
ExternalProject_Add(dep_boost
EXCLUDE_FROM_ALL 1
URL "https://dl.bintray.com/boostorg/release/1.66.0/source/boost_1_66_0.tar.gz"
@@ -25,19 +24,6 @@ ExternalProject_Add(dep_boost
INSTALL_COMMAND "" # b2 does that already
)
-ExternalProject_Add(dep_libpng
- EXCLUDE_FROM_ALL 1
- URL "https://github.com/glennrp/libpng/archive/v1.6.36.tar.gz"
- URL_HASH SHA256=5bef5a850a9255365a2dc344671b7e9ef810de491bd479c2506ac3c337e2d84f
- CMAKE_GENERATOR "${DEP_MSVC_GEN}"
- CMAKE_ARGS
- -DPNG_SHARED=OFF
- -DPNG_TESTS=OFF
- ${DEP_CMAKE_OPTS}
- INSTALL_COMMAND make install "DESTDIR=${DESTDIR}"
- INSTALL_COMMAND ""
-)
-
ExternalProject_Add(dep_libopenssl
EXCLUDE_FROM_ALL 1
URL "https://github.com/openssl/openssl/archive/OpenSSL_1_1_0g.tar.gz"
@@ -55,7 +41,7 @@ ExternalProject_Add(dep_libopenssl
ExternalProject_Add(dep_libcurl
EXCLUDE_FROM_ALL 1
- DEPENDS dep_libopenssl
+ DEPENDS dep_libopenssl
URL "https://curl.haxx.se/download/curl-7.58.0.tar.gz"
URL_HASH SHA256=cc245bf9a1a42a45df491501d97d5593392a03f7b4f07b952793518d97666115
BUILD_IN_SOURCE 1
@@ -127,7 +113,7 @@ ExternalProject_Add(dep_wxwidgets
--with-libxpm=builtin
--with-libjpeg=builtin
--with-libtiff=builtin
- --with-zlib=builtin
+ --with-zlib
--with-expat=builtin
--disable-precomp-headers
--enable-debug_info
diff --git a/deps/deps-macos.cmake b/deps/deps-macos.cmake
index 4676b14fc8..790a65aeba 100644
--- a/deps/deps-macos.cmake
+++ b/deps/deps-macos.cmake
@@ -87,20 +87,6 @@ ExternalProject_Add(dep_libcurl
INSTALL_COMMAND make install "DESTDIR=${DESTDIR}"
)
-ExternalProject_Add(dep_libpng
- EXCLUDE_FROM_ALL 1
- URL "https://github.com/glennrp/libpng/archive/v1.6.36.tar.gz"
- URL_HASH SHA256=5bef5a850a9255365a2dc344671b7e9ef810de491bd479c2506ac3c337e2d84f
- CMAKE_GENERATOR "${DEP_MSVC_GEN}"
- CMAKE_ARGS
- -DPNG_SHARED=OFF
- -DPNG_TESTS=OFF
- ${DEP_CMAKE_OPTS}
- INSTALL_COMMAND make install "DESTDIR=${DESTDIR}"
- INSTALL_COMMAND ""
-)
-
-
ExternalProject_Add(dep_wxwidgets
EXCLUDE_FROM_ALL 1
URL "https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.2/wxWidgets-3.1.2.tar.bz2"
@@ -119,7 +105,7 @@ ExternalProject_Add(dep_wxwidgets
--with-libxpm=builtin
--with-libjpeg=builtin
--with-libtiff=builtin
- --with-zlib=builtin
+ --with-zlib
--with-expat=builtin
--disable-debug
--disable-debug_flag
diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake
index 6c0a1b1822..3cf843f73d 100644
--- a/deps/deps-unix-common.cmake
+++ b/deps/deps-unix-common.cmake
@@ -8,30 +8,27 @@ ExternalProject_Add(dep_tbb
CMAKE_ARGS
-DTBB_BUILD_SHARED=OFF
-DTBB_BUILD_TESTS=OFF
+ -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
${DEP_CMAKE_OPTS}
- INSTALL_COMMAND make install "DESTDIR=${DESTDIR}"
)
ExternalProject_Add(dep_gtest
EXCLUDE_FROM_ALL 1
URL "https://github.com/google/googletest/archive/release-1.8.1.tar.gz"
URL_HASH SHA256=9bf1fe5182a604b4135edc1a425ae356c9ad15e9b23f9f12a02e80184c3a249c
- CMAKE_ARGS -DBUILD_GMOCK=OFF ${DEP_CMAKE_OPTS}
- INSTALL_COMMAND make install "DESTDIR=${DESTDIR}"
+ CMAKE_ARGS -DBUILD_GMOCK=OFF ${DEP_CMAKE_OPTS} -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
)
ExternalProject_Add(dep_nlopt
EXCLUDE_FROM_ALL 1
URL "https://github.com/stevengj/nlopt/archive/v2.5.0.tar.gz"
URL_HASH SHA256=c6dd7a5701fff8ad5ebb45a3dc8e757e61d52658de3918e38bab233e7fd3b4ae
- CMAKE_GENERATOR "${DEP_MSVC_GEN}"
CMAKE_ARGS
-DBUILD_SHARED_LIBS=OFF
-DNLOPT_PYTHON=OFF
-DNLOPT_OCTAVE=OFF
-DNLOPT_MATLAB=OFF
-DNLOPT_GUILE=OFF
+ -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
${DEP_CMAKE_OPTS}
- INSTALL_COMMAND make install "DESTDIR=${DESTDIR}"
- INSTALL_COMMAND ""
)
diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake
index 7276277e65..0806c23887 100644
--- a/deps/deps-windows.cmake
+++ b/deps/deps-windows.cmake
@@ -62,7 +62,7 @@ ExternalProject_Add(dep_tbb
-DTBB_BUILD_SHARED=OFF
-DTBB_BUILD_TESTS=OFF
"-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
- BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
+ BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
INSTALL_COMMAND ""
)
if (${DEP_DEBUG})
@@ -70,7 +70,7 @@ if (${DEP_DEBUG})
ExternalProject_Add_Step(dep_tbb build_debug
DEPENDEES build
DEPENDERS install
- COMMAND msbuild /P:Configuration=Debug INSTALL.vcxproj
+ COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj
WORKING_DIRECTORY "${BINARY_DIR}"
)
endif ()
@@ -86,7 +86,7 @@ ExternalProject_Add(dep_gtest
-Dgtest_force_shared_crt=ON
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
"-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
- BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
+ BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
INSTALL_COMMAND ""
)
if (${DEP_DEBUG})
@@ -94,7 +94,7 @@ if (${DEP_DEBUG})
ExternalProject_Add_Step(dep_gtest build_debug
DEPENDEES build
DEPENDERS install
- COMMAND msbuild /P:Configuration=Debug INSTALL.vcxproj
+ COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj
WORKING_DIRECTORY "${BINARY_DIR}"
)
endif ()
@@ -114,7 +114,7 @@ ExternalProject_Add(dep_nlopt
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DCMAKE_DEBUG_POSTFIX=d
"-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
- BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
+ BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
INSTALL_COMMAND ""
)
if (${DEP_DEBUG})
@@ -122,7 +122,7 @@ if (${DEP_DEBUG})
ExternalProject_Add_Step(dep_nlopt build_debug
DEPENDEES build
DEPENDERS install
- COMMAND msbuild /P:Configuration=Debug INSTALL.vcxproj
+ COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj
WORKING_DIRECTORY "${BINARY_DIR}"
)
endif ()
@@ -138,7 +138,7 @@ ExternalProject_Add(dep_zlib
"-DINSTALL_BIN_DIR=${CMAKE_CURRENT_BINARY_DIR}\\fallout" # I found no better way of preventing zlib from creating & installing DLLs :-/
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
"-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
- BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
+ BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
INSTALL_COMMAND ""
)
if (${DEP_DEBUG})
@@ -146,7 +146,7 @@ if (${DEP_DEBUG})
ExternalProject_Add_Step(dep_zlib build_debug
DEPENDEES build
DEPENDERS install
- COMMAND msbuild /P:Configuration=Debug INSTALL.vcxproj
+ COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj
WORKING_DIRECTORY "${BINARY_DIR}"
)
endif ()
@@ -165,46 +165,6 @@ if (${DEP_DEBUG})
endif ()
-ExternalProject_Add(dep_libpng
- DEPENDS dep_zlib
- EXCLUDE_FROM_ALL 1
- URL "https://github.com/glennrp/libpng/archive/v1.6.36.tar.gz"
- URL_HASH SHA256=5bef5a850a9255365a2dc344671b7e9ef810de491bd479c2506ac3c337e2d84f
- CMAKE_GENERATOR "${DEP_MSVC_GEN}"
- CMAKE_ARGS
- -DPNG_SHARED=OFF
- -DPNG_TESTS=OFF
- -DSKIP_INSTALL_FILES=ON # Prevent installation of man pages et al.
- -DCMAKE_POSITION_INDEPENDENT_CODE=ON
- "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
- BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
- INSTALL_COMMAND ""
-)
-if (${DEP_DEBUG})
- ExternalProject_Get_Property(dep_libpng BINARY_DIR)
- ExternalProject_Add_Step(dep_libpng build_debug
- DEPENDEES build
- DEPENDERS install
- COMMAND msbuild /P:Configuration=Debug INSTALL.vcxproj
- WORKING_DIRECTORY "${BINARY_DIR}"
- )
-endif ()
-# The following steps are unfortunately needed to remove the _static suffix on libraries
-# (And also overwrite the dynamic .lib)
-ExternalProject_Add_Step(dep_libpng fix_static
- DEPENDEES install
- COMMAND "${CMAKE_COMMAND}" -E rename libpng16_static.lib libpng16.lib
- WORKING_DIRECTORY "${DESTDIR}\\usr\\local\\lib\\"
-)
-if (${DEP_DEBUG})
- ExternalProject_Add_Step(dep_libpng fix_static_debug
- DEPENDEES install
- COMMAND "${CMAKE_COMMAND}" -E rename libpng16_staticd.lib libpng16d.lib
- WORKING_DIRECTORY "${DESTDIR}\\usr\\local\\lib\\"
- )
-endif ()
-
-
if (${DEPS_BITS} EQUAL 32)
set(DEP_LIBCURL_TARGET "x86")
else ()
diff --git a/doc/How to build - Linux et al.md b/doc/How to build - Linux et al.md
index cf7bc86f26..14ec23b2df 100644
--- a/doc/How to build - Linux et al.md
+++ b/doc/How to build - Linux et al.md
@@ -58,11 +58,20 @@ Note that Slic3r PE is tested with wxWidgets 3.0 somewhat sporadically and so th
### Build variant
-By default Scli3r builds the release variant.
+By default Slic3r builds the release variant.
To create a debug build, use the following CMake flag:
-DCMAKE_BUILD_TYPE=Debug
+### Enabling address sanitizer
+
+If you're using GCC/Clang compiler, it is possible to build Slic3r with the built-in address sanitizer enabled to help detect memory-corruption issues.
+To enable it, simply use the following CMake flag:
+
+ -DSLIC3R_ASAN=1
+
+This requires GCC>4.8 or Clang>3.1.
+
### Installation
At runtime, Slic3r needs a way to access its resource files. By default, it looks for a `resources` directory relative to its binary.
diff --git a/doc/How to build - Windows.md b/doc/How to build - Windows.md
index 627f1c99bb..331a3533a1 100644
--- a/doc/How to build - Windows.md
+++ b/doc/How to build - Windows.md
@@ -12,7 +12,7 @@ _Note:_ Thanks to [**@supermerill**](https://github.com/supermerill) for testing
### Dependencies
On Windows Slic3r is built against statically built libraries.
-We provide a prebuilt package of all the needed dependencies.
+We provide a prebuilt package of all the needed dependencies. This package only works on Visual Studio 2013, so if you are using a newer version of Visual Studio, you need to compile the dependencies yourself as per [below](#building-the-dependencies-package-yourself).
The package comes in a several variants:
- [64 bit, Release mode only](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=destdir-64.7z) (41 MB, 578 MB unpacked)
@@ -28,7 +28,7 @@ Alternatively you can also compile the dependencies yourself, see below.
### Building Slic3r PE with Visual Studio
-First obtain the Slic3 PE sources via either git or by extracting the source archive.
+First obtain the Slic3r PE sources via either git or by extracting the source archive.
Then you will need to note down the so-called 'prefix path' to the dependencies, this is the location of the dependencies packages + `\usr\local` appended.
For example on 64 bits this would be `C:\local\destdir-64\usr\local`. The prefix path will need to be passed to CMake.
@@ -66,7 +66,7 @@ There are several options for building from the command line:
To build with msbuild, use the same CMake command as in previous paragraph and then build using
- msbuild /P:Configuration=Release ALL_BUILD.vcxproj
+ msbuild /m /P:Configuration=Release ALL_BUILD.vcxproj
To build with Ninja or nmake, replace the `-G` option in the CMake call with `-G Ninja` or `-G "NMake Makefiles"` , respectively.
Then use either `ninja` or `nmake` to start the build.
@@ -84,15 +84,26 @@ Then `cd` into the `deps` directory and use these commands to build:
mkdir build
cd build
cmake .. -G "Visual Studio 12 Win64" -DDESTDIR="C:\local\destdir-custom"
- msbuild ALL_BUILD.vcxproj
+ msbuild /m ALL_BUILD.vcxproj
You can also use the Visual Studio GUI or other generators as mentioned above.
The `DESTDIR` option is the location where the bundle will be installed.
-This may be customized. If you leave it empty, the `DESTDIR` will be places inside the same `build` directory.
+This may be customized. If you leave it empty, the `DESTDIR` will be placed inside the same `build` directory.
+
+Warning: If the `build` directory is nested too deep inside other folders, various file paths during the build
+become too long and the build might fail due to file writing errors (\*). For this reason, it is recommended to
+place the `build` directory relatively close to the drive root.
Note that the build variant that you may choose using Visual Studio (i.e. _Release_ or _Debug_ etc.) when building the dependency package is **not relevant**.
The dependency build will by default build _both_ the _Release_ and _Debug_ variants regardless of what you choose in Visual Studio.
-You can disable building of the debug variant by passing the `-DDEP_DEBUG=OFF` option to CMake, this will only produce a _Release_ build.
+You can disable building of the debug variant by passing the
+
+ -DDEP_DEBUG=OFF
+
+option to CMake, this will only produce a _Release_ build.
Refer to the CMake scripts inside the `deps` directory to see which dependencies are built in what versions and how this is done.
+
+\*) Specifically, the problem arises when building boost. Boost build tool appends all build options into paths of
+intermediate files, which are not handled correctly by either `b2.exe` or possibly `ninja` (?).
diff --git a/resources/icons/bed/mk2_4096_bottom.png b/resources/icons/bed/mk2_4096_bottom.png
deleted file mode 100644
index dcded1f7ee..0000000000
Binary files a/resources/icons/bed/mk2_4096_bottom.png and /dev/null differ
diff --git a/resources/icons/bed/mk2_4096_top.png b/resources/icons/bed/mk2_4096_top.png
deleted file mode 100644
index 1fe8c67060..0000000000
Binary files a/resources/icons/bed/mk2_4096_top.png and /dev/null differ
diff --git a/resources/icons/bed/mk2_8192_bottom.png b/resources/icons/bed/mk2_8192_bottom.png
deleted file mode 100644
index ccc83d3111..0000000000
Binary files a/resources/icons/bed/mk2_8192_bottom.png and /dev/null differ
diff --git a/resources/icons/bed/mk2_8192_top.png b/resources/icons/bed/mk2_8192_top.png
deleted file mode 100644
index 5e2ab6a500..0000000000
Binary files a/resources/icons/bed/mk2_8192_top.png and /dev/null differ
diff --git a/resources/icons/bed/mk2_bottom.png b/resources/icons/bed/mk2_bottom.png
deleted file mode 100644
index ed94b0d621..0000000000
Binary files a/resources/icons/bed/mk2_bottom.png and /dev/null differ
diff --git a/resources/icons/bed/mk2_top.png b/resources/icons/bed/mk2_top.png
deleted file mode 100644
index 63b034a996..0000000000
Binary files a/resources/icons/bed/mk2_top.png and /dev/null differ
diff --git a/resources/icons/bed/mk3_4096_bottom.png b/resources/icons/bed/mk3_4096_bottom.png
deleted file mode 100644
index 4a02aaefff..0000000000
Binary files a/resources/icons/bed/mk3_4096_bottom.png and /dev/null differ
diff --git a/resources/icons/bed/mk3_4096_top.png b/resources/icons/bed/mk3_4096_top.png
deleted file mode 100644
index a2a1824dd2..0000000000
Binary files a/resources/icons/bed/mk3_4096_top.png and /dev/null differ
diff --git a/resources/icons/bed/mk3_8192_bottom.png b/resources/icons/bed/mk3_8192_bottom.png
deleted file mode 100644
index aaa6243734..0000000000
Binary files a/resources/icons/bed/mk3_8192_bottom.png and /dev/null differ
diff --git a/resources/icons/bed/mk3_8192_top.png b/resources/icons/bed/mk3_8192_top.png
deleted file mode 100644
index 6b1e41e5c3..0000000000
Binary files a/resources/icons/bed/mk3_8192_top.png and /dev/null differ
diff --git a/resources/icons/bed/mk3_bottom.png b/resources/icons/bed/mk3_bottom.png
deleted file mode 100644
index f72cd93da9..0000000000
Binary files a/resources/icons/bed/mk3_bottom.png and /dev/null differ
diff --git a/resources/icons/bed/mk3_top.png b/resources/icons/bed/mk3_top.png
deleted file mode 100644
index e6f3261b52..0000000000
Binary files a/resources/icons/bed/mk3_top.png and /dev/null differ
diff --git a/resources/icons/bed/sl1_4096_bottom.png b/resources/icons/bed/sl1_4096_bottom.png
deleted file mode 100644
index 0998c0e3bf..0000000000
Binary files a/resources/icons/bed/sl1_4096_bottom.png and /dev/null differ
diff --git a/resources/icons/bed/sl1_4096_top.png b/resources/icons/bed/sl1_4096_top.png
deleted file mode 100644
index 7abd8b049e..0000000000
Binary files a/resources/icons/bed/sl1_4096_top.png and /dev/null differ
diff --git a/resources/icons/bed/sl1_8192_bottom.png b/resources/icons/bed/sl1_8192_bottom.png
deleted file mode 100644
index 4e38fc9204..0000000000
Binary files a/resources/icons/bed/sl1_8192_bottom.png and /dev/null differ
diff --git a/resources/icons/bed/sl1_8192_top.png b/resources/icons/bed/sl1_8192_top.png
deleted file mode 100644
index c01d59c7b4..0000000000
Binary files a/resources/icons/bed/sl1_8192_top.png and /dev/null differ
diff --git a/resources/icons/bed/sl1_bottom.png b/resources/icons/bed/sl1_bottom.png
deleted file mode 100644
index 04272ac22c..0000000000
Binary files a/resources/icons/bed/sl1_bottom.png and /dev/null differ
diff --git a/resources/icons/bed/sl1_top.png b/resources/icons/bed/sl1_top.png
deleted file mode 100644
index 49305f531f..0000000000
Binary files a/resources/icons/bed/sl1_top.png and /dev/null differ
diff --git a/resources/icons/cog.svg b/resources/icons/cog.svg
new file mode 100644
index 0000000000..07adb66101
--- /dev/null
+++ b/resources/icons/cog.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/resources/icons/cooling.svg b/resources/icons/cooling.svg
new file mode 100644
index 0000000000..b5d80e434a
--- /dev/null
+++ b/resources/icons/cooling.svg
@@ -0,0 +1,25 @@
+
+
+
diff --git a/resources/icons/empty_icon.png b/resources/icons/empty_icon.png
new file mode 100644
index 0000000000..2dd8f0afe1
Binary files /dev/null and b/resources/icons/empty_icon.png differ
diff --git a/resources/icons/funnel.svg b/resources/icons/funnel.svg
new file mode 100644
index 0000000000..8877722e33
--- /dev/null
+++ b/resources/icons/funnel.svg
@@ -0,0 +1,15 @@
+
+
+
diff --git a/resources/icons/infill.svg b/resources/icons/infill.svg
new file mode 100644
index 0000000000..fcb1f99c9e
--- /dev/null
+++ b/resources/icons/infill.svg
@@ -0,0 +1,33 @@
+
+
+
diff --git a/resources/icons/layers.svg b/resources/icons/layers.svg
index 7718a8cbd5..cd71fab3a3 100644
--- a/resources/icons/layers.svg
+++ b/resources/icons/layers.svg
@@ -1,25 +1,27 @@
-
+
diff --git a/resources/icons/note.svg b/resources/icons/note.svg
new file mode 100644
index 0000000000..c142142010
--- /dev/null
+++ b/resources/icons/note.svg
@@ -0,0 +1,25 @@
+
+
+
diff --git a/resources/icons/output+page_white.svg b/resources/icons/output+page_white.svg
new file mode 100644
index 0000000000..ec1518f252
--- /dev/null
+++ b/resources/icons/output+page_white.svg
@@ -0,0 +1,21 @@
+
+
+
diff --git a/resources/icons/printer.svg b/resources/icons/printer.svg
new file mode 100644
index 0000000000..91e103ec70
--- /dev/null
+++ b/resources/icons/printer.svg
@@ -0,0 +1,14 @@
+
+
+
diff --git a/resources/icons/skirt+brim.svg b/resources/icons/skirt+brim.svg
new file mode 100644
index 0000000000..9242761b61
--- /dev/null
+++ b/resources/icons/skirt+brim.svg
@@ -0,0 +1,19 @@
+
+
+
diff --git a/resources/icons/spool.svg b/resources/icons/spool.svg
new file mode 100644
index 0000000000..8403659381
--- /dev/null
+++ b/resources/icons/spool.svg
@@ -0,0 +1,21 @@
+
+
+
diff --git a/resources/icons/support.svg b/resources/icons/support.svg
new file mode 100644
index 0000000000..65c7592c83
--- /dev/null
+++ b/resources/icons/support.svg
@@ -0,0 +1,25 @@
+
+
+
diff --git a/resources/icons/time.svg b/resources/icons/time.svg
new file mode 100644
index 0000000000..5d8f23cfc3
--- /dev/null
+++ b/resources/icons/time.svg
@@ -0,0 +1,16 @@
+
+
+
diff --git a/resources/icons/wrench.svg b/resources/icons/wrench.svg
new file mode 100644
index 0000000000..7966da8d8f
--- /dev/null
+++ b/resources/icons/wrench.svg
@@ -0,0 +1,24 @@
+
+
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index aae3436ae0..45d6f81968 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -21,30 +21,6 @@ add_subdirectory(libnest2d)
include_directories(${LIBDIR}/qhull/src)
#message(STATUS ${LIBDIR}/qhull/src)
-# ##############################################################################
-# Configure rasterizer target
-# ##############################################################################
-
-find_package(PNG QUIET)
-
-option(RASTERIZER_FORCE_BUILTIN_LIBPNG "Force the usage of builting libpng instead of the system version." OFF)
-
-if(PNG_FOUND AND NOT RASTERIZER_FORCE_BUILTIN_LIBPNG)
- message(STATUS "Using system libpng.")
-else()
- set(ZLIB_LIBRARY "")
- message(WARNING "Using builtin libpng. This can cause crashes on some platforms.")
- set(SKIP_INSTALL_ALL 1) # Prevent png+zlib from creating install targets
- add_subdirectory(png/zlib)
- set(ZLIB_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/png/zlib ${CMAKE_CURRENT_BINARY_DIR}/png/zlib)
- include_directories(${ZLIB_INCLUDE_DIR})
- add_subdirectory(png/libpng)
- set_target_properties(zlibstatic PROPERTIES POSITION_INDEPENDENT_CODE ON)
- set_target_properties(png_static PROPERTIES POSITION_INDEPENDENT_CODE ON)
- set(PNG_LIBRARIES png_static zlibstatic)
- set(PNG_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR} ${PROJECT_SOURCE_DIR}/png/libpng ${CMAKE_CURRENT_BINARY_DIR}/png/libpng)
-endif()
-
add_subdirectory(libslic3r)
if (SLIC3R_GUI)
@@ -60,10 +36,12 @@ if (SLIC3R_GUI)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ set (wxWidgets_CONFIG_OPTIONS "--toolkit=gtk${SLIC3R_GTK}")
if (SLIC3R_WX_STABLE)
find_package(wxWidgets 3.0 REQUIRED COMPONENTS base core adv html gl)
else ()
find_package(wxWidgets 3.1 QUIET COMPONENTS base core adv html gl)
+
if (NOT wxWidgets_FOUND)
message(FATAL_ERROR "\nCould not find wxWidgets 3.1.\n"
"Hint: On Linux you can set -DSLIC3R_WX_STABLE=1 to use wxWidgets 3.0\n")
@@ -115,12 +93,16 @@ endif ()
# Add the Slic3r GUI library, libcurl, OpenGL and GLU libraries.
if (SLIC3R_GUI)
- target_link_libraries(slic3r libslic3r_gui ${wxWidgets_LIBRARIES})
+ target_link_libraries(slic3r libslic3r_gui ${wxWidgets_LIBRARIES})
- # Configure libcurl & OpenSSL
+ # Configure libcurl and its dependencies OpenSSL & zlib
find_package(CURL REQUIRED)
+ if (NOT MSVC)
+ # Required by libcurl
+ find_package(ZLIB REQUIRED)
+ endif()
target_include_directories(slic3r PRIVATE ${CURL_INCLUDE_DIRS})
- target_link_libraries(slic3r CURL::libcurl)
+ target_link_libraries(slic3r ${CURL_LIBRARIES} ${ZLIB_LIBRARIES})
if (SLIC3R_STATIC)
if (NOT APPLE)
# libcurl is always linked dynamically to the system libcurl on OSX.
@@ -155,7 +137,7 @@ if (MSVC)
add_executable(slic3r_app_gui WIN32 slic3r_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/slic3r.rc)
target_compile_definitions(slic3r_app_gui PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE)
add_dependencies(slic3r_app_gui slic3r)
- set_target_properties(slic3r_app_gui PROPERTIES OUTPUT_NAME "slic3r")
+ set_target_properties(slic3r_app_gui PROPERTIES OUTPUT_NAME "slic3r" PDB_NAME "slic3r_gui")
add_executable(slic3r_app_console slic3r_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/slic3r.rc)
target_compile_definitions(slic3r_app_console PRIVATE -DSLIC3R_WRAPPER_CONSOLE)
@@ -188,7 +170,7 @@ if (MSVC)
)
endif ()
elseif (XCODE)
- #Â Because of Debug/Release/etc. configurations (similar to MSVC) the slic3r binary is located in an extra level
+ # Because of Debug/Release/etc. configurations (similar to MSVC) the slic3r binary is located in an extra level
add_custom_command(TARGET slic3r POST_BUILD
COMMAND ln -sf "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/resources"
COMMENT "Symlinking the resources directory into the build tree"
diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp
index 5fc992b63f..fb32132194 100644
--- a/src/admesh/connect.cpp
+++ b/src/admesh/connect.cpp
@@ -39,8 +39,7 @@ static void stl_record_neighbors(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b);
static void stl_initialize_facet_check_exact(stl_file *stl);
static void stl_initialize_facet_check_nearby(stl_file *stl);
-static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge,
- stl_vertex *a, stl_vertex *b);
+static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, const stl_vertex *a, const stl_vertex *b);
static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge,
stl_vertex *a, stl_vertex *b, float tolerance);
static void insert_hash_edge(stl_file *stl, stl_hash_edge edge,
@@ -60,41 +59,40 @@ extern int stl_check_normal_vector(stl_file *stl,
int facet_num, int normal_fix_flag);
static void stl_update_connects_remove_1(stl_file *stl, int facet_num);
-
-void
-stl_check_facets_exact(stl_file *stl) {
- /* This function builds the neighbors list. No modifications are made
- * to any of the facets. The edges are said to match only if all six
- * floats of the first edge matches all six floats of the second edge.
- */
-
- stl_hash_edge edge;
- stl_facet facet;
- int i;
- int j;
-
- if (stl->error) return;
+// This function builds the neighbors list. No modifications are made
+// to any of the facets. The edges are said to match only if all six
+// floats of the first edge matches all six floats of the second edge.
+void stl_check_facets_exact(stl_file *stl)
+{
+ if (stl->error)
+ return;
stl->stats.connected_edges = 0;
stl->stats.connected_facets_1_edge = 0;
stl->stats.connected_facets_2_edge = 0;
stl->stats.connected_facets_3_edge = 0;
- stl_initialize_facet_check_exact(stl);
+ // If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet.
+ // Do it before the next step, as the next step stores references to the face indices in the hash tables and removing a facet
+ // will break the references.
+ for (int i = 0; i < stl->stats.number_of_facets;) {
+ stl_facet &facet = stl->facet_start[i];
+ if (facet.vertex[0] == facet.vertex[1] || facet.vertex[1] == facet.vertex[2] || facet.vertex[0] == facet.vertex[2]) {
+ // Remove the degenerate facet.
+ facet = stl->facet_start[--stl->stats.number_of_facets];
+ stl->stats.facets_removed += 1;
+ stl->stats.degenerate_facets += 1;
+ } else
+ ++ i;
+ }
- for(i = 0; i < stl->stats.number_of_facets; i++) {
- facet = stl->facet_start[i];
- // If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet.
- if (facet.vertex[0] == facet.vertex[1] ||
- facet.vertex[1] == facet.vertex[2] ||
- facet.vertex[0] == facet.vertex[2]) {
- stl->stats.degenerate_facets += 1;
- stl_remove_facet(stl, i);
- -- i;
- continue;
- }
- for(j = 0; j < 3; j++) {
- edge.facet_number = i;
+ // Connect neighbor edges.
+ stl_initialize_facet_check_exact(stl);
+ for (int i = 0; i < stl->stats.number_of_facets; i++) {
+ const stl_facet &facet = stl->facet_start[i];
+ for (int j = 0; j < 3; j++) {
+ stl_hash_edge edge;
+ edge.facet_number = i;
edge.which_edge = j;
stl_load_edge_exact(stl, &edge, &facet.vertex[j], &facet.vertex[(j + 1) % 3]);
insert_hash_edge(stl, edge, stl_record_neighbors);
@@ -109,9 +107,7 @@ stl_check_facets_exact(stl_file *stl) {
#endif
}
-static void
-stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge,
- stl_vertex *a, stl_vertex *b) {
+static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, const stl_vertex *a, const stl_vertex *b) {
if (stl->error) return;
@@ -333,7 +329,9 @@ static void stl_free_edges(stl_file *stl)
}
}
free(stl->heads);
+ stl->heads = nullptr;
free(stl->tail);
+ stl->tail = nullptr;
}
static void stl_initialize_facet_check_nearby(stl_file *stl)
diff --git a/src/admesh/stl.h b/src/admesh/stl.h
index afff3deac1..2c436b426b 100644
--- a/src/admesh/stl.h
+++ b/src/admesh/stl.h
@@ -127,7 +127,6 @@ typedef struct {
typedef struct {
FILE *fp;
stl_facet *facet_start;
- stl_edge *edge_start;
stl_hash_edge **heads;
stl_hash_edge *tail;
int M;
@@ -142,7 +141,6 @@ typedef struct {
extern void stl_open(stl_file *stl, const char *file);
extern void stl_close(stl_file *stl);
extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file);
-extern void stl_print_edges(stl_file *stl, FILE *file);
extern void stl_print_neighbors(stl_file *stl, char *file);
extern void stl_put_little_int(FILE *fp, int value_in);
extern void stl_put_little_float(FILE *fp, float value_in);
diff --git a/src/admesh/stl_io.cpp b/src/admesh/stl_io.cpp
index 71e434cbcd..85f66785b3 100644
--- a/src/admesh/stl_io.cpp
+++ b/src/admesh/stl_io.cpp
@@ -33,24 +33,6 @@
#define SEEK_END 2
#endif
-void
-stl_print_edges(stl_file *stl, FILE *file) {
- int i;
- int edges_allocated;
-
- if (stl->error) return;
-
- edges_allocated = stl->stats.number_of_facets * 3;
- for(i = 0; i < edges_allocated; i++) {
- fprintf(file, "%d, %f, %f, %f, %f, %f, %f\n",
- stl->edge_start[i].facet_number,
- stl->edge_start[i].p1(0), stl->edge_start[i].p1(1),
- stl->edge_start[i].p1(2), stl->edge_start[i].p2(0),
- stl->edge_start[i].p2(1), stl->edge_start[i].p2(2));
- }
-}
-
-
void
stl_stats_out(stl_file *stl, FILE *file, char *input_file) {
if (stl->error) return;
diff --git a/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt b/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt
index 995afcc76d..462d1dd068 100644
--- a/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt
+++ b/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt
@@ -64,6 +64,7 @@ endif()
target_include_directories(ClipperBackend INTERFACE ${Boost_INCLUDE_DIRS} )
target_sources(ClipperBackend INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/geometries.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/clipper_polygon.hpp
${SRC_DIR}/libnest2d/utils/boost_alg.hpp )
target_compile_definitions(ClipperBackend INTERFACE LIBNEST2D_BACKEND_CLIPPER)
diff --git a/src/libnest2d/include/libnest2d/backends/clipper/clipper_polygon.hpp b/src/libnest2d/include/libnest2d/backends/clipper/clipper_polygon.hpp
new file mode 100644
index 0000000000..e9fbfbd18a
--- /dev/null
+++ b/src/libnest2d/include/libnest2d/backends/clipper/clipper_polygon.hpp
@@ -0,0 +1,72 @@
+#ifndef CLIPPER_POLYGON_HPP
+#define CLIPPER_POLYGON_HPP
+
+#include
+
+namespace ClipperLib {
+
+struct Polygon {
+ Path Contour;
+ Paths Holes;
+
+ inline Polygon() = default;
+
+ inline explicit Polygon(const Path& cont): Contour(cont) {}
+ inline explicit Polygon(const Paths& holes):
+ Holes(holes) {}
+ inline Polygon(const Path& cont, const Paths& holes):
+ Contour(cont), Holes(holes) {}
+
+ inline explicit Polygon(Path&& cont): Contour(std::move(cont)) {}
+ inline explicit Polygon(Paths&& holes): Holes(std::move(holes)) {}
+ inline Polygon(Path&& cont, Paths&& holes):
+ Contour(std::move(cont)), Holes(std::move(holes)) {}
+};
+
+inline IntPoint& operator +=(IntPoint& p, const IntPoint& pa ) {
+ // This could be done with SIMD
+ p.X += pa.X;
+ p.Y += pa.Y;
+ return p;
+}
+
+inline IntPoint operator+(const IntPoint& p1, const IntPoint& p2) {
+ IntPoint ret = p1;
+ ret += p2;
+ return ret;
+}
+
+inline IntPoint& operator -=(IntPoint& p, const IntPoint& pa ) {
+ p.X -= pa.X;
+ p.Y -= pa.Y;
+ return p;
+}
+
+inline IntPoint operator -(IntPoint& p ) {
+ IntPoint ret = p;
+ ret.X = -ret.X;
+ ret.Y = -ret.Y;
+ return ret;
+}
+
+inline IntPoint operator-(const IntPoint& p1, const IntPoint& p2) {
+ IntPoint ret = p1;
+ ret -= p2;
+ return ret;
+}
+
+inline IntPoint& operator *=(IntPoint& p, const IntPoint& pa ) {
+ p.X *= pa.X;
+ p.Y *= pa.Y;
+ return p;
+}
+
+inline IntPoint operator*(const IntPoint& p1, const IntPoint& p2) {
+ IntPoint ret = p1;
+ ret *= p2;
+ return ret;
+}
+
+}
+
+#endif // CLIPPER_POLYGON_HPP
diff --git a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp
index 9f881e7e0d..232668f610 100644
--- a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp
+++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp
@@ -10,84 +10,15 @@
#include
#include
-#include
-
-namespace ClipperLib {
-using PointImpl = IntPoint;
-using PathImpl = Path;
-using HoleStore = std::vector;
-
-struct PolygonImpl {
- PathImpl Contour;
- HoleStore Holes;
-
- inline PolygonImpl() = default;
-
- inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {}
- inline explicit PolygonImpl(const HoleStore& holes):
- Holes(holes) {}
- inline PolygonImpl(const Path& cont, const HoleStore& holes):
- Contour(cont), Holes(holes) {}
-
- inline explicit PolygonImpl(PathImpl&& cont): Contour(std::move(cont)) {}
- inline explicit PolygonImpl(HoleStore&& holes): Holes(std::move(holes)) {}
- inline PolygonImpl(Path&& cont, HoleStore&& holes):
- Contour(std::move(cont)), Holes(std::move(holes)) {}
-};
-
-inline PointImpl& operator +=(PointImpl& p, const PointImpl& pa ) {
- // This could be done with SIMD
- p.X += pa.X;
- p.Y += pa.Y;
- return p;
-}
-
-inline PointImpl operator+(const PointImpl& p1, const PointImpl& p2) {
- PointImpl ret = p1;
- ret += p2;
- return ret;
-}
-
-inline PointImpl& operator -=(PointImpl& p, const PointImpl& pa ) {
- p.X -= pa.X;
- p.Y -= pa.Y;
- return p;
-}
-
-inline PointImpl operator -(PointImpl& p ) {
- PointImpl ret = p;
- ret.X = -ret.X;
- ret.Y = -ret.Y;
- return ret;
-}
-
-inline PointImpl operator-(const PointImpl& p1, const PointImpl& p2) {
- PointImpl ret = p1;
- ret -= p2;
- return ret;
-}
-
-inline PointImpl& operator *=(PointImpl& p, const PointImpl& pa ) {
- p.X *= pa.X;
- p.Y *= pa.Y;
- return p;
-}
-
-inline PointImpl operator*(const PointImpl& p1, const PointImpl& p2) {
- PointImpl ret = p1;
- ret *= p2;
- return ret;
-}
-
-}
+#include "clipper_polygon.hpp"
namespace libnest2d {
// Aliases for convinience
-using ClipperLib::PointImpl;
-using ClipperLib::PathImpl;
-using ClipperLib::PolygonImpl;
-using ClipperLib::HoleStore;
+using PointImpl = ClipperLib::IntPoint;
+using PathImpl = ClipperLib::Path;
+using HoleStore = ClipperLib::Paths;
+using PolygonImpl = ClipperLib::Polygon;
// Type of coordinate units used by Clipper
template<> struct CoordType {
@@ -158,33 +89,24 @@ template<> inline TCoord& y(PointImpl& p)
#define DISABLE_BOOST_AREA
namespace _smartarea {
+
template
inline double area(const PolygonImpl& /*sh*/) {
return std::nan("");
}
template<>
-inline double area(const PolygonImpl& sh) {
- double a = 0;
-
- std::for_each(sh.Holes.begin(), sh.Holes.end(), [&a](const PathImpl& h)
- {
- a -= ClipperLib::Area(h);
+inline double area(const PolygonImpl& sh) {
+ return std::accumulate(sh.Holes.begin(), sh.Holes.end(),
+ ClipperLib::Area(sh.Contour),
+ [](double a, const ClipperLib::Path& pt){
+ return a + ClipperLib::Area(pt);
});
-
- return -ClipperLib::Area(sh.Contour) + a;
}
template<>
-inline double area(const PolygonImpl& sh) {
- double a = 0;
-
- std::for_each(sh.Holes.begin(), sh.Holes.end(), [&a](const PathImpl& h)
- {
- a += ClipperLib::Area(h);
- });
-
- return ClipperLib::Area(sh.Contour) + a;
+inline double area(const PolygonImpl& sh) {
+ return -area(sh);
}
}
@@ -228,9 +150,10 @@ template<> inline void offset(PolygonImpl& sh, TCoord distance)
// but throwing would be an overkill. Instead, we should warn the
// caller about the inability to create correct geometries
if(!found_the_contour) {
- sh.Contour = r;
+ sh.Contour = std::move(r);
ClipperLib::ReversePath(sh.Contour);
- sh.Contour.push_back(sh.Contour.front());
+ auto front_p = sh.Contour.front();
+ sh.Contour.emplace_back(std::move(front_p));
found_the_contour = true;
} else {
dout() << "Warning: offsetting result is invalid!";
@@ -240,9 +163,10 @@ template<> inline void offset(PolygonImpl& sh, TCoord distance)
// TODO If there are multiple contours we can't be sure which hole
// belongs to the first contour. (But in this case the situation is
// bad enough to let it go...)
- sh.Holes.push_back(r);
+ sh.Holes.emplace_back(std::move(r));
ClipperLib::ReversePath(sh.Holes.back());
- sh.Holes.back().push_back(sh.Holes.back().front());
+ auto front_p = sh.Holes.back().front();
+ sh.Holes.back().emplace_back(std::move(front_p));
}
}
}
@@ -390,34 +314,53 @@ inline void rotate(PolygonImpl& sh, const Radians& rads)
} // namespace shapelike
#define DISABLE_BOOST_NFP_MERGE
-inline std::vector _merge(ClipperLib::Clipper& clipper) {
+inline std::vector clipper_execute(
+ ClipperLib::Clipper& clipper,
+ ClipperLib::ClipType clipType,
+ ClipperLib::PolyFillType subjFillType = ClipperLib::pftEvenOdd,
+ ClipperLib::PolyFillType clipFillType = ClipperLib::pftEvenOdd)
+{
shapelike::Shapes retv;
ClipperLib::PolyTree result;
- clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative);
+ clipper.Execute(clipType, result, subjFillType, clipFillType);
+
retv.reserve(static_cast(result.Total()));
std::function processHole;
auto processPoly = [&retv, &processHole](ClipperLib::PolyNode *pptr) {
- PolygonImpl poly(pptr->Contour);
- poly.Contour.push_back(poly.Contour.front());
+ PolygonImpl poly;
+ poly.Contour.swap(pptr->Contour);
+
+ assert(!pptr->IsHole());
+
+ if(pptr->IsOpen()) {
+ auto front_p = poly.Contour.front();
+ poly.Contour.emplace_back(front_p);
+ }
+
for(auto h : pptr->Childs) { processHole(h, poly); }
retv.push_back(poly);
};
processHole = [&processPoly](ClipperLib::PolyNode *pptr, PolygonImpl& poly)
{
- poly.Holes.push_back(pptr->Contour);
- poly.Holes.back().push_back(poly.Holes.back().front());
+ poly.Holes.emplace_back(std::move(pptr->Contour));
+
+ assert(pptr->IsHole());
+
+ if(pptr->IsOpen()) {
+ auto front_p = poly.Holes.back().front();
+ poly.Holes.back().emplace_back(front_p);
+ }
+
for(auto c : pptr->Childs) processPoly(c);
};
auto traverse = [&processPoly] (ClipperLib::PolyNode *node)
{
- for(auto ch : node->Childs) {
- processPoly(ch);
- }
+ for(auto ch : node->Childs) processPoly(ch);
};
traverse(&result);
@@ -438,14 +381,13 @@ merge(const std::vector& shapes)
for(auto& path : shapes) {
valid &= clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
- for(auto& hole : path.Holes) {
- valid &= clipper.AddPath(hole, ClipperLib::ptSubject, closed);
- }
+ for(auto& h : path.Holes)
+ valid &= clipper.AddPath(h, ClipperLib::ptSubject, closed);
}
if(!valid) throw GeometryException(GeomErr::MERGE);
- return _merge(clipper);
+ return clipper_execute(clipper, ClipperLib::ctUnion, ClipperLib::pftNegative);
}
}
diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp
index 49baa65f25..c7b252e5d5 100644
--- a/src/libnest2d/include/libnest2d/libnest2d.hpp
+++ b/src/libnest2d/include/libnest2d/libnest2d.hpp
@@ -966,7 +966,7 @@ private:
for(size_t i = 0; i < pckgrp.size(); i++) {
auto items = pckgrp[i];
- pg.push_back({});
+ pg.emplace_back();
pg[i].reserve(items.size());
for(Item& itemA : items) {
diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
index 6fb717a7a6..91affe9786 100644
--- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
+++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
@@ -261,7 +261,7 @@ template class EdgeCache {
while(next != endit) {
contour_.emap.emplace_back(*(first++), *(next++));
contour_.full_distance += contour_.emap.back().length();
- contour_.distances.push_back(contour_.full_distance);
+ contour_.distances.emplace_back(contour_.full_distance);
}
}
@@ -276,10 +276,10 @@ template class EdgeCache {
while(next != endit) {
hc.emap.emplace_back(*(first++), *(next++));
hc.full_distance += hc.emap.back().length();
- hc.distances.push_back(hc.full_distance);
+ hc.distances.emplace_back(hc.full_distance);
}
- holes_.push_back(hc);
+ holes_.emplace_back(std::move(hc));
}
}
diff --git a/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp b/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp
index 309a5007de..abcd861830 100644
--- a/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp
+++ b/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp
@@ -63,7 +63,7 @@ public:
bool pack(Item& item, const Range& rem = Range()) {
auto&& r = static_cast(this)->trypack(item, rem);
if(r) {
- items_.push_back(*(r.item_ptr_));
+ items_.emplace_back(*(r.item_ptr_));
farea_valid_ = false;
}
return r;
@@ -78,7 +78,7 @@ public:
if(r) {
r.item_ptr_->translation(r.move_);
r.item_ptr_->rotation(r.rot_);
- items_.push_back(*(r.item_ptr_));
+ items_.emplace_back(*(r.item_ptr_));
farea_valid_ = false;
}
}
diff --git a/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp b/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp
index b03534dc46..25007e580e 100644
--- a/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp
+++ b/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp
@@ -667,7 +667,7 @@ public:
addBin();
ItemList& not_packed = not_packeds[b];
for(unsigned idx = b; idx < store_.size(); idx+=bincount_guess) {
- not_packed.push_back(store_[idx]);
+ not_packed.emplace_back(store_[idx]);
}
}
diff --git a/src/libnest2d/include/libnest2d/utils/boost_alg.hpp b/src/libnest2d/include/libnest2d/utils/boost_alg.hpp
index a6988ca007..baf1c6a105 100644
--- a/src/libnest2d/include/libnest2d/utils/boost_alg.hpp
+++ b/src/libnest2d/include/libnest2d/utils/boost_alg.hpp
@@ -463,7 +463,7 @@ template<> inline std::string serialize(
auto& v = *it;
hf.emplace_back(getX(v)*scale, getY(v)*scale);
};
- holes.push_back(hf);
+ holes.emplace_back(std::move(hf));
}
Polygonf poly;
diff --git a/src/libnest2d/tests/printer_parts.h b/src/libnest2d/tests/printer_parts.h
index b9a4eb8fab..1e65826bd3 100644
--- a/src/libnest2d/tests/printer_parts.h
+++ b/src/libnest2d/tests/printer_parts.h
@@ -2,36 +2,10 @@
#define PRINTER_PARTS_H
#include
-#include
-
-#ifndef CLIPPER_BACKEND_HPP
-namespace ClipperLib {
-using PointImpl = IntPoint;
-using PathImpl = Path;
-using HoleStore = std::vector;
-
-struct PolygonImpl {
- PathImpl Contour;
- HoleStore Holes;
-
- inline PolygonImpl() {}
-
- inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {}
- inline explicit PolygonImpl(const HoleStore& holes):
- Holes(holes) {}
- inline PolygonImpl(const Path& cont, const HoleStore& holes):
- Contour(cont), Holes(holes) {}
-
- inline explicit PolygonImpl(PathImpl&& cont): Contour(std::move(cont)) {}
- inline explicit PolygonImpl(HoleStore&& holes): Holes(std::move(holes)) {}
- inline PolygonImpl(Path&& cont, HoleStore&& holes):
- Contour(std::move(cont)), Holes(std::move(holes)) {}
-};
-}
-#endif
+#include
using TestData = std::vector;
-using TestDataEx = std::vector;
+using TestDataEx = std::vector;
extern const TestData PRINTER_PART_POLYGONS;
extern const TestData STEGOSAUR_POLYGONS;
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 2502c1b3e8..5bd4ea2b69 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -161,6 +161,8 @@ add_library(libslic3r STATIC
utils.cpp
Utils.hpp
MTUtils.hpp
+ Zipper.hpp
+ Zipper.cpp
SLA/SLABoilerPlate.hpp
SLA/SLABasePool.hpp
SLA/SLABasePool.cpp
@@ -177,8 +179,8 @@ if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE)
endif ()
-target_compile_definitions(libslic3r PUBLIC -DUSE_TBB ${PNG_DEFINITIONS})
-target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNEST2D_INCLUDES} ${PNG_INCLUDE_DIRS} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
+target_compile_definitions(libslic3r PUBLIC -DUSE_TBB)
+target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNEST2D_INCLUDES} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(libslic3r
libnest2d
admesh
@@ -188,7 +190,6 @@ target_link_libraries(libslic3r
nowide
${EXPAT_LIBRARIES}
${GLEW_LIBRARIES}
- ${PNG_LIBRARIES}
glu-libtess
polypartition
poly2tri
diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp
index f00e908ce5..4c6e542f4d 100644
--- a/src/libslic3r/ClipperUtils.cpp
+++ b/src/libslic3r/ClipperUtils.cpp
@@ -120,7 +120,7 @@ Slic3r::Polygon ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input)
{
Polygon retval;
for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit)
- retval.points.push_back(Point( (*pit).X, (*pit).Y ));
+ retval.points.emplace_back(pit->X, pit->Y);
return retval;
}
@@ -128,7 +128,7 @@ Slic3r::Polyline ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input)
{
Polyline retval;
for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit)
- retval.points.push_back(Point( (*pit).X, (*pit).Y ));
+ retval.points.emplace_back(pit->X, pit->Y);
return retval;
}
@@ -137,7 +137,7 @@ Slic3r::Polygons ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input)
Slic3r::Polygons retval;
retval.reserve(input.size());
for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it)
- retval.push_back(ClipperPath_to_Slic3rPolygon(*it));
+ retval.emplace_back(ClipperPath_to_Slic3rPolygon(*it));
return retval;
}
@@ -146,7 +146,7 @@ Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input
Slic3r::Polylines retval;
retval.reserve(input.size());
for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it)
- retval.push_back(ClipperPath_to_Slic3rPolyline(*it));
+ retval.emplace_back(ClipperPath_to_Slic3rPolyline(*it));
return retval;
}
@@ -171,7 +171,7 @@ Slic3rMultiPoint_to_ClipperPath(const MultiPoint &input)
{
ClipperLib::Path retval;
for (Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit)
- retval.push_back(ClipperLib::IntPoint( (*pit)(0), (*pit)(1) ));
+ retval.emplace_back((*pit)(0), (*pit)(1));
return retval;
}
@@ -181,7 +181,7 @@ Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input)
ClipperLib::Path output;
output.reserve(input.points.size());
for (Slic3r::Points::const_reverse_iterator pit = input.points.rbegin(); pit != input.points.rend(); ++pit)
- output.push_back(ClipperLib::IntPoint( (*pit)(0), (*pit)(1) ));
+ output.emplace_back((*pit)(0), (*pit)(1));
return output;
}
@@ -189,7 +189,7 @@ ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polygons &input)
{
ClipperLib::Paths retval;
for (Polygons::const_iterator it = input.begin(); it != input.end(); ++it)
- retval.push_back(Slic3rMultiPoint_to_ClipperPath(*it));
+ retval.emplace_back(Slic3rMultiPoint_to_ClipperPath(*it));
return retval;
}
@@ -197,7 +197,7 @@ ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input)
{
ClipperLib::Paths retval;
for (Polylines::const_iterator it = input.begin(); it != input.end(); ++it)
- retval.push_back(Slic3rMultiPoint_to_ClipperPath(*it));
+ retval.emplace_back(Slic3rMultiPoint_to_ClipperPath(*it));
return retval;
}
@@ -226,7 +226,7 @@ ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType
ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit)
{
ClipperLib::Paths paths;
- paths.push_back(std::move(input));
+ paths.emplace_back(std::move(input));
return _offset(std::move(paths), endType, delta, joinType, miterLimit);
}
@@ -585,7 +585,7 @@ Polylines _clipper_pl(ClipperLib::ClipType clipType, const Polygons &subject, co
Polylines polylines;
polylines.reserve(subject.size());
for (Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon)
- polylines.push_back(*polygon); // implicit call to split_at_first_point()
+ polylines.emplace_back(polygon->operator Polyline()); // implicit call to split_at_first_point()
// perform clipping
Polylines retval = _clipper_pl(clipType, polylines, clip, safety_offset_);
@@ -643,7 +643,7 @@ _clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons
// convert Polylines to Lines
Lines retval;
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline)
- retval.push_back(*polyline);
+ retval.emplace_back(polyline->operator Line());
return retval;
}
@@ -673,7 +673,7 @@ void traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval)
ordering_points.reserve(nodes.size());
for (ClipperLib::PolyNodes::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
Point p((*it)->Contour.front().X, (*it)->Contour.front().Y);
- ordering_points.push_back(p);
+ ordering_points.emplace_back(p);
}
// perform the ordering
@@ -684,7 +684,7 @@ void traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval)
for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) {
// traverse the next depth
traverse_pt((*it)->Childs, retval);
- retval->push_back(ClipperPath_to_Slic3rPolygon((*it)->Contour));
+ retval->emplace_back(ClipperPath_to_Slic3rPolygon((*it)->Contour));
if ((*it)->IsHole()) retval->back().reverse(); // ccw
}
}
@@ -791,8 +791,8 @@ Polygons top_level_islands(const Slic3r::Polygons &polygons)
Polygons out;
out.reserve(polytree.ChildCount());
for (int i = 0; i < polytree.ChildCount(); ++i)
- out.push_back(ClipperPath_to_Slic3rPolygon(polytree.Childs[i]->Contour));
+ out.emplace_back(ClipperPath_to_Slic3rPolygon(polytree.Childs[i]->Contour));
return out;
}
-}
\ No newline at end of file
+}
diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp
index a5ab4e99b0..0e58d7fac6 100644
--- a/src/libslic3r/ClipperUtils.hpp
+++ b/src/libslic3r/ClipperUtils.hpp
@@ -28,8 +28,8 @@ namespace Slic3r {
//-----------------------------------------------------------
// legacy code from Clipper documentation
-void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons);
-void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons);
+void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons *expolygons);
+Slic3r::ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree& polytree);
//-----------------------------------------------------------
ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input);
@@ -228,4 +228,4 @@ Polygons top_level_islands(const Slic3r::Polygons &polygons);
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp
index f5219263d6..ff1da37cc1 100644
--- a/src/libslic3r/Format/AMF.cpp
+++ b/src/libslic3r/Format/AMF.cpp
@@ -175,6 +175,11 @@ struct AMFParserContext
bool mirrory_set;
float mirrorz;
bool mirrorz_set;
+
+ bool anything_set() const { return deltax_set || deltay_set || deltaz_set ||
+ rx_set || ry_set || rz_set ||
+ scalex_set || scaley_set || scalez_set ||
+ mirrorx_set || mirrory_set || mirrorz_set; }
};
struct Object {
@@ -644,11 +649,7 @@ void AMFParserContext::endDocument()
continue;
}
for (const Instance &instance : object.second.instances)
-#if ENABLE_VOLUMES_CENTERING_FIXES
- {
-#else
- if (instance.deltax_set && instance.deltay_set) {
-#endif // ENABLE_VOLUMES_CENTERING_FIXES
+ if (instance.anything_set()) {
ModelInstance *mi = m_model.objects[object.second.idx]->add_instance();
mi->set_offset(Vec3d(instance.deltax_set ? (double)instance.deltax : 0.0, instance.deltay_set ? (double)instance.deltay : 0.0, instance.deltaz_set ? (double)instance.deltaz : 0.0));
mi->set_rotation(Vec3d(instance.rx_set ? (double)instance.rx : 0.0, instance.ry_set ? (double)instance.ry : 0.0, instance.rz_set ? (double)instance.rz : 0.0));
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 94be37b218..dc017f737f 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -1034,6 +1034,15 @@ void GCode::_do_export(Print &print, FILE *file)
}
_write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
_write(file, m_writer.postamble());
+
+ // adds tags for time estimators
+ if (print.config().remaining_times.value)
+ {
+ _writeln(file, GCodeTimeEstimator::Normal_Last_M73_Output_Placeholder_Tag);
+ if (m_silent_time_estimator_enabled)
+ _writeln(file, GCodeTimeEstimator::Silent_Last_M73_Output_Placeholder_Tag);
+ }
+
print.throw_if_canceled();
// calculates estimated printing time
@@ -2408,6 +2417,9 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
{
std::string gcode;
+ if (is_bridge(path.role()))
+ description += " (bridge)";
+
// go to first point of extrusion path
if (!m_last_pos_defined || m_last_pos != path.first_point()) {
gcode += this->travel_to(
diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp
index f1f828776f..321d9a3427 100644
--- a/src/libslic3r/GCode/Analyzer.cpp
+++ b/src/libslic3r/GCode/Analyzer.cpp
@@ -726,7 +726,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
GCodePreviewData::Range volumetric_rate_range;
// to avoid to call the callback too often
- unsigned int cancel_callback_threshold = (unsigned int)extrude_moves->second.size() / 25;
+ unsigned int cancel_callback_threshold = (unsigned int)std::max((int)extrude_moves->second.size() / 25, 1);
unsigned int cancel_callback_curr = 0;
// constructs the polylines while traversing the moves
@@ -776,6 +776,9 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
preview_data.ranges.width.update_from(width_range);
preview_data.ranges.feedrate.update_from(feedrate_range);
preview_data.ranges.volumetric_rate.update_from(volumetric_rate_range);
+
+ // we need to sort the layers by their z as they can be shuffled in case of sequential prints
+ std::sort(preview_data.extrusion.layers.begin(), preview_data.extrusion.layers.end(), [](const GCodePreviewData::Extrusion::Layer& l1, const GCodePreviewData::Extrusion::Layer& l2)->bool { return l1.z < l2.z; });
}
void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data, std::function cancel_callback)
@@ -807,7 +810,7 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data, s
GCodePreviewData::Range feedrate_range;
// to avoid to call the callback too often
- unsigned int cancel_callback_threshold = (unsigned int)travel_moves->second.size() / 25;
+ unsigned int cancel_callback_threshold = (unsigned int)std::max((int)travel_moves->second.size() / 25, 1);
unsigned int cancel_callback_curr = 0;
// constructs the polylines while traversing the moves
@@ -855,6 +858,11 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data, s
preview_data.ranges.height.update_from(height_range);
preview_data.ranges.width.update_from(width_range);
preview_data.ranges.feedrate.update_from(feedrate_range);
+
+ // we need to sort the polylines by their min z as they can be shuffled in case of sequential prints
+ std::sort(preview_data.travel.polylines.begin(), preview_data.travel.polylines.end(),
+ [](const GCodePreviewData::Travel::Polyline& p1, const GCodePreviewData::Travel::Polyline& p2)->bool
+ { return unscale(p1.polyline.bounding_box().min(2)) < unscale(p2.polyline.bounding_box().min(2)); });
}
void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_data, std::function cancel_callback)
@@ -864,7 +872,7 @@ void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_da
return;
// to avoid to call the callback too often
- unsigned int cancel_callback_threshold = (unsigned int)retraction_moves->second.size() / 25;
+ unsigned int cancel_callback_threshold = (unsigned int)std::max((int)retraction_moves->second.size() / 25, 1);
unsigned int cancel_callback_curr = 0;
for (const GCodeMove& move : retraction_moves->second)
@@ -877,6 +885,11 @@ void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_da
Vec3crd position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z()));
preview_data.retraction.positions.emplace_back(position, move.data.width, move.data.height);
}
+
+ // we need to sort the positions by their z as they can be shuffled in case of sequential prints
+ std::sort(preview_data.retraction.positions.begin(), preview_data.retraction.positions.end(),
+ [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2)->bool
+ { return unscale(p1.position(2)) < unscale(p2.position(2)); });
}
void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_data, std::function cancel_callback)
@@ -886,7 +899,7 @@ void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_
return;
// to avoid to call the callback too often
- unsigned int cancel_callback_threshold = (unsigned int)unretraction_moves->second.size() / 25;
+ unsigned int cancel_callback_threshold = (unsigned int)std::max((int)unretraction_moves->second.size() / 25, 1);
unsigned int cancel_callback_curr = 0;
for (const GCodeMove& move : unretraction_moves->second)
@@ -899,6 +912,11 @@ void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_
Vec3crd position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z()));
preview_data.unretraction.positions.emplace_back(position, move.data.width, move.data.height);
}
+
+ // we need to sort the positions by their z as they can be shuffled in case of sequential prints
+ std::sort(preview_data.unretraction.positions.begin(), preview_data.unretraction.positions.end(),
+ [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2)->bool
+ { return unscale(p1.position(2)) < unscale(p2.position(2)); });
}
// Return an estimate of the memory consumed by the time estimator.
diff --git a/src/libslic3r/GCode/PostProcessor.cpp b/src/libslic3r/GCode/PostProcessor.cpp
index df4acc1bf8..25982959be 100644
--- a/src/libslic3r/GCode/PostProcessor.cpp
+++ b/src/libslic3r/GCode/PostProcessor.cpp
@@ -4,6 +4,7 @@
#include
#include
#include
+#include
#ifdef WIN32
@@ -11,6 +12,7 @@
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include
+#include
// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
// This routine appends the given argument to a command line such that CommandLineToArgvW will return the argument string unchanged.
diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp
index 461b4cd356..4b4e9f587e 100644
--- a/src/libslic3r/GCodeTimeEstimator.cpp
+++ b/src/libslic3r/GCodeTimeEstimator.cpp
@@ -171,6 +171,8 @@ namespace Slic3r {
const std::string GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag = "; NORMAL_FIRST_M73_OUTPUT_PLACEHOLDER";
const std::string GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag = "; SILENT_FIRST_M73_OUTPUT_PLACEHOLDER";
+ const std::string GCodeTimeEstimator::Normal_Last_M73_Output_Placeholder_Tag = "; NORMAL_LAST_M73_OUTPUT_PLACEHOLDER";
+ const std::string GCodeTimeEstimator::Silent_Last_M73_Output_Placeholder_Tag = "; SILENT_LAST_M73_OUTPUT_PLACEHOLDER";
GCodeTimeEstimator::GCodeTimeEstimator(EMode mode)
: _mode(mode)
@@ -306,9 +308,17 @@ namespace Slic3r {
sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(_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)))
+ {
+ sprintf(time_line, time_mask.c_str(), "100", "0");
+ gcode_line = time_line;
+ }
else
gcode_line += "\n";
+
// add remaining time lines where needed
_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)
diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp
index ef91d5ff1d..1fbc1c14bf 100644
--- a/src/libslic3r/GCodeTimeEstimator.hpp
+++ b/src/libslic3r/GCodeTimeEstimator.hpp
@@ -19,6 +19,8 @@ namespace Slic3r {
public:
static const std::string Normal_First_M73_Output_Placeholder_Tag;
static const std::string Silent_First_M73_Output_Placeholder_Tag;
+ static const std::string Normal_Last_M73_Output_Placeholder_Tag;
+ static const std::string Silent_Last_M73_Output_Placeholder_Tag;
enum EMode : unsigned char
{
diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp
index 380245b5fc..d556f664c8 100644
--- a/src/libslic3r/Geometry.hpp
+++ b/src/libslic3r/Geometry.hpp
@@ -246,6 +246,7 @@ public:
const Vec3d& get_mirror() const { return m_mirror; }
double get_mirror(Axis axis) const { return m_mirror(axis); }
+ bool is_left_handed() const { return m_mirror.x() * m_mirror.y() * m_mirror.z() < 0.; }
void set_mirror(const Vec3d& mirror);
void set_mirror(Axis axis, double mirror);
diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp
index 913ba76a6d..19fbb3bb6e 100644
--- a/src/libslic3r/LayerRegion.cpp
+++ b/src/libslic3r/LayerRegion.cpp
@@ -258,13 +258,18 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
#ifdef SLIC3R_DEBUG
printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id());
#endif
- if (bd.detect_angle(Geometry::deg2rad(this->region()->config().bridge_angle.value))) {
+ double custom_angle = Geometry::deg2rad(this->region()->config().bridge_angle.value);
+ if (bd.detect_angle(custom_angle)) {
bridges[idx_last].bridge_angle = bd.angle;
if (this->layer()->object()->config().support_material) {
polygons_append(this->bridged, bd.coverage());
this->unsupported_bridge_edges.append(bd.unsupported_edges());
}
- }
+ } else if (custom_angle > 0) {
+ // Bridge was not detected (likely it is only supported at one side). Still it is a surface filled in
+ // using a bridging flow, therefore it makes sense to respect the custom bridging direction.
+ bridges[idx_last].bridge_angle = custom_angle;
+ }
// without safety offset, artifacts are generated (GH #2494)
surfaces_append(bottom, union_ex(grown, true), bridges[idx_last]);
}
diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp
index 1e0fb426aa..7e91ace328 100644
--- a/src/libslic3r/MTUtils.hpp
+++ b/src/libslic3r/MTUtils.hpp
@@ -56,6 +56,132 @@ public:
}
};
+/// An std compatible random access iterator which uses indices to the source
+/// vector thus resistant to invalidation caused by relocations. It also "knows"
+/// its container. No comparison is neccesary to the container "end()" iterator.
+/// The template can be instantiated with a different value type than that of
+/// the container's but the types must be compatible. E.g. a base class of the
+/// contained objects is compatible.
+///
+/// For a constant iterator, one can instantiate this template with a value
+/// type preceded with 'const'.
+template
+class IndexBasedIterator {
+ static const size_t NONE = size_t(-1);
+
+ std::reference_wrapper m_index_ref;
+ size_t m_idx = NONE;
+public:
+
+ using value_type = Value;
+ using pointer = Value *;
+ using reference = Value &;
+ using difference_type = long;
+ using iterator_category = std::random_access_iterator_tag;
+
+ inline explicit
+ IndexBasedIterator(Vector& index, size_t idx):
+ m_index_ref(index), m_idx(idx) {}
+
+ // Post increment
+ inline IndexBasedIterator operator++(int) {
+ IndexBasedIterator cpy(*this); ++m_idx; return cpy;
+ }
+
+ inline IndexBasedIterator operator--(int) {
+ IndexBasedIterator cpy(*this); --m_idx; return cpy;
+ }
+
+ inline IndexBasedIterator& operator++() {
+ ++m_idx; return *this;
+ }
+
+ inline IndexBasedIterator& operator--() {
+ --m_idx; return *this;
+ }
+
+ inline IndexBasedIterator& operator+=(difference_type l) {
+ m_idx += size_t(l); return *this;
+ }
+
+ inline IndexBasedIterator operator+(difference_type l) {
+ auto cpy = *this; cpy += l; return cpy;
+ }
+
+ inline IndexBasedIterator& operator-=(difference_type l) {
+ m_idx -= size_t(l); return *this;
+ }
+
+ inline IndexBasedIterator operator-(difference_type l) {
+ auto cpy = *this; cpy -= l; return cpy;
+ }
+
+ operator difference_type() { return difference_type(m_idx); }
+
+ /// Tesing the end of the container... this is not possible with std
+ /// iterators.
+ inline bool is_end() const { return m_idx >= m_index_ref.get().size();}
+
+ inline Value & operator*() const {
+ assert(m_idx < m_index_ref.get().size());
+ return m_index_ref.get().operator[](m_idx);
+ }
+
+ inline Value * operator->() const {
+ assert(m_idx < m_index_ref.get().size());
+ return &m_index_ref.get().operator[](m_idx);
+ }
+
+ /// If both iterators point past the container, they are equal...
+ inline bool operator ==(const IndexBasedIterator& other) {
+ size_t e = m_index_ref.get().size();
+ return m_idx == other.m_idx || (m_idx >= e && other.m_idx >= e);
+ }
+
+ inline bool operator !=(const IndexBasedIterator& other) {
+ return !(*this == other);
+ }
+
+ inline bool operator <=(const IndexBasedIterator& other) {
+ return (m_idx < other.m_idx) || (*this == other);
+ }
+
+ inline bool operator <(const IndexBasedIterator& other) {
+ return m_idx < other.m_idx && (*this != other);
+ }
+
+ inline bool operator >=(const IndexBasedIterator& other) {
+ return m_idx > other.m_idx || *this == other;
+ }
+
+ inline bool operator >(const IndexBasedIterator& other) {
+ return m_idx > other.m_idx && *this != other;
+ }
+};
+
+/// A very simple range concept implementation with iterator-like objects.
+template class Range {
+ It from, to;
+public:
+
+ // The class is ready for range based for loops.
+ It begin() const { return from; }
+ It end() const { return to; }
+
+ // The iterator type can be obtained this way.
+ using Type = It;
+
+ Range() = default;
+ Range(It &&b, It &&e):
+ from(std::forward(b)), to(std::forward(e)) {}
+
+ // Some useful container-like methods...
+ inline size_t size() const { return end() - begin(); }
+ inline bool empty() const { return size() == 0; }
+};
+
}
#endif // MTUTILS_HPP
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 68f71c3088..e634dd1383 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -61,7 +61,7 @@ Model& Model::assign_copy(Model &&rhs)
this->objects = std::move(rhs.objects);
for (ModelObject *model_object : this->objects)
model_object->set_model(this);
- rhs.objects.clear();
+ rhs.objects.clear();
return *this;
}
@@ -556,19 +556,9 @@ std::string Model::propose_export_file_name_and_path() const
for (const ModelObject *model_object : this->objects)
for (ModelInstance *model_instance : model_object->instances)
if (model_instance->is_printable()) {
- input_file = model_object->input_file;
- if (! model_object->name.empty()) {
- if (input_file.empty())
- // model_object->input_file was empty, just use model_object->name
- input_file = model_object->name;
- else {
- // Replace file name in input_file with model_object->name, but keep the path and file extension.
- input_file = (boost::filesystem::path(model_object->name).parent_path().empty()) ?
- (boost::filesystem::path(input_file).parent_path() / model_object->name).make_preferred().string() :
- model_object->name;
- }
- }
- if (! input_file.empty())
+ input_file = model_object->get_export_filename();
+
+ if (!input_file.empty())
goto end;
// Other instances will produce the same name, skip them.
break;
@@ -651,7 +641,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
for (ModelInstance *model_instance : this->instances)
model_instance->set_model_object(this);
- return *this;
+ return *this;
}
void ModelObject::assign_new_unique_ids_recursive()
@@ -970,8 +960,8 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance)
}
}
}
- std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); });
- pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end());
+ std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); });
+ pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end());
Polygon hull;
int n = (int)pts.size();
@@ -997,12 +987,16 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance)
return hull;
}
+#if ENABLE_VOLUMES_CENTERING_FIXES
+void ModelObject::center_around_origin(bool include_modifiers)
+#else
void ModelObject::center_around_origin()
+#endif // ENABLE_VOLUMES_CENTERING_FIXES
{
// calculate the displacements needed to
// center this object around the origin
#if ENABLE_VOLUMES_CENTERING_FIXES
- BoundingBoxf3 bb = full_raw_mesh_bounding_box();
+ BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box() : raw_mesh_bounding_box();
#else
BoundingBoxf3 bb;
for (ModelVolume *v : this->volumes)
@@ -1183,8 +1177,9 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
else {
TriangleMesh upper_mesh, lower_mesh;
- // Transform the mesh by the combined transformation matrix
- volume->mesh.transform(instance_matrix * volume_matrix);
+ // Transform the mesh by the combined transformation matrix.
+ // Flip the triangles in case the composite transformation is left handed.
+ volume->mesh.transform(instance_matrix * volume_matrix, true);
// Perform cut
TriangleMeshSlicer tms(&volume->mesh);
@@ -1287,11 +1282,11 @@ 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;
- new_object->instances.reserve(this->instances.size());
- for (const ModelInstance *model_instance : this->instances)
- new_object->add_instance(*model_instance);
+ new_object->name = this->name;
+ new_object->config = this->config;
+ new_object->instances.reserve(this->instances.size());
+ for (const ModelInstance *model_instance : this->instances)
+ new_object->add_instance(*model_instance);
ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh));
#if !ENABLE_VOLUMES_CENTERING_FIXES
new_vol->center_geometry();
@@ -1428,6 +1423,26 @@ void ModelObject::print_info() const
cout << "volume = " << mesh.volume() << endl;
}
+std::string ModelObject::get_export_filename() const
+{
+ std::string ret = input_file;
+
+ if (!name.empty())
+ {
+ if (ret.empty())
+ // input_file was empty, just use name
+ ret = name;
+ else
+ {
+ // Replace file name in input_file with name, but keep the path and file extension.
+ ret = (boost::filesystem::path(name).parent_path().empty()) ?
+ (boost::filesystem::path(ret).parent_path() / name).make_preferred().string() : name;
+ }
+ }
+
+ return ret;
+}
+
void ModelVolume::set_material_id(t_model_material_id material_id)
{
m_material_id = material_id;
@@ -1463,9 +1478,9 @@ int ModelVolume::extruder_id() const
bool ModelVolume::is_splittable() const
{
- // the call mesh.has_multiple_patches() is expensive, so cache the value to calculate it only once
+ // the call mesh.is_splittable() is expensive, so cache the value to calculate it only once
if (m_is_splittable == -1)
- m_is_splittable = (int)mesh.has_multiple_patches();
+ m_is_splittable = (int)mesh.is_splittable();
return m_is_splittable == 1;
}
@@ -1605,6 +1620,7 @@ void ModelVolume::rotate(double angle, Axis axis)
case X: { rotate(angle, Vec3d::UnitX()); break; }
case Y: { rotate(angle, Vec3d::UnitY()); break; }
case Z: { rotate(angle, Vec3d::UnitZ()); break; }
+ default: break;
}
}
@@ -1621,6 +1637,7 @@ void ModelVolume::mirror(Axis axis)
case X: { mirror(0) *= -1.0; break; }
case Y: { mirror(1) *= -1.0; break; }
case Z: { mirror(2) *= -1.0; break; }
+ default: break;
}
set_mirror(mirror);
}
@@ -1707,7 +1724,6 @@ bool model_object_list_extended(const Model &model_old, const Model &model_new)
bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolumeType type)
{
- bool modifiers_differ = false;
size_t i_old, i_new;
for (i_old = 0, i_new = 0; i_old < model_object_old.volumes.size() && i_new < model_object_new.volumes.size();) {
const ModelVolume &mv_old = *model_object_old.volumes[i_old];
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index 1260500eec..9514012434 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -236,7 +236,11 @@ public:
// This method is used by the auto arrange function.
Polygon convex_hull_2d(const Transform3d &trafo_instance);
+#if ENABLE_VOLUMES_CENTERING_FIXES
+ void center_around_origin(bool include_modifiers = true);
+#else
void center_around_origin();
+#endif // ENABLE_VOLUMES_CENTERING_FIXES
void ensure_on_bed();
void translate_instances(const Vec3d& vector);
void translate_instance(size_t instance_idx, const Vec3d& vector);
@@ -271,6 +275,8 @@ public:
// Print object statistics to console.
void print_info() const;
+ std::string get_export_filename() const;
+
protected:
friend class Print;
friend class SLAPrint;
@@ -390,6 +396,7 @@ public:
const Vec3d& get_mirror() const { return m_transformation.get_mirror(); }
double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); }
+ bool is_left_handed() const { return m_transformation.is_left_handed(); }
void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); }
void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); }
@@ -494,6 +501,7 @@ public:
const Vec3d& get_mirror() const { return m_transformation.get_mirror(); }
double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); }
+ bool is_left_handed() const { return m_transformation.is_left_handed(); }
void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); }
void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); }
diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp
index fd3cf86489..54627ba86b 100644
--- a/src/libslic3r/ModelArrange.cpp
+++ b/src/libslic3r/ModelArrange.cpp
@@ -556,29 +556,7 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
// TODO export the exact 2D projection. Cannot do it as libnest2d
// does not support concave shapes (yet).
ClipperLib::Path clpath;
-//WIP Vojtech's optimization of the calculation of the convex hull is not working correctly yet.
-#if 1
- {
- TriangleMesh rmesh = objptr->raw_mesh();
- ModelInstance * finst = objptr->instances.front();
-
- // Object instances should carry the same scaling and
- // x, y rotation that is why we use the first instance.
- // The next line will apply only the full mirroring and scaling
- rmesh.transform(finst->get_matrix(true, true, false, false));
- rmesh.rotate_x(float(finst->get_rotation()(X)));
- rmesh.rotate_y(float(finst->get_rotation()(Y)));
-
- // TODO export the exact 2D projection. Cannot do it as libnest2d
- // does not support concave shapes (yet).
- auto p = rmesh.convex_hull();
-
- p.make_clockwise();
- p.append(p.first_point());
- clpath = Slic3rMultiPoint_to_ClipperPath(p);
- }
-#else
// Object instances should carry the same scaling and
// x, y rotation that is why we use the first instance.
{
@@ -593,11 +571,10 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
p.append(p.first_point());
clpath = Slic3rMultiPoint_to_ClipperPath(p);
}
-#endif
for(ModelInstance* objinst : objptr->instances) {
if(objinst) {
- ClipperLib::PolygonImpl pn;
+ ClipperLib::Polygon pn;
pn.Contour = clpath;
// Efficient conversion to item.
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index 7943901339..c13f0bc2a3 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -10,7 +10,7 @@
#include "GCode/WipeTowerPrusaMM.hpp"
#include "Utils.hpp"
-#include "PrintExport.hpp"
+//#include "PrintExport.hpp"
#include
#include
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index a9fc1c63f1..630dbfb5fd 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -208,7 +208,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Horizontal width of the brim that will be printed around each object on the first layer.");
def->sidetext = L("mm");
def->min = 0;
- def->mode = comAdvanced;
+ def->mode = comSimple;
def->default_value = new ConfigOptionFloat(0);
def = this->add("clip_multipart_objects", coBool);
@@ -1375,6 +1375,7 @@ void PrintConfigDef::init_fff_params()
def->sidetext = L("(minimum)");
def->aliases = { "perimeter_offsets" };
def->min = 0;
+ def->max = 10000;
def->default_value = new ConfigOptionInt(3);
def = this->add("post_process", coStrings);
diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp
index df9446cf52..ce62f7cb0f 100644
--- a/src/libslic3r/PrintExport.hpp
+++ b/src/libslic3r/PrintExport.hpp
@@ -7,6 +7,7 @@
#include
#include
+#include
#include "Rasterizer/Rasterizer.hpp"
//#include
@@ -42,8 +43,9 @@ template
class FilePrinter {
public:
- // Draw an ExPolygon which is a polygon inside a slice on the specified layer.
+ // Draw a polygon which is a polygon inside a slice on the specified layer.
void draw_polygon(const ExPolygon& p, unsigned lyr);
+ void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr);
// Tell the printer how many layers should it consider.
void layers(unsigned layernum);
@@ -71,7 +73,8 @@ public:
void finish_layer();
// Save all the layers into the file (or dir) specified in the path argument
- void save(const std::string& path);
+ // An optional project name can be added to be used for the layer file names
+ void save(const std::string& path, const std::string& projectname = "");
// Save only the selected layer to the file specified in path argument.
void save_layer(unsigned lyr, const std::string& path);
@@ -80,28 +83,35 @@ public:
// Provokes static_assert in the right way.
template struct VeryFalse { static const bool value = false; };
-// This has to be explicitly implemented in the gui layer or a default zlib
-// based implementation is needed. I don't have time for that and I'm delegating
-// the implementation to the gui layer where the gui toolkit can cover this.
+// This can be explicitly implemented in the gui layer or the default Zipper
+// API in libslic3r with minz.
template class LayerWriter {
public:
- LayerWriter(const std::string& /*zipfile_path*/) {
+ LayerWriter(const std::string& /*zipfile_path*/)
+ {
static_assert(VeryFalse::value,
"No layer writer implementation provided!");
}
+ // Should create a new file within the zip with the given filename. It
+ // should also finish any previous entry.
void next_entry(const std::string& /*fname*/) {}
- std::string get_name() { return ""; }
+ // Should create a new file within the archive and write the provided data.
+ void binary_entry(const std::string& /*fname*/,
+ const std::uint8_t* buf, size_t len);
+ // Test whether the object can still be used for writing.
bool is_ok() { return false; }
- template LayerWriter& operator<<(const T& /*arg*/) {
+ // Write some data (text) into the current file (entry) within the archive.
+ template LayerWriter& operator<<(T&& /*arg*/) {
return *this;
}
- void close() {}
+ // Flush the current entry into the archive.
+ void finalize() {}
};
// Implementation for PNG raster output
@@ -110,14 +120,14 @@ public:
template<> class FilePrinter
{
struct Layer {
- Raster first;
- std::stringstream second;
+ Raster raster;
+ RawBytes rawbytes;
Layer() {}
Layer(const Layer&) = delete;
Layer(Layer&& m):
- first(std::move(m.first))/*, second(std::move(m.second))*/ {}
+ raster(std::move(m.raster)) {}
};
// We will save the compressed PNG data into stringstreams which can be done
@@ -135,14 +145,11 @@ template<> class FilePrinter
int m_cnt_fast_layers = 0;
std::string createIniContent(const std::string& projectname) {
-// double layer_height = m_layer_height;
-
using std::string;
using std::to_string;
auto expt_str = to_string(m_exp_time_s);
auto expt_first_str = to_string(m_exp_time_first_s);
-// auto stepnum_str = to_string(static_cast(800*layer_height));
auto layerh_str = to_string(m_layer_height);
const std::string cnt_fade_layers = to_string(m_cnt_fade_layers);
@@ -211,41 +218,48 @@ public:
inline void draw_polygon(const ExPolygon& p, unsigned lyr) {
assert(lyr < m_layers_rst.size());
- m_layers_rst[lyr].first.draw(p);
+ m_layers_rst[lyr].raster.draw(p);
+ }
+
+ inline void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr) {
+ assert(lyr < m_layers_rst.size());
+ m_layers_rst[lyr].raster.draw(p);
}
inline void begin_layer(unsigned lyr) {
if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1);
- m_layers_rst[lyr].first.reset(m_res, m_pxdim, m_o);
+ m_layers_rst[lyr].raster.reset(m_res, m_pxdim, m_o);
}
inline void begin_layer() {
m_layers_rst.emplace_back();
- m_layers_rst.front().first.reset(m_res, m_pxdim, m_o);
+ m_layers_rst.front().raster.reset(m_res, m_pxdim, m_o);
}
inline void finish_layer(unsigned lyr_id) {
assert(lyr_id < m_layers_rst.size());
- m_layers_rst[lyr_id].first.save(m_layers_rst[lyr_id].second,
- Raster::Compression::PNG);
- m_layers_rst[lyr_id].first.reset();
+ m_layers_rst[lyr_id].rawbytes =
+ m_layers_rst[lyr_id].raster.save(Raster::Compression::PNG);
+ m_layers_rst[lyr_id].raster.reset();
}
inline void finish_layer() {
if(!m_layers_rst.empty()) {
- m_layers_rst.back().first.save(m_layers_rst.back().second,
- Raster::Compression::PNG);
- m_layers_rst.back().first.reset();
+ m_layers_rst.back().rawbytes =
+ m_layers_rst.back().raster.save(Raster::Compression::PNG);
+ m_layers_rst.back().raster.reset();
}
}
template
- inline void save(const std::string& path) {
+ inline void save(const std::string& fpath, const std::string& prjname = "")
+ {
try {
- LayerWriter writer(path);
+ LayerWriter writer(fpath);
if(!writer.is_ok()) return;
- std::string project = writer.get_name();
+ std::string project = prjname.empty()?
+ boost::filesystem::path(fpath).stem().string() : prjname;
writer.next_entry("config.ini");
if(!writer.is_ok()) return;
@@ -254,20 +268,19 @@ public:
for(unsigned i = 0; i < m_layers_rst.size() && writer.is_ok(); i++)
{
- if(m_layers_rst[i].second.rdbuf()->in_avail() > 0) {
+ if(m_layers_rst[i].rawbytes.size() > 0) {
char lyrnum[6];
std::sprintf(lyrnum, "%.5d", i);
auto zfilename = project + lyrnum + ".png";
- writer.next_entry(zfilename);
-
if(!writer.is_ok()) break;
- writer << m_layers_rst[i].second.str();
- // writer << m_layers_rst[i].second.rdbuf();
- // we can keep the date for later calls of this method
- //m_layers_rst[i].second.str("");
+ writer.binary_entry(zfilename,
+ m_layers_rst[i].rawbytes.data(),
+ m_layers_rst[i].rawbytes.size());
}
}
+
+ writer.finalize();
} catch(std::exception& e) {
BOOST_LOG_TRIVIAL(error) << e.what();
// Rethrow the exception
@@ -285,13 +298,13 @@ public:
std::fstream out(loc, std::fstream::out | std::fstream::binary);
if(out.good()) {
- m_layers_rst[i].first.save(out, Raster::Compression::PNG);
+ m_layers_rst[i].raster.save(out, Raster::Compression::PNG);
} else {
BOOST_LOG_TRIVIAL(error) << "Can't create file for layer";
}
out.close();
- m_layers_rst[i].first.reset();
+ m_layers_rst[i].raster.reset();
}
void set_statistics(const std::vector statistics)
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 6c04c7781a..954e583f7c 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -1790,15 +1790,21 @@ std::vector PrintObject::_slice_volumes(const std::vector &z,
if (! volumes.empty()) {
// Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
- TriangleMesh mesh;
- for (const ModelVolume *v : volumes)
- {
- TriangleMesh vol_mesh(v->mesh);
- vol_mesh.transform(v->get_matrix());
+ TriangleMesh mesh(volumes.front()->mesh);
+ mesh.transform(volumes.front()->get_matrix(), true);
+ assert(mesh.repaired);
+ if (volumes.size() == 1 && 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);
+ }
+ for (size_t idx_volume = 1; idx_volume < volumes.size(); ++ idx_volume) {
+ const ModelVolume &model_volume = *volumes[idx_volume];
+ TriangleMesh vol_mesh(model_volume.mesh);
+ vol_mesh.transform(model_volume.get_matrix(), true);
mesh.merge(vol_mesh);
}
if (mesh.stl.stats.number_of_facets > 0) {
- mesh.transform(m_trafo);
+ mesh.transform(m_trafo, true);
// apply XY shift
mesh.translate(- unscale(m_copies_shift(0)), - unscale(m_copies_shift(1)), 0);
// perform actual slicing
@@ -1819,9 +1825,13 @@ std::vector PrintObject::_slice_volume(const std::vector &z,
// 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());
+ 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);
+ mesh.transform(m_trafo, true);
// apply XY shift
mesh.translate(- unscale(m_copies_shift(0)), - unscale(m_copies_shift(1)), 0);
// perform actual slicing
diff --git a/src/libslic3r/Rasterizer/Rasterizer.cpp b/src/libslic3r/Rasterizer/Rasterizer.cpp
index 5961d9b789..496a584a24 100644
--- a/src/libslic3r/Rasterizer/Rasterizer.cpp
+++ b/src/libslic3r/Rasterizer/Rasterizer.cpp
@@ -1,7 +1,6 @@
#include "Rasterizer.hpp"
#include
-
-#include
+#include
// For rasterizing
#include
@@ -15,8 +14,8 @@
#include
#include
-// For png compression
-#include
+// Experimental minz image write:
+#include
namespace Slic3r {
@@ -91,6 +90,25 @@ public:
agg::render_scanlines(ras, scanlines, m_renderer);
}
+ void draw(const ClipperLib::Polygon &poly) {
+ agg::rasterizer_scanline_aa<> ras;
+ agg::scanline_p8 scanlines;
+
+ auto&& path = to_path(poly.Contour);
+
+ if(m_o == Origin::TOP_LEFT) flipy(path);
+
+ ras.add_path(path);
+
+ for(auto h : poly.Holes) {
+ auto&& holepath = to_path(h);
+ if(m_o == Origin::TOP_LEFT) flipy(holepath);
+ ras.add_path(holepath);
+ }
+
+ agg::render_scanlines(ras, scanlines, m_renderer);
+ }
+
inline void clear() {
m_raw_renderer.clear(ColorBlack);
}
@@ -110,14 +128,36 @@ private:
return p(1) * SCALING_FACTOR/m_pxdim.h_mm;
}
- agg::path_storage to_path(const Polygon& poly) {
+ agg::path_storage to_path(const Polygon& poly)
+ {
agg::path_storage path;
+
auto it = poly.points.begin();
path.move_to(getPx(*it), getPy(*it));
- while(++it != poly.points.end())
+ while(++it != poly.points.end()) path.line_to(getPx(*it), getPy(*it));
+ path.line_to(getPx(poly.points.front()), getPy(poly.points.front()));
+
+ return path;
+ }
+
+
+ double getPx(const ClipperLib::IntPoint& p) {
+ return p.X * SCALING_FACTOR/m_pxdim.w_mm;
+ }
+
+ double getPy(const ClipperLib::IntPoint& p) {
+ return p.Y * SCALING_FACTOR/m_pxdim.h_mm;
+ }
+
+ agg::path_storage to_path(const ClipperLib::Path& poly)
+ {
+ agg::path_storage path;
+ auto it = poly.begin();
+ path.move_to(getPx(*it), getPy(*it));
+ while(++it != poly.end())
path.line_to(getPx(*it), getPy(*it));
- path.line_to(getPx(poly.points.front()), getPy(poly.points.front()));
+ path.line_to(getPx(poly.front()), getPy(poly.front()));
return path;
}
@@ -169,38 +209,36 @@ void Raster::clear()
m_impl->clear();
}
-void Raster::draw(const ExPolygon &poly)
+void Raster::draw(const ExPolygon &expoly)
+{
+ m_impl->draw(expoly);
+}
+
+void Raster::draw(const ClipperLib::Polygon &poly)
{
- assert(m_impl);
m_impl->draw(poly);
}
void Raster::save(std::ostream& stream, Compression comp)
{
assert(m_impl);
+ if(!stream.good()) return;
+
switch(comp) {
case Compression::PNG: {
-
- png::writer wr(stream);
-
- wr.set_bit_depth(8);
- wr.set_color_type(png::color_type_gray);
- wr.set_width(resolution().width_px);
- wr.set_height(resolution().height_px);
- wr.set_compression_type(png::compression_type_default);
-
- wr.write_info();
-
auto& b = m_impl->buffer();
- auto ptr = reinterpret_cast( b.data() );
- unsigned stride =
- sizeof(Impl::TBuffer::value_type) * resolution().width_px;
+ size_t out_len = 0;
+ void * rawdata = tdefl_write_image_to_png_file_in_memory(
+ b.data(),
+ int(resolution().width_px),
+ int(resolution().height_px), 1, &out_len);
- for(unsigned r = 0; r < resolution().height_px; r++, ptr+=stride) {
- wr.write_row(ptr);
- }
+ if(rawdata == nullptr) break;
- wr.write_end_info();
+ stream.write(static_cast(rawdata),
+ std::streamsize(out_len));
+
+ MZ_FREE(rawdata);
break;
}
@@ -217,4 +255,47 @@ void Raster::save(std::ostream& stream, Compression comp)
}
}
+RawBytes Raster::save(Raster::Compression comp)
+{
+ assert(m_impl);
+
+ std::uint8_t *ptr = nullptr; size_t s = 0;
+
+ switch(comp) {
+ case Compression::PNG: {
+
+ void *rawdata = tdefl_write_image_to_png_file_in_memory(
+ m_impl->buffer().data(),
+ int(resolution().width_px),
+ int(resolution().height_px), 1, &s);
+
+ if(rawdata == nullptr) break;
+
+ ptr = static_cast(rawdata);
+
+ break;
+ }
+ case Compression::RAW: {
+ auto header = std::string("P5 ") +
+ std::to_string(m_impl->resolution().width_px) + " " +
+ std::to_string(m_impl->resolution().height_px) + " " + "255 ";
+
+ auto sz = m_impl->buffer().size()*sizeof(Impl::TBuffer::value_type);
+
+ s = sz + header.size();
+ ptr = static_cast(MZ_MALLOC(s));
+
+ auto buff = reinterpret_cast(m_impl->buffer().data());
+ std::copy(buff, buff+sz, ptr + header.size());
+ }
+ }
+
+ return {ptr, s};
+}
+
+void RawBytes::MinzDeleter::operator()(uint8_t *rawptr)
+{
+ MZ_FREE(rawptr);
+}
+
}
diff --git a/src/libslic3r/Rasterizer/Rasterizer.hpp b/src/libslic3r/Rasterizer/Rasterizer.hpp
index 06d5b88c6d..671dcbb3d9 100644
--- a/src/libslic3r/Rasterizer/Rasterizer.hpp
+++ b/src/libslic3r/Rasterizer/Rasterizer.hpp
@@ -3,11 +3,52 @@
#include
#include
+#include
+#include
+
+namespace ClipperLib { struct Polygon; }
namespace Slic3r {
class ExPolygon;
+// Raw byte buffer paired with its size. Suitable for compressed PNG data.
+class RawBytes {
+
+ class MinzDeleter {
+ public:
+ void operator()(std::uint8_t *rawptr);
+ };
+
+ std::unique_ptr m_buffer = nullptr;
+ size_t m_size = 0;
+
+public:
+
+ RawBytes() = default;
+ RawBytes(std::uint8_t *rawptr, size_t s): m_buffer(rawptr), m_size(s) {}
+
+ size_t size() const { return m_size; }
+ const uint8_t * data() { return m_buffer.get(); }
+
+ // /////////////////////////////////////////////////////////////////////////
+ // FIXME: the following is needed for MSVC2013 compatibility
+ // /////////////////////////////////////////////////////////////////////////
+
+ RawBytes(const RawBytes&) = delete;
+ RawBytes(RawBytes&& mv):
+ m_buffer(std::move(mv.m_buffer)), m_size(mv.m_size) {}
+
+ RawBytes& operator=(const RawBytes&) = delete;
+ RawBytes& operator=(RawBytes&& mv) {
+ m_buffer.swap(mv.m_buffer);
+ m_size = mv.m_size;
+ return *this;
+ }
+
+ // /////////////////////////////////////////////////////////////////////////
+};
+
/**
* @brief Raster captures an anti-aliased monochrome canvas where vectorial
* polygons can be rasterized. Fill color is always white and the background is
@@ -84,9 +125,12 @@ public:
/// Draw a polygon with holes.
void draw(const ExPolygon& poly);
+ void draw(const ClipperLib::Polygon& poly);
/// Save the raster on the specified stream.
void save(std::ostream& stream, Compression comp = Compression::RAW);
+
+ RawBytes save(Compression comp = Compression::RAW);
};
}
diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp
index 9e9f07b6cb..0a2537b919 100644
--- a/src/libslic3r/SLA/SLAAutoSupports.cpp
+++ b/src/libslic3r/SLA/SLAAutoSupports.cpp
@@ -49,8 +49,8 @@ float SLAAutoSupports::distance_limit(float angle) const
}*/
SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, const std::vector& heights,
- const Config& config, std::function throw_on_cancel)
-: m_config(config), m_emesh(emesh), m_throw_on_cancel(throw_on_cancel)
+ const Config& config, std::function throw_on_cancel, std::function statusfn)
+: m_config(config), m_emesh(emesh), m_throw_on_cancel(throw_on_cancel), m_statusfn(statusfn)
{
process(slices, heights);
project_onto_mesh(m_output);
@@ -197,6 +197,9 @@ void SLAAutoSupports::process(const std::vector& slices, const std::
PointGrid3D point_grid;
point_grid.cell_size = Vec3f(10.f, 10.f, 10.f);
+ double increment = 100.0 / layers.size();
+ double status = 0;
+
for (unsigned int layer_id = 0; layer_id < layers.size(); ++ layer_id) {
SLAAutoSupports::MyLayer *layer_top = &layers[layer_id];
SLAAutoSupports::MyLayer *layer_bottom = (layer_id > 0) ? &layers[layer_id - 1] : nullptr;
@@ -252,6 +255,9 @@ void SLAAutoSupports::process(const std::vector& slices, const std::
m_throw_on_cancel();
+ status += increment;
+ m_statusfn(int(std::round(status)));
+
#ifdef SLA_AUTOSUPPORTS_DEBUG
/*std::string layer_num_str = std::string((i<10 ? "0" : "")) + std::string((i<100 ? "0" : "")) + std::to_string(i);
output_expolygons(expolys_top, "top" + layer_num_str + ".svg");
diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp
index 6aef7eac9e..3b696212ea 100644
--- a/src/libslic3r/SLA/SLAAutoSupports.hpp
+++ b/src/libslic3r/SLA/SLAAutoSupports.hpp
@@ -24,7 +24,7 @@ public:
};
SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices,
- const std::vector& heights, const Config& config, std::function throw_on_cancel);
+ const std::vector& heights, const Config& config, std::function throw_on_cancel, std::function statusfn);
const std::vector& output() { return m_output; }
struct MyLayer;
@@ -196,12 +196,13 @@ private:
static void output_structures(const std::vector &structures);
#endif // SLA_AUTOSUPPORTS_DEBUG
- std::function m_throw_on_cancel;
const sla::EigenMesh3D& m_emesh;
+ std::function m_throw_on_cancel;
+ std::function m_statusfn;
};
} // namespace Slic3r
-#endif // SLAAUTOSUPPORTS_HPP_
\ No newline at end of file
+#endif // SLAAUTOSUPPORTS_HPP_
diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp
index 437f07fcbc..34dd80cee0 100644
--- a/src/libslic3r/SLA/SLASupportTree.cpp
+++ b/src/libslic3r/SLA/SLASupportTree.cpp
@@ -755,9 +755,12 @@ public:
return m_compact_bridges;
}
- template inline
- typename std::enable_if::value, const Pillar&>::type
- pillar(T id) const { assert(id >= 0); return m_pillars.at(size_t(id)); }
+ template inline const Pillar& pillar(T id) const {
+ static_assert(std::is_integral::value, "Invalid index type");
+ assert(id >= 0 && id < m_pillars.size() &&
+ id < std::numeric_limits::max());
+ return m_pillars[size_t(id)];
+ }
const Pad& create_pad(const TriangleMesh& object_supports,
const ExPolygons& baseplate,
@@ -1463,7 +1466,7 @@ public:
m_cfg.head_back_radius_mm,
w);
- if(t <= w || (hp(Z) + nn(Z) * w) < m_result.ground_level) {
+ if(t <= w) {
// Let's try to optimize this angle, there might be a
// viable normal that doesn't collide with the model
@@ -1506,7 +1509,7 @@ public:
// save the verified and corrected normal
m_support_nmls.row(fidx) = nn;
- if(t > w && (hp(Z) + nn(Z) * w) > m_result.ground_level) {
+ if(t > w) {
// mark the point for needing a head.
m_iheads.emplace_back(fidx);
} else if( polar >= 3*PI/4 ) {
@@ -2237,6 +2240,18 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
return ret;
}
+SlicedSupports SLASupportTree::slice(const std::vector &heights,
+ float cr) const
+{
+ TriangleMesh fullmesh = m_impl->merged_mesh();
+ fullmesh.merge(get_pad());
+ TriangleMeshSlicer slicer(&fullmesh);
+ SlicedSupports ret;
+ slicer.slice(heights, cr, &ret, get().ctl().cancelfn);
+
+ return ret;
+}
+
const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate,
const PoolConfig& pcfg) const
{
diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp
index 74d7da9cad..66677e4d7a 100644
--- a/src/libslic3r/SLA/SLASupportTree.hpp
+++ b/src/libslic3r/SLA/SLASupportTree.hpp
@@ -181,6 +181,8 @@ public:
/// Get the sliced 2d layers of the support geometry.
SlicedSupports slice(float layerh, float init_layerh = -1.0) const;
+ SlicedSupports slice(const std::vector&, float closing_radius) const;
+
/// Adding the "pad" (base pool) under the supports
const TriangleMesh& add_pad(const SliceLayer& baseplate,
const PoolConfig& pcfg) const;
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 0e8e717cc6..694bc64447 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -12,6 +12,9 @@
#include
#include
+// For geometry algorithms with native Clipper types (no copies and conversions)
+#include
+
//#include //#include "tbb/mutex.h"
#include "I18N.hpp"
@@ -30,7 +33,6 @@ public:
std::vector support_points; // all the support points (manual/auto)
SupportTreePtr support_tree_ptr; // the supports
SlicedSupports support_slices; // sliced supports
- std::vector level_ids;
inline SupportData(const TriangleMesh& trmesh): emesh(trmesh) {}
};
@@ -40,35 +42,33 @@ namespace {
// should add up to 100 (%)
const std::array OBJ_STEP_LEVELS =
{
- 10, // slaposObjectSlice,
- 30, // slaposSupportPoints,
- 25, // slaposSupportTree,
- 25, // slaposBasePool,
- 5, // slaposSliceSupports,
- 5 // slaposIndexSlices
+ 30, // slaposObjectSlice,
+ 20, // slaposSupportPoints,
+ 10, // slaposSupportTree,
+ 10, // slaposBasePool,
+ 30, // slaposSliceSupports,
};
const std::array OBJ_STEP_LABELS =
{
L("Slicing model"), // slaposObjectSlice,
- L("Generating support points"), // slaposSupportPoints,
+ L("Generating support points"), // slaposSupportPoints,
L("Generating support tree"), // slaposSupportTree,
L("Generating pad"), // slaposBasePool,
L("Slicing supports"), // slaposSliceSupports,
- L("Slicing supports") // slaposIndexSlices,
};
// Should also add up to 100 (%)
const std::array PRINT_STEP_LEVELS =
{
- 80, // slapsRasterize
- 20, // slapsValidate
+ 10, // slapsMergeSlicesAndEval
+ 90, // slapsRasterize
};
const std::array PRINT_STEP_LABELS =
{
+ L("Merging slices and calculating statistics"), // slapsStats
L("Rasterizing layers"), // slapsRasterize
- L("Validating"), // slapsValidate
};
}
@@ -93,7 +93,10 @@ static Transform3d sla_trafo(const ModelObject &model_object)
offset(0) = 0.;
offset(1) = 0.;
rotation(2) = 0.;
- return Geometry::assemble_transform(offset, rotation, model_instance.get_scaling_factor(), model_instance.get_mirror());
+ Transform3d trafo = Geometry::assemble_transform(offset, rotation, model_instance.get_scaling_factor(), model_instance.get_mirror());
+ if (model_instance.is_left_handed())
+ trafo = Eigen::Scaling(Vec3d(-1., 1., 1.)) * trafo;
+ return trafo;
}
// List of instances, where the ModelInstance transformation is a composite of sla_trafo and the transformation defined by SLAPrintObject::Instance.
@@ -102,10 +105,10 @@ static std::vector sla_instances(const ModelObject &mo
std::vector instances;
for (ModelInstance *model_instance : model_object.instances)
if (model_instance->is_printable()) {
- instances.emplace_back(SLAPrintObject::Instance(
+ instances.emplace_back(
model_instance->id(),
Point::new_scale(model_instance->get_offset(X), model_instance->get_offset(Y)),
- float(model_instance->get_rotation(Z))));
+ float(model_instance->get_rotation(Z)));
}
return instances;
}
@@ -205,7 +208,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
} else if (model_object_list_extended(m_model, model)) {
// Add new objects. Their volumes and configs will be synchronized later.
- update_apply_status(this->invalidate_step(slapsRasterize));
+ update_apply_status(this->invalidate_step(slapsMergeSlicesAndEval));
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
@@ -217,7 +220,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
// Reorder the objects, add new objects.
// First stop background processing before shuffling or deleting the PrintObjects in the object list.
this->call_cancel_callback();
- update_apply_status(this->invalidate_step(slapsRasterize));
+ update_apply_status(this->invalidate_step(slapsMergeSlicesAndEval));
// Second create a new list of objects.
std::vector model_objects_old(std::move(m_model.objects));
m_model.objects.clear();
@@ -314,8 +317,11 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
it_print_object_status = print_object_status.end();
// Check whether a model part volume was added or removed, their transformations or order changed.
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
- bool sla_trafo_differs = model_object.instances.empty() != model_object_new.instances.empty() ||
- (! model_object.instances.empty() && ! sla_trafo(model_object).isApprox(sla_trafo(model_object_new)));
+ bool sla_trafo_differs =
+ model_object.instances.empty() != model_object_new.instances.empty() ||
+ (! model_object.instances.empty() &&
+ (! sla_trafo(model_object).isApprox(sla_trafo(model_object_new)) ||
+ model_object.instances.front()->is_left_handed() != model_object_new.instances.front()->is_left_handed()));
if (model_parts_differ || sla_trafo_differs) {
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
if (it_print_object_status != print_object_status.end()) {
@@ -389,7 +395,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
if (new_instances != it_print_object_status->print_object->instances()) {
// Instances changed.
it_print_object_status->print_object->set_instances(new_instances);
- update_apply_status(this->invalidate_step(slapsRasterize));
+ update_apply_status(this->invalidate_step(slapsMergeSlicesAndEval));
}
print_objects_new.emplace_back(it_print_object_status->print_object);
const_cast(*it_print_object_status).status = PrintObjectStatus::Reused;
@@ -399,9 +405,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
// FIXME: this invalidates the transformed mesh in SLAPrintObject
// which is expensive to calculate (especially the raw_mesh() call)
- print_object->set_trafo(sla_trafo(model_object));
+ print_object->set_trafo(sla_trafo(model_object), model_object.instances.front()->is_left_handed());
- print_object->set_instances(new_instances);
+ print_object->set_instances(std::move(new_instances));
print_object->config_apply(config, true);
print_objects_new.emplace_back(print_object);
new_objects = true;
@@ -567,52 +573,77 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
return scfg;
}
-void swapXY(ExPolygon& expoly) {
- for(auto& p : expoly.contour.points) std::swap(p(X), p(Y));
- for(auto& h : expoly.holes) for(auto& p : h.points) std::swap(p(X), p(Y));
+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.max_merge_distance_mm = c.pad_max_merge_distance.getFloat();
+ pcfg.min_wall_height_mm = c.pad_wall_height.getFloat();
+
+ return pcfg;
+}
}
-}
-
-std::vector SLAPrint::calculate_heights(const BoundingBoxf3& bb3d,
- float elevation,
- float initial_layer_height,
- float layer_height) const
+std::string SLAPrint::validate() const
{
- std::vector heights;
- float minZ = float(bb3d.min(Z)) - float(elevation);
- float maxZ = float(bb3d.max(Z));
- auto flh = float(layer_height);
- auto gnd = float(bb3d.min(Z));
+ for(SLAPrintObject * po : m_objects) {
- for(float h = minZ + initial_layer_height; h < maxZ; h += flh)
- if(h >= gnd) heights.emplace_back(h);
+ const ModelObject *mo = po->model_object();
- return heights;
+ if(po->config().supports_enable.getBool() &&
+ mo->sla_points_status == sla::PointsStatus::UserModified &&
+ mo->sla_support_points.empty())
+ return L("Cannot proceed without support points! "
+ "Add support points or disable support generation.");
+
+ sla::SupportConfig cfg = make_support_cfg(po->config());
+
+ double pinhead_width =
+ 2 * cfg.head_front_radius_mm +
+ cfg.head_width_mm +
+ 2 * cfg.head_back_radius_mm -
+ cfg.head_penetration_mm;
+
+ if(pinhead_width > cfg.object_elevation_mm)
+ return L("Elevation is too low for object.");
+ }
+
+ return "";
}
-template
-void report_status(SLAPrint& p, int st, const std::string& msg, Args&&...args) {
- BOOST_LOG_TRIVIAL(info) << st << "% " << msg;
- p.set_status(st, msg, std::forward(args)...);
-}
+bool SLAPrint::invalidate_step(SLAPrintStep step)
+{
+ bool invalidated = Inherited::invalidate_step(step);
+ // propagate to dependent steps
+ if (step == slapsMergeSlicesAndEval) {
+ invalidated |= this->invalidate_all_steps();
+ }
+
+ return invalidated;
+}
void SLAPrint::process()
{
using namespace sla;
using ExPolygon = Slic3r::ExPolygon;
+ if(m_objects.empty()) return;
+
// Assumption: at this point the print objects should be populated only with
// the model objects we have to process and the instances are also filtered
// shortcut to initial layer height
double ilhd = m_material_config.initial_layer_height.getFloat();
auto ilh = float(ilhd);
+
+ auto ilhs = coord_t(ilhd / SCALING_FACTOR);
const size_t objcount = m_objects.size();
const unsigned min_objstatus = 0; // where the per object operations start
- const unsigned max_objstatus = PRINT_STEP_LEVELS[slapsRasterize]; // where the per object operations end
+ const unsigned max_objstatus = 50; // where the per object operations end
// the coefficient that multiplies the per object status values which
// are set up for <0, 100>. They need to be scaled into the whole process
@@ -629,24 +660,65 @@ void SLAPrint::process()
// Slicing the model object. This method is oversimplified and needs to
// be compared with the fff slicing algorithm for verification
- auto slice_model = [this, ilh](SLAPrintObject& po) {
- double lh = po.m_config.layer_height.getFloat();
-
+ auto slice_model = [this, ilhs, ilh](SLAPrintObject& po) {
TriangleMesh mesh = po.transformed_mesh();
+
+ // We need to prepare the slice index...
+
+ double lhd = m_objects.front()->m_config.layer_height.getFloat();
+ float lh = float(lhd);
+ auto lhs = coord_t(lhd / SCALING_FACTOR);
+
+ auto&& bb3d = mesh.bounding_box();
+ double minZ = bb3d.min(Z) - po.get_elevation();
+ double maxZ = bb3d.max(Z);
+
+ auto minZs = coord_t(minZ / SCALING_FACTOR);
+ auto maxZs = coord_t(maxZ / SCALING_FACTOR);
+
+ po.m_slice_index.clear();
+ po.m_slice_index.reserve(size_t(maxZs - (minZs + ilhs) / lhs) + 1);
+ po.m_slice_index.emplace_back(minZs + ilhs, float(minZ) + ilh / 2.f, ilh);
+
+ for(coord_t h = minZs + ilhs + lhs; h <= maxZs; h += lhs) {
+ po.m_slice_index.emplace_back(h, float(h*SCALING_FACTOR) - lh / 2.f, lh);
+ }
+
+ // Just get the first record that is form the model:
+ auto slindex_it =
+ po.closest_slice_record(po.m_slice_index, float(bb3d.min(Z)));
+
+ if(slindex_it == po.m_slice_index.end())
+ throw std::runtime_error(L("Slicing had to be stopped "
+ "due to an internal error."));
+
+ po.m_model_height_levels.clear();
+ po.m_model_height_levels.reserve(po.m_slice_index.size());
+ for(auto it = slindex_it; it != po.m_slice_index.end(); ++it)
+ {
+ po.m_model_height_levels.emplace_back(it->slice_level());
+ }
+
TriangleMeshSlicer slicer(&mesh);
- // The 1D grid heights
- std::vector heights = calculate_heights(mesh.bounding_box(),
- float(po.get_elevation()),
- ilh, float(lh));
+ po.m_model_slices.clear();
+ slicer.slice(po.m_model_height_levels,
+ float(po.config().slice_closing_radius.value),
+ &po.m_model_slices,
+ [this](){ throw_if_canceled(); });
- auto& layers = po.m_model_slices; layers.clear();
- slicer.slice(heights, float(po.config().slice_closing_radius.value), &layers, [this](){ throw_if_canceled(); });
+ auto mit = slindex_it;
+ for(size_t id = 0;
+ id < po.m_model_slices.size() && mit != po.m_slice_index.end();
+ id++)
+ {
+ mit->set_model_slice_idx(po, id); ++mit;
+ }
};
// 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, ilh](SLAPrintObject& po) {
+ auto support_points = [this, ostepd](SLAPrintObject& po) {
const ModelObject& mo = *po.m_model_object;
po.m_supportdata.reset(
new SLAPrintObject::SupportData(po.transformed_mesh()) );
@@ -662,13 +734,14 @@ void SLAPrint::process()
// into the backend cache.
if (mo.sla_points_status != sla::PointsStatus::UserModified) {
- // calculate heights of slices (slices are calculated already)
- double lh = po.m_config.layer_height.getFloat();
+ // Hypotetical use of the slice index:
+ // auto bb = po.transformed_mesh().bounding_box();
+ // auto range = po.get_slice_records(bb.min(Z));
+ // std::vector heights; heights.reserve(range.size());
+ // for(auto& record : range) heights.emplace_back(record.slice_level());
- std::vector heights =
- calculate_heights(po.transformed_mesh().bounding_box(),
- float(po.get_elevation()),
- ilh, float(lh));
+ // calculate heights of slices (slices are calculated already)
+ const std::vector& heights = po.m_model_height_levels;
this->throw_if_canceled();
SLAAutoSupports::Config config;
@@ -679,6 +752,19 @@ void SLAPrint::process()
config.minimal_distance = float(cfg.support_points_minimal_distance);
config.head_diameter = float(cfg.support_head_front_diameter);
+ // scaling for the sub operations
+ double d = ostepd * OBJ_STEP_LEVELS[slaposSupportPoints] / 100.0;
+ double init = m_report_status.status();
+
+ auto statuscb = [this, d, init](unsigned st)
+ {
+ double current = init + st * d;
+ if(std::round(m_report_status.status()) < std::round(current))
+ m_report_status(*this, current,
+ OBJ_STEP_LABELS[slaposSupportPoints]);
+
+ };
+
// Construction of this object does the calculation.
this->throw_if_canceled();
SLAAutoSupports auto_supports(po.transformed_mesh(),
@@ -686,7 +772,8 @@ void SLAPrint::process()
po.get_model_slices(),
heights,
config,
- [this]() { throw_if_canceled(); });
+ [this]() { throw_if_canceled(); },
+ statuscb);
// Now let's extract the result.
const std::vector& points = auto_supports.output();
@@ -697,7 +784,7 @@ void SLAPrint::process()
<< po.m_supportdata->support_points.size();
// Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass the update status to GLGizmoSlaSupports
- report_status(*this, -1, L("Generating support points"), SlicingStatus::RELOAD_SLA_SUPPORT_POINTS);
+ 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.
@@ -706,7 +793,8 @@ void SLAPrint::process()
};
// In this step we create the supports
- auto support_tree = [this, objcount, ostepd](SLAPrintObject& po) {
+ auto support_tree = [this, ostepd](SLAPrintObject& po)
+ {
if(!po.m_supportdata) return;
if(!po.m_config.supports_enable.getBool()) {
@@ -718,22 +806,17 @@ void SLAPrint::process()
sla::SupportConfig scfg = make_support_cfg(po.m_config);
sla::Controller ctl;
- // some magic to scale the status values coming from the support
- // tree creation into the whole print process
- auto stfirst = OBJ_STEP_LEVELS.begin();
- auto stthis = stfirst + slaposSupportTree;
- // we need to add up the status portions until this operation
- int init = std::accumulate(stfirst, stthis, 0);
- init = int(init * ostepd); // scale the init portion
-
// scaling for the sub operations
- double d = *stthis / (objcount * 100.0);
+ double d = ostepd * OBJ_STEP_LEVELS[slaposSupportTree] / 100.0;
+ double init = m_report_status.status();
- ctl.statuscb = [this, init, d](unsigned st, const std::string& msg)
+ ctl.statuscb = [this, d, init](unsigned st, const std::string&)
{
- //FIXME this status line scaling does not seem to be correct.
- // How does it account for an increasing object index?
- report_status(*this, int(init + st*d), msg);
+ double current = init + st * d;
+ if(std::round(m_report_status.status()) < std::round(current))
+ m_report_status(*this, current,
+ OBJ_STEP_LABELS[slaposSupportTree]);
+
};
ctl.stopcondition = [this](){ return canceled(); };
@@ -749,7 +832,7 @@ void SLAPrint::process()
auto rc = SlicingStatus::RELOAD_SCENE;
// This is to prevent "Done." being displayed during merged_mesh()
- report_status(*this, -1, L("Visualizing supports"));
+ m_report_status(*this, -1, L("Visualizing supports"));
po.m_supportdata->support_tree_ptr->merged_mesh();
BOOST_LOG_TRIVIAL(debug) << "Processed support point count "
@@ -759,8 +842,7 @@ void SLAPrint::process()
if(po.support_mesh().empty())
BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty";
- report_status(*this, -1, L("Visualizing supports"), rc);
-
+ m_report_status(*this, -1, L("Visualizing supports"), rc);
};
// This step generates the sla base pad
@@ -808,134 +890,357 @@ void SLAPrint::process()
po.throw_if_canceled();
auto rc = SlicingStatus::RELOAD_SCENE;
- report_status(*this, -1, L("Visualizing supports"), rc);
+ m_report_status(*this, -1, L("Visualizing supports"), rc);
};
// Slicing the support geometries similarly to the model slicing procedure.
// If the pad had been added previously (see step "base_pool" than it will
// be part of the slices)
- auto slice_supports = [ilh](SLAPrintObject& po) {
+ auto slice_supports = [this](SLAPrintObject& po) {
auto& sd = po.m_supportdata;
+
+ if(sd) sd->support_slices.clear();
+
if(sd && sd->support_tree_ptr) {
- auto lh = float(po.m_config.layer_height.getFloat());
- sd->support_slices = sd->support_tree_ptr->slice(lh, ilh);
- }
- };
- // We have the layer polygon collection but we need to unite them into
- // an index where the key is the height level in discrete levels (clipper)
- auto index_slices = [this, ilhd](SLAPrintObject& po) {
- po.m_slice_index.clear();
- auto sih = LevelID(scale_(ilhd));
+ std::vector heights; heights.reserve(po.m_slice_index.size());
- // Establish the slice grid boundaries
- auto bb = po.transformed_mesh().bounding_box();
- double modelgnd = bb.min(Z);
- double elevation = po.get_elevation();
- double lh = po.m_config.layer_height.getFloat();
- double minZ = modelgnd - elevation;
-
- // scaled values:
- auto sminZ = LevelID(scale_(minZ));
- auto smaxZ = LevelID(scale_(bb.max(Z)));
- auto smodelgnd = LevelID(scale_(modelgnd));
- auto slh = LevelID(scale_(lh));
-
- // It is important that the next levels match the levels in
- // model_slice method. Only difference is that here it works with
- // scaled coordinates
- po.m_level_ids.clear();
- for(LevelID h = sminZ + sih; h < smaxZ; h += slh)
- if(h >= smodelgnd) po.m_level_ids.emplace_back(h);
-
- std::vector& oslices = po.m_model_slices;
-
- // If everything went well this code should not run at all, but
- // let's be robust...
- // assert(levelids.size() == oslices.size());
- if(po.m_level_ids.size() < oslices.size()) { // extend the levels until...
-
- BOOST_LOG_TRIVIAL(warning)
- << "Height level mismatch at rasterization!\n";
-
- LevelID lastlvl = po.m_level_ids.back();
- while(po.m_level_ids.size() < oslices.size()) {
- lastlvl += slh;
- po.m_level_ids.emplace_back(lastlvl);
+ for(auto& rec : po.m_slice_index) {
+ heights.emplace_back(rec.slice_level());
}
+
+ sd->support_slices = sd->support_tree_ptr->slice(
+ heights, float(po.config().slice_closing_radius.value));
}
- for(size_t i = 0; i < oslices.size(); ++i) {
- LevelID h = po.m_level_ids[i];
-
- float fh = float(double(h) * SCALING_FACTOR);
-
- // now for the public slice index:
- SLAPrintObject::SliceRecord& sr = po.m_slice_index[fh];
- // There should be only one slice layer for each print object
- assert(sr.model_slices_idx == SLAPrintObject::SliceRecord::NONE);
- sr.model_slices_idx = i;
- }
-
- if(po.m_supportdata) { // deal with the support slices if present
- std::vector& sslices = po.m_supportdata->support_slices;
- po.m_supportdata->level_ids.clear();
- po.m_supportdata->level_ids.reserve(sslices.size());
-
- for(int i = 0; i < int(sslices.size()); ++i) {
- LevelID h = sminZ + sih + i * slh;
- po.m_supportdata->level_ids.emplace_back(h);
-
- float fh = float(double(h) * SCALING_FACTOR);
-
- SLAPrintObject::SliceRecord& sr = po.m_slice_index[fh];
- assert(sr.support_slices_idx == SLAPrintObject::SliceRecord::NONE);
- sr.support_slices_idx = SLAPrintObject::SliceRecord::Idx(i);
- }
+ for(size_t i = 0;
+ i < sd->support_slices.size() && i < po.m_slice_index.size();
+ ++i)
+ {
+ 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.
- report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
+ m_report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
+ };
+
+ // Merging the slices from all the print objects into one slice grid and
+ // calculating print statistics from the merge result.
+ auto merge_slices_and_eval_stats = [this, ilhs]() {
+
+ // clear the rasterizer input
+ m_printer_input.clear();
+
+ size_t mx = 0;
+ for(SLAPrintObject * o : m_objects) {
+ if(auto m = o->get_slice_index().size() > mx) mx = m;
+ }
+
+ m_printer_input.reserve(mx);
+
+ auto eps = coord_t(SCALED_EPSILON);
+
+ for(SLAPrintObject * o : m_objects) {
+ coord_t gndlvl = o->get_slice_index().front().print_level() - ilhs;
+
+ for(const SliceRecord& slicerecord : o->get_slice_index()) {
+ coord_t lvlid = slicerecord.print_level() - gndlvl;
+
+ // Neat trick to round the layer levels to the grid.
+ lvlid = eps * (lvlid / eps);
+
+ auto it = std::lower_bound(m_printer_input.begin(),
+ m_printer_input.end(),
+ PrintLayer(lvlid));
+
+ if(it == m_printer_input.end() || it->level() != lvlid)
+ it = m_printer_input.insert(it, PrintLayer(lvlid));
+
+
+ it->add(slicerecord);
+ }
+ }
+
+ m_print_statistics.clear();
+
+ using ClipperPoint = ClipperLib::IntPoint;
+ using ClipperPolygon = ClipperLib::Polygon; // see clipper_polygon.hpp in libnest2d
+ using ClipperPolygons = std::vector;
+ namespace sl = libnest2d::shapelike; // For algorithms
+
+ // If the raster has vertical orientation, we will flip the coordinates
+ bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait;
+
+ // Set up custom union and diff functions for clipper polygons
+ auto polyunion = [] (const ClipperPolygons& subjects)
+ {
+ ClipperLib::Clipper clipper;
+
+ bool closed = true;
+
+ for(auto& path : subjects) {
+ clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
+ clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
+ }
+
+ auto mode = ClipperLib::pftPositive;
+
+ return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode);
+ };
+
+ auto polydiff = [](const ClipperPolygons& subjects, const ClipperPolygons& clips)
+ {
+ ClipperLib::Clipper clipper;
+
+ bool closed = true;
+
+ for(auto& path : subjects) {
+ clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
+ clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
+ }
+
+ for(auto& path : clips) {
+ clipper.AddPath(path.Contour, ClipperLib::ptClip, closed);
+ clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed);
+ }
+
+ auto mode = ClipperLib::pftPositive;
+
+ return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode);
+ };
+
+ // libnest calculates positive area for clockwise polygons, Slic3r is in counter-clockwise
+ auto areafn = [](const ClipperPolygon& poly) { return - sl::area(poly); };
+
+ const double area_fill = m_printer_config.area_fill.getFloat()*0.01;// 0.5 (50%);
+ const double fast_tilt = m_printer_config.fast_tilt_time.getFloat();// 5.0;
+ const double slow_tilt = m_printer_config.slow_tilt_time.getFloat();// 8.0;
+
+ const double init_exp_time = m_material_config.initial_exposure_time.getFloat();
+ const double exp_time = m_material_config.exposure_time.getFloat();
+
+ const int fade_layers_cnt = m_default_object_config.faded_layers.getInt();// 10 // [3;20]
+
+ const double width = m_printer_config.display_width.getFloat() / SCALING_FACTOR;
+ const double height = m_printer_config.display_height.getFloat() / SCALING_FACTOR;
+ const double display_area = width*height;
+
+ // get polygons for all instances in the object
+ auto get_all_polygons =
+ [flpXY](const ExPolygons& input_polygons,
+ const std::vector& instances,
+ bool is_lefthanded)
+ {
+ ClipperPolygons polygons;
+ polygons.reserve(input_polygons.size() * instances.size());
+
+ for (const ExPolygon& polygon : input_polygons) {
+ if(polygon.contour.empty()) continue;
+
+ for (size_t i = 0; i < instances.size(); ++i)
+ {
+ ClipperPolygon poly;
+
+ // We need to reverse if flpXY OR is_lefthanded is true but
+ // not if both are true which is a logical inequality (XOR)
+ bool needreverse = flpXY != is_lefthanded;
+
+ // should be a move
+ poly.Contour.reserve(polygon.contour.size() + 1);
+
+ auto& cntr = polygon.contour.points;
+ if(needreverse)
+ for(auto it = cntr.rbegin(); it != cntr.rend(); ++it)
+ poly.Contour.emplace_back(it->x(), it->y());
+ else
+ for(auto& p : cntr)
+ poly.Contour.emplace_back(p.x(), p.y());
+
+ for(auto& h : polygon.holes) {
+ poly.Holes.emplace_back();
+ auto& hole = poly.Holes.back();
+ hole.reserve(h.points.size() + 1);
+
+ if(needreverse)
+ for(auto it = h.points.rbegin(); it != h.points.rend(); ++it)
+ hole.emplace_back(it->x(), it->y());
+ else
+ for(auto& p : h.points)
+ hole.emplace_back(p.x(), p.y());
+ }
+
+ if(is_lefthanded) {
+ for(auto& p : poly.Contour) p.X = -p.X;
+ for(auto& h : poly.Holes) for(auto& p : h) p.X = -p.X;
+ }
+
+ sl::rotate(poly, double(instances[i].rotation));
+ sl::translate(poly, ClipperPoint{instances[i].shift(X),
+ instances[i].shift(Y)});
+
+ if (flpXY) {
+ for(auto& p : poly.Contour) std::swap(p.X, p.Y);
+ for(auto& h : poly.Holes) for(auto& p : h) std::swap(p.X, p.Y);
+ }
+
+ polygons.emplace_back(std::move(poly));
+ }
+ }
+ return polygons;
+ };
+
+ double supports_volume(0.0);
+ double models_volume(0.0);
+
+ double estim_time(0.0);
+
+ size_t slow_layers = 0;
+ size_t fast_layers = 0;
+
+ const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1);
+ double fade_layer_time = init_exp_time;
+
+ SpinMutex mutex;
+ using Lock = std::lock_guard;
+
+ // Going to parallel:
+ auto printlayerfn = [this,
+ // functions and read only vars
+ get_all_polygons, polyunion, polydiff, areafn,
+ area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time,
+
+ // write vars
+ &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers,
+ &fast_layers, &fade_layer_time](size_t sliced_layer_cnt)
+ {
+ PrintLayer& layer = m_printer_input[sliced_layer_cnt];
+
+ // vector of slice record references
+ auto& slicerecord_references = layer.slices();
+
+ if(slicerecord_references.empty()) return;
+
+ // Layer height should match for all object slices for a given level.
+ const auto l_height = double(slicerecord_references.front().get().layer_height());
+
+ // Calculation of the consumed material
+
+ ClipperPolygons model_polygons;
+ ClipperPolygons supports_polygons;
+
+ size_t c = std::accumulate(layer.slices().begin(), layer.slices().end(), 0u, [](size_t a, const SliceRecord& sr) {
+ return a + sr.get_slice(soModel).size();
+ });
+
+ model_polygons.reserve(c);
+
+ c = std::accumulate(layer.slices().begin(), layer.slices().end(), 0u, [](size_t a, const SliceRecord& sr) {
+ return a + sr.get_slice(soModel).size();
+ });
+
+ supports_polygons.reserve(c);
+
+ for(const SliceRecord& record : layer.slices()) {
+ const SLAPrintObject *po = record.print_obj();
+
+ const ExPolygons &modelslices = record.get_slice(soModel);
+ bool is_lefth = record.print_obj()->is_left_handed();
+ if (!modelslices.empty()) {
+ ClipperPolygons v = get_all_polygons(modelslices, po->instances(), is_lefth);
+ for(ClipperPolygon& p_tmp : v) model_polygons.emplace_back(std::move(p_tmp));
+ }
+
+ const ExPolygons &supportslices = record.get_slice(soSupport);
+ if (!supportslices.empty()) {
+ ClipperPolygons v = get_all_polygons(supportslices, po->instances(), is_lefth);
+ for(ClipperPolygon& p_tmp : v) supports_polygons.emplace_back(std::move(p_tmp));
+ }
+ }
+
+ model_polygons = polyunion(model_polygons);
+ double layer_model_area = 0;
+ for (const ClipperPolygon& polygon : model_polygons)
+ layer_model_area += areafn(polygon);
+
+ if (layer_model_area < 0 || layer_model_area > 0) {
+ Lock lck(mutex); models_volume += layer_model_area * l_height;
+ }
+
+ if(!supports_polygons.empty()) {
+ if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons);
+ else supports_polygons = polydiff(supports_polygons, model_polygons);
+ // allegedly, union of subject is done withing the diff according to the pftPositive polyFillType
+ }
+
+ double layer_support_area = 0;
+ for (const ClipperPolygon& polygon : supports_polygons)
+ layer_support_area += areafn(polygon);
+
+ if (layer_support_area < 0 || layer_support_area > 0) {
+ Lock lck(mutex); supports_volume += layer_support_area * l_height;
+ }
+
+ // Here we can save the expensively calculated polygons for printing
+ ClipperPolygons trslices;
+ trslices.reserve(model_polygons.size() + supports_polygons.size());
+ for(ClipperPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly));
+ for(ClipperPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly));
+
+ layer.transformed_slices(polyunion(trslices));
+
+ // Calculation of the slow and fast layers to the future controlling those values on FW
+
+ const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill;
+ const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt;
+
+ { Lock lck(mutex);
+ if (is_fast_layer)
+ fast_layers++;
+ else
+ slow_layers++;
+
+
+ // Calculation of the printing time
+
+ if (sliced_layer_cnt < 3)
+ estim_time += init_exp_time;
+ else if (fade_layer_time > exp_time)
+ {
+ fade_layer_time -= delta_fade_time;
+ estim_time += fade_layer_time;
+ }
+ else
+ estim_time += exp_time;
+
+ estim_time += tilt_time;
+ }
+ };
+
+ // sequential version for debugging:
+ // for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i);
+ tbb::parallel_for(0, m_printer_input.size(), printlayerfn);
+
+ m_print_statistics.support_used_material = supports_volume * SCALING_FACTOR * SCALING_FACTOR;
+ m_print_statistics.objects_used_material = models_volume * SCALING_FACTOR * SCALING_FACTOR;
+
+ // Estimated printing time
+ // A layers count o the highest object
+ if (m_printer_input.size() == 0)
+ m_print_statistics.estimated_print_time = "N/A";
+ else
+ m_print_statistics.estimated_print_time = get_time_dhms(float(estim_time));
+
+ m_print_statistics.fast_layers_count = fast_layers;
+ m_print_statistics.slow_layers_count = slow_layers;
+
+ m_report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
};
// Rasterizing the model objects, and their supports
auto rasterize = [this, max_objstatus]() {
if(canceled()) return;
- // clear the rasterizer input
- m_printer_input.clear();
-
- for(SLAPrintObject * o : m_objects) {
- auto& po = *o;
- std::vector& oslices = po.m_model_slices;
-
- // We need to adjust the min Z level of the slices to be zero
- LevelID smfirst =
- po.m_supportdata && !po.m_supportdata->level_ids.empty() ?
- po.m_supportdata->level_ids.front() : 0;
- LevelID mfirst = po.m_level_ids.empty()? 0 : po.m_level_ids.front();
- LevelID gndlvl = -(std::min(smfirst, mfirst));
-
- // now merge this object's support and object slices with the rest
- // of the print object slices
-
- for(size_t i = 0; i < oslices.size(); ++i) {
- auto& lyrs = m_printer_input[gndlvl + po.m_level_ids[i]];
- lyrs.emplace_back(oslices[i], po.m_instances);
- }
-
- if(!po.m_supportdata) continue;
- std::vector& sslices = po.m_supportdata->support_slices;
- for(size_t i = 0; i < sslices.size(); ++i) {
- LayerRefs& lyrs =
- m_printer_input[gndlvl + po.m_supportdata->level_ids[i]];
- lyrs.emplace_back(sslices[i], po.m_instances);
- }
- }
-
// collect all the keys
- std::vector keys; keys.reserve(m_printer_input.size());
- for(auto& e : m_printer_input) keys.emplace_back(e.first);
// If the raster has vertical orientation, we will flip the coordinates
bool flpXY = m_printer_config.display_orientation.getInt() ==
@@ -967,55 +1272,49 @@ void SLAPrint::process()
auto lvlcnt = unsigned(m_printer_input.size());
printer.layers(lvlcnt);
- // slot is the portion of 100% that is realted to rasterization
- unsigned slot = PRINT_STEP_LEVELS[slapsRasterize];
- // ist: initial state; pst: previous state
- unsigned ist = max_objstatus, pst = ist;
// coefficient to map the rasterization state (0-99) to the allocated
// portion (slot) of the process state
- double sd = (100 - ist) / 100.0;
+ double sd = (100 - max_objstatus) / 100.0;
+
+ // slot is the portion of 100% that is realted to rasterization
+ unsigned slot = PRINT_STEP_LEVELS[slapsRasterize];
+
+ // pst: previous state
+ double pst = m_report_status.status();
+
+ double increment = (slot * sd) / m_printer_input.size();
+ double dstatus = m_report_status.status();
+
SpinMutex slck;
// procedure to process one height level. This will run in parallel
auto lvlfn =
- [this, &slck, &keys, &printer, slot, sd, ist, &pst, flpXY]
+ [this, &slck, &printer, increment, &dstatus, &pst]
(unsigned level_id)
{
if(canceled()) return;
- LayerRefs& lrange = m_printer_input[keys[level_id]];
+ PrintLayer& printlayer = m_printer_input[level_id];
// Switch to the appropriate layer in the printer
printer.begin_layer(level_id);
- for(auto& lyrref : lrange) { // for all layers in the current level
- if(canceled()) break;
- const Layer& sl = lyrref.lref; // get the layer reference
- const LayerCopies& copies = lyrref.copies;
-
- // Draw all the polygons in the slice to the actual layer.
- for(auto& cp : copies) {
- for(ExPolygon slice : sl) {
- // The order is important here:
- // apply rotation before translation...
- slice.rotate(double(cp.rotation));
- slice.translate(cp.shift(X), cp.shift(Y));
- if(flpXY) swapXY(slice);
- printer.draw_polygon(slice, level_id);
- }
- }
- }
+ for(const ClipperLib::Polygon& poly : printlayer.transformed_slices())
+ printer.draw_polygon(poly, level_id);
// Finish the layer for later saving it.
printer.finish_layer(level_id);
// Status indication guarded with the spinlock
- auto st = ist + unsigned(sd*level_id*slot/m_printer_input.size());
- { std::lock_guard lck(slck);
- if( st > pst) {
- report_status(*this, int(st), PRINT_STEP_LABELS[slapsRasterize]);
- pst = st;
- }
+ {
+ std::lock_guard lck(slck);
+ dstatus += increment;
+ double st = std::round(dstatus);
+ if(st > pst) {
+ m_report_status(*this, st,
+ PRINT_STEP_LABELS[slapsRasterize]);
+ pst = st;
+ }
}
};
@@ -1028,8 +1327,6 @@ void SLAPrint::process()
// Print all the layers in parallel
tbb::parallel_for(0, lvlcnt, lvlfn);
- // Fill statistics
- this->fill_statistics();
// Set statistics values to the printer
m_printer->set_statistics({(m_print_statistics.objects_used_material + m_print_statistics.support_used_material)/1000,
double(m_default_object_config.faded_layers.getInt()),
@@ -1047,17 +1344,16 @@ void SLAPrint::process()
support_points,
support_tree,
base_pool,
- slice_supports,
- index_slices
+ slice_supports
};
std::array print_program =
{
- rasterize,
- [](){} // validate
+ merge_slices_and_eval_stats,
+ rasterize
};
- unsigned st = min_objstatus;
+ double st = min_objstatus;
unsigned incr = 0;
BOOST_LOG_TRIVIAL(info) << "Start slicing process.";
@@ -1071,18 +1367,18 @@ void SLAPrint::process()
BOOST_LOG_TRIVIAL(info) << "Slicing object " << po->model_object()->name;
- for (int s = (int)step_ranges[idx_range]; s < (int)step_ranges[idx_range + 1]; ++s) {
- auto currentstep = (SLAPrintObjectStep)s;
+ for (int s = int(step_ranges[idx_range]); s < int(step_ranges[idx_range + 1]); ++s) {
+ auto currentstep = static_cast(s);
// Cancellation checking. Each step will check for cancellation
// on its own and return earlier gracefully. Just after it returns
// execution gets to this point and throws the canceled signal.
throw_if_canceled();
- st += unsigned(incr * ostepd);
+ st += incr * ostepd;
if(po->m_stepmask[currentstep] && po->set_started(currentstep)) {
- report_status(*this, int(st), OBJ_STEP_LABELS[currentstep]);
+ m_report_status(*this, st, OBJ_STEP_LABELS[currentstep]);
pobj_program[currentstep](*po);
throw_if_canceled();
po->set_done(currentstep);
@@ -1094,7 +1390,7 @@ void SLAPrint::process()
}
std::array printsteps = {
- slapsRasterize, slapsValidate
+ slapsMergeSlicesAndEval, slapsRasterize
};
// this would disable the rasterization step
@@ -1109,17 +1405,17 @@ void SLAPrint::process()
if(m_stepmask[currentstep] && set_started(currentstep))
{
- report_status(*this, int(st), PRINT_STEP_LABELS[currentstep]);
+ m_report_status(*this, st, PRINT_STEP_LABELS[currentstep]);
print_program[currentstep]();
throw_if_canceled();
set_done(currentstep);
}
- st += unsigned(PRINT_STEP_LEVELS[currentstep] * pstd);
+ st += PRINT_STEP_LEVELS[currentstep] * pstd;
}
// If everything vent well
- report_status(*this, 100, L("Slicing done"));
+ m_report_status(*this, 100, L("Slicing done"));
}
bool SLAPrint::invalidate_state_by_config_options(const std::vector &opt_keys)
@@ -1160,11 +1456,11 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector& instances) {
- const size_t inst_cnt = instances.size();
-
- size_t polygon_cnt = 0;
- for (const ExPolygon& polygon : input_polygons)
- polygon_cnt += polygon.holes.size() + 1;
-
- Polygons polygons;
- polygons.reserve(polygon_cnt * inst_cnt);
- for (const ExPolygon& polygon : input_polygons) {
- for (size_t i = 0; i < inst_cnt; ++i)
- {
- ExPolygon tmp = polygon;
- tmp.rotate(Geometry::rad2deg(instances[i].rotation));
- tmp.translate(instances[i].shift.x(), instances[i].shift.y());
- polygons_append(polygons, to_polygons(std::move(tmp)));
- }
- }
- return polygons;
- };
-
- double supports_volume = 0.0;
- double models_volume = 0.0;
-
- double estim_time = 0.0;
-
- size_t slow_layers = 0;
- size_t fast_layers = 0;
-
- // find highest object
- // Which is a better bet? To compare by max_z or by number of layers in the index?
- double max_z = 0.;
- size_t max_layers_cnt = 0;
- size_t highest_obj_idx = 0;
- for (SLAPrintObject *&po : m_objects) {
- const SLAPrintObject::SliceIndex& slice_index = po->get_slice_index();
- if (! slice_index.empty()) {
- double z = (-- slice_index.end())->first;
- size_t cnt = slice_index.size();
- //if (z > max_z) {
- if (cnt > max_layers_cnt) {
- max_layers_cnt = cnt;
- max_z = z;
- highest_obj_idx = &po - &m_objects.front();
- }
- }
- }
-
- const SLAPrintObject * highest_obj = m_objects[highest_obj_idx];
- const SLAPrintObject::SliceIndex& highest_obj_slice_index = highest_obj->get_slice_index();
-
- const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1);
- double fade_layer_time = init_exp_time;
-
- int sliced_layer_cnt = 0;
- for (const auto& layer : highest_obj_slice_index)
- {
- const double l_height = (layer.first == highest_obj_slice_index.begin()->first) ? init_layer_height : layer_height;
-
- // Calculation of the consumed material
-
- Polygons model_polygons;
- Polygons supports_polygons;
-
- for (SLAPrintObject * po : m_objects)
- {
- const SLAPrintObject::SliceRecord *record = nullptr;
- {
- const SLAPrintObject::SliceIndex& index = po->get_slice_index();
- auto key = layer.first;
- const SLAPrintObject::SliceIndex::const_iterator it_key = index.lower_bound(key - float(EPSILON));
- if (it_key == index.end() || it_key->first > key + EPSILON)
- continue;
- record = &it_key->second;
- }
-
- if (record->model_slices_idx != SLAPrintObject::SliceRecord::NONE)
- append(model_polygons, get_all_polygons(po->get_model_slices()[record->model_slices_idx], po->instances()));
-
- if (record->support_slices_idx != SLAPrintObject::SliceRecord::NONE)
- append(supports_polygons, get_all_polygons(po->get_support_slices()[record->support_slices_idx], po->instances()));
- }
-
- model_polygons = union_(model_polygons);
- double layer_model_area = 0;
- for (const Polygon& polygon : model_polygons)
- layer_model_area += polygon.area();
-
- if (layer_model_area != 0)
- models_volume += layer_model_area * l_height;
-
- if (!supports_polygons.empty() && !model_polygons.empty())
- supports_polygons = diff(supports_polygons, model_polygons);
- double layer_support_area = 0;
- for (const Polygon& polygon : supports_polygons)
- layer_support_area += polygon.area();
-
- if (layer_support_area != 0)
- supports_volume += layer_support_area * l_height;
-
- // Calculation of the slow and fast layers to the future controlling those values on FW
-
- const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill;
- const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt;
- if (is_fast_layer)
- fast_layers++;
- else
- slow_layers++;
-
-
- // Calculation of the printing time
-
- if (sliced_layer_cnt < 3)
- estim_time += init_exp_time;
- else if (fade_layer_time > exp_time)
- {
- fade_layer_time -= delta_fade_time;
- estim_time += fade_layer_time;
- }
- else
- estim_time += exp_time;
-
- estim_time += tilt_time;
-
- sliced_layer_cnt++;
- }
-
- m_print_statistics.support_used_material = supports_volume * SCALING_FACTOR * SCALING_FACTOR;
- m_print_statistics.objects_used_material = models_volume * SCALING_FACTOR * SCALING_FACTOR;
-
- // Estimated printing time
- // A layers count o the highest object
- if (max_layers_cnt == 0)
- m_print_statistics.estimated_print_time = "N/A";
- else
- m_print_statistics.estimated_print_time = get_time_dhms(float(estim_time));
-
- m_print_statistics.fast_layers_count = fast_layers;
- m_print_statistics.slow_layers_count = slow_layers;
-}
-
// Returns true if an object step is done on all objects and there's at least one object.
bool SLAPrint::is_step_done(SLAPrintObjectStep step) const
{
@@ -1377,11 +1513,15 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vectorinvalidate_all_steps();
} else if (step == slaposSupportPoints) {
- invalidated |= this->invalidate_steps({ slaposSupportTree, slaposBasePool, slaposSliceSupports, slaposIndexSlices });
- invalidated |= m_print->invalidate_step(slapsRasterize);
+ invalidated |= this->invalidate_steps({ slaposSupportTree, slaposBasePool, slaposSliceSupports });
+ invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
} else if (step == slaposSupportTree) {
- invalidated |= this->invalidate_steps({ slaposBasePool, slaposSliceSupports, slaposIndexSlices });
- invalidated |= m_print->invalidate_step(slapsRasterize);
+ invalidated |= this->invalidate_steps({ slaposBasePool, slaposSliceSupports });
+ invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
} else if (step == slaposBasePool) {
- invalidated |= this->invalidate_steps({slaposSliceSupports, slaposIndexSlices});
- invalidated |= m_print->invalidate_step(slapsRasterize);
+ invalidated |= this->invalidate_steps({slaposSliceSupports});
+ invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
} else if (step == slaposSliceSupports) {
- invalidated |= this->invalidate_step(slaposIndexSlices);
- invalidated |= m_print->invalidate_step(slapsRasterize);
- } else if(step == slaposIndexSlices) {
- invalidated |= m_print->invalidate_step(slapsRasterize);
+ invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
}
return invalidated;
}
@@ -1457,11 +1592,7 @@ double SLAPrintObject::get_elevation() const {
// 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;
- pcfg.min_wall_height_mm = m_config.pad_wall_height.getFloat();
- pcfg.min_wall_thickness_mm = m_config.pad_wall_thickness.getFloat();
- pcfg.edge_radius_mm = m_config.pad_edge_radius.getFloat();
- pcfg.max_merge_distance_mm = m_config.pad_max_merge_distance.getFloat();
+ sla::PoolConfig pcfg = make_pool_config(m_config);
ret += sla::get_pad_elevation(pcfg);
}
@@ -1485,8 +1616,11 @@ double SLAPrintObject::get_current_elevation() const
namespace { // dummy empty static containers for return values in some methods
const std::vector EMPTY_SLICES;
const TriangleMesh EMPTY_MESH;
+const ExPolygons EMPTY_SLICE;
}
+const SliceRecord SliceRecord::EMPTY(0, std::nanf(""), 0.f);
+
const std::vector& SLAPrintObject::get_support_points() const
{
return m_supportdata->support_points;
@@ -1499,16 +1633,19 @@ const std::vector &SLAPrintObject::get_support_slices() const
return m_supportdata->support_slices;
}
-const SLAPrintObject::SliceIndex &SLAPrintObject::get_slice_index() const
+const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const
{
- // assert(is_step_done(slaposIndexSlices));
- return m_slice_index;
-}
+ size_t idx = o == soModel ? m_model_slices_idx :
+ m_support_slices_idx;
-const std::vector &SLAPrintObject::get_model_slices() const
-{
- // assert(is_step_done(slaposObjectSlice));
- return m_model_slices;
+ if(m_po == nullptr) return EMPTY_SLICE;
+
+ const std::vector& v = o == soModel? m_po->get_model_slices() :
+ m_po->get_support_slices();
+
+ if(idx >= v.size()) return EMPTY_SLICE;
+
+ return idx >= v.size() ? EMPTY_SLICE : v[idx];
}
bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const
@@ -1602,7 +1739,8 @@ DynamicConfig SLAPrintStatistics::placeholders()
"print_time", "total_cost", "total_weight",
"objects_used_material", "support_used_material" })
config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}"));
- return config;
+
+ return config;
}
std::string SLAPrintStatistics::finalize_output_path(const std::string &path_in) const
@@ -1622,4 +1760,12 @@ std::string SLAPrintStatistics::finalize_output_path(const std::string &path_in)
return final_path;
}
+void SLAPrint::StatusReporter::operator()(
+ SLAPrint &p, double st, const std::string &msg, unsigned flags)
+{
+ m_st = st;
+ BOOST_LOG_TRIVIAL(info) << st << "% " << msg;
+ p.set_status(int(std::round(st)), msg, flags);
+}
+
} // namespace Slic3r
diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp
index eb927c769d..d4443d9155 100644
--- a/src/libslic3r/SLAPrint.hpp
+++ b/src/libslic3r/SLAPrint.hpp
@@ -6,12 +6,14 @@
#include "PrintExport.hpp"
#include "Point.hpp"
#include "MTUtils.hpp"
+#include
+#include "Zipper.hpp"
namespace Slic3r {
enum SLAPrintStep : unsigned int {
- slapsRasterize,
- slapsValidate,
+ slapsMergeSlicesAndEval,
+ slapsRasterize,
slapsCount
};
@@ -20,8 +22,7 @@ enum SLAPrintObjectStep : unsigned int {
slaposSupportPoints,
slaposSupportTree,
slaposBasePool,
- slaposSliceSupports,
- slaposIndexSlices,
+ slaposSliceSupports,
slaposCount
};
@@ -33,7 +34,9 @@ using _SLAPrintObjectBase =
// Layers according to quantized height levels. This will be consumed by
// the printer (rasterizer) in the SLAPrint class.
-using LevelID = long long;
+// using coord_t = long long;
+
+enum SliceOrigin { soSupport, soModel };
class SLAPrintObject : public _SLAPrintObjectBase
{
@@ -41,8 +44,14 @@ private: // Prevents erroneous use by other classes.
using Inherited = _SLAPrintObjectBase;
public:
+
+ // I refuse to grantee copying (Tamas)
+ SLAPrintObject(const SLAPrintObject&) = delete;
+ SLAPrintObject& operator=(const SLAPrintObject&) = delete;
+
const SLAPrintObjectConfig& config() const { return m_config; }
const Transform3d& trafo() const { return m_trafo; }
+ 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) {}
@@ -82,39 +91,145 @@ public:
// pad is not, then without the pad, otherwise the full value is returned.
double get_current_elevation() const;
- // These two methods should be callable on the client side (e.g. UI thread)
- // when the appropriate steps slaposObjectSlice and slaposSliceSupports
- // are ready. All the print objects are processed before slapsRasterize so
- // it is safe to call them during and/or after slapsRasterize.
- const std::vector& get_model_slices() const;
- const std::vector& get_support_slices() const;
-
// This method returns the support points of this SLAPrintObject.
const std::vector& get_support_points() const;
- // An index record referencing the slices
- // (get_model_slices(), get_support_slices()) where the keys are the height
- // levels of the model in scaled-clipper coordinates. The levels correspond
- // to the z coordinate of the object coordinate system.
- struct SliceRecord {
- using Key = float;
+ // The public Slice record structure. It corresponds to one printable layer.
+ class SliceRecord {
+ public:
+ // this will be the max limit of size_t
+ static const size_t NONE = size_t(-1);
- using Idx = size_t;
- static const Idx NONE = Idx(-1); // this will be the max limit of size_t
+ static const SliceRecord EMPTY;
- Idx model_slices_idx = NONE;
- Idx support_slices_idx = NONE;
+ private:
+ coord_t m_print_z = 0; // Top of the layer
+ float m_slice_z = 0.f; // Exact level of the slice
+ float m_height = 0.f; // Height of the sliced layer
+
+ size_t m_model_slices_idx = NONE;
+ size_t m_support_slices_idx = NONE;
+ const SLAPrintObject *m_po = nullptr;
+
+ public:
+
+ SliceRecord(coord_t key, float slicez, float height):
+ m_print_z(key), m_slice_z(slicez), m_height(height) {}
+
+ // The key will be the integer height level of the top of the layer.
+ coord_t print_level() const { return m_print_z; }
+
+ // Returns the exact floating point Z coordinate of the slice
+ float slice_level() const { return m_slice_z; }
+
+ // Returns the current layer height
+ float layer_height() const { return m_height; }
+
+ bool is_valid() const { return ! std::isnan(m_slice_z); }
+
+ const SLAPrintObject* print_obj() const { assert(m_po); return m_po; }
+
+ // Methods for setting the indices into the slice vectors.
+ void set_model_slice_idx(const SLAPrintObject &po, size_t id) {
+ m_po = &po; m_model_slices_idx = id;
+ }
+
+ void set_support_slice_idx(const SLAPrintObject& po, size_t id) {
+ m_po = &po; m_support_slices_idx = id;
+ }
+
+ const ExPolygons& get_slice(SliceOrigin o) const;
};
- using SliceIndex = std::map;
+private:
- // Retrieve the slice index which is readable only after slaposIndexSlices
- // is done.
- const SliceIndex& get_slice_index() const;
+ template inline static T level(const SliceRecord& sr) {
+ static_assert(std::is_arithmetic::value, "Arithmetic only!");
+ return std::is_integral::value ? T(sr.print_level()) : T(sr.slice_level());
+ }
- // I refuse to grantee copying (Tamas)
- SLAPrintObject(const SLAPrintObject&) = delete;
- SLAPrintObject& operator=(const SLAPrintObject&) = delete;
+ template inline static SliceRecord create_slice_record(T val) {
+ static_assert(std::is_arithmetic::value, "Arithmetic only!");
+ return std::is_integral::value ? SliceRecord{ coord_t(val), 0.f, 0.f } : SliceRecord{ 0, float(val), 0.f };
+ }
+
+ // This is a template method for searching the slice index either by
+ // an integer key: print_level or a floating point key: slice_level.
+ // The eps parameter gives the max deviation in + or - direction.
+ //
+ // This method can be used in const or non-const contexts as well.
+ template
+ static auto closest_slice_record(
+ Container& cont,
+ T lvl,
+ T eps = std::numeric_limits::max()) -> decltype (cont.begin())
+ {
+ if(cont.empty()) return cont.end();
+ if(cont.size() == 1 && std::abs(level(cont.front()) - lvl) > eps)
+ return cont.end();
+
+ SliceRecord query = create_slice_record(lvl);
+
+ auto it = std::lower_bound(cont.begin(), cont.end(), query,
+ [](const SliceRecord& r1,
+ const SliceRecord& r2)
+ {
+ return level(r1) < level(r2);
+ });
+
+ T diff = std::abs(level(*it) - lvl);
+
+ if(it != cont.begin()) {
+ auto it_prev = std::prev(it);
+ T diff_prev = std::abs(level(*it_prev) - lvl);
+ if(diff_prev < diff) { diff = diff_prev; it = it_prev; }
+ }
+
+ if(diff > eps) it = cont.end();
+
+ return it;
+ }
+
+ const std::vector& get_model_slices() const { return m_model_slices; }
+ const std::vector& get_support_slices() const;
+
+public:
+
+ // /////////////////////////////////////////////////////////////////////////
+ //
+ // These methods should be callable on the client side (e.g. UI thread)
+ // when the appropriate steps slaposObjectSlice and slaposSliceSupports
+ // are ready. All the print objects are processed before slapsRasterize so
+ // it is safe to call them during and/or after slapsRasterize.
+ //
+ // /////////////////////////////////////////////////////////////////////////
+
+ // Retrieve the slice index.
+ const std::vector& get_slice_index() const {
+ return m_slice_index;
+ }
+
+ // Search slice index for the closest slice to given print_level.
+ // max_epsilon gives the allowable deviation of the returned slice record's
+ // level.
+ const SliceRecord& closest_slice_to_print_level(
+ coord_t print_level,
+ coord_t max_epsilon = std::numeric_limits::max()) const
+ {
+ auto it = closest_slice_record(m_slice_index, print_level, max_epsilon);
+ return it == m_slice_index.end() ? SliceRecord::EMPTY : *it;
+ }
+
+ // Search slice index for the closest slice to given slice_level.
+ // max_epsilon gives the allowable deviation of the returned slice record's
+ // level. Use SliceRecord::is_valid() to check the result.
+ const SliceRecord& closest_slice_to_slice_level(
+ float slice_level,
+ float max_epsilon = std::numeric_limits::max()) const
+ {
+ auto it = closest_slice_record(m_slice_index, slice_level, max_epsilon);
+ return it == m_slice_index.end() ? SliceRecord::EMPTY : *it;
+ }
protected:
// to be called from SLAPrint only.
@@ -127,11 +242,12 @@ protected:
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false)
{ this->m_config.apply_only(other, keys, ignore_nonexistent); }
- void set_trafo(const Transform3d& trafo) {
- m_transformed_rmesh.invalidate([this, &trafo](){ m_trafo = trafo; });
+ void set_trafo(const Transform3d& trafo, bool left_handed) {
+ m_transformed_rmesh.invalidate([this, &trafo, left_handed](){ m_trafo = trafo; m_left_handed = left_handed; });
}
- void set_instances(const std::vector &instances) { m_instances = instances; }
+ template inline void set_instances(InstVec&& instances) { m_instances = std::forward(instances); }
+
// Invalidates the step, and its depending steps in SLAPrintObject and SLAPrint.
bool invalidate_step(SLAPrintObjectStep step);
bool invalidate_all_steps();
@@ -145,8 +261,12 @@ protected:
private:
// Object specific configuration, pulled from the configuration layer.
SLAPrintObjectConfig m_config;
+
// Translation in Z + Rotation by Y and Z + Scaling / Mirroring.
Transform3d m_trafo = Transform3d::Identity();
+ // m_trafo is left handed -> 3x3 affine transformation has negative determinant.
+ bool m_left_handed = false;
+
std::vector m_instances;
// Individual 2d slice polygons from lower z to higher z levels
@@ -154,11 +274,9 @@ private:
// Exact (float) height levels mapped to the slices. Each record contains
// the index to the model and the support slice vectors.
- SliceIndex m_slice_index;
+ std::vector m_slice_index;
- // The height levels corrected and scaled up in integer values. This will
- // be used at rasterization.
- std::vector m_level_ids;
+ std::vector m_model_height_levels;
// Caching the transformed (m_trafo) raw mesh of the object
mutable CachedObject m_transformed_rmesh;
@@ -169,6 +287,8 @@ private:
using PrintObjects = std::vector;
+using SliceRecord = SLAPrintObject::SliceRecord;
+
class TriangleMesh;
struct SLAPrintStatistics
@@ -200,6 +320,37 @@ struct SLAPrintStatistics
}
};
+// The implementation of creating zipped archives with wxWidgets
+template<> class LayerWriter {
+ Zipper m_zip;
+public:
+
+ LayerWriter(const std::string& zipfile_path): m_zip(zipfile_path) {}
+
+ void next_entry(const std::string& fname) { m_zip.add_entry(fname); }
+
+ void binary_entry(const std::string& fname,
+ const std::uint8_t* buf,
+ size_t l)
+ {
+ m_zip.add_entry(fname, buf, l);
+ }
+
+ template inline LayerWriter& operator<<(T&& arg) {
+ m_zip << std::forward(arg); return *this;
+ }
+
+ bool is_ok() const {
+ return true; // m_zip blows up if something goes wrong...
+ }
+
+ // After finalize, no writing to the archive will have an effect. The only
+ // valid operation is to dispose the object calling the destructor which
+ // should close the file. This method can throw and signal potential errors
+ // when flushing the archive. This is why its present.
+ void finalize() { m_zip.finalize(); }
+};
+
/**
* @brief This class is the high level FSM for the SLA printing process.
*
@@ -214,6 +365,7 @@ private: // Prevents erroneous use by other classes.
typedef PrintBaseWithState Inherited;
public:
+
SLAPrint(): m_stepmask(slapsCount, true) {}
virtual ~SLAPrint() override { this->clear(); }
@@ -229,31 +381,79 @@ public:
// Returns true if an object step is done on all objects and there's at least one object.
bool is_step_done(SLAPrintObjectStep step) const;
// Returns true if the last step was finished with success.
- bool finished() const override { return this->is_step_done(slaposIndexSlices) && this->Inherited::is_step_done(slapsRasterize); }
+ bool finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); }
- template void export_raster(const std::string& fname) {
- if(m_printer) m_printer->save(fname);
+ template
+ inline void export_raster(const std::string& fpath,
+ const std::string& projectname = "")
+ {
+ if(m_printer) m_printer->save(fpath, projectname);
}
+
const PrintObjects& objects() const { return m_objects; }
+ const SLAPrintConfig& print_config() const { return m_print_config; }
+ const SLAPrinterConfig& printer_config() const { return m_printer_config; }
+ const SLAMaterialConfig& material_config() const { return m_material_config; }
+ const SLAPrintObjectConfig& default_object_config() const { return m_default_object_config; }
+
std::string output_filename() const override;
const SLAPrintStatistics& print_statistics() const { return m_print_statistics; }
+ std::string validate() const override;
+
+ // An aggregation of SliceRecord-s from all the print objects for each
+ // occupied layer. Slice record levels dont have to match exactly.
+ // They are unified if the level difference is within +/- SCALED_EPSILON
+ class PrintLayer {
+ coord_t m_level;
+
+ // The collection of slice records for the current level.
+ std::vector> m_slices;
+
+ std::vector m_transformed_slices;
+
+ template void transformed_slices(Container&& c) {
+ m_transformed_slices = std::forward(c);
+ }
+
+ friend void SLAPrint::process();
+
+ public:
+
+ explicit PrintLayer(coord_t lvl) : m_level(lvl) {}
+
+ // for being sorted in their container (see m_printer_input)
+ bool operator<(const PrintLayer& other) const {
+ return m_level < other.m_level;
+ }
+
+ void add(const SliceRecord& sr) { m_slices.emplace_back(sr); }
+
+ coord_t level() const { return m_level; }
+
+ auto slices() const -> const decltype (m_slices)& { return m_slices; }
+
+ const std::vector & transformed_slices() const {
+ return m_transformed_slices;
+ }
+ };
+
+ // The aggregated and leveled print records from various objects.
+ // TODO: use this structure for the preview in the future.
+ const std::vector& print_layers() const { return m_printer_input; }
+
private:
using SLAPrinter = FilePrinter;
using SLAPrinterPtr = std::unique_ptr;
+ // Implement same logic as in SLAPrintObject
+ bool invalidate_step(SLAPrintStep st);
+
// Invalidate steps based on a set of parameters changed.
bool invalidate_state_by_config_options(const std::vector &opt_keys);
- std::vector calculate_heights(const BoundingBoxf3& bb,
- float elevation,
- float initial_layer_height,
- float layer_height) const;
-
- void fill_statistics();
-
SLAPrintConfig m_print_config;
SLAPrinterConfig m_printer_config;
SLAMaterialConfig m_material_config;
@@ -262,23 +462,8 @@ private:
PrintObjects m_objects;
std::vector m_stepmask;
- // Definition of the print input map. It consists of the slices indexed
- // with scaled (clipper) Z coordinates. Also contains the instance
- // transformations in scaled and filtered version. This is enough for the
- // rasterizer to be able to draw every layer in the right position
- using Layer = ExPolygons;
- using LayerCopies = std::vector;
- struct LayerRef {
- std::reference_wrapper lref;
- std::reference_wrapper copies;
- LayerRef(const Layer& lyr, const LayerCopies& cp) :
- lref(std::cref(lyr)), copies(std::cref(cp)) {}
- };
-
- // One level may contain multiple slices from multiple objects and their
- // supports
- using LayerRefs = std::vector;
- std::map m_printer_input;
+ // Ready-made data for rasterization.
+ std::vector m_printer_input;
// The printer itself
SLAPrinterPtr m_printer;
@@ -286,6 +471,15 @@ private:
// Estimated print time, material consumed.
SLAPrintStatistics m_print_statistics;
+ class StatusReporter {
+ double m_st = 0;
+ public:
+ void operator() (SLAPrint& p, double st, const std::string& msg,
+ unsigned flags = SlicingStatus::DEFAULT);
+ double status() const { return m_st; }
+ } m_report_status;
+
+
friend SLAPrintObject;
};
diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index e978b58385..ea3d878888 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -20,9 +20,8 @@
// Disable synchronization of unselected instances
#define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0_ALPHA1)
-// Scene's GUI made using imgui library
-#define ENABLE_IMGUI (1 && ENABLE_1_42_0_ALPHA1)
-#define DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI (1 && ENABLE_IMGUI)
+// Disable imgui dialog for move, rotate and scale gizmos
+#define DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI (1 && ENABLE_1_42_0_ALPHA1)
// Use wxDataViewRender instead of wxDataViewCustomRenderer
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1)
@@ -34,8 +33,6 @@
// Changed algorithm to extract euler angles from rotation matrix
#define ENABLE_NEW_EULER_ANGLES (1 && ENABLE_1_42_0_ALPHA4)
-// Added minimum threshold for click and drag movements
-#define ENABLE_MOVE_MIN_THRESHOLD (1 && ENABLE_1_42_0_ALPHA4)
// Modified initial default placement of generic subparts
#define ENABLE_GENERIC_SUBPARTS_PLACEMENT (1 && ENABLE_1_42_0_ALPHA4)
// Bunch of fixes related to volumes centering
@@ -59,4 +56,5 @@
// 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 eaa11b7381..bfba364af6 100644
--- a/src/libslic3r/TriangleMesh.cpp
+++ b/src/libslic3r/TriangleMesh.cpp
@@ -55,7 +55,7 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& f
stl.stats.original_num_facets = stl.stats.number_of_facets;
stl_allocate(&stl);
- for (int i = 0; i < stl.stats.number_of_facets; i++) {
+ for (uint32_t i = 0; i < stl.stats.number_of_facets; i++) {
stl_facet facet;
facet.vertex[0] = points[facets[i](0)].cast();
facet.vertex[1] = points[facets[i](1)].cast();
@@ -125,9 +125,9 @@ void TriangleMesh::repair()
float tolerance = stl.stats.shortest_edge;
float increment = stl.stats.bounding_diameter / 10000.0;
int iterations = 2;
- if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
+ if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
for (int i = 0; i < iterations; i++) {
- if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
+ if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
//printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations);
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_nearby";
@@ -143,7 +143,7 @@ void TriangleMesh::repair()
}
// remove_unconnected
- if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
+ if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets";
#endif /* SLIC3R_TRACE_REPAIR */
@@ -212,9 +212,9 @@ void TriangleMesh::check_topology()
float tolerance = stl.stats.shortest_edge;
float increment = stl.stats.bounding_diameter / 10000.0;
int iterations = 2;
- if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
+ if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
for (int i = 0; i < iterations; i++) {
- if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
+ if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
//printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations);
stl_check_facets_nearby(&stl, tolerance);
//printf(" Fixed %d edges.\n", stl.stats.edges_fixed - last_edges_fixed);
@@ -273,6 +273,11 @@ void TriangleMesh::translate(float x, float y, float z)
stl_invalidate_shared_vertices(&this->stl);
}
+void TriangleMesh::translate(const Vec3f &displacement)
+{
+ translate(displacement(0), displacement(1), displacement(2));
+}
+
void TriangleMesh::rotate(float angle, const Axis &axis)
{
if (angle == 0.f)
@@ -314,10 +319,15 @@ void TriangleMesh::mirror(const Axis &axis)
stl_invalidate_shared_vertices(&this->stl);
}
-void TriangleMesh::transform(const Transform3d& t)
+void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed)
{
stl_transform(&stl, t);
stl_invalidate_shared_vertices(&stl);
+ if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) {
+ // Left handed transformation is being applied. It is a good idea to flip the faces and their normals.
+ this->repair();
+ stl_reverse_all_facets(&stl);
+ }
}
void TriangleMesh::align_to_origin()
@@ -338,113 +348,79 @@ void TriangleMesh::rotate(double angle, Point* center)
this->translate(c(0), c(1), 0);
}
-bool TriangleMesh::has_multiple_patches() const
+/**
+ * Calculates whether or not the mesh is splittable.
+ */
+bool TriangleMesh::is_splittable() const
{
- // we need neighbors
- if (!this->repaired)
- throw std::runtime_error("split() requires repair()");
-
- if (this->stl.stats.number_of_facets == 0)
- return false;
+ std::vector visited;
+ find_unvisited_neighbors(visited);
- std::vector facet_queue(this->stl.stats.number_of_facets, 0);
- std::vector facet_visited(this->stl.stats.number_of_facets, false);
- int facet_queue_cnt = 1;
- facet_queue[0] = 0;
- facet_visited[0] = true;
- while (facet_queue_cnt > 0) {
- int facet_idx = facet_queue[-- facet_queue_cnt];
- facet_visited[facet_idx] = true;
- for (int j = 0; j < 3; ++ j) {
- int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
- if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
- facet_queue[facet_queue_cnt ++] = neighbor_idx;
- }
- }
-
- // If any of the face was not visited at the first time, return "multiple bodies".
- for (int facet_idx = 0; facet_idx < this->stl.stats.number_of_facets; ++ facet_idx)
- if (! facet_visited[facet_idx])
- return true;
- return false;
+ // Try finding an unvisited facet. If there are none, the mesh is not splittable.
+ auto it = std::find(visited.begin(), visited.end(), false);
+ return it != visited.end();
}
-size_t TriangleMesh::number_of_patches() const
+/**
+ * Visit all unvisited neighboring facets that are reachable from the first unvisited facet,
+ * and return them.
+ *
+ * @param facet_visited A reference to a vector of booleans. Contains whether or not a
+ * facet with the same index has been visited.
+ * @return A deque with all newly visited facets.
+ */
+std::deque TriangleMesh::find_unvisited_neighbors(std::vector &facet_visited) const
{
- // we need neighbors
+ // Make sure we're not operating on a broken mesh.
if (!this->repaired)
- throw std::runtime_error("split() requires repair()");
-
- if (this->stl.stats.number_of_facets == 0)
- return false;
+ throw std::runtime_error("find_unvisited_neighbors() requires repair()");
- std::vector facet_queue(this->stl.stats.number_of_facets, 0);
- std::vector facet_visited(this->stl.stats.number_of_facets, false);
- int facet_queue_cnt = 0;
- size_t num_bodies = 0;
- for (;;) {
- // Find a seeding triangle for a new body.
- int facet_idx = 0;
- for (; facet_idx < this->stl.stats.number_of_facets; ++ facet_idx)
- if (! facet_visited[facet_idx]) {
- // A seed triangle was found.
- facet_queue[facet_queue_cnt ++] = facet_idx;
- facet_visited[facet_idx] = true;
- break;
- }
- if (facet_idx == this->stl.stats.number_of_facets)
- // No seed found.
- break;
- ++ num_bodies;
- while (facet_queue_cnt > 0) {
- int facet_idx = facet_queue[-- facet_queue_cnt];
- facet_visited[facet_idx] = true;
- for (int j = 0; j < 3; ++ j) {
- int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
- if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
- facet_queue[facet_queue_cnt ++] = neighbor_idx;
- }
- }
+ // If the visited list is empty, populate it with false for every facet.
+ if (facet_visited.empty())
+ facet_visited = std::vector(this->stl.stats.number_of_facets, false);
+
+ // Find the first unvisited facet.
+ std::queue facet_queue;
+ std::deque facets;
+ auto facet = std::find(facet_visited.begin(), facet_visited.end(), false);
+ if (facet != facet_visited.end()) {
+ uint32_t idx = uint32_t(facet - facet_visited.begin());
+ facet_queue.push(idx);
+ facet_visited[idx] = true;
+ facets.emplace_back(idx);
}
- return num_bodies;
+ // Traverse all reachable neighbors and mark them as visited.
+ while (! facet_queue.empty()) {
+ uint32_t facet_idx = facet_queue.front();
+ facet_queue.pop();
+ for (int neighbor_idx : this->stl.neighbors_start[facet_idx].neighbor)
+ if (neighbor_idx != -1 && ! facet_visited[neighbor_idx]) {
+ facet_queue.push(uint32_t(neighbor_idx));
+ facet_visited[neighbor_idx] = true;
+ facets.emplace_back(uint32_t(neighbor_idx));
+ }
+ }
+
+ return facets;
}
+/**
+ * Splits a mesh into multiple meshes when possible.
+ *
+ * @return A TriangleMeshPtrs with the newly created meshes.
+ */
TriangleMeshPtrs TriangleMesh::split() const
{
- TriangleMeshPtrs meshes;
- std::vector facet_visited(this->stl.stats.number_of_facets, false);
-
- // we need neighbors
- if (!this->repaired)
- throw std::runtime_error("split() requires repair()");
-
- // loop while we have remaining facets
+ // Loop while we have remaining facets.
+ std::vector facet_visited;
+ TriangleMeshPtrs meshes;
for (;;) {
- // get the first facet
- std::queue facet_queue;
- std::deque facets;
- for (int facet_idx = 0; facet_idx < this->stl.stats.number_of_facets; ++ facet_idx) {
- if (! facet_visited[facet_idx]) {
- // if facet was not seen put it into queue and start searching
- facet_queue.push(facet_idx);
- break;
- }
- }
- if (facet_queue.empty())
+ std::deque facets = find_unvisited_neighbors(facet_visited);
+ if (facets.empty())
break;
- while (! facet_queue.empty()) {
- int facet_idx = facet_queue.front();
- facet_queue.pop();
- if (! facet_visited[facet_idx]) {
- facets.emplace_back(facet_idx);
- for (int j = 0; j < 3; ++ j)
- facet_queue.push(this->stl.neighbors_start[facet_idx].neighbor[j]);
- facet_visited[facet_idx] = true;
- }
- }
-
+ // Create a new mesh for the part that was just split off.
TriangleMesh* mesh = new TriangleMesh;
meshes.emplace_back(mesh);
mesh->stl.stats.type = inmemory;
@@ -452,14 +428,15 @@ TriangleMeshPtrs TriangleMesh::split() const
mesh->stl.stats.original_num_facets = mesh->stl.stats.number_of_facets;
stl_clear_error(&mesh->stl);
stl_allocate(&mesh->stl);
-
+
+ // Assign the facets to the new mesh.
bool first = true;
- for (std::deque::const_iterator facet = facets.begin(); facet != facets.end(); ++ facet) {
+ for (auto facet = facets.begin(); facet != facets.end(); ++ facet) {
mesh->stl.facet_start[facet - facets.begin()] = this->stl.facet_start[*facet];
stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first);
}
}
-
+
return meshes;
}
@@ -476,7 +453,7 @@ void TriangleMesh::merge(const TriangleMesh &mesh)
stl_reallocate(&this->stl);
// copy facets
- for (int i = 0; i < mesh.stl.stats.number_of_facets; ++ i)
+ for (uint32_t i = 0; i < mesh.stl.stats.number_of_facets; ++ i)
this->stl.facet_start[number_of_facets + i] = mesh.stl.facet_start[i];
// update size
@@ -489,7 +466,7 @@ ExPolygons TriangleMesh::horizontal_projection() const
{
Polygons pp;
pp.reserve(this->stl.stats.number_of_facets);
- for (int i = 0; i < this->stl.stats.number_of_facets; ++ i) {
+ for (uint32_t i = 0; i < this->stl.stats.number_of_facets; ++ i) {
stl_facet* facet = &this->stl.facet_start[i];
Polygon p;
p.points.resize(3);
@@ -531,7 +508,7 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) c
BoundingBoxf3 bbox;
if (stl.v_shared == nullptr) {
// Using the STL faces.
- for (int i = 0; i < this->facets_count(); ++ i) {
+ for (size_t i = 0; i < this->facets_count(); ++ i) {
const stl_facet &facet = this->stl.facet_start[i];
for (size_t j = 0; j < 3; ++ j)
bbox.merge(trafo * facet.vertex[j].cast());
@@ -656,7 +633,7 @@ void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type
};
std::vector edges_map;
edges_map.assign(this->mesh->stl.stats.number_of_facets * 3, EdgeToFace());
- for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx)
+ for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx)
for (int i = 0; i < 3; ++ i) {
EdgeToFace &e2f = edges_map[facet_idx*3+i];
e2f.vertex_low = this->mesh->stl.v_indices[facet_idx].vertex[i];
@@ -905,7 +882,6 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal;
// We may ignore this edge for slicing purposes, but we may still use it for object cutting.
FacetSliceType result = Slicing;
- const stl_neighbors &nbr = this->mesh->stl.neighbors_start[facet_idx];
if (min_z == max_z) {
// All three vertices are aligned with slice_z.
line_out->edge_type = feHorizontal;
@@ -917,8 +893,6 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
}
} else {
// Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane.
- int nbr_idx = j % 3;
- int nbr_face = nbr.neighbor[nbr_idx];
// Is the third vertex below the cutting plane?
bool third_below = v0.z() < slice_z || v1.z() < slice_z || v2.z() < slice_z;
// Two vertices on the cutting plane, the third vertex is below the plane. Consider the edge to be part of the slice
@@ -1697,7 +1671,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - slicing object";
float scaled_z = scale_(z);
- for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) {
+ for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) {
stl_facet* facet = &this->mesh->stl.facet_start[facet_idx];
// find facet extents
diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp
index a4387e5c1e..d389500c69 100644
--- a/src/libslic3r/TriangleMesh.hpp
+++ b/src/libslic3r/TriangleMesh.hpp
@@ -40,6 +40,7 @@ public:
void scale(float factor);
void scale(const Vec3d &versor);
void translate(float x, float y, float z);
+ void translate(const Vec3f &displacement);
void rotate(float angle, const Axis &axis);
void rotate(float angle, const Vec3d& axis);
void rotate_x(float angle) { this->rotate(angle, X); }
@@ -49,7 +50,7 @@ public:
void mirror_x() { this->mirror(X); }
void mirror_y() { this->mirror(Y); }
void mirror_z() { this->mirror(Z); }
- void transform(const Transform3d& t);
+ void transform(const Transform3d& t, bool fix_left_handed = false);
void align_to_origin();
void rotate(double angle, Point* center);
TriangleMeshPtrs split() const;
@@ -68,18 +69,14 @@ public:
size_t facets_count() const { return this->stl.stats.number_of_facets; }
bool empty() const { return this->facets_count() == 0; }
- // Returns true, if there are two and more connected patches in the mesh.
- // Returns false, if one or zero connected patch is in the mesh.
- bool has_multiple_patches() const;
-
- // Count disconnected triangle patches.
- size_t number_of_patches() const;
+ bool is_splittable() const;
stl_file stl;
bool repaired;
private:
void require_shared_vertices();
+ std::deque find_unvisited_neighbors(std::vector &facet_visited) const;
friend class TriangleMeshSlicer;
};
diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp
index c13df45462..dfd72b7a91 100644
--- a/src/libslic3r/Utils.hpp
+++ b/src/libslic3r/Utils.hpp
@@ -208,7 +208,7 @@ public:
// Shorten the dhms time by removing the seconds, rounding the dhm to full minutes
// and removing spaces.
-static std::string short_time(const std::string &time)
+inline std::string short_time(const std::string &time)
{
// Parse the dhms time format.
int days = 0;
@@ -247,7 +247,7 @@ static std::string short_time(const std::string &time)
}
// Returns the given time is seconds in format DDd HHh MMm SSs
-static std::string get_time_dhms(float time_in_secs)
+inline std::string get_time_dhms(float time_in_secs)
{
int days = (int)(time_in_secs / 86400.0f);
time_in_secs -= (float)days * 86400.0f;
diff --git a/src/libslic3r/Zipper.cpp b/src/libslic3r/Zipper.cpp
new file mode 100644
index 0000000000..4466f1b045
--- /dev/null
+++ b/src/libslic3r/Zipper.cpp
@@ -0,0 +1,223 @@
+#include
+#include
+#include
+
+#include "Zipper.hpp"
+#include "miniz/miniz_zip.h"
+#include
+
+#include "I18N.hpp"
+
+//! macro used to mark string used at localization,
+//! return same string
+#define L(s) Slic3r::I18N::translate(s)
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800 || __cplusplus < 201103L
+ #define SLIC3R_NORETURN
+#elif __cplusplus >= 201103L
+ #define SLIC3R_NORETURN [[noreturn]]
+#endif
+
+namespace Slic3r {
+
+class Zipper::Impl {
+public:
+ mz_zip_archive arch;
+ std::string m_zipname;
+
+ static std::string get_errorstr(mz_zip_error mz_err)
+ {
+ switch (mz_err)
+ {
+ case MZ_ZIP_NO_ERROR:
+ return "no error";
+ case MZ_ZIP_UNDEFINED_ERROR:
+ return L("undefined error");
+ case MZ_ZIP_TOO_MANY_FILES:
+ return L("too many files");
+ case MZ_ZIP_FILE_TOO_LARGE:
+ return L("file too large");
+ case MZ_ZIP_UNSUPPORTED_METHOD:
+ return L("unsupported method");
+ case MZ_ZIP_UNSUPPORTED_ENCRYPTION:
+ return L("unsupported encryption");
+ case MZ_ZIP_UNSUPPORTED_FEATURE:
+ return L("unsupported feature");
+ case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR:
+ return L("failed finding central directory");
+ case MZ_ZIP_NOT_AN_ARCHIVE:
+ return L("not a ZIP archive");
+ case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED:
+ return L("invalid header or archive is corrupted");
+ case MZ_ZIP_UNSUPPORTED_MULTIDISK:
+ return L("unsupported multidisk archive");
+ case MZ_ZIP_DECOMPRESSION_FAILED:
+ return L("decompression failed or archive is corrupted");
+ case MZ_ZIP_COMPRESSION_FAILED:
+ return L("compression failed");
+ case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE:
+ return L("unexpected decompressed size");
+ case MZ_ZIP_CRC_CHECK_FAILED:
+ return L("CRC-32 check failed");
+ case MZ_ZIP_UNSUPPORTED_CDIR_SIZE:
+ return L("unsupported central directory size");
+ case MZ_ZIP_ALLOC_FAILED:
+ return L("allocation failed");
+ case MZ_ZIP_FILE_OPEN_FAILED:
+ return L("file open failed");
+ case MZ_ZIP_FILE_CREATE_FAILED:
+ return L("file create failed");
+ case MZ_ZIP_FILE_WRITE_FAILED:
+ return L("file write failed");
+ case MZ_ZIP_FILE_READ_FAILED:
+ return L("file read failed");
+ case MZ_ZIP_FILE_CLOSE_FAILED:
+ return L("file close failed");
+ case MZ_ZIP_FILE_SEEK_FAILED:
+ return L("file seek failed");
+ case MZ_ZIP_FILE_STAT_FAILED:
+ return L("file stat failed");
+ case MZ_ZIP_INVALID_PARAMETER:
+ return L("invalid parameter");
+ case MZ_ZIP_INVALID_FILENAME:
+ return L("invalid filename");
+ case MZ_ZIP_BUF_TOO_SMALL:
+ return L("buffer too small");
+ case MZ_ZIP_INTERNAL_ERROR:
+ return L("internal error");
+ case MZ_ZIP_FILE_NOT_FOUND:
+ return L("file not found");
+ case MZ_ZIP_ARCHIVE_TOO_LARGE:
+ return L("archive is too large");
+ case MZ_ZIP_VALIDATION_FAILED:
+ return L("validation failed");
+ case MZ_ZIP_WRITE_CALLBACK_FAILED:
+ return L("write calledback failed");
+ default:
+ break;
+ }
+
+ return "unknown error";
+ }
+
+ std::string formatted_errorstr() const
+ {
+ return L("Error with zip archive") + " " + m_zipname + ": " +
+ get_errorstr(arch.m_last_error) + "!";
+ }
+
+ SLIC3R_NORETURN void blow_up() const
+ {
+ throw std::runtime_error(formatted_errorstr());
+ }
+
+ bool is_alive()
+ {
+ return arch.m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED;
+ }
+};
+
+Zipper::Zipper(const std::string &zipfname, e_compression compression)
+{
+ m_impl.reset(new Impl());
+
+ m_compression = compression;
+ m_impl->m_zipname = zipfname;
+
+ memset(&m_impl->arch, 0, sizeof(m_impl->arch));
+
+ // Initialize the archive data
+ if(!mz_zip_writer_init_file(&m_impl->arch, zipfname.c_str(), 0))
+ m_impl->blow_up();
+}
+
+Zipper::~Zipper()
+{
+ if(m_impl->is_alive()) {
+ // Flush the current entry if not finished yet.
+ try { finish_entry(); } catch(...) {
+ BOOST_LOG_TRIVIAL(error) << m_impl->formatted_errorstr();
+ }
+
+ if(!mz_zip_writer_finalize_archive(&m_impl->arch))
+ BOOST_LOG_TRIVIAL(error) << m_impl->formatted_errorstr();
+ }
+
+ // The file should be closed no matter what...
+ if(!mz_zip_writer_end(&m_impl->arch))
+ BOOST_LOG_TRIVIAL(error) << m_impl->formatted_errorstr();
+}
+
+Zipper::Zipper(Zipper &&m):
+ m_impl(std::move(m.m_impl)),
+ m_data(std::move(m.m_data)),
+ m_entry(std::move(m.m_entry)),
+ m_compression(m.m_compression) {}
+
+Zipper &Zipper::operator=(Zipper &&m) {
+ m_impl = std::move(m.m_impl);
+ m_data = std::move(m.m_data);
+ m_entry = std::move(m.m_entry);
+ m_compression = m.m_compression;
+ return *this;
+}
+
+void Zipper::add_entry(const std::string &name)
+{
+ if(!m_impl->is_alive()) return;
+
+ finish_entry(); // finish previous business
+ m_entry = name;
+}
+
+void Zipper::add_entry(const std::string &name, const uint8_t *data, size_t l)
+{
+ if(!m_impl->is_alive()) return;
+
+ finish_entry();
+ mz_uint cmpr = MZ_NO_COMPRESSION;
+ switch (m_compression) {
+ case NO_COMPRESSION: cmpr = MZ_NO_COMPRESSION; break;
+ case FAST_COMPRESSION: cmpr = MZ_BEST_SPEED; break;
+ case TIGHT_COMPRESSION: cmpr = MZ_BEST_COMPRESSION; break;
+ }
+
+ if(!mz_zip_writer_add_mem(&m_impl->arch, name.c_str(), data, l, cmpr))
+ m_impl->blow_up();
+
+ m_entry.clear();
+ m_data.clear();
+}
+
+void Zipper::finish_entry()
+{
+ if(!m_impl->is_alive()) return;
+
+ if(!m_data.empty() && !m_entry.empty()) {
+ mz_uint compression = MZ_NO_COMPRESSION;
+
+ switch (m_compression) {
+ case NO_COMPRESSION: compression = MZ_NO_COMPRESSION; break;
+ case FAST_COMPRESSION: compression = MZ_BEST_SPEED; break;
+ case TIGHT_COMPRESSION: compression = MZ_BEST_COMPRESSION; break;
+ }
+
+ if(!mz_zip_writer_add_mem(&m_impl->arch, m_entry.c_str(),
+ m_data.c_str(),
+ m_data.size(),
+ compression)) m_impl->blow_up();
+ }
+
+ m_data.clear();
+ m_entry.clear();
+}
+
+void Zipper::finalize()
+{
+ finish_entry();
+
+ if(m_impl->is_alive()) if(!mz_zip_writer_finalize_archive(&m_impl->arch))
+ m_impl->blow_up();
+}
+
+}
diff --git a/src/libslic3r/Zipper.hpp b/src/libslic3r/Zipper.hpp
new file mode 100644
index 0000000000..7d95ffdac7
--- /dev/null
+++ b/src/libslic3r/Zipper.hpp
@@ -0,0 +1,90 @@
+#ifndef ZIPPER_HPP
+#define ZIPPER_HPP
+
+#include