diff --git a/.gitignore b/.gitignore
index c4df3f3f8..704289a22 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,4 +18,4 @@ local-lib
build-linux/*
deps/build-linux/*
**/.DS_Store
-/.idea/
+**/.idea/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 002cd3456..ac058c39c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -458,9 +458,8 @@ if (NOT EIGEN3_FOUND)
endif ()
include_directories(BEFORE SYSTEM ${EIGEN3_INCLUDE_DIR})
-# Find expat or use bundled version
-# Always use the system libexpat on Linux.
-
+# Find expat. We have our overriden FindEXPAT which exports libexpat target
+# no matter what.
find_package(EXPAT REQUIRED)
add_library(libexpat INTERFACE)
diff --git a/cmake/modules/FindCURL.cmake b/cmake/modules/FindCURL.cmake
index e0deafa45..ced591134 100644
--- a/cmake/modules/FindCURL.cmake
+++ b/cmake/modules/FindCURL.cmake
@@ -30,82 +30,101 @@
# ``CURL_VERSION_STRING``
# The version of curl found.
-# Look for the header file.
-find_path(CURL_INCLUDE_DIR NAMES curl/curl.h)
-mark_as_advanced(CURL_INCLUDE_DIR)
+# First, prefer config scripts
+set(_q "")
+if(CURL_FIND_QUIETLY)
+ set(_q QUIET)
+endif()
+find_package(CURL ${CURL_FIND_VERSION} CONFIG ${_q})
-if(NOT CURL_LIBRARY)
- # Look for the library (sorted from most current/relevant entry to least).
- find_library(CURL_LIBRARY_RELEASE NAMES
- curl
- # Windows MSVC prebuilts:
- curllib
- libcurl_imp
- curllib_static
- # Windows older "Win32 - MSVC" prebuilts (libcurl.lib, e.g. libcurl-7.15.5-win32-msvc.zip):
- libcurl
- # Static library on Windows
- libcurl_a
- )
- mark_as_advanced(CURL_LIBRARY_RELEASE)
-
- find_library(CURL_LIBRARY_DEBUG NAMES
- # Windows MSVC CMake builds in debug configuration on vcpkg:
- libcurl-d_imp
- libcurl-d
- # Static library on Windows, compiled in debug mode
- libcurl_a_debug
- )
- mark_as_advanced(CURL_LIBRARY_DEBUG)
-
- include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations_SLIC3R.cmake)
- select_library_configurations_SLIC3R(CURL)
+if(NOT CURL_FIND_QUIETLY)
+ if (NOT CURL_FOUND)
+ message(STATUS "Falling back to MODULE search for CURL...")
+ else()
+ message(STATUS "CURL found in ${CURL_DIR}")
+ endif()
endif()
-if(CURL_INCLUDE_DIR)
- foreach(_curl_version_header curlver.h curl.h)
- if(EXISTS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}")
- file(STRINGS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}" curl_version_str REGEX "^#define[\t ]+LIBCURL_VERSION[\t ]+\".*\"")
+if (NOT CURL_FOUND)
- string(REGEX REPLACE "^#define[\t ]+LIBCURL_VERSION[\t ]+\"([^\"]*)\".*" "\\1" CURL_VERSION_STRING "${curl_version_str}")
- unset(curl_version_str)
- break()
- endif()
- endforeach()
-endif()
+ # Look for the header file.
+ find_path(CURL_INCLUDE_DIR NAMES curl/curl.h)
+ mark_as_advanced(CURL_INCLUDE_DIR)
-include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs_SLIC3R.cmake)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS_SLIC3R(CURL
- REQUIRED_VARS CURL_LIBRARY CURL_INCLUDE_DIR
- VERSION_VAR CURL_VERSION_STRING)
+ if(NOT CURL_LIBRARY)
+ # Look for the library (sorted from most current/relevant entry to least).
+ find_library(CURL_LIBRARY_RELEASE NAMES
+ curl
+ # Windows MSVC prebuilts:
+ curllib
+ libcurl_imp
+ curllib_static
+ # Windows older "Win32 - MSVC" prebuilts (libcurl.lib, e.g. libcurl-7.15.5-win32-msvc.zip):
+ libcurl
+ # Static library on Windows
+ libcurl_a
+ )
+ mark_as_advanced(CURL_LIBRARY_RELEASE)
-if(CURL_FOUND)
- set(CURL_LIBRARIES ${CURL_LIBRARY})
- set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR})
+ find_library(CURL_LIBRARY_DEBUG NAMES
+ # Windows MSVC CMake builds in debug configuration on vcpkg:
+ libcurl-d_imp
+ libcurl-d
+ # Static library on Windows, compiled in debug mode
+ libcurl_a_debug
+ )
+ mark_as_advanced(CURL_LIBRARY_DEBUG)
- if(NOT TARGET CURL::libcurl)
- add_library(CURL::libcurl UNKNOWN IMPORTED)
- set_target_properties(CURL::libcurl PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIRS}")
+ include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations_SLIC3R.cmake)
+ select_library_configurations_SLIC3R(CURL)
+ endif()
- if(EXISTS "${CURL_LIBRARY}")
+ if(CURL_INCLUDE_DIR)
+ foreach(_curl_version_header curlver.h curl.h)
+ if(EXISTS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}")
+ file(STRINGS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}" curl_version_str REGEX "^#define[\t ]+LIBCURL_VERSION[\t ]+\".*\"")
+
+ string(REGEX REPLACE "^#define[\t ]+LIBCURL_VERSION[\t ]+\"([^\"]*)\".*" "\\1" CURL_VERSION_STRING "${curl_version_str}")
+ unset(curl_version_str)
+ break()
+ endif()
+ endforeach()
+ endif()
+
+ include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs_SLIC3R.cmake)
+ FIND_PACKAGE_HANDLE_STANDARD_ARGS_SLIC3R(CURL
+ REQUIRED_VARS CURL_LIBRARY CURL_INCLUDE_DIR
+ VERSION_VAR CURL_VERSION_STRING)
+
+ if(CURL_FOUND)
+ set(CURL_LIBRARIES ${CURL_LIBRARY})
+ set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR})
+
+ if(NOT TARGET CURL::libcurl)
+ add_library(CURL::libcurl UNKNOWN IMPORTED)
set_target_properties(CURL::libcurl PROPERTIES
- IMPORTED_LINK_INTERFACE_LANGUAGES "C"
- IMPORTED_LOCATION "${CURL_LIBRARY}")
- endif()
- if(CURL_LIBRARY_RELEASE)
- set_property(TARGET CURL::libcurl APPEND PROPERTY
- IMPORTED_CONFIGURATIONS RELEASE)
- set_target_properties(CURL::libcurl PROPERTIES
- IMPORTED_LINK_INTERFACE_LANGUAGES "C"
- IMPORTED_LOCATION_RELEASE "${CURL_LIBRARY_RELEASE}")
- endif()
- if(CURL_LIBRARY_DEBUG)
- set_property(TARGET CURL::libcurl APPEND PROPERTY
- IMPORTED_CONFIGURATIONS DEBUG)
- set_target_properties(CURL::libcurl PROPERTIES
- IMPORTED_LINK_INTERFACE_LANGUAGES "C"
- IMPORTED_LOCATION_DEBUG "${CURL_LIBRARY_DEBUG}")
+ INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIRS}")
+
+ if(EXISTS "${CURL_LIBRARY}")
+ set_target_properties(CURL::libcurl PROPERTIES
+ IMPORTED_LINK_INTERFACE_LANGUAGES "C"
+ IMPORTED_LOCATION "${CURL_LIBRARY}")
+ endif()
+ if(CURL_LIBRARY_RELEASE)
+ set_property(TARGET CURL::libcurl APPEND PROPERTY
+ IMPORTED_CONFIGURATIONS RELEASE)
+ set_target_properties(CURL::libcurl PROPERTIES
+ IMPORTED_LINK_INTERFACE_LANGUAGES "C"
+ IMPORTED_LOCATION_RELEASE "${CURL_LIBRARY_RELEASE}")
+ endif()
+ if(CURL_LIBRARY_DEBUG)
+ set_property(TARGET CURL::libcurl APPEND PROPERTY
+ IMPORTED_CONFIGURATIONS DEBUG)
+ set_target_properties(CURL::libcurl PROPERTIES
+ IMPORTED_LINK_INTERFACE_LANGUAGES "C"
+ IMPORTED_LOCATION_DEBUG "${CURL_LIBRARY_DEBUG}")
+ endif()
endif()
endif()
-endif()
+
+endif (NOT CURL_FOUND)
diff --git a/deps/Boost/Boost.cmake b/deps/Boost/Boost.cmake
index fd2f202e9..d819e28cf 100644
--- a/deps/Boost/Boost.cmake
+++ b/deps/Boost/Boost.cmake
@@ -75,7 +75,9 @@ file(TO_NATIVE_PATH ${DESTDIR}/usr/local/ _prefix)
set(_boost_flags "")
if (UNIX)
set(_boost_flags "cflags=-fPIC;cxxflags=-fPIC")
-elseif(APPLE)
+endif ()
+
+if(APPLE)
set(_boost_flags
"cflags=-fPIC -mmacosx-version-min=${DEP_OSX_TARGET};"
"cxxflags=-fPIC -mmacosx-version-min=${DEP_OSX_TARGET};"
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index d129ff1c2..eb0c420fa 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -188,6 +188,7 @@ endif ()
include(JPEG/JPEG.cmake)
include(TIFF/TIFF.cmake)
+include(NanoSVG/NanoSVG.cmake)
include(wxWidgets/wxWidgets.cmake)
set(_dep_list
diff --git a/deps/GLEW/GLEW.cmake b/deps/GLEW/GLEW.cmake
index ae2832577..76ffc0a8d 100644
--- a/deps/GLEW/GLEW.cmake
+++ b/deps/GLEW/GLEW.cmake
@@ -4,8 +4,8 @@ find_package(OpenGL QUIET REQUIRED)
prusaslicer_add_cmake_project(
GLEW
- URL https://sourceforge.net/projects/glew/files/glew/2.1.0/glew-2.1.0.zip
- URL_HASH SHA256=2700383d4de2455f06114fbaf872684f15529d4bdc5cdea69b5fb0e9aa7763f1
+ URL https://sourceforge.net/projects/glew/files/glew/2.2.0/glew-2.2.0.zip
+ URL_HASH SHA256=a9046a913774395a095edcc0b0ac2d81c3aacca61787b39839b941e9be14e0d4
SOURCE_SUBDIR build/cmake
CMAKE_ARGS
-DBUILD_UTILS=OFF
diff --git a/deps/NanoSVG/NanoSVG.cmake b/deps/NanoSVG/NanoSVG.cmake
new file mode 100644
index 000000000..9623d3226
--- /dev/null
+++ b/deps/NanoSVG/NanoSVG.cmake
@@ -0,0 +1,4 @@
+prusaslicer_add_cmake_project(NanoSVG
+ URL https://github.com/memononen/nanosvg/archive/4c8f0139b62c6e7faa3b67ce1fbe6e63590ed148.zip
+ URL_HASH SHA256=584e084af1a75bf633f79753ce2f6f6ec8686002ca27f35f1037c25675fecfb6
+)
\ No newline at end of file
diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake
index bf5fd6289..e2d48ce65 100644
--- a/deps/wxWidgets/wxWidgets.cmake
+++ b/deps/wxWidgets/wxWidgets.cmake
@@ -1,5 +1,3 @@
-set(_wx_git_tag v3.1.4-patched)
-
set(_wx_toolkit "")
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(_gtk_ver 2)
@@ -15,11 +13,9 @@ if (UNIX AND NOT APPLE) # wxWidgets will not use char as the underlying type for
endif()
prusaslicer_add_cmake_project(wxWidgets
- # GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets"
- # GIT_TAG tm_cross_compile #${_wx_git_tag}
- URL https://github.com/prusa3d/wxWidgets/archive/489f6118256853cf5b299d595868641938566cdb.zip
- URL_HASH SHA256=5b22d465377cedd8044bba69bea958b248953fd3628c1de4913a84d4e6f6175b
- DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG
+ URL https://github.com/prusa3d/wxWidgets/archive/e616df45b3a8aedc31beb5540df793dd1f0f3914.zip
+ URL_HASH SHA256=30bc6cad64dce5cdc755e3a4119cfeb24c12d43279e1e062d8ac350d3880f315
+ DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG dep_NanoSVG
CMAKE_ARGS
-DwxBUILD_PRECOMP=ON
${_wx_toolkit}
@@ -32,13 +28,16 @@ prusaslicer_add_cmake_project(wxWidgets
-DwxUSE_OPENGL=ON
-DwxUSE_LIBPNG=sys
-DwxUSE_ZLIB=sys
- -DwxUSE_REGEX=builtin
+ -DwxUSE_NANOSVG=sys
+ -DwxUSE_NANOSVG_EXTERNAL=ON
+ -DwxUSE_REGEX=OFF
-DwxUSE_LIBXPM=builtin
-DwxUSE_LIBJPEG=sys
-DwxUSE_LIBTIFF=sys
-DwxUSE_EXPAT=sys
-DwxUSE_LIBSDL=OFF
-DwxUSE_XTEST=OFF
+ -DwxUSE_GLCANVAS_EGL=OFF
)
if (MSVC)
diff --git a/resources/profiles/Creality.idx b/resources/profiles/Creality.idx
index 1ff148aad..8f7dd0e8c 100644
--- a/resources/profiles/Creality.idx
+++ b/resources/profiles/Creality.idx
@@ -1,3 +1,5 @@
+min_slic3r_version = 2.5.0-alpha0
+0.1.5 Added Ender-3 S1 Pro
min_slic3r_version = 2.4.1
0.1.4 Added Ender-3 Pro. Added M25 support for some printers.
min_slic3r_version = 2.4.0-rc
diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini
index 0fa14d8a8..ce39cd6d6 100644
--- a/resources/profiles/Creality.ini
+++ b/resources/profiles/Creality.ini
@@ -5,7 +5,7 @@
name = Creality
# Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded.
-config_version = 0.1.4
+config_version = 0.1.5
# Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Creality/
# changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@@ -21,7 +21,7 @@ technology = FFF
family = ENDER
bed_model = ender3_bed.stl
bed_texture = ender3.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER3BLTOUCH]
name = Creality Ender-3 BLTouch
@@ -30,7 +30,7 @@ technology = FFF
family = ENDER
bed_model = ender3_bed.stl
bed_texture = ender3.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER3PRO]
name = Creality Ender-3 Pro
@@ -39,7 +39,7 @@ technology = FFF
family = ENDER
bed_model = ender3_bed.stl
bed_texture = ender3.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER3V2]
name = Creality Ender-3 V2
@@ -48,7 +48,7 @@ technology = FFF
family = ENDER
bed_model = ender3v2_bed.stl
bed_texture = ender3v2.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER3S1]
name = Creality Ender-3 S1
@@ -57,7 +57,16 @@ technology = FFF
family = ENDER
bed_model = ender3v2_bed.stl
bed_texture = ender3v2.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
+
+[printer_model:ENDER3S1PRO]
+name = Creality Ender-3 S1 Pro
+variants = 0.4
+technology = FFF
+family = ENDER
+bed_model = ender3v2_bed.stl
+bed_texture = ender3v2.svg
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER3MAX]
name = Creality Ender-3 Max
@@ -66,7 +75,7 @@ technology = FFF
family = ENDER
bed_model = cr10v2_bed.stl
bed_texture = cr10spro.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER4]
name = Creality Ender-4
@@ -75,7 +84,7 @@ technology = FFF
family = ENDER
bed_model = ender3v2_bed.stl
bed_texture = ender3v2.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER5]
name = Creality Ender-5
@@ -84,7 +93,7 @@ technology = FFF
family = ENDER
bed_model = ender3_bed.stl
bed_texture = ender3.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER5PLUS]
name = Creality Ender-5 Plus
@@ -93,7 +102,7 @@ technology = FFF
family = ENDER
bed_model = ender5plus_bed.stl
bed_texture = ender5plus.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER6]
name = Creality Ender-6
@@ -102,7 +111,7 @@ technology = FFF
family = ENDER
bed_model = ender6_bed.stl
bed_texture = ender6.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER7]
name = Creality Ender-7
@@ -111,7 +120,7 @@ technology = FFF
family = ENDER
bed_model = ender7_bed.stl
bed_texture = ender7.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER2]
name = Creality Ender-2
@@ -120,7 +129,7 @@ technology = FFF
family = ENDER
bed_model = ender2_bed.stl
bed_texture = ender2.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER2PRO]
name = Creality Ender-2 Pro
@@ -129,7 +138,7 @@ technology = FFF
family = ENDER
bed_model = ender2pro_bed.stl
bed_texture = ender2pro.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR5PRO]
name = Creality CR-5 Pro
@@ -138,7 +147,7 @@ technology = FFF
family = CR
bed_model = cr5pro_bed.stl
bed_texture = cr5pro.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR5PROH]
name = Creality CR-5 Pro H
@@ -147,7 +156,7 @@ technology = FFF
family = CR
bed_model = cr5pro_bed.stl
bed_texture = cr5pro.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR6SE]
name = Creality CR-6 SE
@@ -156,7 +165,7 @@ technology = FFF
family = CR
bed_model = cr6se_bed.stl
bed_texture = cr6se.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR6MAX]
name = Creality CR-6 Max
@@ -165,7 +174,7 @@ technology = FFF
family = CR
bed_model = cr10s4_bed.stl
bed_texture = cr10s4.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10SMART]
name = Creality CR-10 SMART
@@ -174,7 +183,7 @@ technology = FFF
family = CR
bed_model = cr10v2_bed.stl
bed_texture = cr10spro.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10MINI]
name = Creality CR-10 Mini
@@ -183,7 +192,7 @@ technology = FFF
family = CR
bed_model = cr10mini_bed.stl
bed_texture = cr10mini.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10MAX]
name = Creality CR-10 Max
@@ -192,7 +201,7 @@ technology = FFF
family = CR
bed_model = cr10max_bed.stl
bed_texture = cr10max.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10]
name = Creality CR-10
@@ -201,7 +210,7 @@ technology = FFF
family = CR
bed_model = cr10_bed.stl
bed_texture = cr10.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10V2]
name = Creality CR-10 V2
@@ -210,7 +219,7 @@ technology = FFF
family = CR
bed_model = cr10v2_bed.stl
bed_texture = cr10.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10V3]
name = Creality CR-10 V3
@@ -219,7 +228,7 @@ technology = FFF
family = CR
bed_model = cr10v2_bed.stl
bed_texture = cr10.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10S]
name = Creality CR-10 S
@@ -228,7 +237,7 @@ technology = FFF
family = CR
bed_model = cr10_bed.stl
bed_texture = cr10.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10SPRO]
name = Creality CR-10 S Pro
@@ -237,7 +246,7 @@ technology = FFF
family = CR
bed_model = cr10v2_bed.stl
bed_texture = cr10spro.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10SPROV2]
name = Creality CR-10 S Pro V2
@@ -246,7 +255,7 @@ technology = FFF
family = CR
bed_model = cr10v2_bed.stl
bed_texture = cr10.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10S4]
name = Creality CR-10 S4
@@ -255,7 +264,7 @@ technology = FFF
family = CR
bed_model = cr10s4_bed.stl
bed_texture = cr10s4.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10S5]
name = Creality CR-10 S5
@@ -264,7 +273,7 @@ technology = FFF
family = CR
bed_model = cr10s5_bed.stl
bed_texture = cr10s5.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR20]
name = Creality CR-20
@@ -273,7 +282,7 @@ technology = FFF
family = CR
bed_model = ender3_bed.stl
bed_texture = cr20.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR20PRO]
name = Creality CR-20 Pro
@@ -282,7 +291,7 @@ technology = FFF
family = CR
bed_model = ender3_bed.stl
bed_texture = cr20.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR200B]
name = Creality CR-200B
@@ -291,7 +300,7 @@ technology = FFF
family = CR
bed_model = cr200b_bed.stl
bed_texture = cr200b.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR8]
name = Creality CR-8
@@ -300,7 +309,7 @@ technology = FFF
family = CR
bed_model = cr8_bed.stl
bed_texture = cr8.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
#[printer_model:CRX]
#name = Creality CR-X
@@ -309,7 +318,7 @@ default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @
#family = CR-X
#bed_model = cr10v2_bed.stl
#bed_texture = cr10spro.svg
-#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
#[printer_model:CRXPRO]
#name = Creality CR-X Pro
@@ -318,7 +327,7 @@ default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @
#family = CR-X
#bed_model = cr10v2_bed.stl
#bed_texture = cr10spro.svg
-#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:SERMOOND1]
name = Creality Sermoon-D1
@@ -327,7 +336,7 @@ technology = FFF
family = SERMOON
bed_model = sermoond1_bed.stl
bed_texture = sermoond1.svg
-default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
+default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
# All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface.
@@ -842,6 +851,17 @@ filament_cost = 27.44
filament_density = 1.29
filament_colour = #C7F935
+[filament:Verbatim PLA @CREALITY]
+inherits = *PLA*
+filament_vendor = Verbatim
+temperature = 205
+bed_temperature = 60
+first_layer_temperature = 210
+first_layer_bed_temperature = 60
+filament_cost = 22.99
+filament_density = 1.24
+filament_colour = #001ca8
+
# Common printer preset
[printer:*common*]
printer_technology = FFF
@@ -970,10 +990,7 @@ renamed_from = "Creality ENDER-3 BLTouch"
printer_model = ENDER3BLTOUCH
[printer:Creality Ender-3 Pro]
-inherits = *common*; *pauseprint*
-renamed_from = "Creality Ender-3 Pro"
-bed_shape = 5x0,215x0,215x220,5x220
-max_print_height = 250
+inherits = Creality Ender-3; *pauseprint*
printer_model = ENDER3PRO
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3PRO\nPRINTER_HAS_BOWDEN
@@ -992,6 +1009,13 @@ max_print_height = 270
printer_model = ENDER3S1
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3S1
+[printer:Creality Ender-3 S1 Pro]
+inherits = *common*; *pauseprint*; *spriteextruder*
+bed_shape = 5x0,215x0,215x220,5x220
+max_print_height = 270
+printer_model = ENDER3S1PRO
+printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3S1PRO
+
[printer:Creality Ender-3 Max]
inherits = *common*; *pauseprint*
retract_length = 6
diff --git a/resources/profiles/Creality/ENDER3S1PRO_thumbnail.png b/resources/profiles/Creality/ENDER3S1PRO_thumbnail.png
new file mode 100644
index 000000000..d11396b05
Binary files /dev/null and b/resources/profiles/Creality/ENDER3S1PRO_thumbnail.png differ
diff --git a/resources/profiles/INAT.idx b/resources/profiles/INAT.idx
index a756b34b5..0446399a1 100644
--- a/resources/profiles/INAT.idx
+++ b/resources/profiles/INAT.idx
@@ -1,4 +1,6 @@
-min_slic3r_version = 2.3.1-beta
+min_slic3r_version = 2.5.0-alpha0
+0.0.4 Improve Proton X profiles, Add Proton XE-750 printer
+min_slic3r_version = 2.4.1
0.0.3 Set default filament profile.
0.0.2 Improved start gcode, changed filename format
0.0.1 Initial version
diff --git a/resources/profiles/INAT.ini b/resources/profiles/INAT.ini
index 3c1a753b5..0bba8c976 100644
--- a/resources/profiles/INAT.ini
+++ b/resources/profiles/INAT.ini
@@ -3,7 +3,7 @@
[vendor]
# Vendor name will be shown by the Config Wizard.
name = INAT
-config_version = 0.0.3
+config_version = 0.0.4
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/INAT/
###
@@ -24,9 +24,16 @@ technology = FFF
family = Proton
default_materials = PLA @PROTON_X
+[printer_model:PROTON_XE750]
+name = INAT Proton XE-750
+variants = 0.4
+technology = FFF
+family = Proton
+default_materials = PLA @PROTON_XE750
+
###
-### QUALITY DEFINITIONS
+### COMMON QUALITY DEFINITIONS
###
[print:*common*]
@@ -35,14 +42,15 @@ layer_height = 0.2
first_layer_height = 0.2
perimeters = 3
spiral_vase = 0
-top_solid_layers = 4
+top_solid_layers = 5
bottom_solid_layers = 3
-top_solid_min_thickness = 0.8
+top_solid_min_thickness = 1
bottom_solid_min_thickness = 0.6
extra_perimeters = 1
ensure_vertical_shell_thickness = 1
avoid_crossing_perimeters = 0
thin_walls = 0
+thick_bridges = 0
overhangs = 1
seam_position = aligned
external_perimeters_first = 0
@@ -76,7 +84,9 @@ support_material_auto = 1
support_material_threshold = 0
support_material_enforce_layers = 0
raft_layers = 0
-support_material_contact_distance = 0.2
+support_material_style = grid
+support_material_contact_distance = 0.25
+support_material_bottom_contact_distance = 0.3
support_material_pattern = rectilinear
support_material_with_sheath = 0
support_material_spacing = 5
@@ -92,8 +102,8 @@ perimeter_speed = 60
small_perimeter_speed = 75%
external_perimeter_speed = 50%
infill_speed = 80
-solid_infill_speed = 100%
-top_solid_infill_speed = 30
+solid_infill_speed = 80%
+top_solid_infill_speed = 20
support_material_speed = 80
support_material_interface_speed = 100%
bridge_speed = 60
@@ -126,7 +136,7 @@ infill_overlap = 25%
bridge_flow_ratio = 1
slice_closing_radius = 0.049
resolution = 0
-xy_size_compensation = 0
+xy_size_compensation = -0.05
elefant_foot_compensation = 0.3
clip_multipart_objects = 0
#output
@@ -138,47 +148,50 @@ gcode_label_objects = 0
output_filename_format = {input_filename_base}_{filament_type[0]}.gcode
-[print:0.2mm Standard @PROTON_X]
+[print:*common 0.2mm Standard @INAT*]
inherits = *common*
-[print:0.2mm Strong @PROTON_X]
+[print:*common 0.2mm Strong @INAT*]
inherits = *common*
fill_density = 50%
perimeters = 6
-[print:0.2mm Advanced Material @PROTON_X]
+[print:*common 0.2mm Advanced Material @INAT*]
inherits = *common*
bottom_solid_layers = 5
top_solid_layers = 6
skirts = 0
-brim_width = 30
+brim_width = 20
infill_speed = 60
support_material_speed = 60
travel_speed = 100
first_layer_speed = 20
elefant_foot_compensation = 0
-[print:0.12mm Fine @PROTON_X]
+[print:*common 0.12mm Fine @INAT*]
inherits = *common*
+layer_height = 0.12
bottom_solid_layers = 7
top_solid_layers = 7
infill_every_layers = 2
perimeter_speed = 50
infill_speed = 50
-[print:0.32mm Draft @PROTON_X]
+[print:*common 0.32mm Draft @INAT*]
inherits = *common*
+layer_height = 0.32
perimeter_speed = 80
external_perimeter_speed = 75%
infill_speed = 100
top_solid_infill_speed = 60
fill_density = 15%
+support_material_style = snug
###
-### PRINTER DEFINITIONS
+### COMMON PRINTER DEFINITIONS
###
-[printer:*common*]
+[printer:*proton_x_common*]
printer_vendor = INAT s.r.o.
default_filament_profile = "PLA @PROTON_X"
#general
@@ -206,14 +219,14 @@ machine_max_feedrate_z = 10,10
machine_max_feedrate_e = 100,100
machine_max_acceleration_x = 500,500
machine_max_acceleration_y = 500,500
-machine_max_acceleration_z = 100,100
-machine_max_acceleration_e = 2000,2000
+machine_max_acceleration_z = 200,200
+machine_max_acceleration_e = 8000,8000
machine_max_acceleration_extruding = 1000,1000
-machine_max_acceleration_retracting = 1500,1500
+machine_max_acceleration_retracting = 8000,8000
machine_max_jerk_x = 8,8
machine_max_jerk_y = 8,8
-machine_max_jerk_z = 1,1
-machine_max_jerk_e = 2.5,2.5
+machine_max_jerk_z = 3,3
+machine_max_jerk_e = 10,10
machine_min_extruding_rate = 5
#extruder 1
nozzle_diameter = 0.4
@@ -233,24 +246,65 @@ wipe = 1
retract_before_wipe = 100%
-[printer:Proton X Rail]
-inherits = *common*
-printer_model = PROTON_X_RAIL
-printer_variant = 0.4
-default_print_profile = 0.2mm Standard @PROTON_X
-gcode_flavor = marlin
-machine_max_acceleration_y = 800,800
-
-[printer:Proton X Rod]
-inherits = *common*
-printer_model = PROTON_X_ROD
-printer_variant = 0.4
-default_print_profile = 0.2mm Standard @PROTON_X
+[printer:*proton_xe750_common*]
+printer_vendor = INAT s.r.o.
+default_filament_profile = "PLA @PROTON_XE750"
+#general
+printer_technology = FFF
+bed_shape = 0x0,600x0,600x500,0x500
+max_print_height = 750
+z_offset = 0
+extruders_count = 2
gcode_flavor = marlin
+silent_mode = 0
+remaining_times = 1
+use_relative_e_distances = 0
+use_firmware_retraction = 0
+use_volumetric_e = 0
+variable_layer_height = 1
+#gcodes
+start_gcode = G28 ;Home\nG0 Z10 F1000\nG29\nG0 X0 Y0 Z30 F6000\nM84 E\nM0\nG1 Z15.0 F6000 ;Move the platform down 15mm\n
+end_gcode = M400\nM104 S0\nM140 S0\nM107\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 X R5\nG0 Y300 F2000\nM84\nG4 S180\nM81 S30\n
+color_change_gcode = M600
+#limits
+machine_limits_usage = emit_to_gcode
+machine_max_feedrate_x = 200,200
+machine_max_feedrate_y = 200,200
+machine_max_feedrate_z = 10,10
+machine_max_feedrate_e = 100,100
+machine_max_acceleration_x = 1500,1500
+machine_max_acceleration_y = 1500,1500
+machine_max_acceleration_z = 500,500
+machine_max_acceleration_e = 20000,20000
+machine_max_acceleration_extruding = 2000,2000
+machine_max_acceleration_retracting = 8000,8000
+machine_max_jerk_x = 12,12
+machine_max_jerk_y = 12,12
+machine_max_jerk_z = 3,3
+machine_max_jerk_e = 20,20
+machine_min_extruding_rate = 5
+#extruder 1
+nozzle_diameter = 0.4,0.4
+min_layer_height = 0.05,0.05
+max_layer_height = 0.33,0.33
+extruder_offset = 0x0,0x0
+retract_length = 1.5,1.5
+retract_lift = 0.6,0.6
+retract_lift_above = 0,0
+retract_lift_below = 0,0
+retract_speed = 45,45
+deretract_speed = 0,0
+retract_restart_extra = 0,0
+retract_before_travel = 2,2
+retract_layer_change = 0,0
+wipe = 1,1
+retract_before_wipe = 100%,100%
+retract_length_toolchange = 37,37
+extruder_colour = #33CC33;#3399FF
###
-### MATERIAL DEFINITIONS
+### COMMON MATERIAL DEFINITIONS
###
[filament:*common*]
@@ -272,7 +326,7 @@ min_print_speed = 10
filament_soluble = 0
-[filament:PLA @PROTON_X]
+[filament:*common PLA @INAT*]
inherits = *common*
temperature = 210
bed_temperature = 60
@@ -281,11 +335,12 @@ first_layer_bed_temperature = 60
filament_type = PLA
filament_cost = 20
filament_density = 1.25
+fan_always_on = 1
min_fan_speed = 50
max_fan_speed = 100
-[filament:PETG @PROTON_X]
+[filament:*common PETG @INAT*]
inherits = *common*
temperature = 240
bed_temperature = 80
@@ -294,10 +349,11 @@ first_layer_bed_temperature = 80
filament_type = PETG
filament_cost = 25
filament_density = 1.27
-min_fan_speed = 0
+fan_always_on = 1
+min_fan_speed = 25
max_fan_speed = 50
-[filament:ABS @PROTON_X]
+[filament:*common ABS @INAT*]
inherits = *common*
temperature = 235
bed_temperature = 100
@@ -309,7 +365,7 @@ filament_density = 1.01
cooling = 0
bridge_fan_speed = 0
-[filament:ASA @PROTON_X]
+[filament:*common ASA @INAT*]
inherits = *common*
temperature = 240
bed_temperature = 110
@@ -320,7 +376,7 @@ filament_cost = 22
filament_density = 1.07
cooling = 0
-[filament:TPE @PROTON_X]
+[filament:*common TPE @INAT*]
inherits = *common*
temperature = 220
bed_temperature = 40
@@ -334,7 +390,7 @@ max_fan_speed = 50
filament_retract_length = 0.8
filament_retract_speed = 25
-[filament:HIPS @PROTON_X]
+[filament:*common HIPS @INAT*]
inherits = *common*
temperature = 245
bed_temperature = 100
@@ -347,7 +403,7 @@ min_fan_speed = 0
max_fan_speed = 50
filament_soluble = 1
-[filament:Nylon @PROTON_X]
+[filament:*common Nylon @INAT*]
inherits = *common*
temperature = 235
bed_temperature = 130
@@ -359,19 +415,19 @@ filament_density = 1.01
cooling = 0
bridge_fan_speed = 0
-[filament:PC @PROTON_X]
+[filament:*common PC @INAT*]
inherits = *common*
temperature = 270
-bed_temperature = 130
+bed_temperature = 115
first_layer_temperature = 270
-first_layer_bed_temperature = 130
+first_layer_bed_temperature = 115
filament_type = PC
filament_cost = 65
filament_density = 1.19
cooling = 0
bridge_fan_speed = 0
-[filament:CPE @PROTON_X]
+[filament:*common CPE @INAT*]
inherits = *common*
temperature = 280
bed_temperature = 90
@@ -383,7 +439,7 @@ filament_density = 1.27
cooling = 0
bridge_fan_speed = 0
-[filament:PEEK @PROTON_X]
+[filament:*common PEEK @INAT*]
inherits = *common*
temperature = 440
bed_temperature = 150
@@ -395,7 +451,7 @@ filament_density = 1.3
cooling = 0
bridge_fan_speed = 0
-[filament:PEI @PROTON_X]
+[filament:*common PEI @INAT*]
inherits = *common*
temperature = 400
bed_temperature = 150
@@ -407,7 +463,7 @@ filament_density = 1.27
cooling = 0
bridge_fan_speed = 0
-[filament:Polymaker PolyMide CoPA @PROTON_X]
+[filament:*common Polymaker PolyMide CoPA @INAT*]
inherits = *common*
filament_vendor = Polymaker
temperature = 265
@@ -419,7 +475,7 @@ filament_cost = 93
filament_density = 1.12
cooling = 0
-[filament:Polymaker PolyMide PA6-CF @PROTON_X]
+[filament:*common Polymaker PolyMide PA6-CF @INAT*]
inherits = *common*
filament_vendor = Polymaker
temperature = 300
@@ -431,7 +487,7 @@ filament_cost = 95
filament_density = 1.17
cooling = 0
-[filament:Polymaker PolyMide PA6-GF @PROTON_X]
+[filament:*common Polymaker PolyMide PA6-GF @INAT*]
inherits = *common*
filament_vendor = Polymaker
temperature = 300
@@ -443,20 +499,21 @@ filament_cost = 95
filament_density = 1.2
cooling = 0
-[filament:Devil Design PETG @PROTON_X]
+[filament:*common Devil Design PETG @INAT*]
inherits = *common*
filament_vendor = Devil Design
-temperature = 250
+temperature = 245
bed_temperature = 80
-first_layer_temperature = 250
+first_layer_temperature = 245
first_layer_bed_temperature = 80
filament_type = PETG
filament_cost = 22
filament_density = 1.23
-min_fan_speed = 0
+fan_always_on = 1
+min_fan_speed = 25
max_fan_speed = 50
-[filament:Filament PM PETG FRJet @PROTON_X]
+[filament:*common Filament PM PETG FRJet @INAT*]
inherits = *common*
filament_vendor = Filament PM
temperature = 250
@@ -467,3 +524,207 @@ filament_type = PETG
filament_cost = 45.5
filament_density = 1.27
cooling = 0
+
+
+######
+###### PROTON X PRINTERS
+######
+
+[printer:Proton X Rail]
+inherits = *proton_x_common*
+printer_model = PROTON_X_RAIL
+printer_variant = 0.4
+default_print_profile = 0.2mm Standard @PROTON_X
+gcode_flavor = marlin
+machine_max_acceleration_x = 800,800
+machine_max_acceleration_y = 800,800
+machine_max_jerk_x = 10,10
+machine_max_jerk_y = 10,10
+
+[printer:Proton X Rod]
+inherits = *proton_x_common*
+printer_model = PROTON_X_ROD
+printer_variant = 0.4
+default_print_profile = 0.2mm Standard @PROTON_X
+gcode_flavor = marlin
+
+[print:0.2mm Standard @PROTON_X]
+inherits = *common 0.2mm Standard @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[print:0.2mm Strong @PROTON_X]
+inherits = *common 0.2mm Strong @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[print:0.2mm Advanced Material @PROTON_X]
+inherits = *common 0.2mm Advanced Material @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[print:0.12mm Fine @PROTON_X]
+inherits = *common 0.12mm Fine @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[print:0.32mm Draft @PROTON_X]
+inherits = *common 0.32mm Draft @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+
+
+[filament:PLA @PROTON_X]
+inherits =*common PLA @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:PETG @PROTON_X]
+inherits =*common PETG @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:ABS @PROTON_X]
+inherits =*common ABS @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:ASA @PROTON_X]
+inherits =*common ASA @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:TPE @PROTON_X]
+inherits =*common TPE @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:HIPS @PROTON_X]
+inherits =*common HIPS @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:Nylon @PROTON_X]
+inherits =*common Nylon @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:PC @PROTON_X]
+inherits =*common PC @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:CPE @PROTON_X]
+inherits =*common CPE @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:PEEK @PROTON_X]
+inherits =*common PEEK @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:PEI @PROTON_X]
+inherits =*common PEI @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:Polymaker PolyMide CoPA @PROTON_X]
+inherits =*common Polymaker PolyMide CoPA @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:Polymaker PolyMide PA6-CF @PROTON_X]
+inherits =*common Polymaker PolyMide PA6-CF @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:Polymaker PolyMide PA6-GF @PROTON_X]
+inherits =*common Polymaker PolyMide PA6-GF @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:Devil Design PETG @PROTON_X]
+inherits =*common Devil Design PETG @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+[filament:Filament PM PETG FRJet @PROTON_X]
+inherits =*common Filament PM PETG FRJet @INAT*
+compatible_printers = "Proton X Rail";"Proton X Rod"
+
+
+
+
+######### #########
+######### Proton XE 750 #########
+######### #########
+
+[printer:Proton XE-750]
+inherits = *proton_xe750_common*
+printer_model = PROTON_XE750
+printer_variant = 0.4
+default_print_profile = 0.2mm Standard @PROTON_XE750
+gcode_flavor = marlin
+
+
+[print:0.2mm Standard @PROTON_XE750]
+inherits = *common 0.2mm Standard @INAT*
+compatible_printers = "Proton XE-750"
+
+[print:0.2mm Strong @PROTON_XE750]
+inherits = *common 0.2mm Strong @INAT*
+compatible_printers = "Proton XE-750"
+
+[print:0.2mm Advanced Material @PROTON_XE750]
+inherits = *common 0.2mm Advanced Material @INAT*
+compatible_printers = "Proton XE-750"
+
+[print:0.12mm Fine @PROTON_XE750]
+inherits = *common 0.12mm Fine @INAT*
+compatible_printers = "Proton XE-750"
+
+[print:0.32mm Draft @PROTON_XE750]
+inherits = *common 0.32mm Draft @INAT*
+compatible_printers = "Proton XE-750"
+
+
+
+
+
+
+[filament:*start_end_gcode @PROTON_XE750*]
+start_filament_gcode = "; Filament start gcode BEGIN\nM104 S[temperature[current_extruder]]\nG4 S20\n; Filament start gcode END\n"
+end_filament_gcode = "; Filament end gcode BEGIN\nG0 X-5 Y250 F10000\nM104 S{temperature[current_extruder] - 50}\n; Filament end gcode END\n"
+compatible_printers = "Proton XE-750"
+
+
+
+[filament:PLA @PROTON_XE750]
+inherits =*common PLA @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:PETG @PROTON_XE750]
+inherits =*common PETG @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:ABS @PROTON_XE750]
+inherits =*common ABS @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:ASA @PROTON_XE750]
+inherits =*common ASA @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:TPE @PROTON_XE750]
+inherits =*common TPE @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:HIPS @PROTON_XE750]
+inherits =*common HIPS @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:Nylon @PROTON_XE750]
+inherits =*common Nylon @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:PC @PROTON_XE750]
+inherits =*common PC @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:CPE @PROTON_XE750]
+inherits =*common CPE @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:PEEK @PROTON_XE750]
+inherits =*common PEEK @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:PEI @PROTON_XE750]
+inherits =*common PEI @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:Polymaker PolyMide CoPA @PROTON_XE750]
+inherits =*common Polymaker PolyMide CoPA @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:Polymaker PolyMide PA6-CF @PROTON_XE750]
+inherits =*common Polymaker PolyMide PA6-CF @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:Polymaker PolyMide PA6-GF @PROTON_XE750]
+inherits =*common Polymaker PolyMide PA6-GF @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:Devil Design PETG @PROTON_XE750]
+inherits =*common Devil Design PETG @INAT*; *start_end_gcode @PROTON_XE750*
+
+[filament:Filament PM PETG FRJet @PROTON_XE750]
+inherits =*common Filament PM PETG FRJet @INAT*; *start_end_gcode @PROTON_XE750*
diff --git a/resources/profiles/INAT/PROTON_XE750_thumbnail.png b/resources/profiles/INAT/PROTON_XE750_thumbnail.png
new file mode 100644
index 000000000..114247ade
Binary files /dev/null and b/resources/profiles/INAT/PROTON_XE750_thumbnail.png differ
diff --git a/resources/profiles/Infinity3D.idx b/resources/profiles/Infinity3D.idx
new file mode 100644
index 000000000..0af9cc73b
--- /dev/null
+++ b/resources/profiles/Infinity3D.idx
@@ -0,0 +1,2 @@
+min_slic3r_version = 2.5.0-alpha0
+1.0.0 Initial Infinity3D bundle
diff --git a/resources/profiles/Infinity3D.ini b/resources/profiles/Infinity3D.ini
new file mode 100644
index 000000000..73703b43d
--- /dev/null
+++ b/resources/profiles/Infinity3D.ini
@@ -0,0 +1,812 @@
+# Infinity3D profiles
+
+[vendor]
+# Vendor name will be shown by the Config Wizard.
+name = Infinity3D
+# Configuration version of this file. Config file will only be installed, if the config_version differs.
+# This means, the server may force the Slic3r configuration to be downgraded.
+config_version = 1.0.0
+# Where to get the updates from?
+config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Infinity3D/
+# The printer models will be shown by the Configuration Wizard in this order,
+
+[printer_model:DEV-200]
+name = Infinity3D DEV-200
+variants = 0.4
+technology = FFF
+bed_model = DEV_200_bed.stl
+bed_texture = DEV_200_texture.svg
+default_materials = Generic PLA @Infinity3D; Generic PETG @Infinity3D
+
+[printer_model:DEV-350]
+name = Infinity3D DEV-350
+variants = 0.4
+technology = FFF
+bed_model = DEV_350_bed.stl
+bed_texture = DEV_350_texture.svg
+default_materials = Generic PLA @Infinity3D; Generic PETG @Infinity3D
+
+[print:*common*]
+avoid_crossing_perimeters = 1
+avoid_crossing_perimeters_max_detour = 0
+bottom_fill_pattern = monotonic
+bottom_solid_layers = 4
+bottom_solid_min_thickness = 0
+bridge_acceleration = 0
+bridge_angle = 0
+bridge_flow_ratio = 1
+bridge_speed = 60
+brim_separation = 0
+brim_type = outer_only
+brim_width = 0
+clip_multipart_objects = 1
+complete_objects = 0
+default_acceleration = 0
+dont_support_bridges = 1
+draft_shield = disabled
+elefant_foot_compensation = 0.1
+ensure_vertical_shell_thickness = 1
+external_perimeter_extrusion_width = 0.45
+external_perimeter_speed = 80%
+external_perimeters_first = 0
+extra_perimeters = 0
+extruder_clearance_height = 25
+extruder_clearance_radius = 75
+extrusion_width = 0.45
+fill_angle = 45
+fill_density = 20%
+fill_pattern = gyroid
+first_layer_acceleration = 0
+first_layer_acceleration_over_raft = 0
+first_layer_extrusion_width = 0.45
+first_layer_height = 0.2
+first_layer_speed = 45
+first_layer_speed_over_raft = 45
+fuzzy_skin = none
+fuzzy_skin_point_dist = 0.8
+fuzzy_skin_thickness = 0.3
+gap_fill_enabled = 1
+gap_fill_speed = 20
+gcode_comments = 0
+gcode_label_objects = 0
+gcode_resolution = 0.0125
+gcode_substitutions =
+infill_acceleration = 0
+infill_anchor = 600%
+infill_anchor_max = 50
+infill_every_layers = 1
+infill_extruder = 1
+infill_extrusion_width = 0.45
+infill_first = 0
+infill_only_where_needed = 0
+infill_overlap = 25%
+infill_speed = 60
+inherits =
+interface_shells = 0
+ironing = 0
+ironing_flowrate = 15%
+ironing_spacing = 0.25
+ironing_speed = 30
+ironing_type = top
+layer_height = 0.2
+max_print_speed = 100
+max_volumetric_speed = 0
+min_skirt_length = 4
+mmu_segmented_region_max_width = 0
+notes =
+only_retract_when_crossing_perimeters = 0
+ooze_prevention = 0
+output_filename_format = {input_filename_base}_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode
+overhangs = 0
+perimeter_acceleration = 0
+perimeter_extruder = 1
+perimeter_extrusion_width = 0.45
+perimeter_speed = 60
+perimeters = 2
+post_process =
+print_settings_id =
+raft_contact_distance = 0.1
+raft_expansion = 1.5
+raft_first_layer_density = 90%
+raft_first_layer_expansion = 3
+raft_layers = 0
+resolution = 0
+seam_position = nearest
+single_extruder_multi_material_priming = 1
+skirt_distance = 5
+skirt_height = 1
+skirts = 3
+slice_closing_radius = 0.049
+slicing_mode = regular
+small_perimeter_speed = 70%
+solid_infill_below_area = 0
+solid_infill_every_layers = 0
+solid_infill_extruder = 1
+solid_infill_extrusion_width= 0.45
+solid_infill_speed = 80%
+spiral_vase = 0
+standby_temperature_delta = -5
+support_material = 0
+support_material_angle = 0
+support_material_auto = 1
+support_material_bottom_contact_distance = 0
+support_material_bottom_interface_layers = -1
+support_material_buildplate_only = 0
+support_material_closing_radius = 2
+support_material_contact_distance = 0.15
+support_material_enforce_layers = 0
+support_material_extruder = 0
+support_material_extrusion_width = 0.38
+support_material_interface_contact_loops = 0
+support_material_interface_extruder = 0
+support_material_interface_layers = 2
+support_material_interface_pattern = rectilinear
+support_material_interface_spacing = 0.2
+support_material_interface_speed = 100%
+support_material_pattern = rectilinear
+support_material_spacing = 2
+support_material_speed = 60
+support_material_style = grid
+support_material_synchronize_layers = 0
+support_material_threshold = 45
+support_material_with_sheath = 0
+support_material_xy_spacing = 60%
+thick_bridges = 1
+thin_walls = 0
+threads = 8
+top_fill_pattern = monotonic
+top_infill_extrusion_width = 0.45
+top_solid_infill_speed = 60%
+top_solid_layers = 4
+top_solid_min_thickness = 0
+travel_speed = 70
+travel_speed_z = 0
+wipe_tower = 0
+wipe_tower_bridging = 10
+wipe_tower_brim_width = 2
+wipe_tower_no_sparse_layers = 0
+wipe_tower_rotation_angle = 0
+wipe_tower_width = 60
+wipe_tower_x = 170
+wipe_tower_y = 140
+xy_size_compensation = 0
+compatible_printers_condition = nozzle_diameter[0]==0.4
+
+[print:0.06mm SUPERFINE @Infinity3D_DEV_200]
+inherits = *common*
+layer_height = 0.06
+bottom_solid_layers = 12
+top_solid_layers = 12
+top_solid_min_thickness = 0.72
+bottom_solid_min_thickness = 0.72
+bridge_speed = 45
+infill_speed = 60
+perimeter_speed = 50
+support_material_speed = 50
+max_print_speed = 60
+skirt_distance = 10
+first_layer_speed = 80%
+first_layer_extrusion_width = 0.45
+perimeter_extrusion_width = 0.4
+external_perimeter_extrusion_width = 0.4
+infill_extrusion_width = 0.4
+solid_infill_extrusion_width = 0.4
+top_infill_extrusion_width = 0.4
+compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-200"
+
+[print:0.10mm Fine @Infinity3D_DEV_200]
+inherits = *common*
+layer_height = 0.10
+top_solid_layers = 8
+bottom_solid_layers = 8
+bridge_speed = 45
+infill_speed = 60
+perimeter_speed = 50
+support_material_speed = 50
+max_print_speed = 60
+skirt_distance = 10
+first_layer_speed = 80%
+first_layer_extrusion_width = 0.45
+perimeter_extrusion_width = 0.4
+external_perimeter_extrusion_width = 0.4
+infill_extrusion_width = 0.4
+solid_infill_extrusion_width = 0.4
+top_infill_extrusion_width = 0.4
+compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-200"
+
+[print:0.20mm GOOD @Infinity3D_DEV_200]
+inherits = *common*
+layer_height = 0.20
+top_solid_layers = 5
+bottom_solid_layers = 5
+bridge_speed = 45
+infill_speed = 60
+perimeter_speed = 50
+support_material_speed = 50
+max_print_speed = 60
+skirt_distance = 10
+first_layer_speed = 80%
+first_layer_extrusion_width = 0.45
+perimeter_extrusion_width = 0.4
+external_perimeter_extrusion_width = 0.4
+infill_extrusion_width = 0.4
+solid_infill_extrusion_width = 0.4
+top_infill_extrusion_width = 0.4
+compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-200"
+
+[print:0.30mm RAPID @Infinity3D_DEV_200]
+inherits = *common*
+layer_height = 0.30
+top_solid_layers = 3
+bottom_solid_layers = 3
+bridge_speed = 45
+infill_speed = 60
+perimeter_speed = 50
+support_material_speed = 50
+max_print_speed = 60
+skirt_distance = 10
+first_layer_speed = 80%
+compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-200"
+
+[print:0.40mm FAST @Infinity3D_DEV_200]
+inherits = *common*
+layer_height = 0.40
+top_solid_layers = 3
+bottom_solid_layers = 3
+bridge_speed = 45
+infill_speed = 60
+perimeter_speed = 50
+support_material_speed = 50
+max_print_speed = 60
+skirt_distance = 10
+first_layer_speed = 80%
+support_material_extrusion_width = 0.45
+compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-200"
+
+
+[print:0.06mm SUPERFINE @Infinity3D_DEV_350]
+inherits = *common*
+layer_height = 0.06
+bottom_solid_layers = 12
+top_solid_layers = 12
+top_solid_min_thickness = 0.72
+bottom_solid_min_thickness = 0.72
+bridge_speed = 45
+infill_speed = 60
+perimeter_speed = 50
+support_material_speed = 50
+max_print_speed = 60
+skirt_distance = 10
+first_layer_speed = 80%
+first_layer_extrusion_width = 0.45
+perimeter_extrusion_width = 0.4
+external_perimeter_extrusion_width = 0.4
+infill_extrusion_width = 0.4
+solid_infill_extrusion_width = 0.4
+top_infill_extrusion_width = 0.4
+compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-350"
+
+[print:0.10mm Fine @Infinity3D_DEV_350]
+inherits = *common*
+layer_height = 0.10
+top_solid_layers = 8
+bottom_solid_layers = 8
+bridge_speed = 45
+infill_speed = 60
+perimeter_speed = 50
+support_material_speed = 50
+max_print_speed = 60
+skirt_distance = 10
+first_layer_speed = 80%
+first_layer_extrusion_width = 0.45
+perimeter_extrusion_width = 0.4
+external_perimeter_extrusion_width = 0.4
+infill_extrusion_width = 0.4
+solid_infill_extrusion_width = 0.4
+top_infill_extrusion_width = 0.4
+compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-350"
+
+[print:0.20mm GOOD @Infinity3D_DEV_350]
+inherits = *common*
+layer_height = 0.20
+top_solid_layers = 5
+bottom_solid_layers = 5
+bridge_speed = 45
+infill_speed = 60
+perimeter_speed = 50
+support_material_speed = 50
+max_print_speed = 60
+skirt_distance = 10
+first_layer_speed = 80%
+first_layer_extrusion_width = 0.45
+perimeter_extrusion_width = 0.4
+external_perimeter_extrusion_width = 0.4
+infill_extrusion_width = 0.4
+solid_infill_extrusion_width = 0.4
+top_infill_extrusion_width = 0.4
+compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-350"
+
+[print:0.30mm RAPID @Infinity3D_DEV_350]
+inherits = *common*
+layer_height = 0.30
+top_solid_layers = 3
+bottom_solid_layers = 3
+bridge_speed = 45
+infill_speed = 60
+perimeter_speed = 50
+support_material_speed = 50
+max_print_speed = 60
+skirt_distance = 10
+first_layer_speed = 80%
+compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-350"
+
+[print:0.40mm FAST @Infinity3D_DEV_350]
+inherits = *common*
+layer_height = 0.40
+top_solid_layers = 3
+bottom_solid_layers = 3
+bridge_speed = 45
+infill_speed = 60
+perimeter_speed = 50
+support_material_speed = 50
+max_print_speed = 60
+skirt_distance = 10
+first_layer_speed = 80%
+support_material_extrusion_width = 0.45
+compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-350"
+
+
+[filament:*common*]
+bed_temperature = 60
+bridge_fan_speed = 100
+compatible_printers =
+compatible_printers_condition =
+compatible_prints =
+compatible_prints_condition =
+cooling = 1
+disable_fan_first_layers = 2
+end_filament_gcode = "; Filament-specific end gcode \n;END gcode for filament\n"
+extrusion_multiplier = 1
+fan_always_on = 1
+fan_below_layer_time = 60
+filament_colour = #29B2B2
+filament_cooling_final_speed = 3.4
+filament_cooling_initial_speed = 2.2
+filament_cooling_moves = 4
+filament_cost = 0
+filament_density = 0
+filament_deretract_speed = nil
+filament_diameter = 1.75
+filament_load_time = 0
+filament_loading_speed = 28
+filament_loading_speed_start = 3
+filament_max_volumetric_speed = 0
+filament_minimal_purge_on_wipe_tower = 15
+filament_notes = ""
+filament_ramming_parameters = "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6"
+filament_retract_before_travel = nil
+filament_retract_before_wipe = nil
+filament_retract_layer_change = nil
+filament_retract_length = nil
+filament_retract_lift = nil
+filament_retract_lift_above = nil
+filament_retract_lift_below = nil
+filament_retract_restart_extra = nil
+filament_retract_speed = nil
+filament_settings_id = ""
+filament_soluble = 0
+filament_spool_weight = 0
+filament_toolchange_delay = 0
+filament_type = PLA
+filament_unload_time = 0
+filament_unloading_speed = 90
+filament_unloading_speed_start = 100
+filament_wipe = nil
+first_layer_bed_temperature = 60
+first_layer_temperature = 210
+full_fan_speed_layer = 0
+inherits =
+max_fan_speed = 100
+min_fan_speed = 35
+min_print_speed = 10
+slowdown_below_layer_time = 5
+start_filament_gcode = "; Filament gcode\n"
+temperature = 210
+
+[filament:*PLA*]
+inherits = *common*
+bed_temperature = 60
+fan_below_layer_time = 100
+filament_colour = #DDDDDD
+filament_max_volumetric_speed = 15
+filament_type = PLA
+filament_density = 1.24
+filament_cost = 20
+first_layer_bed_temperature = 60
+first_layer_temperature = 210
+fan_always_on = 1
+cooling = 1
+max_fan_speed = 100
+min_fan_speed = 100
+bridge_fan_speed = 100
+disable_fan_first_layers = 1
+temperature = 205
+
+[filament:*PET*]
+inherits = *common*
+bed_temperature = 70
+cooling = 1
+disable_fan_first_layers = 3
+fan_below_layer_time = 20
+filament_colour = #DDDDDD
+filament_max_volumetric_speed = 8
+filament_type = PETG
+filament_density = 1.27
+filament_cost = 30
+first_layer_bed_temperature = 70
+first_layer_temperature = 240
+fan_always_on = 1
+max_fan_speed = 50
+min_fan_speed = 20
+bridge_fan_speed = 100
+temperature = 240
+
+[filament:*ABS*]
+inherits = *common*
+bed_temperature = 100
+cooling = 0
+disable_fan_first_layers = 3
+fan_below_layer_time = 20
+filament_colour = #DDDDDD
+filament_max_volumetric_speed = 11
+filament_type = ABS
+filament_density = 1.04
+filament_cost = 20
+first_layer_bed_temperature = 100
+first_layer_temperature = 245
+fan_always_on = 0
+max_fan_speed = 0
+min_fan_speed = 0
+bridge_fan_speed = 30
+top_fan_speed = 0
+temperature = 245
+
+[filament:Generic PLA @Infinity3D]
+inherits = *PLA*
+renamed_from = "Generic PLA @Infinity3D"
+filament_vendor = Generic
+
+[filament:Generic PETG @Infinity3D]
+inherits = *PET*
+renamed_from = "Generic PETG @Infinity3D"
+filament_vendor = Generic
+
+[filament:Generic ABS @Infinity3D]
+inherits = *ABS*
+renamed_from = "Generic ABS @Infinity3D"
+first_layer_bed_temperature = 90
+bed_temperature = 90
+filament_vendor = Generic
+
+[filament:Infinity3D PLA @Infinity3D]
+inherits = *PLA*
+renamed_from = "Infinity3D PLA @Infinity3D"
+filament_vendor = Infinity3D
+temperature = 200
+bed_temperature = 60
+first_layer_temperature = 205
+first_layer_bed_temperature = 60
+filament_colour = #42BDD8
+
+[filament:Infinity3D PETG @Infinity3D]
+inherits = *PET*
+renamed_from = "Infinity3D PETG @Infinity3D"
+filament_vendor = Infinity3D
+temperature = 240
+bed_temperature = 70
+first_layer_temperature = 240
+first_layer_bed_temperature = 70
+max_fan_speed = 40
+min_fan_speed = 20
+filament_colour = #42BDD8
+
+[filament:Infinity3D ABS @Infinity3D]
+inherits = *ABS*
+renamed_from = "Infinity3D ABS @Infinity3D"
+filament_vendor = Infinity3D
+temperature = 240
+bed_temperature = 90
+first_layer_temperature = 240
+first_layer_bed_temperature = 90
+filament_colour = #42BDD8
+
+[filament:Prusament PLA @Infinity3D]
+inherits = *PLA*
+renamed_from = "Prusament PLA @Infinity3D"
+filament_vendor = Prusa Polymers
+temperature = 210
+bed_temperature = 60
+first_layer_temperature = 215
+first_layer_bed_temperature = 60
+filament_cost = 24.99
+filament_density = 1.24
+filament_colour = #F94D0C
+
+[filament:Prusament PETG @Infinity3D]
+inherits = *PET*
+renamed_from = "Prusament PETG @Infinity3D"
+filament_vendor = Prusa Polymers
+temperature = 245
+bed_temperature = 70
+first_layer_temperature = 245
+first_layer_bed_temperature = 70
+filament_cost = 24.99
+filament_density = 1.27
+filament_colour = #F94D0C
+
+[filament:AzureFilm PLA @Infinity3D]
+inherits = *PLA*
+filament_vendor = AzureFilm
+temperature = 210
+bed_temperature = 60
+first_layer_temperature = 215
+first_layer_bed_temperature = 60
+filament_cost = 19.97
+filament_density = 1.24
+filament_colour = #006AA6
+
+[filament:Devil Design PLA @Infinity3D]
+inherits = *PLA*
+filament_vendor = Devil Design
+temperature = 215
+bed_temperature = 60
+first_layer_temperature = 215
+first_layer_bed_temperature = 60
+filament_cost = 19.00
+filament_density = 1.24
+filament_colour = #FF0000
+filament_spool_weight = 256
+
+[filament:Devil Design PLA Matt @Infinity3D]
+inherits = *PLA*
+filament_vendor = Devil Design
+temperature = 205
+bed_temperature = 60
+first_layer_temperature = 205
+first_layer_bed_temperature = 60
+filament_cost = 20.00
+filament_density = 1.38
+filament_colour = #FF0000
+filament_spool_weight = 256
+
+[filament:Devil Design PLA Galaxy @Infinity3D]
+inherits = *PLA*
+renamed_from = "Devil Design PLA (Galaxy) @Infinity3D"
+filament_vendor = Devil Design
+temperature = 225
+bed_temperature = 65
+first_layer_temperature = 225
+first_layer_bed_temperature = 65
+filament_cost = 19.00
+filament_density = 1.24
+filament_colour = #FF0000
+filament_spool_weight = 256
+
+[filament:Extrudr PLA NX2 @Infinity3D]
+inherits = *PLA*
+filament_vendor = Extrudr
+temperature = 200
+bed_temperature = 60
+first_layer_temperature = 205
+first_layer_bed_temperature = 60
+filament_cost = 23.63
+filament_density = 1.3
+filament_colour = #3C4547
+filament_spool_weight = 256
+
+[filament:Extrudr GreenTEC Pro @Infinity3D]
+inherits = *PLA*
+filament_vendor = Extrudr
+temperature = 210
+bed_temperature = 60
+first_layer_temperature = 215
+first_layer_bed_temperature = 60
+filament_cost = 56.24
+filament_density = 1.35
+filament_colour = #3C4547
+
+[filament:Real Filament PLA @Infinity3D]
+inherits = *PLA*
+filament_vendor = Real Filament
+temperature = 195
+bed_temperature = 60
+first_layer_temperature = 200
+first_layer_bed_temperature = 60
+filament_cost = 24.99
+filament_density = 1.24
+filament_colour = #007ABF
+
+[filament:Velleman PLA @Infinity3D]
+inherits = *PLA*
+filament_vendor = Velleman
+temperature = 200
+bed_temperature = 60
+first_layer_temperature = 205
+first_layer_bed_temperature = 60
+filament_cost = 27.99
+filament_density = 1.24
+filament_colour = #7EA60D
+
+[filament:3DJAKE ecoPLA @Infinity3D]
+inherits = *PLA*
+filament_vendor = 3DJAKE
+temperature = 200
+bed_temperature = 60
+first_layer_temperature = 205
+first_layer_bed_temperature = 60
+filament_cost = 21.99
+filament_density = 1.24
+filament_colour = #125467
+filament_spool_weight = 238
+
+[filament:3DJAKE ecoPLA Matt @Infinity3D]
+inherits = *PLA*
+filament_vendor = 3DJAKE
+temperature = 195
+bed_temperature = 60
+first_layer_temperature = 195
+first_layer_bed_temperature = 60
+filament_cost = 24.99
+filament_density = 1.38
+filament_colour = #125467
+filament_spool_weight = 238
+
+[filament:3DJAKE ecoPLA Tough @Infinity3D]
+inherits = *PLA*
+filament_vendor = 3DJAKE
+temperature = 215
+bed_temperature = 60
+first_layer_temperature = 215
+first_layer_bed_temperature = 60
+filament_cost = 29.99
+filament_density = 1.21
+filament_colour = #125467
+
+[filament:FormFutura Tough PLA @Infinity3D]
+inherits = *PLA*
+filament_vendor = FormFutura
+temperature = 215
+bed_temperature = 60
+first_layer_temperature = 215
+first_layer_bed_temperature = 60
+filament_cost = 46.65
+filament_density = 1.21
+filament_colour = #ed000e
+
+[filament:123-3D Jupiter PLA @Infinity3D]
+inherits = *PLA*
+filament_vendor = 123-3D
+temperature = 200
+bed_temperature = 60
+first_layer_temperature = 205
+first_layer_bed_temperature = 60
+filament_cost = 19.50
+filament_density = 1.24
+filament_colour = #FFE200
+
+[filament:Das Filament PLA @Infinity3D]
+inherits = *PLA*
+filament_vendor = Das Filament
+temperature = 210
+bed_temperature = 60
+first_layer_temperature = 215
+first_layer_bed_temperature = 60
+filament_cost = 20.56
+filament_density = 1.24
+filament_colour = #C7F935
+
+[filament:Das Filament PETG @Infinity3D]
+inherits = *PET*
+filament_vendor = Das Filament
+temperature = 240
+bed_temperature = 70
+first_layer_temperature = 240
+first_layer_bed_temperature = 70
+filament_cost = 27.44
+filament_density = 1.29
+filament_colour = #C7F935
+
+[filament:Verbatim PLA @Infinity3D]
+inherits = *PLA*
+filament_vendor = Verbatim
+temperature = 205
+bed_temperature = 60
+first_layer_temperature = 210
+first_layer_bed_temperature = 60
+filament_cost = 22.99
+filament_density = 1.24
+filament_colour = #001ca8
+
+# Common printer preset
+[printer:*common*]
+bed_shape = 0x0,350x0,350x350,0x350
+color_change_gcode = M600
+cooling_tube_length = 5
+cooling_tube_retraction = 91.5
+default_filament_profile = ""
+default_print_profile =
+end_gcode = ;End GCode begin\nM140 S0 ;Heated bed heater off\nM104 S0 ;Extruder heater off\nG90 ;absolute positioning\nG92 E0 ;Retract the filament\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z350 E-1 F3000 ;move Z up a bit and retract filament even more\nG1 X0 F3000 ;move X to min endstops, so the head is out of the way\nG1 Y350 F3000 ;so the head is out of the way and Plate is moved forward\nM84 ;stepper off\nM107 ; fan off\nM82 ; absolute extrusion\n;End GCode end
+extra_loading_move = -2
+extruder_colour = ""
+extruder_offset = 0x0
+gcode_flavor = marlin
+high_current_on_filament_swap = 0
+machine_limits_usage = time_estimate_only
+machine_max_acceleration_e = 10000
+machine_max_acceleration_extruding = 1000
+machine_max_acceleration_retracting = 1000
+machine_max_acceleration_travel = 1500
+machine_max_acceleration_x = 3000
+machine_max_acceleration_y = 3000
+machine_max_acceleration_z = 100
+machine_max_feedrate_e = 25
+machine_max_feedrate_x = 150
+machine_max_feedrate_y = 150
+machine_max_feedrate_z = 50
+machine_max_jerk_e = 2.5
+machine_max_jerk_x = 10
+machine_max_jerk_y = 10
+machine_max_jerk_z = 0.2
+machine_min_extruding_rate = 0
+machine_min_travel_rate = 0
+max_layer_height = 0.4
+max_print_height = 350
+min_layer_height = 0.08
+nozzle_diameter = 0.4
+parking_pos_retraction = 92
+pause_print_gcode =
+printer_technology = FFF
+remaining_times = 0
+retract_before_travel = 2
+retract_before_wipe = 0%
+retract_layer_change = 1
+retract_length = 2
+retract_length_toolchange = 10
+retract_lift = 0
+retract_lift_above = 0
+retract_lift_below = 328
+retract_restart_extra = 0
+retract_restart_extra_toolchange = 0
+retract_speed = 60
+deretract_speed = 40
+silent_mode = 0
+single_extruder_multi_material = 0
+start_gcode = Start GCode begin\nM140 S[first_layer_bed_temperature] ;Start Warming Bed\nM104 S[first_layer_temperature] ;Preheat\nG28 ;home\nG29 ;Auto Bed-level\nG90 ;absolute positioning\nG1 X-10 Y-10 F3000 ;Move to corner\nM190 S[first_layer_bed_temperature] ;Wait For Bed Temperature\nM109 S[first_layer_temperature] ;Wait for Hotend Temperature\nG92 E0 ;Zero set extruder position\nG1 E3 F200 ;Feed filament to clear nozzle\nG92 E0 ;Zero set extruder position
+thumbnails = 16x16,220x124
+use_firmware_retraction = 0
+use_relative_e_distances = 0
+use_volumetric_e = 0
+variable_layer_height = 1
+wipe = 0
+z_offset = 0
+
+[printer:Infinity3D DEV-350]
+inherits = *common*
+printer_model = DEV-350
+printer_variant = 0.4
+default_filament_profile = Generic PLA @Infinity3D
+default_print_profile = 0.20mm GOOD @Infinity3D_DEV_350
+
+[printer:Infinity3D DEV-200]
+inherits = *common*
+printer_model = DEV-200
+printer_variant = 0.4
+bed_shape = 0x0,200x0,200x200,0x200
+thumbnails =
+variable_layer_height = 0
+retract_lift_below = 0
+max_print_height = 235
+start_gcode = Start GCode begin\nM140 S[first_layer_bed_temperature] ;Start Warming Bed\nM104 S[first_layer_temperature] ;Preheat\nG28 ;home\nG29 ;Auto Bed-level\nG90 ;absolute positioning\nG1 X-10 Y-10 F3000 ;Move to corner\nM190 S[first_layer_bed_temperature] ;Wait For Bed Temperature\nM109 S[first_layer_temperature] ;Wait for Hotend Temperature\nG92 E0 ;Zero set extruder position\nG1 E3 F200 ;Feed filament to clear nozzle\nG92 E0 ;Zero set extruder position
+end_gcode = ;End GCode begin\nM140 S0 ;Heated bed heater off\nM104 S0 ;Extruder heater off\nG90 ;absolute positioning\nG92 E0 ;Retract the filament\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z240 E-1 F3000 ;move Z up a bit and retract filament even more\nG1 X0 F3000 ;move X to min endstops, so the head is out of the way\nG1 Y200 F3000 ;so the head is out of the way and Plate is moved forward\nM84 ;stepper off\nM107 ; fan off\nM82 ; absolute extrusion\n;End GCode end
+default_filament_profile = Generic PLA @Infinity3D
+default_print_profile = 0.20mm GOOD @Infinity3D_DEV_200
diff --git a/resources/profiles/Infinity3D/DEV-200_thumbnail.png b/resources/profiles/Infinity3D/DEV-200_thumbnail.png
new file mode 100644
index 000000000..937944773
Binary files /dev/null and b/resources/profiles/Infinity3D/DEV-200_thumbnail.png differ
diff --git a/resources/profiles/Infinity3D/DEV-350_thumbnail.png b/resources/profiles/Infinity3D/DEV-350_thumbnail.png
new file mode 100644
index 000000000..02ae09fa1
Binary files /dev/null and b/resources/profiles/Infinity3D/DEV-350_thumbnail.png differ
diff --git a/resources/profiles/Infinity3D/DEV_200_bed.stl b/resources/profiles/Infinity3D/DEV_200_bed.stl
new file mode 100644
index 000000000..b07905150
Binary files /dev/null and b/resources/profiles/Infinity3D/DEV_200_bed.stl differ
diff --git a/resources/profiles/Infinity3D/DEV_200_texture.svg b/resources/profiles/Infinity3D/DEV_200_texture.svg
new file mode 100644
index 000000000..c2ee3a68b
--- /dev/null
+++ b/resources/profiles/Infinity3D/DEV_200_texture.svg
@@ -0,0 +1,487 @@
+
+
+
diff --git a/resources/profiles/Infinity3D/DEV_350_bed.stl b/resources/profiles/Infinity3D/DEV_350_bed.stl
new file mode 100644
index 000000000..7250f3867
Binary files /dev/null and b/resources/profiles/Infinity3D/DEV_350_bed.stl differ
diff --git a/resources/profiles/Infinity3D/DEV_350_texture.svg b/resources/profiles/Infinity3D/DEV_350_texture.svg
new file mode 100644
index 000000000..bbfde73d2
--- /dev/null
+++ b/resources/profiles/Infinity3D/DEV_350_texture.svg
@@ -0,0 +1,589 @@
+
+
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 801760b8c..8a093f639 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -105,7 +105,10 @@ if (SLIC3R_GUI)
# wrong libs for opengl in the link line and it does not link to it by himself.
# libslic3r_gui will link to opengl anyway, so lets override wx
list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX OpenGL)
-
+
+ if (UNIX AND NOT APPLE)
+ list(APPEND wxWidgets_LIBRARIES X11 wayland-client wayland-egl EGL)
+ endif ()
# list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc)
message(STATUS "wx libs: ${wxWidgets_LIBRARIES}")
diff --git a/src/PrusaSlicer_app_msvc.cpp b/src/PrusaSlicer_app_msvc.cpp
index 2ccf2f1ff..83f24c307 100644
--- a/src/PrusaSlicer_app_msvc.cpp
+++ b/src/PrusaSlicer_app_msvc.cpp
@@ -66,16 +66,25 @@ public:
return this->success;
}
- void unload_opengl_dll()
+ bool unload_opengl_dll()
{
- if (this->hOpenGL) {
- BOOL released = FreeLibrary(this->hOpenGL);
- if (released)
- printf("System OpenGL library released\n");
+ if (this->hOpenGL != nullptr) {
+ if (::FreeLibrary(this->hOpenGL) != FALSE) {
+ if (::GetModuleHandle(L"opengl32.dll") == nullptr) {
+ printf("System OpenGL library successfully released\n");
+ this->hOpenGL = nullptr;
+ return true;
+ }
+ else
+ printf("System OpenGL library released but not removed\n");
+ }
else
printf("System OpenGL library NOT released\n");
- this->hOpenGL = nullptr;
+
+ return false;
}
+
+ return true;
}
bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
@@ -270,20 +279,26 @@ int wmain(int argc, wchar_t **argv)
// https://wiki.qt.io/Cross_compiling_Mesa_for_Windows
// http://download.qt.io/development_releases/prebuilt/llvmpipe/windows/
if (load_mesa) {
- opengl_version_check.unload_opengl_dll();
- wchar_t path_to_mesa[MAX_PATH + 1] = { 0 };
- wcscpy(path_to_mesa, path_to_exe);
- wcscat(path_to_mesa, L"mesa\\opengl32.dll");
- printf("Loading MESA OpenGL library: %S\n", path_to_mesa);
- HINSTANCE hInstance_OpenGL = LoadLibraryExW(path_to_mesa, nullptr, 0);
- if (hInstance_OpenGL == nullptr) {
- printf("MESA OpenGL library was not loaded\n");
- } else
- printf("MESA OpenGL library was loaded sucessfully\n");
+ bool res = opengl_version_check.unload_opengl_dll();
+ if (!res) {
+ MessageBox(nullptr, L"PrusaSlicer was unable to automatically switch to MESA OpenGL library\nPlease, try to run the application using the '--sw-renderer' option.\n",
+ L"PrusaSlicer Warning", MB_OK);
+ return -1;
+ }
+ else {
+ wchar_t path_to_mesa[MAX_PATH + 1] = { 0 };
+ wcscpy(path_to_mesa, path_to_exe);
+ wcscat(path_to_mesa, L"mesa\\opengl32.dll");
+ printf("Loading MESA OpenGL library: %S\n", path_to_mesa);
+ HINSTANCE hInstance_OpenGL = LoadLibraryExW(path_to_mesa, nullptr, 0);
+ if (hInstance_OpenGL == nullptr)
+ printf("MESA OpenGL library was not loaded\n");
+ else
+ printf("MESA OpenGL library was loaded sucessfully\n");
+ }
}
#endif /* SLIC3R_GUI */
-
wchar_t path_to_slic3r[MAX_PATH + 1] = { 0 };
wcscpy(path_to_slic3r, path_to_exe);
wcscat(path_to_slic3r, L"PrusaSlicer.dll");
diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp
index 84d68b1e6..84109398a 100644
--- a/src/clipper/clipper.cpp
+++ b/src/clipper/clipper.cpp
@@ -2290,6 +2290,12 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge)
if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times
{
+#ifdef CLIPPERLIB_USE_XYZ
+ if (dir == dLeftToRight)
+ SetZ(e->Curr, *horzEdge, *e);
+ else
+ SetZ(e->Curr, *e, *horzEdge);
+#endif
op1 = AddOutPt(horzEdge, e->Curr);
TEdge* eNextHorz = m_SortedEdges;
while (eNextHorz)
@@ -2614,6 +2620,9 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
{
e->Curr.x() = TopX( *e, topY );
e->Curr.y() = topY;
+#ifdef CLIPPERLIB_USE_XYZ
+ e->Curr.z() = topY == e->Top.y() ? e->Top.z() : (topY == e->Bot.y() ? e->Bot.z() : 0);
+#endif
}
//When StrictlySimple and 'e' is being touched by another edge, then
diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp
index 014b49af1..f2847579c 100644
--- a/src/libslic3r/AABBTreeIndirect.hpp
+++ b/src/libslic3r/AABBTreeIndirect.hpp
@@ -446,6 +446,57 @@ namespace detail {
}
}
+ // Real-time collision detection, Ericson, Chapter 5
+ template
+ static inline Vector closest_point_to_triangle(const Vector &p, const Vector &a, const Vector &b, const Vector &c)
+ {
+ using Scalar = typename Vector::Scalar;
+ // Check if P in vertex region outside A
+ Vector ab = b - a;
+ Vector ac = c - a;
+ Vector ap = p - a;
+ Scalar d1 = ab.dot(ap);
+ Scalar d2 = ac.dot(ap);
+ if (d1 <= 0 && d2 <= 0)
+ return a;
+ // Check if P in vertex region outside B
+ Vector bp = p - b;
+ Scalar d3 = ab.dot(bp);
+ Scalar d4 = ac.dot(bp);
+ if (d3 >= 0 && d4 <= d3)
+ return b;
+ // Check if P in edge region of AB, if so return projection of P onto AB
+ Scalar vc = d1*d4 - d3*d2;
+ if (a != b && vc <= 0 && d1 >= 0 && d3 <= 0) {
+ Scalar v = d1 / (d1 - d3);
+ return a + v * ab;
+ }
+ // Check if P in vertex region outside C
+ Vector cp = p - c;
+ Scalar d5 = ab.dot(cp);
+ Scalar d6 = ac.dot(cp);
+ if (d6 >= 0 && d5 <= d6)
+ return c;
+ // Check if P in edge region of AC, if so return projection of P onto AC
+ Scalar vb = d5*d2 - d1*d6;
+ if (vb <= 0 && d2 >= 0 && d6 <= 0) {
+ Scalar w = d2 / (d2 - d6);
+ return a + w * ac;
+ }
+ // Check if P in edge region of BC, if so return projection of P onto BC
+ Scalar va = d3*d6 - d5*d4;
+ if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
+ Scalar w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
+ return b + w * (c - b);
+ }
+ // P inside face region. Compute Q through its barycentric coordinates (u,v,w)
+ Scalar denom = Scalar(1.0) / (va + vb + vc);
+ Scalar v = vb * denom;
+ Scalar w = vc * denom;
+ return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = 1.0-v-w
+ };
+
+
// Nothing to do with COVID-19 social distancing.
template
struct IndexedTriangleSetDistancer {
@@ -453,74 +504,36 @@ namespace detail {
using IndexedFaceType = AIndexedFaceType;
using TreeType = ATreeType;
using VectorType = AVectorType;
+ using ScalarType = typename VectorType::Scalar;
const std::vector &vertices;
const std::vector &faces;
const TreeType &tree;
const VectorType origin;
+
+ inline VectorType closest_point_to_origin(size_t primitive_index,
+ ScalarType& squared_distance){
+ const auto &triangle = this->faces[primitive_index];
+ VectorType closest_point = closest_point_to_triangle(origin,
+ this->vertices[triangle(0)].template cast(),
+ this->vertices[triangle(1)].template cast(),
+ this->vertices[triangle(2)].template cast());
+ squared_distance = (origin - closest_point).squaredNorm();
+ return closest_point;
+ }
};
- // Real-time collision detection, Ericson, Chapter 5
- template
- static inline Vector closest_point_to_triangle(const Vector &p, const Vector &a, const Vector &b, const Vector &c)
- {
- using Scalar = typename Vector::Scalar;
- // Check if P in vertex region outside A
- Vector ab = b - a;
- Vector ac = c - a;
- Vector ap = p - a;
- Scalar d1 = ab.dot(ap);
- Scalar d2 = ac.dot(ap);
- if (d1 <= 0 && d2 <= 0)
- return a;
- // Check if P in vertex region outside B
- Vector bp = p - b;
- Scalar d3 = ab.dot(bp);
- Scalar d4 = ac.dot(bp);
- if (d3 >= 0 && d4 <= d3)
- return b;
- // Check if P in edge region of AB, if so return projection of P onto AB
- Scalar vc = d1*d4 - d3*d2;
- if (a != b && vc <= 0 && d1 >= 0 && d3 <= 0) {
- Scalar v = d1 / (d1 - d3);
- return a + v * ab;
- }
- // Check if P in vertex region outside C
- Vector cp = p - c;
- Scalar d5 = ab.dot(cp);
- Scalar d6 = ac.dot(cp);
- if (d6 >= 0 && d5 <= d6)
- return c;
- // Check if P in edge region of AC, if so return projection of P onto AC
- Scalar vb = d5*d2 - d1*d6;
- if (vb <= 0 && d2 >= 0 && d6 <= 0) {
- Scalar w = d2 / (d2 - d6);
- return a + w * ac;
- }
- // Check if P in edge region of BC, if so return projection of P onto BC
- Scalar va = d3*d6 - d5*d4;
- if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
- Scalar w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
- return b + w * (c - b);
- }
- // P inside face region. Compute Q through its barycentric coordinates (u,v,w)
- Scalar denom = Scalar(1.0) / (va + vb + vc);
- Scalar v = vb * denom;
- Scalar w = vc * denom;
- return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = 1.0-v-w
- };
-
- template
- static inline Scalar squared_distance_to_indexed_triangle_set_recursive(
- IndexedTriangleSetDistancerType &distancer,
+ template
+ static inline Scalar squared_distance_to_indexed_primitives_recursive(
+ IndexedPrimitivesDistancerType &distancer,
size_t node_idx,
Scalar low_sqr_d,
Scalar up_sqr_d,
size_t &i,
- Eigen::PlainObjectBase &c)
+ Eigen::PlainObjectBase &c)
{
- using Vector = typename IndexedTriangleSetDistancerType::VectorType;
+ using Vector = typename IndexedPrimitivesDistancerType::VectorType;
if (low_sqr_d > up_sqr_d)
return low_sqr_d;
@@ -538,13 +551,9 @@ namespace detail {
assert(node.is_valid());
if (node.is_leaf())
{
- const auto &triangle = distancer.faces[node.idx];
- Vector c_candidate = closest_point_to_triangle(
- distancer.origin,
- distancer.vertices[triangle(0)].template cast(),
- distancer.vertices[triangle(1)].template cast(),
- distancer.vertices[triangle(2)].template cast());
- set_min((c_candidate - distancer.origin).squaredNorm(), node.idx, c_candidate);
+ Scalar sqr_dist;
+ Vector c_candidate = distancer.closest_point_to_origin(node.idx, sqr_dist);
+ set_min(sqr_dist, node.idx, c_candidate);
}
else
{
@@ -561,7 +570,7 @@ namespace detail {
{
size_t i_left;
Vector c_left = c;
- Scalar sqr_d_left = squared_distance_to_indexed_triangle_set_recursive(distancer, left_node_idx, low_sqr_d, up_sqr_d, i_left, c_left);
+ Scalar sqr_d_left = squared_distance_to_indexed_primitives_recursive(distancer, left_node_idx, low_sqr_d, up_sqr_d, i_left, c_left);
set_min(sqr_d_left, i_left, c_left);
looked_left = true;
};
@@ -569,13 +578,13 @@ namespace detail {
{
size_t i_right;
Vector c_right = c;
- Scalar sqr_d_right = squared_distance_to_indexed_triangle_set_recursive(distancer, right_node_idx, low_sqr_d, up_sqr_d, i_right, c_right);
+ Scalar sqr_d_right = squared_distance_to_indexed_primitives_recursive(distancer, right_node_idx, low_sqr_d, up_sqr_d, i_right, c_right);
set_min(sqr_d_right, i_right, c_right);
looked_right = true;
};
// must look left or right if in box
- using BBoxScalar = typename IndexedTriangleSetDistancerType::TreeType::BoundingBox::Scalar;
+ using BBoxScalar = typename IndexedPrimitivesDistancerType::TreeType::BoundingBox::Scalar;
if (node_left.bbox.contains(distancer.origin.template cast()))
look_left();
if (node_right.bbox.contains(distancer.origin.template cast()))
@@ -747,7 +756,7 @@ inline typename VectorType::Scalar squared_distance_to_indexed_triangle_set(
auto distancer = detail::IndexedTriangleSetDistancer
{ vertices, faces, tree, point };
return tree.empty() ? Scalar(-1) :
- detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), std::numeric_limits::infinity(), hit_idx_out, hit_point_out);
+ detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0), std::numeric_limits::infinity(), hit_idx_out, hit_point_out);
}
// Decides if exists some triangle in defined radius on a 3D indexed triangle set using a pre-built AABBTreeIndirect::Tree.
@@ -779,7 +788,7 @@ inline bool is_any_triangle_in_radius(
return false;
}
- detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), max_distance_squared, hit_idx, hit_point);
+ detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0), max_distance_squared, hit_idx, hit_point);
return hit_point.allFinite();
}
diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp
new file mode 100644
index 000000000..7b9595419
--- /dev/null
+++ b/src/libslic3r/AABBTreeLines.hpp
@@ -0,0 +1,112 @@
+#ifndef SRC_LIBSLIC3R_AABBTREELINES_HPP_
+#define SRC_LIBSLIC3R_AABBTREELINES_HPP_
+
+#include "libslic3r/Point.hpp"
+#include "libslic3r/EdgeGrid.hpp"
+#include "libslic3r/AABBTreeIndirect.hpp"
+#include "libslic3r/Line.hpp"
+
+namespace Slic3r {
+
+namespace AABBTreeLines {
+
+namespace detail {
+
+template
+struct IndexedLinesDistancer {
+ using LineType = ALineType;
+ using TreeType = ATreeType;
+ using VectorType = AVectorType;
+ using ScalarType = typename VectorType::Scalar;
+
+ const std::vector &lines;
+ const TreeType &tree;
+
+ const VectorType origin;
+
+ inline VectorType closest_point_to_origin(size_t primitive_index,
+ ScalarType &squared_distance) {
+ VectorType nearest_point;
+ const LineType &line = lines[primitive_index];
+ squared_distance = line_alg::distance_to_squared(line, origin, &nearest_point);
+ return nearest_point;
+ }
+};
+
+}
+
+// Build a balanced AABB Tree over a vector of float lines, balancing the tree
+// on centroids of the lines.
+// Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
+// during tree traversal.
+template
+inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(
+ const std::vector &lines,
+ //FIXME do we want to apply an epsilon?
+ const float eps = 0)
+ {
+ using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>;
+// using CoordType = typename TreeType::CoordType;
+ using VectorType = typename TreeType::VectorType;
+ using BoundingBox = typename TreeType::BoundingBox;
+
+ struct InputType {
+ size_t idx() const {
+ return m_idx;
+ }
+ const BoundingBox& bbox() const {
+ return m_bbox;
+ }
+ const VectorType& centroid() const {
+ return m_centroid;
+ }
+
+ size_t m_idx;
+ BoundingBox m_bbox;
+ VectorType m_centroid;
+ };
+
+ std::vector input;
+ input.reserve(lines.size());
+ const VectorType veps(eps, eps);
+ for (size_t i = 0; i < lines.size(); ++i) {
+ const LineType &line = lines[i];
+ InputType n;
+ n.m_idx = i;
+ n.m_centroid = (line.a + line.b) * 0.5;
+ n.m_bbox = BoundingBox(line.a, line.a);
+ n.m_bbox.extend(line.b);
+ n.m_bbox.min() -= veps;
+ n.m_bbox.max() += veps;
+ input.emplace_back(n);
+ }
+
+ TreeType out;
+ out.build(std::move(input));
+ return out;
+}
+
+// Finding a closest line, its closest point and squared distance to the closest point
+// Returns squared distance to the closest point or -1 if the input is empty.
+template
+inline typename VectorType::Scalar squared_distance_to_indexed_lines(
+ const std::vector &lines,
+ const TreeType &tree,
+ const VectorType &point,
+ size_t &hit_idx_out,
+ Eigen::PlainObjectBase &hit_point_out)
+ {
+ using Scalar = typename VectorType::Scalar;
+ auto distancer = detail::IndexedLinesDistancer
+ { lines, tree, point };
+ return tree.empty() ?
+ Scalar(-1) :
+ AABBTreeIndirect::detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0),
+ std::numeric_limits::infinity(), hit_idx_out, hit_point_out);
+}
+
+}
+
+}
+
+#endif /* SRC_LIBSLIC3R_AABBTREELINES_HPP_ */
diff --git a/src/libslic3r/AStar.hpp b/src/libslic3r/AStar.hpp
index 052d0e814..b35b6a4af 100644
--- a/src/libslic3r/AStar.hpp
+++ b/src/libslic3r/AStar.hpp
@@ -1,11 +1,12 @@
#ifndef ASTAR_HPP
#define ASTAR_HPP
+#include // std::isinf() is here
+#include
+
#include "libslic3r/Point.hpp"
#include "libslic3r/MutablePriorityQueue.hpp"
-#include
-
namespace Slic3r { namespace astar {
// Input interface for the Astar algorithm. Specialize this struct for a
@@ -34,6 +35,8 @@ template struct TracerTraits_
// Get the estimated distance heuristic from node 'n' to the destination.
// This is referred to as the h value in AStar context.
// If node 'n' is the goal, this function should return a negative value.
+ // Note that this heuristic should be admissible (never bigger than the real
+ // cost) in order for Astar to work.
static float goal_heuristic(const T &tracer, const Node &n)
{
return tracer.goal_heuristic(n);
@@ -50,131 +53,136 @@ template struct TracerTraits_
template
using TracerNodeT = typename TracerTraits_>::Node;
-namespace detail {
-// Helper functions dispatching calls through the TracerTraits_ interface
+constexpr auto Unassigned = std::numeric_limits::max();
-template using TracerTraits = TracerTraits_>;
-
-template
-void foreach_reachable(const T &tracer, const TracerNodeT &from, Fn &&fn)
+template
+struct QNode // Queue node. Keeps track of scores g, and h
{
- TracerTraits::foreach_reachable(tracer, from, fn);
-}
+ TracerNodeT node; // The actual node itself
+ size_t queue_id; // Position in the open queue or Unassigned if closed
+ size_t parent; // unique id of the parent or Unassigned
-template
-float trace_distance(const T &tracer, const TracerNodeT &a, const TracerNodeT &b)
-{
- return TracerTraits::distance(tracer, a, b);
-}
+ float g, h;
+ float f() const { return g + h; }
-template
-float goal_heuristic(const T &tracer, const TracerNodeT &n)
-{
- return TracerTraits::goal_heuristic(tracer, n);
-}
-
-template
-size_t unique_id(const T &tracer, const TracerNodeT &n)
-{
- return TracerTraits::unique_id(tracer, n);
-}
-
-} // namespace astar_detail
+ QNode(TracerNodeT n = {},
+ size_t p = Unassigned,
+ float gval = std::numeric_limits::infinity(),
+ float hval = 0.f)
+ : node{std::move(n)}
+ , parent{p}
+ , queue_id{InvalidQueueID}
+ , g{gval}
+ , h{hval}
+ {}
+};
// Run the AStar algorithm on a tracer implementation.
// The 'tracer' argument encapsulates the domain (grid, point cloud, etc...)
// The 'source' argument is the starting node.
// The 'out' argument is the output iterator into which the output nodes are
-// written.
-// Note that no destination node is given. The tracer's goal_heuristic() method
-// should return a negative value if a node is a destination node.
-template
-bool search_route(const Tracer &tracer, const TracerNodeT &source, It out)
+// written. For performance reasons, the order is reverse, from the destination
+// to the source -- (destination included, source is not).
+// The 'cached_nodes' argument is an optional associative container to hold a
+// QNode entry for each visited node. Any compatible container can be used
+// (like std::map or maps with different allocators, even a sufficiently large
+// std::vector).
+//
+// Note that no destination node is given in the signature. The tracer's
+// goal_heuristic() method should return a negative value if a node is a
+// destination node.
+template>>
+bool search_route(const Tracer &tracer,
+ const TracerNodeT &source,
+ It out,
+ NodeMap &&cached_nodes = {})
{
- using namespace detail;
+ using Node = TracerNodeT;
+ using QNode = QNode;
+ using TracerTraits = TracerTraits_>;
- using Node = TracerNodeT;
- enum class QueueType { Open, Closed, None };
-
- struct QNode // Queue node. Keeps track of scores g, and h
- {
- Node node; // The actual node itself
- QueueType qtype = QueueType::None; // Which queue holds this node
-
- float g = 0.f, h = 0.f;
- float f() const { return g + h; }
- };
-
- // TODO: apply a linear memory allocator
- using QMap = std::unordered_map;
-
- // The traversed nodes are stored here encapsulated in QNodes
- QMap cached_nodes;
-
- struct LessPred { // Comparison functor needed by MutablePriorityQueue
- QMap &m;
+ struct LessPred { // Comparison functor needed by the priority queue
+ NodeMap &m;
bool operator ()(size_t node_a, size_t node_b) {
- auto ait = m.find(node_a);
- auto bit = m.find(node_b);
- assert (ait != m.end() && bit != m.end());
-
- return ait->second.f() < bit->second.f();
+ return m[node_a].f() < m[node_b].f();
}
};
- auto qopen =
- make_mutable_priority_queue([](size_t, size_t){},
- LessPred{cached_nodes});
+ auto qopen = make_mutable_priority_queue(
+ [&cached_nodes](size_t el, size_t qidx) {
+ cached_nodes[el].queue_id = qidx;
+ },
+ LessPred{cached_nodes});
- auto qclosed =
- make_mutable_priority_queue([](size_t, size_t){},
- LessPred{cached_nodes});
+ QNode initial{source, /*parent = */ Unassigned, /*g = */0.f};
+ size_t source_id = TracerTraits::unique_id(tracer, source);
+ cached_nodes[source_id] = initial;
+ qopen.push(source_id);
- QNode initial{source, QueueType::Open};
- cached_nodes.insert({unique_id(tracer, source), initial});
- qopen.push(unique_id(tracer, source));
+ size_t goal_id = TracerTraits::goal_heuristic(tracer, source) < 0.f ?
+ source_id :
+ Unassigned;
- bool goal_reached = false;
-
- while (!goal_reached && !qopen.empty()) {
+ while (goal_id == Unassigned && !qopen.empty()) {
size_t q_id = qopen.top();
qopen.pop();
- QNode q = cached_nodes.at(q_id);
+ QNode &q = cached_nodes[q_id];
- foreach_reachable(tracer, q.node, [&](const Node &nd) {
- if (goal_reached) return goal_reached;
+ // This should absolutely be initialized in the cache already
+ assert(!std::isinf(q.g));
+
+ TracerTraits::foreach_reachable(tracer, q.node, [&](const Node &succ_nd) {
+ if (goal_id != Unassigned)
+ return true;
+
+ float h = TracerTraits::goal_heuristic(tracer, succ_nd);
+ float dst = TracerTraits::distance(tracer, q.node, succ_nd);
+ size_t succ_id = TracerTraits::unique_id(tracer, succ_nd);
+ QNode qsucc_nd{succ_nd, q_id, q.g + dst, h};
- float h = goal_heuristic(tracer, nd);
if (h < 0.f) {
- goal_reached = true;
+ goal_id = succ_id;
+ cached_nodes[succ_id] = qsucc_nd;
} else {
- float dst = trace_distance(tracer, q.node, nd);
- QNode qnd{nd, QueueType::None, q.g + dst, h};
- size_t qnd_id = unique_id(tracer, nd);
+ // If succ_id is not in cache, it gets created with g = infinity
+ QNode &prev_nd = cached_nodes[succ_id];
- auto it = cached_nodes.find(qnd_id);
+ if (qsucc_nd.g < prev_nd.g) {
+ // new route is better, apply it:
- if (it == cached_nodes.end() ||
- (it->second.qtype != QueueType::None && qnd.f() < it->second.f())) {
- qnd.qtype = QueueType::Open;
- cached_nodes.insert_or_assign(qnd_id, qnd);
- qopen.push(qnd_id);
+ // Save the old queue id, it would be lost after the next line
+ size_t queue_id = prev_nd.queue_id;
+
+ // The cache needs to be updated either way
+ prev_nd = qsucc_nd;
+
+ if (queue_id == InvalidQueueID)
+ // was in closed or unqueued, rescheduling
+ qopen.push(succ_id);
+ else // was in open, updating
+ qopen.update(queue_id);
}
}
- return goal_reached;
+ return goal_id != Unassigned;
});
-
- q.qtype = QueueType::Closed;
- cached_nodes.insert_or_assign(q_id, q);
- qclosed.push(q_id);
-
- // write the output
- *out = q.node;
- ++out;
}
- return goal_reached;
+ // Write the output, do not reverse. Clients can do so if they need to.
+ if (goal_id != Unassigned) {
+ const QNode *q = &cached_nodes[goal_id];
+ while (q->parent != Unassigned) {
+ assert(!std::isinf(q->g)); // Uninitialized nodes are NOT allowed
+
+ *out = q->node;
+ ++out;
+ q = &cached_nodes[q->parent];
+ }
+ }
+
+ return goal_id != Unassigned;
}
}} // namespace Slic3r::astar
diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp
index 928febffd..82c0a72a6 100644
--- a/src/libslic3r/AppConfig.cpp
+++ b/src/libslic3r/AppConfig.cpp
@@ -230,8 +230,13 @@ static std::string appconfig_md5_hash_line(const std::string_view data)
return "# MD5 checksum " + md5_digest_str + "\n";
};
+struct ConfigFileInfo {
+ bool correct_checksum {false};
+ bool contains_null {false};
+};
+
// Assume that the last line with the comment inside the config file contains a checksum and that the user didn't modify the config file.
-static bool verify_config_file_checksum(boost::nowide::ifstream &ifs)
+static ConfigFileInfo check_config_file_and_verify_checksum(boost::nowide::ifstream &ifs)
{
auto read_whole_config_file = [&ifs]() -> std::string {
std::stringstream ss;
@@ -240,7 +245,8 @@ static bool verify_config_file_checksum(boost::nowide::ifstream &ifs)
};
ifs.seekg(0, boost::nowide::ifstream::beg);
- std::string whole_config = read_whole_config_file();
+ const std::string whole_config = read_whole_config_file();
+ const bool contains_null = whole_config.find_first_of('\0') != std::string::npos;
// The checksum should be on the last line in the config file.
if (size_t last_comment_pos = whole_config.find_last_of('#'); last_comment_pos != std::string::npos) {
@@ -249,9 +255,9 @@ static bool verify_config_file_checksum(boost::nowide::ifstream &ifs)
// When the checksum isn't found, the checksum was not saved correctly, it was removed or it is an older config file without the checksum.
// If the checksum is incorrect, then the file was either not saved correctly or modified.
if (std::string_view(whole_config.c_str() + last_comment_pos, whole_config.size() - last_comment_pos) == appconfig_md5_hash_line({ whole_config.data(), last_comment_pos }))
- return true;
+ return {true, contains_null};
}
- return false;
+ return {false, contains_null};
}
#endif
@@ -269,14 +275,25 @@ std::string AppConfig::load(const std::string &path)
ifs.open(path);
#ifdef WIN32
// Verify the checksum of the config file without taking just for debugging purpose.
- if (!verify_config_file_checksum(ifs))
- BOOST_LOG_TRIVIAL(info) << "The configuration file " << path <<
- " has a wrong MD5 checksum or the checksum is missing. This may indicate a file corruption or a harmless user edit.";
+ const ConfigFileInfo config_file_info = check_config_file_and_verify_checksum(ifs);
+ if (!config_file_info.correct_checksum)
+ BOOST_LOG_TRIVIAL(info)
+ << "The configuration file " << path
+ << " has a wrong MD5 checksum or the checksum is missing. This may indicate a file corruption or a harmless user edit.";
+
+ if (!config_file_info.correct_checksum && config_file_info.contains_null) {
+ BOOST_LOG_TRIVIAL(info) << "The configuration file " + path + " is corrupted, because it is contains null characters.";
+ throw Slic3r::CriticalException("The configuration file contains null characters.");
+ }
ifs.seekg(0, boost::nowide::ifstream::beg);
#endif
- pt::read_ini(ifs, tree);
- } catch (pt::ptree_error& ex) {
+ try {
+ pt::read_ini(ifs, tree);
+ } catch (pt::ptree_error &ex) {
+ throw Slic3r::CriticalException(ex.what());
+ }
+ } catch (Slic3r::CriticalException &ex) {
#ifdef WIN32
// The configuration file is corrupted, try replacing it with the backup configuration.
ifs.close();
@@ -284,29 +301,29 @@ std::string AppConfig::load(const std::string &path)
if (boost::filesystem::exists(backup_path)) {
// Compute checksum of the configuration backup file and try to load configuration from it when the checksum is correct.
boost::nowide::ifstream backup_ifs(backup_path);
- if (!verify_config_file_checksum(backup_ifs)) {
- BOOST_LOG_TRIVIAL(error) << format("Both \"%1%\" and \"%2%\" are corrupted. It isn't possible to restore configuration from the backup.", path, backup_path);
+ if (const ConfigFileInfo config_file_info = check_config_file_and_verify_checksum(backup_ifs); !config_file_info.correct_checksum || config_file_info.contains_null) {
+ BOOST_LOG_TRIVIAL(error) << format(R"(Both "%1%" and "%2%" are corrupted. It isn't possible to restore configuration from the backup.)", path, backup_path);
backup_ifs.close();
boost::filesystem::remove(backup_path);
} else if (std::string error_message; copy_file(backup_path, path, error_message, false) != SUCCESS) {
- BOOST_LOG_TRIVIAL(error) << format("Configuration file \"%1%\" is corrupted. Failed to restore from backup \"%2%\": %3%", path, backup_path, error_message);
+ BOOST_LOG_TRIVIAL(error) << format(R"(Configuration file "%1%" is corrupted. Failed to restore from backup "%2%": %3%)", path, backup_path, error_message);
backup_ifs.close();
boost::filesystem::remove(backup_path);
} else {
- BOOST_LOG_TRIVIAL(info) << format("Configuration file \"%1%\" was corrupted. It has been succesfully restored from the backup \"%2%\".", path, backup_path);
+ BOOST_LOG_TRIVIAL(info) << format(R"(Configuration file "%1%" was corrupted. It has been successfully restored from the backup "%2%".)", path, backup_path);
// Try parse configuration file after restore from backup.
try {
ifs.open(path);
pt::read_ini(ifs, tree);
recovered = true;
} catch (pt::ptree_error& ex) {
- BOOST_LOG_TRIVIAL(info) << format("Failed to parse configuration file \"%1%\" after it has been restored from backup: %2%", path, ex.what());
+ BOOST_LOG_TRIVIAL(info) << format(R"(Failed to parse configuration file "%1%" after it has been restored from backup: %2%)", path, ex.what());
}
}
} else
#endif // WIN32
- BOOST_LOG_TRIVIAL(info) << format("Failed to parse configuration file \"%1%\": %2%", path, ex.what());
- if (! recovered) {
+ BOOST_LOG_TRIVIAL(info) << format(R"(Failed to parse configuration file "%1%": %2%)", path, ex.what());
+ if (!recovered) {
// Report the initial error of parsing PrusaSlicer.ini.
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
// ! But to avoid the use of _utf8 (related to use of wxWidgets)
diff --git a/src/libslic3r/BuildVolume.cpp b/src/libslic3r/BuildVolume.cpp
index c580e6f87..899055355 100644
--- a/src/libslic3r/BuildVolume.cpp
+++ b/src/libslic3r/BuildVolume.cpp
@@ -238,7 +238,7 @@ BuildVolume::ObjectState object_state_templ(const indexed_triangle_set &its, con
const stl_vertex p2 = trafo * its.vertices[tri(iedge)];
assert(sign(p1) == s[iprev]);
assert(sign(p2) == s[iedge]);
- assert(p1.z() * p2.z() < 0);
+ assert((p1.z() - world_min_z) * (p2.z() - world_min_z) < 0);
// Edge crosses the z plane. Calculate intersection point with the plane.
const float t = (world_min_z - p1.z()) / (p2.z() - p1.z());
(is_inside(Vec3f(p1.x() + (p2.x() - p1.x()) * t, p1.y() + (p2.y() - p1.y()) * t, world_min_z)) ? inside : outside) = true;
diff --git a/src/libslic3r/Fill/FillLightning.cpp b/src/libslic3r/Fill/FillLightning.cpp
index 447fd8057..dd2189e6b 100644
--- a/src/libslic3r/Fill/FillLightning.cpp
+++ b/src/libslic3r/Fill/FillLightning.cpp
@@ -1,29 +1,34 @@
#include "../Print.hpp"
+#include "../ShortestPath.hpp"
#include "FillLightning.hpp"
#include "Lightning/Generator.hpp"
-#include "../Surface.hpp"
-
-#include
-#include
-#include
-#include
namespace Slic3r::FillLightning {
-Polylines Filler::fill_surface(const Surface *surface, const FillParams ¶ms)
+void Filler::_fill_surface_single(
+ const FillParams ¶ms,
+ unsigned int thickness_layers,
+ const std::pair &direction,
+ ExPolygon expolygon,
+ Polylines &polylines_out)
{
- const Layer &layer = generator->getTreesForLayer(this->layer_id);
- return layer.convertToLines(to_polygons(surface->expolygon), generator->infilll_extrusion_width());
+ const Layer &layer = generator->getTreesForLayer(this->layer_id);
+ Polylines fill_lines = layer.convertToLines(to_polygons(expolygon), scaled(0.5 * this->spacing - this->overlap));
+
+ if (params.dont_connect() || fill_lines.size() <= 1) {
+ append(polylines_out, chain_polylines(std::move(fill_lines)));
+ } else
+ connect_infill(std::move(fill_lines), expolygon, polylines_out, this->spacing, params);
}
void GeneratorDeleter::operator()(Generator *p) {
delete p;
}
-GeneratorPtr build_generator(const PrintObject &print_object)
+GeneratorPtr build_generator(const PrintObject &print_object, const std::function &throw_on_cancel_callback)
{
- return GeneratorPtr(new Generator(print_object));
+ return GeneratorPtr(new Generator(print_object, throw_on_cancel_callback));
}
} // namespace Slic3r::FillAdaptive
diff --git a/src/libslic3r/Fill/FillLightning.hpp b/src/libslic3r/Fill/FillLightning.hpp
index b4e7e35f1..6e672783a 100644
--- a/src/libslic3r/Fill/FillLightning.hpp
+++ b/src/libslic3r/Fill/FillLightning.hpp
@@ -14,7 +14,7 @@ class Generator;
struct GeneratorDeleter { void operator()(Generator *p); };
using GeneratorPtr = std::unique_ptr;
-GeneratorPtr build_generator(const PrintObject &print_object);
+GeneratorPtr build_generator(const PrintObject &print_object, const std::function &throw_on_cancel_callback);
class Filler : public Slic3r::Fill
{
@@ -24,8 +24,13 @@ public:
Generator *generator { nullptr };
protected:
Fill* clone() const override { return new Filler(*this); }
- // Perform the fill.
- Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
+
+ void _fill_surface_single(const FillParams ¶ms,
+ unsigned int thickness_layers,
+ const std::pair &direction,
+ ExPolygon expolygon,
+ Polylines &polylines_out) override;
+
// Let the G-code export reoder the infill lines.
bool no_sort() const override { return false; }
};
diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp
index 264ae8a59..ba7461c5f 100644
--- a/src/libslic3r/Fill/FillRectilinear.cpp
+++ b/src/libslic3r/Fill/FillRectilinear.cpp
@@ -3043,14 +3043,18 @@ Polylines FillSupportBase::fill_surface(const Surface *surface, const FillParams
return polylines_out;
}
-Points sample_grid_pattern(const ExPolygon &expolygon, coord_t spacing)
+// Lightning infill assumes that the distance between any two sampled points is always
+// at least equal to the value of spacing. To meet this assumption, we need to use
+// BoundingBox for whole layers instead of bounding box just around processing ExPolygon.
+// Using just BoundingBox around processing ExPolygon could produce two points closer
+// than spacing (in cases where two ExPolygon are closer than spacing).
+Points sample_grid_pattern(const ExPolygon &expolygon, coord_t spacing, const BoundingBox &global_bounding_box)
{
ExPolygonWithOffset poly_with_offset(expolygon, 0, 0, 0);
- BoundingBox bounding_box = poly_with_offset.bounding_box_src();
std::vector segs = slice_region_by_vertical_lines(
poly_with_offset,
- (bounding_box.max.x() - bounding_box.min.x() + spacing - 1) / spacing,
- bounding_box.min.x(),
+ (global_bounding_box.max.x() - global_bounding_box.min.x() + spacing - 1) / spacing,
+ global_bounding_box.min.x(),
spacing);
Points out;
@@ -3066,17 +3070,17 @@ Points sample_grid_pattern(const ExPolygon &expolygon, coord_t spacing)
return out;
}
-Points sample_grid_pattern(const ExPolygons &expolygons, coord_t spacing)
+Points sample_grid_pattern(const ExPolygons &expolygons, coord_t spacing, const BoundingBox &global_bounding_box)
{
Points out;
for (const ExPolygon &expoly : expolygons)
- append(out, sample_grid_pattern(expoly, spacing));
+ append(out, sample_grid_pattern(expoly, spacing, global_bounding_box));
return out;
}
-Points sample_grid_pattern(const Polygons &polygons, coord_t spacing)
+Points sample_grid_pattern(const Polygons &polygons, coord_t spacing, const BoundingBox &global_bounding_box)
{
- return sample_grid_pattern(union_ex(polygons), spacing);
+ return sample_grid_pattern(union_ex(polygons), spacing, global_bounding_box);
}
} // namespace Slic3r
diff --git a/src/libslic3r/Fill/FillRectilinear.hpp b/src/libslic3r/Fill/FillRectilinear.hpp
index ba735dd02..0a6c976ad 100644
--- a/src/libslic3r/Fill/FillRectilinear.hpp
+++ b/src/libslic3r/Fill/FillRectilinear.hpp
@@ -109,9 +109,9 @@ protected:
float _layer_angle(size_t idx) const override { return 0.f; }
};
-Points sample_grid_pattern(const ExPolygon &expolygon, coord_t spacing);
-Points sample_grid_pattern(const ExPolygons &expolygons, coord_t spacing);
-Points sample_grid_pattern(const Polygons &polygons, coord_t spacing);
+Points sample_grid_pattern(const ExPolygon &expolygon, coord_t spacing, const BoundingBox &global_bounding_box);
+Points sample_grid_pattern(const ExPolygons &expolygons, coord_t spacing, const BoundingBox &global_bounding_box);
+Points sample_grid_pattern(const Polygons &polygons, coord_t spacing, const BoundingBox &global_bounding_box);
} // namespace Slic3r
diff --git a/src/libslic3r/Fill/Lightning/DistanceField.cpp b/src/libslic3r/Fill/Lightning/DistanceField.cpp
index 308ca41c6..0cbfa9af5 100644
--- a/src/libslic3r/Fill/Lightning/DistanceField.cpp
+++ b/src/libslic3r/Fill/Lightning/DistanceField.cpp
@@ -5,11 +5,36 @@
#include "../FillRectilinear.hpp"
#include "../../ClipperUtils.hpp"
+#include
+
+#ifdef LIGHTNING_DISTANCE_FIELD_DEBUG_OUTPUT
+#include "../../SVG.hpp"
+#endif
+
namespace Slic3r::FillLightning
{
constexpr coord_t radius_per_cell_size = 6; // The cell-size should be small compared to the radius, but not so small as to be inefficient.
+#ifdef LIGHTNING_DISTANCE_FIELD_DEBUG_OUTPUT
+void export_distance_field_to_svg(const std::string &path, const Polygons &outline, const Polygons &overhang, const std::list &unsupported_points, const Points &points = {})
+{
+ coordf_t stroke_width = scaled(0.01);
+ BoundingBox bbox = get_extents(outline);
+
+ bbox.offset(SCALED_EPSILON);
+ SVG svg(path, bbox);
+ svg.draw_outline(outline, "green", stroke_width);
+ svg.draw_outline(overhang, "blue", stroke_width);
+
+ for (const DistanceField::UnsupportedCell &cell : unsupported_points)
+ svg.draw(cell.loc, "cyan", coord_t(stroke_width));
+
+ for (const Point &pt : points)
+ svg.draw(pt, "red", coord_t(stroke_width));
+}
+#endif
+
DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outline, const BoundingBox& current_outlines_bbox, const Polygons& current_overhang) :
m_cell_size(radius / radius_per_cell_size),
m_supporting_radius(radius),
@@ -17,37 +42,54 @@ DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outl
{
m_supporting_radius2 = Slic3r::sqr(int64_t(radius));
// Sample source polygons with a regular grid sampling pattern.
+ const BoundingBox overhang_bbox = get_extents(current_overhang);
for (const ExPolygon &expoly : union_ex(current_overhang)) {
- for (const Point &p : sample_grid_pattern(expoly, m_cell_size)) {
- // Find a squared distance to the source expolygon boundary.
- double d2 = std::numeric_limits::max();
- for (size_t icontour = 0; icontour <= expoly.holes.size(); ++icontour) {
- const Polygon &contour = icontour == 0 ? expoly.contour : expoly.holes[icontour - 1];
- if (contour.size() > 2) {
- Point prev = contour.points.back();
- for (const Point &p2 : contour.points) {
- d2 = std::min(d2, Line::distance_to_squared(p, prev, p2));
- prev = p2;
+ const Points sampled_points = sample_grid_pattern(expoly, m_cell_size, overhang_bbox);
+ const size_t unsupported_points_prev_size = m_unsupported_points.size();
+ m_unsupported_points.resize(unsupported_points_prev_size + sampled_points.size());
+
+ tbb::parallel_for(tbb::blocked_range(0, sampled_points.size()), [&self = *this, &expoly = std::as_const(expoly), &sampled_points = std::as_const(sampled_points), &unsupported_points_prev_size = std::as_const(unsupported_points_prev_size)](const tbb::blocked_range &range) -> void {
+ for (size_t sp_idx = range.begin(); sp_idx < range.end(); ++sp_idx) {
+ const Point &sp = sampled_points[sp_idx];
+ // Find a squared distance to the source expolygon boundary.
+ double d2 = std::numeric_limits::max();
+ for (size_t icontour = 0; icontour <= expoly.holes.size(); ++icontour) {
+ const Polygon &contour = icontour == 0 ? expoly.contour : expoly.holes[icontour - 1];
+ if (contour.size() > 2) {
+ Point prev = contour.points.back();
+ for (const Point &p2 : contour.points) {
+ d2 = std::min(d2, Line::distance_to_squared(sp, prev, p2));
+ prev = p2;
+ }
}
}
+ self.m_unsupported_points[unsupported_points_prev_size + sp_idx] = {sp, coord_t(std::sqrt(d2))};
+ assert(self.m_unsupported_points_bbox.contains(sp));
}
- m_unsupported_points.emplace_back(p, sqrt(d2));
- assert(m_unsupported_points_bbox.contains(p));
- }
+ }); // end of parallel_for
}
- m_unsupported_points.sort([&radius](const UnsupportedCell &a, const UnsupportedCell &b) {
+ std::stable_sort(m_unsupported_points.begin(), m_unsupported_points.end(), [&radius](const UnsupportedCell &a, const UnsupportedCell &b) {
constexpr coord_t prime_for_hash = 191;
return std::abs(b.dist_to_boundary - a.dist_to_boundary) > radius ?
a.dist_to_boundary < b.dist_to_boundary :
(PointHash{}(a.loc) % prime_for_hash) < (PointHash{}(b.loc) % prime_for_hash);
});
- for (auto it = m_unsupported_points.begin(); it != m_unsupported_points.end(); ++it) {
- UnsupportedCell& cell = *it;
- m_unsupported_points_grid.emplace(this->to_grid_point(cell.loc), it);
- }
+
+ m_unsupported_points_erased.resize(m_unsupported_points.size());
+ std::fill(m_unsupported_points_erased.begin(), m_unsupported_points_erased.end(), false);
+
+ m_unsupported_points_grid.initialize(m_unsupported_points, [&self = std::as_const(*this)](const Point &p) -> Point { return self.to_grid_point(p); });
+
// Because the distance between two points is at least one axis equal to m_cell_size, every cell
// in m_unsupported_points_grid contains exactly one point.
assert(m_unsupported_points.size() == m_unsupported_points_grid.size());
+
+#ifdef LIGHTNING_DISTANCE_FIELD_DEBUG_OUTPUT
+ {
+ static int iRun = 0;
+ export_distance_field_to_svg(debug_out_path("FillLightning-DistanceField-%d.svg", iRun++), current_outline, current_overhang, m_unsupported_points);
+ }
+#endif
}
void DistanceField::update(const Point& to_node, const Point& added_leaf)
@@ -96,12 +138,11 @@ void DistanceField::update(const Point& to_node, const Point& added_leaf)
}
// Inside a circle at the end of the new leaf, or inside a rotated rectangle.
// Remove unsupported leafs at this grid location.
- if (auto it = m_unsupported_points_grid.find(grid_addr); it != m_unsupported_points_grid.end()) {
- std::list::iterator& list_it = it->second;
- UnsupportedCell& cell = *list_it;
+ if (const size_t cell_idx = m_unsupported_points_grid.find_cell_idx(grid_addr); cell_idx != std::numeric_limits::max()) {
+ const UnsupportedCell &cell = m_unsupported_points[cell_idx];
if ((cell.loc - added_leaf).cast().squaredNorm() <= m_supporting_radius2) {
- m_unsupported_points.erase(list_it);
- m_unsupported_points_grid.erase(it);
+ m_unsupported_points_erased[cell_idx] = true;
+ m_unsupported_points_grid.mark_erased(grid_addr);
}
}
}
diff --git a/src/libslic3r/Fill/Lightning/DistanceField.hpp b/src/libslic3r/Fill/Lightning/DistanceField.hpp
index beb46c5c5..007ac235e 100644
--- a/src/libslic3r/Fill/Lightning/DistanceField.hpp
+++ b/src/libslic3r/Fill/Lightning/DistanceField.hpp
@@ -8,6 +8,8 @@
#include "../../Point.hpp"
#include "../../Polygon.hpp"
+//#define LIGHTNING_DISTANCE_FIELD_DEBUG_OUTPUT
+
namespace Slic3r::FillLightning
{
@@ -38,11 +40,17 @@ public:
* \return ``true`` if successful, or ``false`` if there are no more points
* to consider.
*/
- bool tryGetNextPoint(Point* p) const {
- if (m_unsupported_points.empty())
- return false;
- *p = m_unsupported_points.front().loc;
- return true;
+ bool tryGetNextPoint(Point *out_unsupported_location, size_t *out_unsupported_cell_idx, const size_t start_idx = 0) const
+ {
+ for (size_t point_idx = start_idx; point_idx < m_unsupported_points.size(); ++point_idx) {
+ if (!m_unsupported_points_erased[point_idx]) {
+ *out_unsupported_cell_idx = point_idx;
+ *out_unsupported_location = m_unsupported_points[point_idx].loc;
+ return true;
+ }
+ }
+
+ return false;
}
/*!
@@ -77,7 +85,6 @@ protected:
*/
struct UnsupportedCell
{
- UnsupportedCell(const Point &loc, coord_t dist_to_boundary) : loc(loc), dist_to_boundary(dist_to_boundary) {}
// The position of the center of this cell.
Point loc;
// How far this cell is removed from the ``current_outline`` polygon, the edge of the infill area.
@@ -87,7 +94,8 @@ protected:
/*!
* Cells which still need to be supported at some point.
*/
- std::list m_unsupported_points;
+ std::vector m_unsupported_points;
+ std::vector m_unsupported_points_erased;
/*!
* BoundingBox of all points in m_unsupported_points. Used for mapping of sign integer numbers to positive integer numbers.
@@ -98,7 +106,84 @@ protected:
* Links the unsupported points to a grid point, so that we can quickly look
* up the cell belonging to a certain position in the grid.
*/
- std::unordered_map::iterator, PointHash> m_unsupported_points_grid;
+
+ class UnsupportedPointsGrid
+ {
+ public:
+ UnsupportedPointsGrid() = default;
+ void initialize(const std::vector &unsupported_points, const std::function &map_cell_to_grid)
+ {
+ if (unsupported_points.empty())
+ return;
+
+ BoundingBox unsupported_points_bbox;
+ for (const UnsupportedCell &cell : unsupported_points)
+ unsupported_points_bbox.merge(cell.loc);
+
+ m_size = unsupported_points.size();
+ m_grid_range = BoundingBox(map_cell_to_grid(unsupported_points_bbox.min), map_cell_to_grid(unsupported_points_bbox.max));
+ m_grid_size = m_grid_range.size() + Point::Ones();
+
+ m_data.assign(m_grid_size.y() * m_grid_size.x(), std::numeric_limits::max());
+ m_data_erased.assign(m_grid_size.y() * m_grid_size.x(), true);
+
+ for (size_t cell_idx = 0; cell_idx < unsupported_points.size(); ++cell_idx) {
+ const size_t flat_idx = map_to_flat_array(map_cell_to_grid(unsupported_points[cell_idx].loc));
+ assert(m_data[flat_idx] == std::numeric_limits::max());
+ m_data[flat_idx] = cell_idx;
+ m_data_erased[flat_idx] = false;
+ }
+ }
+
+ size_t size() const { return m_size; }
+
+ size_t find_cell_idx(const Point &grid_addr)
+ {
+ if (!m_grid_range.contains(grid_addr))
+ return std::numeric_limits::max();
+
+ if (const size_t flat_idx = map_to_flat_array(grid_addr); !m_data_erased[flat_idx]) {
+ assert(m_data[flat_idx] != std::numeric_limits::max());
+ return m_data[flat_idx];
+ }
+
+ return std::numeric_limits::max();
+ }
+
+ void mark_erased(const Point &grid_addr)
+ {
+ assert(m_grid_range.contains(grid_addr));
+ if (!m_grid_range.contains(grid_addr))
+ return;
+
+ const size_t flat_idx = map_to_flat_array(grid_addr);
+ assert(!m_data_erased[flat_idx] && m_data[flat_idx] != std::numeric_limits::max());
+ assert(m_size != 0);
+
+ m_data_erased[flat_idx] = true;
+ --m_size;
+ }
+
+ private:
+ size_t m_size = 0;
+
+ BoundingBox m_grid_range;
+ Point m_grid_size;
+
+ std::vector m_data;
+ std::vector m_data_erased;
+
+ inline size_t map_to_flat_array(const Point &loc) const
+ {
+ const Point offset_loc = loc - m_grid_range.min;
+ const size_t flat_idx = m_grid_size.x() * offset_loc.y() + offset_loc.x();
+ assert(offset_loc.x() >= 0 && offset_loc.y() >= 0);
+ assert(flat_idx < size_t(m_grid_size.y() * m_grid_size.x()));
+ return flat_idx;
+ }
+ };
+
+ UnsupportedPointsGrid m_unsupported_points_grid;
/*!
* Maps the point to the grid coordinates.
@@ -113,6 +198,10 @@ protected:
Point from_grid_point(const Point &point) const {
return point * m_cell_size + m_unsupported_points_bbox.min;
}
+
+#ifdef LIGHTNING_DISTANCE_FIELD_DEBUG_OUTPUT
+ friend void export_distance_field_to_svg(const std::string &path, const Polygons &outline, const Polygons &overhang, const std::list &unsupported_points, const Points &points);
+#endif
};
} // namespace Slic3r::FillLightning
diff --git a/src/libslic3r/Fill/Lightning/Generator.cpp b/src/libslic3r/Fill/Lightning/Generator.cpp
index 0bdd1c7e8..75ca5583e 100644
--- a/src/libslic3r/Fill/Lightning/Generator.cpp
+++ b/src/libslic3r/Fill/Lightning/Generator.cpp
@@ -7,7 +7,6 @@
#include "../../ClipperUtils.hpp"
#include "../../Layer.hpp"
#include "../../Print.hpp"
-#include "../../Surface.hpp"
/* Possible future tasks/optimizations,etc.:
* - Improve connecting heuristic to favor connecting to shorter trees
@@ -25,7 +24,7 @@
namespace Slic3r::FillLightning {
-Generator::Generator(const PrintObject &print_object)
+Generator::Generator(const PrintObject &print_object, const std::function &throw_on_cancel_callback)
{
const PrintConfig &print_config = print_object.print()->config();
const PrintObjectConfig &object_config = print_object.config();
@@ -47,24 +46,23 @@ Generator::Generator(const PrintObject &print_object)
m_prune_length = coord_t(layer_thickness * std::tan(lightning_infill_prune_angle));
m_straightening_max_distance = coord_t(layer_thickness * std::tan(lightning_infill_straightening_angle));
- generateInitialInternalOverhangs(print_object);
- generateTrees(print_object);
+ generateInitialInternalOverhangs(print_object, throw_on_cancel_callback);
+ generateTrees(print_object, throw_on_cancel_callback);
}
-void Generator::generateInitialInternalOverhangs(const PrintObject &print_object)
+void Generator::generateInitialInternalOverhangs(const PrintObject &print_object, const std::function &throw_on_cancel_callback)
{
m_overhang_per_layer.resize(print_object.layers().size());
- // FIXME: It can be adjusted to improve bonding between infill and perimeters.
- const float infill_wall_offset = 0;// m_infill_extrusion_width;
Polygons infill_area_above;
//Iterate from top to bottom, to subtract the overhang areas above from the overhang areas on the layer below, to get only overhang in the top layer where it is overhanging.
- for (int layer_nr = int(print_object.layers().size()) - 1; layer_nr >= 0; layer_nr--) {
+ for (int layer_nr = int(print_object.layers().size()) - 1; layer_nr >= 0; --layer_nr) {
+ throw_on_cancel_callback();
Polygons infill_area_here;
for (const LayerRegion* layerm : print_object.get_layer(layer_nr)->regions())
for (const Surface& surface : layerm->fill_surfaces.surfaces)
if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid)
- append(infill_area_here, infill_wall_offset == 0 ? surface.expolygon : offset(surface.expolygon, infill_wall_offset));
+ append(infill_area_here, to_polygons(surface.expolygon));
//Remove the part of the infill area that is already supported by the walls.
Polygons overhang = diff(offset(infill_area_here, -float(m_wall_supporting_radius)), infill_area_above);
@@ -80,20 +78,20 @@ const Layer& Generator::getTreesForLayer(const size_t& layer_id) const
return m_lightning_layers[layer_id];
}
-void Generator::generateTrees(const PrintObject &print_object)
+void Generator::generateTrees(const PrintObject &print_object, const std::function &throw_on_cancel_callback)
{
m_lightning_layers.resize(print_object.layers().size());
- // FIXME: It can be adjusted to improve bonding between infill and perimeters.
- const coord_t infill_wall_offset = 0;// m_infill_extrusion_width;
std::vector infill_outlines(print_object.layers().size(), Polygons());
// For-each layer from top to bottom:
- for (int layer_id = int(print_object.layers().size()) - 1; layer_id >= 0; layer_id--)
+ for (int layer_id = int(print_object.layers().size()) - 1; layer_id >= 0; layer_id--) {
+ throw_on_cancel_callback();
for (const LayerRegion *layerm : print_object.get_layer(layer_id)->regions())
for (const Surface &surface : layerm->fill_surfaces.surfaces)
if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid)
- append(infill_outlines[layer_id], infill_wall_offset == 0 ? surface.expolygon : offset(surface.expolygon, infill_wall_offset));
+ append(infill_outlines[layer_id], to_polygons(surface.expolygon));
+ }
// For various operations its beneficial to quickly locate nearby features on the polygon:
const size_t top_layer_id = print_object.layers().size() - 1;
@@ -102,6 +100,7 @@ void Generator::generateTrees(const PrintObject &print_object)
// For-each layer from top to bottom:
for (int layer_id = int(top_layer_id); layer_id >= 0; layer_id--) {
+ throw_on_cancel_callback();
Layer ¤t_lightning_layer = m_lightning_layers[layer_id];
const Polygons ¤t_outlines = infill_outlines[layer_id];
const BoundingBox ¤t_outlines_bbox = get_extents(current_outlines);
@@ -109,7 +108,7 @@ void Generator::generateTrees(const PrintObject &print_object)
// register all trees propagated from the previous layer as to-be-reconnected
std::vector to_be_reconnected_tree_roots = current_lightning_layer.tree_roots;
- current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius);
+ current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius, throw_on_cancel_callback);
current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius);
// Initialize trees for next lower layer from the current one.
@@ -121,6 +120,9 @@ void Generator::generateTrees(const PrintObject &print_object)
if (const BoundingBox &outlines_locator_bbox = outlines_locator.bbox(); outlines_locator_bbox.defined)
below_outlines_bbox.merge(outlines_locator_bbox);
+ if (!current_lightning_layer.tree_roots.empty())
+ below_outlines_bbox.merge(get_extents(current_lightning_layer.tree_roots).inflated(SCALED_EPSILON));
+
outlines_locator.set_bbox(below_outlines_bbox);
outlines_locator.create(below_outlines, locator_cell_size);
diff --git a/src/libslic3r/Fill/Lightning/Generator.hpp b/src/libslic3r/Fill/Lightning/Generator.hpp
index b538c4dbe..d7e5f63f9 100644
--- a/src/libslic3r/Fill/Lightning/Generator.hpp
+++ b/src/libslic3r/Fill/Lightning/Generator.hpp
@@ -43,9 +43,8 @@ public:
* This generator will pre-compute things in preparation of generating
* Lightning Infill for the infill areas in that mesh. The infill areas must
* already be calculated at this point.
- * \param mesh The mesh to generate infill for.
*/
- explicit Generator(const PrintObject &print_object);
+ explicit Generator(const PrintObject &print_object, const std::function &throw_on_cancel_callback);
/*!
* Get a tree of paths generated for a certain layer of the mesh.
@@ -69,12 +68,12 @@ protected:
* only when support is generated. For this pattern, we also need to
* generate overhang areas for the inside of the model.
*/
- void generateInitialInternalOverhangs(const PrintObject &print_object);
+ void generateInitialInternalOverhangs(const PrintObject &print_object, const std::function &throw_on_cancel_callback);
/*!
* Calculate the tree structure of all layers.
*/
- void generateTrees(const PrintObject &print_object);
+ void generateTrees(const PrintObject &print_object, const std::function &throw_on_cancel_callback);
float m_infill_extrusion_width;
diff --git a/src/libslic3r/Fill/Lightning/Layer.cpp b/src/libslic3r/Fill/Lightning/Layer.cpp
index c996b9b7b..354623e51 100644
--- a/src/libslic3r/Fill/Lightning/Layer.cpp
+++ b/src/libslic3r/Fill/Lightning/Layer.cpp
@@ -10,6 +10,10 @@
#include "../../Geometry.hpp"
#include "Utils.hpp"
+#include
+#include
+#include
+
namespace Slic3r::FillLightning {
coord_t Layer::getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location)
@@ -44,18 +48,22 @@ void Layer::generateNewTrees
const BoundingBox& current_outlines_bbox,
const EdgeGrid::Grid& outlines_locator,
const coord_t supporting_radius,
- const coord_t wall_supporting_radius
+ const coord_t wall_supporting_radius,
+ const std::function &throw_on_cancel_callback
)
{
DistanceField distance_field(supporting_radius, current_outlines, current_outlines_bbox, current_overhang);
+ throw_on_cancel_callback();
SparseNodeGrid tree_node_locator;
fillLocator(tree_node_locator, current_outlines_bbox);
// Until no more points need to be added to support all:
// Determine next point from tree/outline areas via distance-field
- Point unsupported_location;
- while (distance_field.tryGetNextPoint(&unsupported_location)) {
+ size_t unsupported_cell_idx = 0;
+ Point unsupported_location;
+ while (distance_field.tryGetNextPoint(&unsupported_location, &unsupported_cell_idx, unsupported_cell_idx)) {
+ throw_on_cancel_callback();
GroundingLocation grounding_loc = getBestGroundingLocation(
unsupported_location, current_outlines, current_outlines_bbox, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator);
@@ -126,9 +134,10 @@ GroundingLocation Layer::getBestGroundingLocation
if (contour.size() > 2) {
Point prev = contour.points.back();
for (const Point &p2 : contour.points) {
- if (double d = Line::distance_to_squared(unsupported_location, prev, p2); d < d2) {
+ Point closest_point;
+ if (double d = line_alg::distance_to_squared(Line{prev, p2}, unsupported_location, &closest_point); d < d2) {
d2 = d;
- node_location = Geometry::foot_pt({ prev, p2 }, unsupported_location).cast();
+ node_location = closest_point;
}
prev = p2;
}
@@ -137,30 +146,52 @@ GroundingLocation Layer::getBestGroundingLocation
const auto within_dist = coord_t((node_location - unsupported_location).cast().norm());
- NodeSPtr sub_tree{ nullptr };
- coord_t current_dist = getWeightedDistance(node_location, unsupported_location);
+ NodeSPtr sub_tree{nullptr};
+ coord_t current_dist = getWeightedDistance(node_location, unsupported_location);
if (current_dist >= wall_supporting_radius) { // Only reconnect tree roots to other trees if they are not already close to the outlines.
const coord_t search_radius = std::min(current_dist, within_dist);
BoundingBox region(unsupported_location - Point(search_radius, search_radius), unsupported_location + Point(search_radius + locator_cell_size, search_radius + locator_cell_size));
region.min = to_grid_point(region.min, current_outlines_bbox);
region.max = to_grid_point(region.max, current_outlines_bbox);
- Point grid_addr;
- for (grid_addr.y() = region.min.y(); grid_addr.y() < region.max.y(); ++ grid_addr.y())
- for (grid_addr.x() = region.min.x(); grid_addr.x() < region.max.x(); ++ grid_addr.x()) {
- auto it_range = tree_node_locator.equal_range(grid_addr);
- for (auto it = it_range.first; it != it_range.second; ++ it) {
- auto candidate_sub_tree = it->second.lock();
- if ((candidate_sub_tree && candidate_sub_tree != exclude_tree) &&
- !(exclude_tree && exclude_tree->hasOffspring(candidate_sub_tree)) &&
- !polygonCollidesWithLineSegment(unsupported_location, candidate_sub_tree->getLocation(), outline_locator)) {
- const coord_t candidate_dist = candidate_sub_tree->getWeightedDistance(unsupported_location, supporting_radius);
- if (candidate_dist < current_dist) {
- current_dist = candidate_dist;
- sub_tree = candidate_sub_tree;
+
+ Point current_dist_grid_addr{std::numeric_limits::lowest(), std::numeric_limits::lowest()};
+ std::mutex current_dist_mutex;
+ tbb::parallel_for(tbb::blocked_range2d(region.min.y(), region.max.y(), region.min.x(), region.max.x()), [¤t_dist, current_dist_copy = current_dist, ¤t_dist_mutex, &sub_tree, ¤t_dist_grid_addr, &exclude_tree = std::as_const(exclude_tree), &outline_locator = std::as_const(outline_locator), &supporting_radius = std::as_const(supporting_radius), &tree_node_locator = std::as_const(tree_node_locator), &unsupported_location = std::as_const(unsupported_location)](const tbb::blocked_range2d &range) -> void {
+ for (coord_t grid_addr_y = range.rows().begin(); grid_addr_y < range.rows().end(); ++grid_addr_y)
+ for (coord_t grid_addr_x = range.cols().begin(); grid_addr_x < range.cols().end(); ++grid_addr_x) {
+ const Point local_grid_addr{grid_addr_x, grid_addr_y};
+ NodeSPtr local_sub_tree{nullptr};
+ coord_t local_current_dist = current_dist_copy;
+ const auto it_range = tree_node_locator.equal_range(local_grid_addr);
+ for (auto it = it_range.first; it != it_range.second; ++it) {
+ const NodeSPtr candidate_sub_tree = it->second.lock();
+ if ((candidate_sub_tree && candidate_sub_tree != exclude_tree) &&
+ !(exclude_tree && exclude_tree->hasOffspring(candidate_sub_tree)) &&
+ !polygonCollidesWithLineSegment(unsupported_location, candidate_sub_tree->getLocation(), outline_locator)) {
+ if (const coord_t candidate_dist = candidate_sub_tree->getWeightedDistance(unsupported_location, supporting_radius); candidate_dist < local_current_dist) {
+ local_current_dist = candidate_dist;
+ local_sub_tree = candidate_sub_tree;
+ }
+ }
+ }
+ // To always get the same result in a parallel version as in a non-parallel version,
+ // we need to preserve that for the same current_dist, we select the same sub_tree
+ // as in the non-parallel version. For this purpose, inside the variable
+ // current_dist_grid_addr is stored from with 2D grid position assigned sub_tree comes.
+ // And when there are two sub_tree with the same current_dist, one which will be found
+ // the first in the non-parallel version is selected.
+ {
+ std::lock_guard lock(current_dist_mutex);
+ if (local_current_dist < current_dist ||
+ (local_current_dist == current_dist && (grid_addr_y < current_dist_grid_addr.y() ||
+ (grid_addr_y == current_dist_grid_addr.y() && grid_addr_x < current_dist_grid_addr.x())))) {
+ current_dist = local_current_dist;
+ sub_tree = local_sub_tree;
+ current_dist_grid_addr = local_grid_addr;
}
}
}
- }
+ }); // end of parallel_for
}
return ! sub_tree ?
@@ -402,15 +433,14 @@ static unsigned int moveInside(const Polygons& polygons, Point& from, int distan
}
#endif
-// Returns 'added someting'.
-Polylines Layer::convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const
+Polylines Layer::convertToLines(const Polygons& limit_to_outline, const coord_t line_overlap) const
{
if (tree_roots.empty())
return {};
Polylines result_lines;
for (const auto &tree : tree_roots)
- tree->convertToPolylines(result_lines, line_width);
+ tree->convertToPolylines(result_lines, line_overlap);
return intersection_pl(result_lines, limit_to_outline);
}
diff --git a/src/libslic3r/Fill/Lightning/Layer.hpp b/src/libslic3r/Fill/Lightning/Layer.hpp
index cc79c15b5..e8c0a38b4 100644
--- a/src/libslic3r/Fill/Lightning/Layer.hpp
+++ b/src/libslic3r/Fill/Lightning/Layer.hpp
@@ -44,7 +44,8 @@ public:
const BoundingBox& current_outlines_bbox,
const EdgeGrid::Grid& outline_locator,
coord_t supporting_radius,
- coord_t wall_supporting_radius
+ coord_t wall_supporting_radius,
+ const std::function &throw_on_cancel_callback
);
/*! Determine & connect to connection point in tree/outline.
@@ -79,7 +80,7 @@ public:
coord_t wall_supporting_radius
);
- Polylines convertToLines(const Polygons& limit_to_outline, coord_t line_width) const;
+ Polylines convertToLines(const Polygons& limit_to_outline, coord_t line_overlap) const;
coord_t getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location);
diff --git a/src/libslic3r/Fill/Lightning/TreeNode.cpp b/src/libslic3r/Fill/Lightning/TreeNode.cpp
index 822550fc4..982d47b10 100644
--- a/src/libslic3r/Fill/Lightning/TreeNode.cpp
+++ b/src/libslic3r/Fill/Lightning/TreeNode.cpp
@@ -180,7 +180,11 @@ bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeG
} visitor { outline_locator, a.cast(), b.cast() };
outline_locator.visit_cells_intersecting_line(a, b, visitor);
- return visitor.d2min < double(within_max_dist) * double(within_max_dist);
+ if (visitor.d2min < double(within_max_dist) * double(within_max_dist)) {
+ result = Point(visitor.intersection_pt);
+ return true;
+ }
+ return false;
}
bool Node::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector& rerooted_parts)
@@ -343,12 +347,12 @@ coord_t Node::prune(const coord_t& pruning_distance)
return max_distance_pruned;
}
-void Node::convertToPolylines(Polylines &output, const coord_t line_width) const
+void Node::convertToPolylines(Polylines &output, const coord_t line_overlap) const
{
Polylines result;
result.emplace_back();
convertToPolylines(0, result);
- removeJunctionOverlap(result, line_width);
+ removeJunctionOverlap(result, line_overlap);
append(output, std::move(result));
}
@@ -372,10 +376,10 @@ void Node::convertToPolylines(size_t long_line_idx, Polylines &output) const
}
}
-void Node::removeJunctionOverlap(Polylines &result_lines, const coord_t line_width) const
+void Node::removeJunctionOverlap(Polylines &result_lines, const coord_t line_overlap) const
{
- const coord_t reduction = line_width / 2; // TODO make configurable?
- size_t res_line_idx = 0;
+ const coord_t reduction = line_overlap;
+ size_t res_line_idx = 0;
while (res_line_idx < result_lines.size()) {
Polyline &polyline = result_lines[res_line_idx];
if (polyline.size() <= 1) {
diff --git a/src/libslic3r/Fill/Lightning/TreeNode.hpp b/src/libslic3r/Fill/Lightning/TreeNode.hpp
index fdb80d2e6..8791b4331 100644
--- a/src/libslic3r/Fill/Lightning/TreeNode.hpp
+++ b/src/libslic3r/Fill/Lightning/TreeNode.hpp
@@ -46,7 +46,7 @@ public:
{
struct EnableMakeShared : public Node
{
- EnableMakeShared(Arg&&...arg) : Node(std::forward(arg)...) {}
+ explicit EnableMakeShared(Arg&&...arg) : Node(std::forward(arg)...) {}
};
return std::make_shared(std::forward(arg)...);
}
@@ -179,16 +179,16 @@ public:
*/
bool hasOffspring(const NodeSPtr& to_be_checked) const;
-protected:
Node() = delete; // Don't allow empty contruction
+protected:
/*!
* Construct a new node, either for insertion in a tree or as root.
* \param p The physical location in the 2D layer that this node represents.
* Connecting other nodes to this node indicates that a line segment should
* be drawn between those two physical positions.
*/
- Node(const Point& p, const std::optional& last_grounding_location = std::nullopt);
+ explicit Node(const Point& p, const std::optional& last_grounding_location = std::nullopt);
/*!
* Copy this node and its entire sub-tree.
@@ -239,7 +239,7 @@ public:
*
* \param output all branches in this tree connected into polylines
*/
- void convertToPolylines(Polylines &output, coord_t line_width) const;
+ void convertToPolylines(Polylines &output, coord_t line_overlap) const;
/*! If this was ever a direct child of the root, it'll have a previous grounding location.
*
@@ -260,7 +260,7 @@ protected:
*/
void convertToPolylines(size_t long_line_idx, Polylines &output) const;
- void removeJunctionOverlap(Polylines &polylines, coord_t line_width) const;
+ void removeJunctionOverlap(Polylines &polylines, coord_t line_overlap) const;
bool m_is_root;
Point m_p;
@@ -269,6 +269,9 @@ protected:
std::optional m_last_grounding_location; // &tree_roots);
+
#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT
friend void export_to_svg(const NodeSPtr &root_node, Slic3r::SVG &svg);
friend void export_to_svg(const std::string &path, const Polygons &contour, const std::vector &root_nodes);
@@ -278,6 +281,23 @@ protected:
bool inside(const Polygons &polygons, const Point &p);
bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeGrid::Grid& outline_locator, Point& result, coord_t within_max_dist);
+inline BoundingBox get_extents(const NodeSPtr &root_node)
+{
+ BoundingBox bbox;
+ for (const NodeSPtr &children : root_node->m_children)
+ bbox.merge(get_extents(children));
+ bbox.merge(root_node->getLocation());
+ return bbox;
+}
+
+inline BoundingBox get_extents(const std::vector &tree_roots)
+{
+ BoundingBox bbox;
+ for (const NodeSPtr &root_node : tree_roots)
+ bbox.merge(get_extents(root_node));
+ return bbox;
+}
+
#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT
void export_to_svg(const NodeSPtr &root_node, SVG &svg);
void export_to_svg(const std::string &path, const Polygons &contour, const std::vector &root_nodes);
diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index 4259562aa..9a71c8978 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -1,3198 +1,3222 @@
-#include "../libslic3r.h"
-#include "../Exception.hpp"
-#include "../Model.hpp"
-#include "../Utils.hpp"
-#include "../LocalesUtils.hpp"
-#include "../GCode.hpp"
-#include "../Geometry.hpp"
-#include "../GCode/ThumbnailData.hpp"
-#include "../Semver.hpp"
-#include "../Time.hpp"
-
-#include "../I18N.hpp"
-
-#include "3mf.hpp"
-
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-namespace pt = boost::property_tree;
-
-#include
-#include
-#include "miniz_extension.hpp"
-
-#include
-
-// Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
-// https://github.com/boostorg/spirit/pull/586
-// where the exported string is one digit shorter than it should be to guarantee lossless round trip.
-// The code is left here for the ocasion boost guys improve.
-#define EXPORT_3MF_USE_SPIRIT_KARMA_FP 0
-
-// VERSION NUMBERS
-// 0 : .3mf, files saved by older slic3r or other applications. No version definition in them.
-// 1 : Introduction of 3mf versioning. No other change in data saved into 3mf files.
-// 2 : Volumes' matrices and source data added to Metadata/Slic3r_PE_model.config file, meshes transformed back to their coordinate system on loading.
-// WARNING !! -> the version number has been rolled back to 1
-// the next change should use 3
-const unsigned int VERSION_3MF = 1;
-// Allow loading version 2 file as well.
-const unsigned int VERSION_3MF_COMPATIBLE = 2;
-const char* SLIC3RPE_3MF_VERSION = "slic3rpe:Version3mf"; // definition of the metadata name saved into .model file
-
-// Painting gizmos data version numbers
-// 0 : 3MF files saved by older PrusaSlicer or the painting gizmo wasn't used. No version definition in them.
-// 1 : Introduction of painting gizmos data versioning. No other changes in painting gizmos data.
-const unsigned int FDM_SUPPORTS_PAINTING_VERSION = 1;
-const unsigned int SEAM_PAINTING_VERSION = 1;
-const unsigned int MM_PAINTING_VERSION = 1;
-
-const std::string SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION = "slic3rpe:FdmSupportsPaintingVersion";
-const std::string SLIC3RPE_SEAM_PAINTING_VERSION = "slic3rpe:SeamPaintingVersion";
-const std::string SLIC3RPE_MM_PAINTING_VERSION = "slic3rpe:MmPaintingVersion";
-
-const std::string MODEL_FOLDER = "3D/";
-const std::string MODEL_EXTENSION = ".model";
-const std::string MODEL_FILE = "3D/3dmodel.model"; // << this is the only format of the string which works with CURA
-const std::string CONTENT_TYPES_FILE = "[Content_Types].xml";
-const std::string RELATIONSHIPS_FILE = "_rels/.rels";
-const std::string THUMBNAIL_FILE = "Metadata/thumbnail.png";
-const std::string PRINT_CONFIG_FILE = "Metadata/Slic3r_PE.config";
-const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config";
-const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt";
-const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config_ranges.xml";
-const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
-const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt";
-const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml";
-
-static constexpr const char* MODEL_TAG = "model";
-static constexpr const char* RESOURCES_TAG = "resources";
-static constexpr const char* OBJECT_TAG = "object";
-static constexpr const char* MESH_TAG = "mesh";
-static constexpr const char* VERTICES_TAG = "vertices";
-static constexpr const char* VERTEX_TAG = "vertex";
-static constexpr const char* TRIANGLES_TAG = "triangles";
-static constexpr const char* TRIANGLE_TAG = "triangle";
-static constexpr const char* COMPONENTS_TAG = "components";
-static constexpr const char* COMPONENT_TAG = "component";
-static constexpr const char* BUILD_TAG = "build";
-static constexpr const char* ITEM_TAG = "item";
-static constexpr const char* METADATA_TAG = "metadata";
-
-static constexpr const char* CONFIG_TAG = "config";
-static constexpr const char* VOLUME_TAG = "volume";
-
-static constexpr const char* UNIT_ATTR = "unit";
-static constexpr const char* NAME_ATTR = "name";
-static constexpr const char* TYPE_ATTR = "type";
-static constexpr const char* ID_ATTR = "id";
-static constexpr const char* X_ATTR = "x";
-static constexpr const char* Y_ATTR = "y";
-static constexpr const char* Z_ATTR = "z";
-static constexpr const char* V1_ATTR = "v1";
-static constexpr const char* V2_ATTR = "v2";
-static constexpr const char* V3_ATTR = "v3";
-static constexpr const char* OBJECTID_ATTR = "objectid";
-static constexpr const char* TRANSFORM_ATTR = "transform";
-static constexpr const char* PRINTABLE_ATTR = "printable";
-static constexpr const char* INSTANCESCOUNT_ATTR = "instances_count";
-static constexpr const char* CUSTOM_SUPPORTS_ATTR = "slic3rpe:custom_supports";
-static constexpr const char* CUSTOM_SEAM_ATTR = "slic3rpe:custom_seam";
-static constexpr const char* MMU_SEGMENTATION_ATTR = "slic3rpe:mmu_segmentation";
-
-static constexpr const char* KEY_ATTR = "key";
-static constexpr const char* VALUE_ATTR = "value";
-static constexpr const char* FIRST_TRIANGLE_ID_ATTR = "firstid";
-static constexpr const char* LAST_TRIANGLE_ID_ATTR = "lastid";
-
-static constexpr const char* OBJECT_TYPE = "object";
-static constexpr const char* VOLUME_TYPE = "volume";
-
-static constexpr const char* NAME_KEY = "name";
-static constexpr const char* MODIFIER_KEY = "modifier";
-static constexpr const char* VOLUME_TYPE_KEY = "volume_type";
-static constexpr const char* MATRIX_KEY = "matrix";
-static constexpr const char* SOURCE_FILE_KEY = "source_file";
-static constexpr const char* SOURCE_OBJECT_ID_KEY = "source_object_id";
-static constexpr const char* SOURCE_VOLUME_ID_KEY = "source_volume_id";
-static constexpr const char* SOURCE_OFFSET_X_KEY = "source_offset_x";
-static constexpr const char* SOURCE_OFFSET_Y_KEY = "source_offset_y";
-static constexpr const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
-static constexpr const char* SOURCE_IN_INCHES_KEY = "source_in_inches";
-static constexpr const char* SOURCE_IN_METERS_KEY = "source_in_meters";
-#if ENABLE_RELOAD_FROM_DISK_REWORK
-static constexpr const char* SOURCE_IS_BUILTIN_VOLUME_KEY = "source_is_builtin_volume";
-#endif // ENABLE_RELOAD_FROM_DISK_REWORK
-
-static constexpr const char* MESH_STAT_EDGES_FIXED = "edges_fixed";
-static constexpr const char* MESH_STAT_DEGENERATED_FACETS = "degenerate_facets";
-static constexpr const char* MESH_STAT_FACETS_REMOVED = "facets_removed";
-static constexpr const char* MESH_STAT_FACETS_RESERVED = "facets_reversed";
-static constexpr const char* MESH_STAT_BACKWARDS_EDGES = "backwards_edges";
-
-
-const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
-const char* VALID_OBJECT_TYPES[] =
-{
- "model"
-};
-
-const char* INVALID_OBJECT_TYPES[] =
-{
- "solidsupport",
- "support",
- "surface",
- "other"
-};
-
-class version_error : public Slic3r::FileIOError
-{
-public:
- version_error(const std::string& what_arg) : Slic3r::FileIOError(what_arg) {}
- version_error(const char* what_arg) : Slic3r::FileIOError(what_arg) {}
-};
-
-const char* get_attribute_value_charptr(const char** attributes, unsigned int attributes_size, const char* attribute_key)
-{
- if ((attributes == nullptr) || (attributes_size == 0) || (attributes_size % 2 != 0) || (attribute_key == nullptr))
- return nullptr;
-
- for (unsigned int a = 0; a < attributes_size; a += 2) {
- if (::strcmp(attributes[a], attribute_key) == 0)
- return attributes[a + 1];
- }
-
- return nullptr;
-}
-
-std::string get_attribute_value_string(const char** attributes, unsigned int attributes_size, const char* attribute_key)
-{
- const char* text = get_attribute_value_charptr(attributes, attributes_size, attribute_key);
- return (text != nullptr) ? text : "";
-}
-
-float get_attribute_value_float(const char** attributes, unsigned int attributes_size, const char* attribute_key)
-{
- float value = 0.0f;
- if (const char *text = get_attribute_value_charptr(attributes, attributes_size, attribute_key); text != nullptr)
- fast_float::from_chars(text, text + strlen(text), value);
- return value;
-}
-
-int get_attribute_value_int(const char** attributes, unsigned int attributes_size, const char* attribute_key)
-{
- int value = 0;
- if (const char *text = get_attribute_value_charptr(attributes, attributes_size, attribute_key); text != nullptr)
- boost::spirit::qi::parse(text, text + strlen(text), boost::spirit::qi::int_, value);
- return value;
-}
-
-bool get_attribute_value_bool(const char** attributes, unsigned int attributes_size, const char* attribute_key)
-{
- const char* text = get_attribute_value_charptr(attributes, attributes_size, attribute_key);
- return (text != nullptr) ? (bool)::atoi(text) : true;
-}
-
-Slic3r::Transform3d get_transform_from_3mf_specs_string(const std::string& mat_str)
-{
- // check: https://3mf.io/3d-manufacturing-format/ or https://github.com/3MFConsortium/spec_core/blob/master/3MF%20Core%20Specification.md
- // to see how matrices are stored inside 3mf according to specifications
- Slic3r::Transform3d ret = Slic3r::Transform3d::Identity();
-
- if (mat_str.empty())
- // empty string means default identity matrix
- return ret;
-
- std::vector mat_elements_str;
- boost::split(mat_elements_str, mat_str, boost::is_any_of(" "), boost::token_compress_on);
-
- unsigned int size = (unsigned int)mat_elements_str.size();
- if (size != 12)
- // invalid data, return identity matrix
- return ret;
-
- unsigned int i = 0;
- // matrices are stored into 3mf files as 4x3
- // we need to transpose them
- for (unsigned int c = 0; c < 4; ++c) {
- for (unsigned int r = 0; r < 3; ++r) {
- ret(r, c) = ::atof(mat_elements_str[i++].c_str());
- }
- }
- return ret;
-}
-
-float get_unit_factor(const std::string& unit)
-{
- const char* text = unit.c_str();
-
- if (::strcmp(text, "micron") == 0)
- return 0.001f;
- else if (::strcmp(text, "centimeter") == 0)
- return 10.0f;
- else if (::strcmp(text, "inch") == 0)
- return 25.4f;
- else if (::strcmp(text, "foot") == 0)
- return 304.8f;
- else if (::strcmp(text, "meter") == 0)
- return 1000.0f;
- else
- // default "millimeters" (see specification)
- return 1.0f;
-}
-
-bool is_valid_object_type(const std::string& type)
-{
- // if the type is empty defaults to "model" (see specification)
- if (type.empty())
- return true;
-
- for (unsigned int i = 0; i < VALID_OBJECT_TYPES_COUNT; ++i) {
- if (::strcmp(type.c_str(), VALID_OBJECT_TYPES[i]) == 0)
- return true;
- }
-
- return false;
-}
-
-namespace Slic3r {
-
-//! macro used to mark string used at localization,
-//! return same string
-#define L(s) (s)
-#define _(s) Slic3r::I18N::translate(s)
-
- // Base class with error messages management
- class _3MF_Base
- {
- std::vector m_errors;
-
- protected:
- void add_error(const std::string& error) { m_errors.push_back(error); }
- void clear_errors() { m_errors.clear(); }
-
- public:
- void log_errors()
- {
- for (const std::string& error : m_errors)
- BOOST_LOG_TRIVIAL(error) << error;
- }
- };
-
- class _3MF_Importer : public _3MF_Base
- {
- struct Component
- {
- int object_id;
- Transform3d transform;
-
- explicit Component(int object_id)
- : object_id(object_id)
- , transform(Transform3d::Identity())
- {
- }
-
- Component(int object_id, const Transform3d& transform)
- : object_id(object_id)
- , transform(transform)
- {
- }
- };
-
- typedef std::vector ComponentsList;
-
- struct Geometry
- {
- std::vector vertices;
- std::vector triangles;
- std::vector custom_supports;
- std::vector custom_seam;
- std::vector mmu_segmentation;
-
- bool empty() { return vertices.empty() || triangles.empty(); }
-
- void reset() {
- vertices.clear();
- triangles.clear();
- custom_supports.clear();
- custom_seam.clear();
- mmu_segmentation.clear();
- }
- };
-
- struct CurrentObject
- {
- // ID of the object inside the 3MF file, 1 based.
- int id;
- // Index of the ModelObject in its respective Model, zero based.
- int model_object_idx;
- Geometry geometry;
- ModelObject* object;
- ComponentsList components;
-
- CurrentObject() { reset(); }
-
- void reset() {
- id = -1;
- model_object_idx = -1;
- geometry.reset();
- object = nullptr;
- components.clear();
- }
- };
-
- struct CurrentConfig
- {
- int object_id;
- int volume_id;
- };
-
- struct Instance
- {
- ModelInstance* instance;
- Transform3d transform;
-
- Instance(ModelInstance* instance, const Transform3d& transform)
- : instance(instance)
- , transform(transform)
- {
- }
- };
-
- struct Metadata
- {
- std::string key;
- std::string value;
-
- Metadata(const std::string& key, const std::string& value)
- : key(key)
- , value(value)
- {
- }
- };
-
- typedef std::vector MetadataList;
-
- struct ObjectMetadata
- {
- struct VolumeMetadata
- {
- unsigned int first_triangle_id;
- unsigned int last_triangle_id;
- MetadataList metadata;
- RepairedMeshErrors mesh_stats;
-
- VolumeMetadata(unsigned int first_triangle_id, unsigned int last_triangle_id)
- : first_triangle_id(first_triangle_id)
- , last_triangle_id(last_triangle_id)
- {
- }
- };
-
- typedef std::vector VolumeMetadataList;
-
- MetadataList metadata;
- VolumeMetadataList volumes;
- };
-
- // Map from a 1 based 3MF object ID to a 0 based ModelObject index inside m_model->objects.
- typedef std::map IdToModelObjectMap;
- typedef std::map IdToAliasesMap;
- typedef std::vector InstancesList;
- typedef std::map IdToMetadataMap;
- typedef std::map IdToGeometryMap;
- typedef std::map> IdToLayerHeightsProfileMap;
- typedef std::map IdToLayerConfigRangesMap;
- typedef std::map> IdToSlaSupportPointsMap;
- typedef std::map> IdToSlaDrainHolesMap;
-
- // Version of the 3mf file
- unsigned int m_version;
- bool m_check_version;
-
- // Semantic version of PrusaSlicer, that generated this 3MF.
- boost::optional m_prusaslicer_generator_version;
- unsigned int m_fdm_supports_painting_version = 0;
- unsigned int m_seam_painting_version = 0;
- unsigned int m_mm_painting_version = 0;
-
- XML_Parser m_xml_parser;
- // Error code returned by the application side of the parser. In that case the expat may not reliably deliver the error state
- // after returning from XML_Parse() function, thus we keep the error state here.
- bool m_parse_error { false };
- std::string m_parse_error_message;
- Model* m_model;
- float m_unit_factor;
- CurrentObject m_curr_object;
- IdToModelObjectMap m_objects;
- IdToAliasesMap m_objects_aliases;
- InstancesList m_instances;
- IdToGeometryMap m_geometries;
- CurrentConfig m_curr_config;
- IdToMetadataMap m_objects_metadata;
- IdToLayerHeightsProfileMap m_layer_heights_profiles;
- IdToLayerConfigRangesMap m_layer_config_ranges;
- IdToSlaSupportPointsMap m_sla_support_points;
- IdToSlaDrainHolesMap m_sla_drain_holes;
- std::string m_curr_metadata_name;
- std::string m_curr_characters;
- std::string m_name;
-
- public:
- _3MF_Importer();
- ~_3MF_Importer();
-
- bool load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version);
- unsigned int version() const { return m_version; }
-
- private:
- void _destroy_xml_parser();
- void _stop_xml_parser(const std::string& msg = std::string());
-
- bool parse_error() const { return m_parse_error; }
- const char* parse_error_message() const {
- return m_parse_error ?
- // The error was signalled by the user code, not the expat parser.
- (m_parse_error_message.empty() ? "Invalid 3MF format" : m_parse_error_message.c_str()) :
- // The error was signalled by the expat parser.
- XML_ErrorString(XML_GetErrorCode(m_xml_parser));
- }
-
- bool _load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions);
- bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
- void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
- void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
- void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
- void _extract_sla_drain_holes_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
-
- void _extract_custom_gcode_per_print_z_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
-
- void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& subs_context, const std::string& archive_filename);
- bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
-
- // handlers to parse the .model file
- void _handle_start_model_xml_element(const char* name, const char** attributes);
- void _handle_end_model_xml_element(const char* name);
- void _handle_model_xml_characters(const XML_Char* s, int len);
-
- // handlers to parse the MODEL_CONFIG_FILE file
- void _handle_start_config_xml_element(const char* name, const char** attributes);
- void _handle_end_config_xml_element(const char* name);
-
- bool _handle_start_model(const char** attributes, unsigned int num_attributes);
- bool _handle_end_model();
-
- bool _handle_start_resources(const char** attributes, unsigned int num_attributes);
- bool _handle_end_resources();
-
- bool _handle_start_object(const char** attributes, unsigned int num_attributes);
- bool _handle_end_object();
-
- bool _handle_start_mesh(const char** attributes, unsigned int num_attributes);
- bool _handle_end_mesh();
-
- bool _handle_start_vertices(const char** attributes, unsigned int num_attributes);
- bool _handle_end_vertices();
-
- bool _handle_start_vertex(const char** attributes, unsigned int num_attributes);
- bool _handle_end_vertex();
-
- bool _handle_start_triangles(const char** attributes, unsigned int num_attributes);
- bool _handle_end_triangles();
-
- bool _handle_start_triangle(const char** attributes, unsigned int num_attributes);
- bool _handle_end_triangle();
-
- bool _handle_start_components(const char** attributes, unsigned int num_attributes);
- bool _handle_end_components();
-
- bool _handle_start_component(const char** attributes, unsigned int num_attributes);
- bool _handle_end_component();
-
- bool _handle_start_build(const char** attributes, unsigned int num_attributes);
- bool _handle_end_build();
-
- bool _handle_start_item(const char** attributes, unsigned int num_attributes);
- bool _handle_end_item();
-
- bool _handle_start_metadata(const char** attributes, unsigned int num_attributes);
- bool _handle_end_metadata();
-
- bool _create_object_instance(int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter);
-
- void _apply_transform(ModelInstance& instance, const Transform3d& transform);
-
- bool _handle_start_config(const char** attributes, unsigned int num_attributes);
- bool _handle_end_config();
-
- bool _handle_start_config_object(const char** attributes, unsigned int num_attributes);
- bool _handle_end_config_object();
-
- bool _handle_start_config_volume(const char** attributes, unsigned int num_attributes);
- bool _handle_start_config_volume_mesh(const char** attributes, unsigned int num_attributes);
- bool _handle_end_config_volume();
- bool _handle_end_config_volume_mesh();
-
- bool _handle_start_config_metadata(const char** attributes, unsigned int num_attributes);
- bool _handle_end_config_metadata();
-
- bool _generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions);
-
- // callbacks to parse the .model file
- static void XMLCALL _handle_start_model_xml_element(void* userData, const char* name, const char** attributes);
- static void XMLCALL _handle_end_model_xml_element(void* userData, const char* name);
- static void XMLCALL _handle_model_xml_characters(void* userData, const XML_Char* s, int len);
-
- // callbacks to parse the MODEL_CONFIG_FILE file
- static void XMLCALL _handle_start_config_xml_element(void* userData, const char* name, const char** attributes);
- static void XMLCALL _handle_end_config_xml_element(void* userData, const char* name);
- };
-
- _3MF_Importer::_3MF_Importer()
- : m_version(0)
- , m_check_version(false)
- , m_xml_parser(nullptr)
- , m_model(nullptr)
- , m_unit_factor(1.0f)
- , m_curr_metadata_name("")
- , m_curr_characters("")
- , m_name("")
- {
- }
-
- _3MF_Importer::~_3MF_Importer()
- {
- _destroy_xml_parser();
- }
-
- bool _3MF_Importer::load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version)
- {
- m_version = 0;
- m_fdm_supports_painting_version = 0;
- m_seam_painting_version = 0;
- m_mm_painting_version = 0;
- m_check_version = check_version;
- m_model = &model;
- m_unit_factor = 1.0f;
- m_curr_object.reset();
- m_objects.clear();
- m_objects_aliases.clear();
- m_instances.clear();
- m_geometries.clear();
- m_curr_config.object_id = -1;
- m_curr_config.volume_id = -1;
- m_objects_metadata.clear();
- m_layer_heights_profiles.clear();
- m_layer_config_ranges.clear();
- m_sla_support_points.clear();
- m_curr_metadata_name.clear();
- m_curr_characters.clear();
- clear_errors();
-
- return _load_model_from_file(filename, model, config, config_substitutions);
- }
-
- void _3MF_Importer::_destroy_xml_parser()
- {
- if (m_xml_parser != nullptr) {
- XML_ParserFree(m_xml_parser);
- m_xml_parser = nullptr;
- }
- }
-
- void _3MF_Importer::_stop_xml_parser(const std::string &msg)
- {
- assert(! m_parse_error);
- assert(m_parse_error_message.empty());
- assert(m_xml_parser != nullptr);
- m_parse_error = true;
- m_parse_error_message = msg;
- XML_StopParser(m_xml_parser, false);
- }
-
- bool _3MF_Importer::_load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions)
- {
- mz_zip_archive archive;
- mz_zip_zero_struct(&archive);
-
- if (!open_zip_reader(&archive, filename)) {
- add_error("Unable to open the file");
- return false;
- }
-
- mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
-
- mz_zip_archive_file_stat stat;
-
- m_name = boost::filesystem::path(filename).stem().string();
-
- // we first loop the entries to read from the archive the .model file only, in order to extract the version from it
- for (mz_uint i = 0; i < num_entries; ++i) {
- if (mz_zip_reader_file_stat(&archive, i, &stat)) {
- std::string name(stat.m_filename);
- std::replace(name.begin(), name.end(), '\\', '/');
-
- if (boost::algorithm::istarts_with(name, MODEL_FOLDER) && boost::algorithm::iends_with(name, MODEL_EXTENSION)) {
- try
- {
- // valid model name -> extract model
- if (!_extract_model_from_archive(archive, stat)) {
- close_zip_reader(&archive);
- add_error("Archive does not contain a valid model");
- return false;
- }
- }
- catch (const std::exception& e)
- {
- // ensure the zip archive is closed and rethrow the exception
- close_zip_reader(&archive);
- throw Slic3r::FileIOError(e.what());
- }
- }
- }
- }
-
- // we then loop again the entries to read other files stored in the archive
- for (mz_uint i = 0; i < num_entries; ++i) {
- if (mz_zip_reader_file_stat(&archive, i, &stat)) {
- std::string name(stat.m_filename);
- std::replace(name.begin(), name.end(), '\\', '/');
-
- if (boost::algorithm::iequals(name, LAYER_HEIGHTS_PROFILE_FILE)) {
- // extract slic3r layer heights profile file
- _extract_layer_heights_profile_config_from_archive(archive, stat);
- }
- else if (boost::algorithm::iequals(name, LAYER_CONFIG_RANGES_FILE)) {
- // extract slic3r layer config ranges file
- _extract_layer_config_ranges_from_archive(archive, stat, config_substitutions);
- }
- else if (boost::algorithm::iequals(name, SLA_SUPPORT_POINTS_FILE)) {
- // extract sla support points file
- _extract_sla_support_points_from_archive(archive, stat);
- }
- else if (boost::algorithm::iequals(name, SLA_DRAIN_HOLES_FILE)) {
- // extract sla support points file
- _extract_sla_drain_holes_from_archive(archive, stat);
- }
- else if (boost::algorithm::iequals(name, PRINT_CONFIG_FILE)) {
- // extract slic3r print config file
- _extract_print_config_from_archive(archive, stat, config, config_substitutions, filename);
- }
- else if (boost::algorithm::iequals(name, CUSTOM_GCODE_PER_PRINT_Z_FILE)) {
- // extract slic3r layer config ranges file
- _extract_custom_gcode_per_print_z_from_archive(archive, stat);
- }
- else if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE)) {
- // extract slic3r model config file
- if (!_extract_model_config_from_archive(archive, stat, model)) {
- close_zip_reader(&archive);
- add_error("Archive does not contain a valid model config");
- return false;
- }
- }
- }
- }
-
- close_zip_reader(&archive);
-
- if (m_version == 0) {
- // if the 3mf was not produced by PrusaSlicer and there is more than one instance,
- // split the object in as many objects as instances
- size_t curr_models_count = m_model->objects.size();
- size_t i = 0;
- while (i < curr_models_count) {
- ModelObject* model_object = m_model->objects[i];
- if (model_object->instances.size() > 1) {
- // select the geometry associated with the original model object
- const Geometry* geometry = nullptr;
- for (const IdToModelObjectMap::value_type& object : m_objects) {
- if (object.second == int(i)) {
- IdToGeometryMap::const_iterator obj_geometry = m_geometries.find(object.first);
- if (obj_geometry == m_geometries.end()) {
- add_error("Unable to find object geometry");
- return false;
- }
- geometry = &obj_geometry->second;
- break;
- }
- }
-
- if (geometry == nullptr) {
- add_error("Unable to find object geometry");
- return false;
- }
-
- // use the geometry to create the volumes in the new model objects
- ObjectMetadata::VolumeMetadataList volumes(1, { 0, (unsigned int)geometry->triangles.size() - 1 });
-
- // for each instance after the 1st, create a new model object containing only that instance
- // and copy into it the geometry
- while (model_object->instances.size() > 1) {
- ModelObject* new_model_object = m_model->add_object(*model_object);
- new_model_object->clear_instances();
- new_model_object->add_instance(*model_object->instances.back());
- model_object->delete_last_instance();
- if (!_generate_volumes(*new_model_object, *geometry, volumes, config_substitutions))
- return false;
- }
- }
- ++i;
- }
- }
-
- for (const IdToModelObjectMap::value_type& object : m_objects) {
- if (object.second >= int(m_model->objects.size())) {
- add_error("Unable to find object");
- return false;
- }
- ModelObject* model_object = m_model->objects[object.second];
- IdToGeometryMap::const_iterator obj_geometry = m_geometries.find(object.first);
- if (obj_geometry == m_geometries.end()) {
- add_error("Unable to find object geometry");
- return false;
- }
-
- // m_layer_heights_profiles are indexed by a 1 based model object index.
- IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.second + 1);
- if (obj_layer_heights_profile != m_layer_heights_profiles.end())
- model_object->layer_height_profile.set(std::move(obj_layer_heights_profile->second));
-
- // m_layer_config_ranges are indexed by a 1 based model object index.
- IdToLayerConfigRangesMap::iterator obj_layer_config_ranges = m_layer_config_ranges.find(object.second + 1);
- if (obj_layer_config_ranges != m_layer_config_ranges.end())
- model_object->layer_config_ranges = std::move(obj_layer_config_ranges->second);
-
- // m_sla_support_points are indexed by a 1 based model object index.
- IdToSlaSupportPointsMap::iterator obj_sla_support_points = m_sla_support_points.find(object.second + 1);
- if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty()) {
- model_object->sla_support_points = std::move(obj_sla_support_points->second);
- model_object->sla_points_status = sla::PointsStatus::UserModified;
- }
-
- IdToSlaDrainHolesMap::iterator obj_drain_holes = m_sla_drain_holes.find(object.second + 1);
- if (obj_drain_holes != m_sla_drain_holes.end() && !obj_drain_holes->second.empty()) {
- model_object->sla_drain_holes = std::move(obj_drain_holes->second);
- }
-
- ObjectMetadata::VolumeMetadataList volumes;
- ObjectMetadata::VolumeMetadataList* volumes_ptr = nullptr;
-
- IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first);
- if (obj_metadata != m_objects_metadata.end()) {
- // config data has been found, this model was saved using slic3r pe
-
- // apply object's name and config data
- for (const Metadata& metadata : obj_metadata->second.metadata) {
- if (metadata.key == "name")
- model_object->name = metadata.value;
- else
- model_object->config.set_deserialize(metadata.key, metadata.value, config_substitutions);
- }
-
- // select object's detected volumes
- volumes_ptr = &obj_metadata->second.volumes;
- }
- else {
- // config data not found, this model was not saved using slic3r pe
-
- // add the entire geometry as the single volume to generate
- volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1);
-
- // select as volumes
- volumes_ptr = &volumes;
- }
-
- if (!_generate_volumes(*model_object, obj_geometry->second, *volumes_ptr, config_substitutions))
- return false;
- }
-
-#if ENABLE_RELOAD_FROM_DISK_REWORK
- for (int obj_id = 0; obj_id < int(model.objects.size()); ++obj_id) {
- ModelObject* o = model.objects[obj_id];
- for (int vol_id = 0; vol_id < int(o->volumes.size()); ++vol_id) {
- ModelVolume* v = o->volumes[vol_id];
- if (v->source.input_file.empty())
- v->source.input_file = v->name.empty() ? filename : v->name;
- if (v->source.volume_idx == -1)
- v->source.volume_idx = vol_id;
- if (v->source.object_idx == -1)
- v->source.object_idx = obj_id;
- }
- }
-#else
- int object_idx = 0;
- for (ModelObject* o : model.objects) {
- int volume_idx = 0;
- for (ModelVolume* v : o->volumes) {
- if (v->source.input_file.empty() && v->type() == ModelVolumeType::MODEL_PART) {
- v->source.input_file = filename;
- if (v->source.volume_idx == -1)
- v->source.volume_idx = volume_idx;
- if (v->source.object_idx == -1)
- v->source.object_idx = object_idx;
- }
- ++volume_idx;
- }
- ++object_idx;
- }
-#endif // ENABLE_RELOAD_FROM_DISK_REWORK
-
-// // fixes the min z of the model if negative
-// model.adjust_min_z();
-
- return true;
- }
-
- bool _3MF_Importer::_extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
- {
- if (stat.m_uncomp_size == 0) {
- add_error("Found invalid size");
- return false;
- }
-
- _destroy_xml_parser();
-
- m_xml_parser = XML_ParserCreate(nullptr);
- if (m_xml_parser == nullptr) {
- add_error("Unable to create parser");
- return false;
- }
-
- XML_SetUserData(m_xml_parser, (void*)this);
- XML_SetElementHandler(m_xml_parser, _3MF_Importer::_handle_start_model_xml_element, _3MF_Importer::_handle_end_model_xml_element);
- XML_SetCharacterDataHandler(m_xml_parser, _3MF_Importer::_handle_model_xml_characters);
-
- struct CallbackData
- {
- XML_Parser& parser;
- _3MF_Importer& importer;
- const mz_zip_archive_file_stat& stat;
-
- CallbackData(XML_Parser& parser, _3MF_Importer& importer, const mz_zip_archive_file_stat& stat) : parser(parser), importer(importer), stat(stat) {}
- };
-
- CallbackData data(m_xml_parser, *this, stat);
-
- mz_bool res = 0;
-
- try
- {
- res = mz_zip_reader_extract_file_to_callback(&archive, stat.m_filename, [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t {
- CallbackData* data = (CallbackData*)pOpaque;
- if (!XML_Parse(data->parser, (const char*)pBuf, (int)n, (file_ofs + n == data->stat.m_uncomp_size) ? 1 : 0) || data->importer.parse_error()) {
- char error_buf[1024];
- ::sprintf(error_buf, "Error (%s) while parsing '%s' at line %d", data->importer.parse_error_message(), data->stat.m_filename, (int)XML_GetCurrentLineNumber(data->parser));
- throw Slic3r::FileIOError(error_buf);
- }
-
- return n;
- }, &data, 0);
- }
- catch (const version_error& e)
- {
- // rethrow the exception
- throw Slic3r::FileIOError(e.what());
- }
- catch (std::exception& e)
- {
- add_error(e.what());
- return false;
- }
-
- if (res == 0) {
- add_error("Error while extracting model data from zip archive");
- return false;
- }
-
- return true;
- }
-
- void _3MF_Importer::_extract_print_config_from_archive(
- mz_zip_archive& archive, const mz_zip_archive_file_stat& stat,
- DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions,
- const std::string& archive_filename)
- {
- if (stat.m_uncomp_size > 0) {
- std::string buffer((size_t)stat.m_uncomp_size, 0);
- mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
- if (res == 0) {
- add_error("Error while reading config data to buffer");
- return;
- }
- //FIXME Loading a "will be one day a legacy format" of configuration in a form of a G-code comment.
- // Each config line is prefixed with a semicolon (G-code comment), that is ugly.
-
- // Replacing the legacy function with load_from_ini_string_commented leads to issues when
- // parsing 3MFs from before PrusaSlicer 2.0.0 (which can have duplicated entries in the INI.
- // See https://github.com/prusa3d/PrusaSlicer/issues/7155. We'll revert it for now.
- //config_substitutions.substitutions = config.load_from_ini_string_commented(std::move(buffer), config_substitutions.rule);
- ConfigBase::load_from_gcode_string_legacy(config, buffer.data(), config_substitutions);
- }
- }
-
- void _3MF_Importer::_extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
- {
- if (stat.m_uncomp_size > 0) {
- std::string buffer((size_t)stat.m_uncomp_size, 0);
- mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
- if (res == 0) {
- add_error("Error while reading layer heights profile data to buffer");
- return;
- }
-
- if (buffer.back() == '\n')
- buffer.pop_back();
-
- std::vector objects;
- boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off);
-
- for (const std::string& object : objects) {
- std::vector object_data;
- boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off);
- if (object_data.size() != 2) {
- add_error("Error while reading object data");
- continue;
- }
-
- std::vector object_data_id;
- boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off);
- if (object_data_id.size() != 2) {
- add_error("Error while reading object id");
- continue;
- }
-
- int object_id = std::atoi(object_data_id[1].c_str());
- if (object_id == 0) {
- add_error("Found invalid object id");
- continue;
- }
-
- IdToLayerHeightsProfileMap::iterator object_item = m_layer_heights_profiles.find(object_id);
- if (object_item != m_layer_heights_profiles.end()) {
- add_error("Found duplicated layer heights profile");
- continue;
- }
-
- std::vector object_data_profile;
- boost::split(object_data_profile, object_data[1], boost::is_any_of(";"), boost::token_compress_off);
- if (object_data_profile.size() <= 4 || object_data_profile.size() % 2 != 0) {
- add_error("Found invalid layer heights profile");
- continue;
- }
-
- std::vector profile;
- profile.reserve(object_data_profile.size());
-
- for (const std::string& value : object_data_profile) {
- profile.push_back((coordf_t)std::atof(value.c_str()));
- }
-
- m_layer_heights_profiles.insert({ object_id, profile });
- }
- }
- }
-
- void _3MF_Importer::_extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions)
- {
- if (stat.m_uncomp_size > 0) {
- std::string buffer((size_t)stat.m_uncomp_size, 0);
- mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
- if (res == 0) {
- add_error("Error while reading layer config ranges data to buffer");
- return;
- }
-
- std::istringstream iss(buffer); // wrap returned xml to istringstream
- pt::ptree objects_tree;
- pt::read_xml(iss, objects_tree);
-
- for (const auto& object : objects_tree.get_child("objects")) {
- pt::ptree object_tree = object.second;
- int obj_idx = object_tree.get(".id", -1);
- if (obj_idx <= 0) {
- add_error("Found invalid object id");
- continue;
- }
-
- IdToLayerConfigRangesMap::iterator object_item = m_layer_config_ranges.find(obj_idx);
- if (object_item != m_layer_config_ranges.end()) {
- add_error("Found duplicated layer config range");
- continue;
- }
-
- t_layer_config_ranges config_ranges;
-
- for (const auto& range : object_tree) {
- if (range.first != "range")
- continue;
- pt::ptree range_tree = range.second;
- double min_z = range_tree.get(".min_z");
- double max_z = range_tree.get(".max_z");
-
- // get Z range information
- DynamicPrintConfig config;
-
- for (const auto& option : range_tree) {
- if (option.first != "option")
- continue;
- std::string opt_key = option.second.get(".opt_key");
- std::string value = option.second.data();
- config.set_deserialize(opt_key, value, config_substitutions);
- }
-
- config_ranges[{ min_z, max_z }].assign_config(std::move(config));
- }
-
- if (!config_ranges.empty())
- m_layer_config_ranges.insert({ obj_idx, std::move(config_ranges) });
- }
- }
- }
-
- void _3MF_Importer::_extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
- {
- if (stat.m_uncomp_size > 0) {
- std::string buffer((size_t)stat.m_uncomp_size, 0);
- mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
- if (res == 0) {
- add_error("Error while reading sla support points data to buffer");
- return;
- }
-
- if (buffer.back() == '\n')
- buffer.pop_back();
-
- std::vector objects;
- boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off);
-
- // Info on format versioning - see 3mf.hpp
- int version = 0;
- std::string key("support_points_format_version=");
- if (!objects.empty() && objects[0].find(key) != std::string::npos) {
- objects[0].erase(objects[0].begin(), objects[0].begin() + long(key.size())); // removes the string
- version = std::stoi(objects[0]);
- objects.erase(objects.begin()); // pop the header
- }
-
- for (const std::string& object : objects) {
- std::vector object_data;
- boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off);
-
- if (object_data.size() != 2) {
- add_error("Error while reading object data");
- continue;
- }
-
- std::vector object_data_id;
- boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off);
- if (object_data_id.size() != 2) {
- add_error("Error while reading object id");
- continue;
- }
-
- int object_id = std::atoi(object_data_id[1].c_str());
- if (object_id == 0) {
- add_error("Found invalid object id");
- continue;
- }
-
- IdToSlaSupportPointsMap::iterator object_item = m_sla_support_points.find(object_id);
- if (object_item != m_sla_support_points.end()) {
- add_error("Found duplicated SLA support points");
- continue;
- }
-
- std::vector object_data_points;
- boost::split(object_data_points, object_data[1], boost::is_any_of(" "), boost::token_compress_off);
-
- std::vector sla_support_points;
-
- if (version == 0) {
- for (unsigned int i=0; i 0) {
- std::string buffer(size_t(stat.m_uncomp_size), 0);
- mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
- if (res == 0) {
- add_error("Error while reading sla support points data to buffer");
- return;
- }
-
- if (buffer.back() == '\n')
- buffer.pop_back();
-
- std::vector objects;
- boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off);
-
- // Info on format versioning - see 3mf.hpp
- int version = 0;
- std::string key("drain_holes_format_version=");
- if (!objects.empty() && objects[0].find(key) != std::string::npos) {
- objects[0].erase(objects[0].begin(), objects[0].begin() + long(key.size())); // removes the string
- version = std::stoi(objects[0]);
- objects.erase(objects.begin()); // pop the header
- }
-
- for (const std::string& object : objects) {
- std::vector object_data;
- boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off);
-
- if (object_data.size() != 2) {
- add_error("Error while reading object data");
- continue;
- }
-
- std::vector object_data_id;
- boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off);
- if (object_data_id.size() != 2) {
- add_error("Error while reading object id");
- continue;
- }
-
- int object_id = std::atoi(object_data_id[1].c_str());
- if (object_id == 0) {
- add_error("Found invalid object id");
- continue;
- }
-
- IdToSlaDrainHolesMap::iterator object_item = m_sla_drain_holes.find(object_id);
- if (object_item != m_sla_drain_holes.end()) {
- add_error("Found duplicated SLA drain holes");
- continue;
- }
-
- std::vector object_data_points;
- boost::split(object_data_points, object_data[1], boost::is_any_of(" "), boost::token_compress_off);
-
- sla::DrainHoles sla_drain_holes;
-
- if (version == 1) {
- for (unsigned int i=0; i 0) {
- std::string buffer((size_t)stat.m_uncomp_size, 0);
- mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
- if (res == 0) {
- add_error("Error while reading custom Gcodes per height data to buffer");
- return;
- }
-
- std::istringstream iss(buffer); // wrap returned xml to istringstream
- pt::ptree main_tree;
- pt::read_xml(iss, main_tree);
-
- if (main_tree.front().first != "custom_gcodes_per_print_z")
- return;
- pt::ptree code_tree = main_tree.front().second;
-
- m_model->custom_gcode_per_print_z.gcodes.clear();
-
- for (const auto& code : code_tree) {
- if (code.first == "mode") {
- pt::ptree tree = code.second;
- std::string mode = tree.get(".value");
- m_model->custom_gcode_per_print_z.mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
- mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
- CustomGCode::Mode::MultiExtruder;
- }
- if (code.first != "code")
- continue;
-
- pt::ptree tree = code.second;
- double print_z = tree.get (".print_z" );
- int extruder = tree.get (".extruder");
- std::string color = tree.get (".color" );
-
- CustomGCode::Type type;
- std::string extra;
- pt::ptree attr_tree = tree.find("")->second;
- if (attr_tree.find("type") == attr_tree.not_found()) {
- // It means that data was saved in old version (2.2.0 and older) of PrusaSlicer
- // read old data ...
- std::string gcode = tree.get (".gcode");
- // ... and interpret them to the new data
- type = gcode == "M600" ? CustomGCode::ColorChange :
- gcode == "M601" ? CustomGCode::PausePrint :
- gcode == "tool_change" ? CustomGCode::ToolChange : CustomGCode::Custom;
- extra = type == CustomGCode::PausePrint ? color :
- type == CustomGCode::Custom ? gcode : "";
- }
- else {
- type = static_cast(tree.get(".type"));
- extra = tree.get(".extra");
- }
- m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, type, extruder, color, extra}) ;
- }
- }
- }
-
- void _3MF_Importer::_handle_start_model_xml_element(const char* name, const char** attributes)
- {
- if (m_xml_parser == nullptr)
- return;
-
- bool res = true;
- unsigned int num_attributes = (unsigned int)XML_GetSpecifiedAttributeCount(m_xml_parser);
-
- if (::strcmp(MODEL_TAG, name) == 0)
- res = _handle_start_model(attributes, num_attributes);
- else if (::strcmp(RESOURCES_TAG, name) == 0)
- res = _handle_start_resources(attributes, num_attributes);
- else if (::strcmp(OBJECT_TAG, name) == 0)
- res = _handle_start_object(attributes, num_attributes);
- else if (::strcmp(MESH_TAG, name) == 0)
- res = _handle_start_mesh(attributes, num_attributes);
- else if (::strcmp(VERTICES_TAG, name) == 0)
- res = _handle_start_vertices(attributes, num_attributes);
- else if (::strcmp(VERTEX_TAG, name) == 0)
- res = _handle_start_vertex(attributes, num_attributes);
- else if (::strcmp(TRIANGLES_TAG, name) == 0)
- res = _handle_start_triangles(attributes, num_attributes);
- else if (::strcmp(TRIANGLE_TAG, name) == 0)
- res = _handle_start_triangle(attributes, num_attributes);
- else if (::strcmp(COMPONENTS_TAG, name) == 0)
- res = _handle_start_components(attributes, num_attributes);
- else if (::strcmp(COMPONENT_TAG, name) == 0)
- res = _handle_start_component(attributes, num_attributes);
- else if (::strcmp(BUILD_TAG, name) == 0)
- res = _handle_start_build(attributes, num_attributes);
- else if (::strcmp(ITEM_TAG, name) == 0)
- res = _handle_start_item(attributes, num_attributes);
- else if (::strcmp(METADATA_TAG, name) == 0)
- res = _handle_start_metadata(attributes, num_attributes);
-
- if (!res)
- _stop_xml_parser();
- }
-
- void _3MF_Importer::_handle_end_model_xml_element(const char* name)
- {
- if (m_xml_parser == nullptr)
- return;
-
- bool res = true;
-
- if (::strcmp(MODEL_TAG, name) == 0)
- res = _handle_end_model();
- else if (::strcmp(RESOURCES_TAG, name) == 0)
- res = _handle_end_resources();
- else if (::strcmp(OBJECT_TAG, name) == 0)
- res = _handle_end_object();
- else if (::strcmp(MESH_TAG, name) == 0)
- res = _handle_end_mesh();
- else if (::strcmp(VERTICES_TAG, name) == 0)
- res = _handle_end_vertices();
- else if (::strcmp(VERTEX_TAG, name) == 0)
- res = _handle_end_vertex();
- else if (::strcmp(TRIANGLES_TAG, name) == 0)
- res = _handle_end_triangles();
- else if (::strcmp(TRIANGLE_TAG, name) == 0)
- res = _handle_end_triangle();
- else if (::strcmp(COMPONENTS_TAG, name) == 0)
- res = _handle_end_components();
- else if (::strcmp(COMPONENT_TAG, name) == 0)
- res = _handle_end_component();
- else if (::strcmp(BUILD_TAG, name) == 0)
- res = _handle_end_build();
- else if (::strcmp(ITEM_TAG, name) == 0)
- res = _handle_end_item();
- else if (::strcmp(METADATA_TAG, name) == 0)
- res = _handle_end_metadata();
-
- if (!res)
- _stop_xml_parser();
- }
-
- void _3MF_Importer::_handle_model_xml_characters(const XML_Char* s, int len)
- {
- m_curr_characters.append(s, len);
- }
-
- void _3MF_Importer::_handle_start_config_xml_element(const char* name, const char** attributes)
- {
- if (m_xml_parser == nullptr)
- return;
-
- bool res = true;
- unsigned int num_attributes = (unsigned int)XML_GetSpecifiedAttributeCount(m_xml_parser);
-
- if (::strcmp(CONFIG_TAG, name) == 0)
- res = _handle_start_config(attributes, num_attributes);
- else if (::strcmp(OBJECT_TAG, name) == 0)
- res = _handle_start_config_object(attributes, num_attributes);
- else if (::strcmp(VOLUME_TAG, name) == 0)
- res = _handle_start_config_volume(attributes, num_attributes);
- else if (::strcmp(MESH_TAG, name) == 0)
- res = _handle_start_config_volume_mesh(attributes, num_attributes);
- else if (::strcmp(METADATA_TAG, name) == 0)
- res = _handle_start_config_metadata(attributes, num_attributes);
-
- if (!res)
- _stop_xml_parser();
- }
-
- void _3MF_Importer::_handle_end_config_xml_element(const char* name)
- {
- if (m_xml_parser == nullptr)
- return;
-
- bool res = true;
-
- if (::strcmp(CONFIG_TAG, name) == 0)
- res = _handle_end_config();
- else if (::strcmp(OBJECT_TAG, name) == 0)
- res = _handle_end_config_object();
- else if (::strcmp(VOLUME_TAG, name) == 0)
- res = _handle_end_config_volume();
- else if (::strcmp(MESH_TAG, name) == 0)
- res = _handle_end_config_volume_mesh();
- else if (::strcmp(METADATA_TAG, name) == 0)
- res = _handle_end_config_metadata();
-
- if (!res)
- _stop_xml_parser();
- }
-
- bool _3MF_Importer::_handle_start_model(const char** attributes, unsigned int num_attributes)
- {
- m_unit_factor = get_unit_factor(get_attribute_value_string(attributes, num_attributes, UNIT_ATTR));
- return true;
- }
-
- bool _3MF_Importer::_handle_end_model()
- {
- // deletes all non-built or non-instanced objects
- for (const IdToModelObjectMap::value_type& object : m_objects) {
- if (object.second >= int(m_model->objects.size())) {
- add_error("Unable to find object");
- return false;
- }
- ModelObject *model_object = m_model->objects[object.second];
- if (model_object != nullptr && model_object->instances.size() == 0)
- m_model->delete_object(model_object);
- }
-
- if (m_version == 0) {
- // if the 3mf was not produced by PrusaSlicer and there is only one object,
- // set the object name to match the filename
- if (m_model->objects.size() == 1)
- m_model->objects.front()->name = m_name;
- }
-
- // applies instances' matrices
- for (Instance& instance : m_instances) {
- if (instance.instance != nullptr && instance.instance->get_object() != nullptr)
- // apply the transform to the instance
- _apply_transform(*instance.instance, instance.transform);
- }
-
- return true;
- }
-
- bool _3MF_Importer::_handle_start_resources(const char** attributes, unsigned int num_attributes)
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_end_resources()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_start_object(const char** attributes, unsigned int num_attributes)
- {
- // reset current data
- m_curr_object.reset();
-
- if (is_valid_object_type(get_attribute_value_string(attributes, num_attributes, TYPE_ATTR))) {
- // create new object (it may be removed later if no instances are generated from it)
- m_curr_object.model_object_idx = (int)m_model->objects.size();
- m_curr_object.object = m_model->add_object();
- if (m_curr_object.object == nullptr) {
- add_error("Unable to create object");
- return false;
- }
-
- // set object data
- m_curr_object.object->name = get_attribute_value_string(attributes, num_attributes, NAME_ATTR);
- if (m_curr_object.object->name.empty())
- m_curr_object.object->name = m_name + "_" + std::to_string(m_model->objects.size());
-
- m_curr_object.id = get_attribute_value_int(attributes, num_attributes, ID_ATTR);
- }
-
- return true;
- }
-
- bool _3MF_Importer::_handle_end_object()
- {
- if (m_curr_object.object != nullptr) {
- if (m_curr_object.geometry.empty()) {
- // no geometry defined
- // remove the object from the model
- m_model->delete_object(m_curr_object.object);
-
- if (m_curr_object.components.empty()) {
- // no components defined -> invalid object, delete it
- IdToModelObjectMap::iterator object_item = m_objects.find(m_curr_object.id);
- if (object_item != m_objects.end())
- m_objects.erase(object_item);
-
- IdToAliasesMap::iterator alias_item = m_objects_aliases.find(m_curr_object.id);
- if (alias_item != m_objects_aliases.end())
- m_objects_aliases.erase(alias_item);
- }
- else
- // adds components to aliases
- m_objects_aliases.insert({ m_curr_object.id, m_curr_object.components });
- }
- else {
- // geometry defined, store it for later use
- m_geometries.insert({ m_curr_object.id, std::move(m_curr_object.geometry) });
-
- // stores the object for later use
- if (m_objects.find(m_curr_object.id) == m_objects.end()) {
- m_objects.insert({ m_curr_object.id, m_curr_object.model_object_idx });
- m_objects_aliases.insert({ m_curr_object.id, { 1, Component(m_curr_object.id) } }); // aliases itself
- }
- else {
- add_error("Found object with duplicate id");
- return false;
- }
- }
- }
-
- return true;
- }
-
- bool _3MF_Importer::_handle_start_mesh(const char** attributes, unsigned int num_attributes)
- {
- // reset current geometry
- m_curr_object.geometry.reset();
- return true;
- }
-
- bool _3MF_Importer::_handle_end_mesh()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_start_vertices(const char** attributes, unsigned int num_attributes)
- {
- // reset current vertices
- m_curr_object.geometry.vertices.clear();
- return true;
- }
-
- bool _3MF_Importer::_handle_end_vertices()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_start_vertex(const char** attributes, unsigned int num_attributes)
- {
- // appends the vertex coordinates
- // missing values are set equal to ZERO
- m_curr_object.geometry.vertices.emplace_back(
- m_unit_factor * get_attribute_value_float(attributes, num_attributes, X_ATTR),
- m_unit_factor * get_attribute_value_float(attributes, num_attributes, Y_ATTR),
- m_unit_factor * get_attribute_value_float(attributes, num_attributes, Z_ATTR));
- return true;
- }
-
- bool _3MF_Importer::_handle_end_vertex()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_start_triangles(const char** attributes, unsigned int num_attributes)
- {
- // reset current triangles
- m_curr_object.geometry.triangles.clear();
- return true;
- }
-
- bool _3MF_Importer::_handle_end_triangles()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_start_triangle(const char** attributes, unsigned int num_attributes)
- {
- // we are ignoring the following attributes:
- // p1
- // p2
- // p3
- // pid
- // see specifications
-
- // appends the triangle's vertices indices
- // missing values are set equal to ZERO
- m_curr_object.geometry.triangles.emplace_back(
- get_attribute_value_int(attributes, num_attributes, V1_ATTR),
- get_attribute_value_int(attributes, num_attributes, V2_ATTR),
- get_attribute_value_int(attributes, num_attributes, V3_ATTR));
-
- m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR));
- m_curr_object.geometry.custom_seam.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR));
- m_curr_object.geometry.mmu_segmentation.push_back(get_attribute_value_string(attributes, num_attributes, MMU_SEGMENTATION_ATTR));
- return true;
- }
-
- bool _3MF_Importer::_handle_end_triangle()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_start_components(const char** attributes, unsigned int num_attributes)
- {
- // reset current components
- m_curr_object.components.clear();
- return true;
- }
-
- bool _3MF_Importer::_handle_end_components()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_start_component(const char** attributes, unsigned int num_attributes)
- {
- int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR);
- Transform3d transform = get_transform_from_3mf_specs_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
-
- IdToModelObjectMap::iterator object_item = m_objects.find(object_id);
- if (object_item == m_objects.end()) {
- IdToAliasesMap::iterator alias_item = m_objects_aliases.find(object_id);
- if (alias_item == m_objects_aliases.end()) {
- add_error("Found component with invalid object id");
- return false;
- }
- }
-
- m_curr_object.components.emplace_back(object_id, transform);
-
- return true;
- }
-
- bool _3MF_Importer::_handle_end_component()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_start_build(const char** attributes, unsigned int num_attributes)
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_end_build()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_start_item(const char** attributes, unsigned int num_attributes)
- {
- // we are ignoring the following attributes
- // thumbnail
- // partnumber
- // pid
- // pindex
- // see specifications
-
- int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR);
- Transform3d transform = get_transform_from_3mf_specs_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
- int printable = get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR);
-
- return _create_object_instance(object_id, transform, printable, 1);
- }
-
- bool _3MF_Importer::_handle_end_item()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_start_metadata(const char** attributes, unsigned int num_attributes)
- {
- m_curr_characters.clear();
-
- std::string name = get_attribute_value_string(attributes, num_attributes, NAME_ATTR);
- if (!name.empty())
- m_curr_metadata_name = name;
-
- return true;
- }
-
- inline static void check_painting_version(unsigned int loaded_version, unsigned int highest_supported_version, const std::string &error_msg)
- {
- if (loaded_version > highest_supported_version)
- throw version_error(error_msg);
- }
-
- bool _3MF_Importer::_handle_end_metadata()
- {
- if (m_curr_metadata_name == SLIC3RPE_3MF_VERSION) {
- m_version = (unsigned int)atoi(m_curr_characters.c_str());
- if (m_check_version && (m_version > VERSION_3MF_COMPATIBLE)) {
- // std::string msg = _(L("The selected 3mf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible."));
- // throw version_error(msg.c_str());
- const std::string msg = (boost::format(_(L("The selected 3mf file has been saved with a newer version of %1% and is not compatible."))) % std::string(SLIC3R_APP_NAME)).str();
- throw version_error(msg);
- }
- } else if (m_curr_metadata_name == "Application") {
- // Generator application of the 3MF.
- // SLIC3R_APP_KEY - SLIC3R_VERSION
- if (boost::starts_with(m_curr_characters, "PrusaSlicer-"))
- m_prusaslicer_generator_version = Semver::parse(m_curr_characters.substr(12));
- } else if (m_curr_metadata_name == SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION) {
- m_fdm_supports_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
- check_painting_version(m_fdm_supports_painting_version, FDM_SUPPORTS_PAINTING_VERSION,
- _(L("The selected 3MF contains FDM supports painted object using a newer version of PrusaSlicer and is not compatible.")));
- } else if (m_curr_metadata_name == SLIC3RPE_SEAM_PAINTING_VERSION) {
- m_seam_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
- check_painting_version(m_seam_painting_version, SEAM_PAINTING_VERSION,
- _(L("The selected 3MF contains seam painted object using a newer version of PrusaSlicer and is not compatible.")));
- } else if (m_curr_metadata_name == SLIC3RPE_MM_PAINTING_VERSION) {
- m_mm_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
- check_painting_version(m_mm_painting_version, MM_PAINTING_VERSION,
- _(L("The selected 3MF contains multi-material painted object using a newer version of PrusaSlicer and is not compatible.")));
- }
-
- return true;
- }
-
- bool _3MF_Importer::_create_object_instance(int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter)
- {
- static const unsigned int MAX_RECURSIONS = 10;
-
- // escape from circular aliasing
- if (recur_counter > MAX_RECURSIONS) {
- add_error("Too many recursions");
- return false;
- }
-
- IdToAliasesMap::iterator it = m_objects_aliases.find(object_id);
- if (it == m_objects_aliases.end()) {
- add_error("Found item with invalid object id");
- return false;
- }
-
- if (it->second.size() == 1 && it->second[0].object_id == object_id) {
- // aliasing to itself
-
- IdToModelObjectMap::iterator object_item = m_objects.find(object_id);
- if (object_item == m_objects.end() || object_item->second == -1) {
- add_error("Found invalid object");
- return false;
- }
- else {
- ModelInstance* instance = m_model->objects[object_item->second]->add_instance();
- if (instance == nullptr) {
- add_error("Unable to add object instance");
- return false;
- }
- instance->printable = printable;
-
- m_instances.emplace_back(instance, transform);
- }
- }
- else {
- // recursively process nested components
- for (const Component& component : it->second) {
- if (!_create_object_instance(component.object_id, transform * component.transform, printable, recur_counter + 1))
- return false;
- }
- }
-
- return true;
- }
-
- void _3MF_Importer::_apply_transform(ModelInstance& instance, const Transform3d& transform)
- {
- Slic3r::Geometry::Transformation t(transform);
- // invalid scale value, return
- if (!t.get_scaling_factor().all())
- return;
-
- instance.set_transformation(t);
- }
-
- bool _3MF_Importer::_handle_start_config(const char** attributes, unsigned int num_attributes)
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_end_config()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_start_config_object(const char** attributes, unsigned int num_attributes)
- {
- int object_id = get_attribute_value_int(attributes, num_attributes, ID_ATTR);
- IdToMetadataMap::iterator object_item = m_objects_metadata.find(object_id);
- if (object_item != m_objects_metadata.end()) {
- add_error("Found duplicated object id");
- return false;
- }
-
- // Added because of github #3435, currently not used by PrusaSlicer
- // int instances_count_id = get_attribute_value_int(attributes, num_attributes, INSTANCESCOUNT_ATTR);
-
- m_objects_metadata.insert({ object_id, ObjectMetadata() });
- m_curr_config.object_id = object_id;
- return true;
- }
-
- bool _3MF_Importer::_handle_end_config_object()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_start_config_volume(const char** attributes, unsigned int num_attributes)
- {
- IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id);
- if (object == m_objects_metadata.end()) {
- add_error("Cannot assign volume to a valid object");
- return false;
- }
-
- m_curr_config.volume_id = (int)object->second.volumes.size();
-
- unsigned int first_triangle_id = (unsigned int)get_attribute_value_int(attributes, num_attributes, FIRST_TRIANGLE_ID_ATTR);
- unsigned int last_triangle_id = (unsigned int)get_attribute_value_int(attributes, num_attributes, LAST_TRIANGLE_ID_ATTR);
-
- object->second.volumes.emplace_back(first_triangle_id, last_triangle_id);
- return true;
- }
-
- bool _3MF_Importer::_handle_start_config_volume_mesh(const char** attributes, unsigned int num_attributes)
- {
- IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id);
- if (object == m_objects_metadata.end()) {
- add_error("Cannot assign volume mesh to a valid object");
- return false;
- }
- if (object->second.volumes.empty()) {
- add_error("Cannot assign mesh to a valid olume");
- return false;
- }
-
- ObjectMetadata::VolumeMetadata& volume = object->second.volumes.back();
-
- int edges_fixed = get_attribute_value_int(attributes, num_attributes, MESH_STAT_EDGES_FIXED );
- int degenerate_facets = get_attribute_value_int(attributes, num_attributes, MESH_STAT_DEGENERATED_FACETS);
- int facets_removed = get_attribute_value_int(attributes, num_attributes, MESH_STAT_FACETS_REMOVED );
- int facets_reversed = get_attribute_value_int(attributes, num_attributes, MESH_STAT_FACETS_RESERVED );
- int backwards_edges = get_attribute_value_int(attributes, num_attributes, MESH_STAT_BACKWARDS_EDGES );
-
- volume.mesh_stats = { edges_fixed, degenerate_facets, facets_removed, facets_reversed, backwards_edges };
-
- return true;
- }
-
- bool _3MF_Importer::_handle_end_config_volume()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_end_config_volume_mesh()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_handle_start_config_metadata(const char** attributes, unsigned int num_attributes)
- {
- IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id);
- if (object == m_objects_metadata.end()) {
- add_error("Cannot assign metadata to valid object id");
- return false;
- }
-
- std::string type = get_attribute_value_string(attributes, num_attributes, TYPE_ATTR);
- std::string key = get_attribute_value_string(attributes, num_attributes, KEY_ATTR);
- std::string value = get_attribute_value_string(attributes, num_attributes, VALUE_ATTR);
-
- if (type == OBJECT_TYPE)
- object->second.metadata.emplace_back(key, value);
- else if (type == VOLUME_TYPE) {
- if (size_t(m_curr_config.volume_id) < object->second.volumes.size())
- object->second.volumes[m_curr_config.volume_id].metadata.emplace_back(key, value);
- }
- else {
- add_error("Found invalid metadata type");
- return false;
- }
-
- return true;
- }
-
- bool _3MF_Importer::_handle_end_config_metadata()
- {
- // do nothing
- return true;
- }
-
- bool _3MF_Importer::_generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions)
- {
- if (!object.volumes.empty()) {
- add_error("Found invalid volumes count");
- return false;
- }
-
- unsigned int geo_tri_count = (unsigned int)geometry.triangles.size();
- unsigned int renamed_volumes_count = 0;
-
- for (const ObjectMetadata::VolumeMetadata& volume_data : volumes) {
- if (geo_tri_count <= volume_data.first_triangle_id || geo_tri_count <= volume_data.last_triangle_id || volume_data.last_triangle_id < volume_data.first_triangle_id) {
- add_error("Found invalid triangle id");
- return false;
- }
-
- Transform3d volume_matrix_to_object = Transform3d::Identity();
- bool has_transform = false;
- // extract the volume transformation from the volume's metadata, if present
- for (const Metadata& metadata : volume_data.metadata) {
- if (metadata.key == MATRIX_KEY) {
- volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value);
- has_transform = ! volume_matrix_to_object.isApprox(Transform3d::Identity(), 1e-10);
- break;
- }
- }
-
- // splits volume out of imported geometry
- indexed_triangle_set its;
- its.indices.assign(geometry.triangles.begin() + volume_data.first_triangle_id, geometry.triangles.begin() + volume_data.last_triangle_id + 1);
- const size_t triangles_count = its.indices.size();
- if (triangles_count == 0) {
- add_error("An empty triangle mesh found");
- return false;
- }
-
- {
- int min_id = its.indices.front()[0];
- int max_id = min_id;
- for (const Vec3i& face : its.indices) {
- for (const int tri_id : face) {
- if (tri_id < 0 || tri_id >= int(geometry.vertices.size())) {
- add_error("Found invalid vertex id");
- return false;
- }
- min_id = std::min(min_id, tri_id);
- max_id = std::max(max_id, tri_id);
- }
- }
- its.vertices.assign(geometry.vertices.begin() + min_id, geometry.vertices.begin() + max_id + 1);
-
- // rebase indices to the current vertices list
- for (Vec3i& face : its.indices)
- for (int& tri_id : face)
- tri_id -= min_id;
- }
-
- if (m_prusaslicer_generator_version &&
- *m_prusaslicer_generator_version >= *Semver::parse("2.4.0-alpha1") &&
- *m_prusaslicer_generator_version < *Semver::parse("2.4.0-alpha3"))
- // PrusaSlicer 2.4.0-alpha2 contained a bug, where all vertices of a single object were saved for each volume the object contained.
- // Remove the vertices, that are not referenced by any face.
- its_compactify_vertices(its, true);
-
- TriangleMesh triangle_mesh(std::move(its), volume_data.mesh_stats);
-
- if (m_version == 0) {
- // if the 3mf was not produced by PrusaSlicer and there is only one instance,
- // bake the transformation into the geometry to allow the reload from disk command
- // to work properly
- if (object.instances.size() == 1) {
- triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false);
- object.instances.front()->set_transformation(Slic3r::Geometry::Transformation());
- //FIXME do the mesh fixing?
- }
- }
- if (triangle_mesh.volume() < 0)
- triangle_mesh.flip_triangles();
-
- ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
- // stores the volume matrix taken from the metadata, if present
- if (has_transform)
- volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
-
- // recreate custom supports, seam and mmu segmentation from previously loaded attribute
- volume->supported_facets.reserve(triangles_count);
- volume->seam_facets.reserve(triangles_count);
- volume->mmu_segmentation_facets.reserve(triangles_count);
- for (size_t i=0; isupported_facets.set_triangle_from_string(i, geometry.custom_supports[index]);
- if (! geometry.custom_seam[index].empty())
- volume->seam_facets.set_triangle_from_string(i, geometry.custom_seam[index]);
- if (! geometry.mmu_segmentation[index].empty())
- volume->mmu_segmentation_facets.set_triangle_from_string(i, geometry.mmu_segmentation[index]);
- }
- volume->supported_facets.shrink_to_fit();
- volume->seam_facets.shrink_to_fit();
- volume->mmu_segmentation_facets.shrink_to_fit();
-
- // apply the remaining volume's metadata
- for (const Metadata& metadata : volume_data.metadata) {
- if (metadata.key == NAME_KEY)
- volume->name = metadata.value;
- else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
- volume->set_type(ModelVolumeType::PARAMETER_MODIFIER);
- else if (metadata.key == VOLUME_TYPE_KEY)
- volume->set_type(ModelVolume::type_from_string(metadata.value));
- else if (metadata.key == SOURCE_FILE_KEY)
- volume->source.input_file = metadata.value;
- else if (metadata.key == SOURCE_OBJECT_ID_KEY)
- volume->source.object_idx = ::atoi(metadata.value.c_str());
- else if (metadata.key == SOURCE_VOLUME_ID_KEY)
- volume->source.volume_idx = ::atoi(metadata.value.c_str());
- else if (metadata.key == SOURCE_OFFSET_X_KEY)
- volume->source.mesh_offset.x() = ::atof(metadata.value.c_str());
- else if (metadata.key == SOURCE_OFFSET_Y_KEY)
- volume->source.mesh_offset.y() = ::atof(metadata.value.c_str());
- else if (metadata.key == SOURCE_OFFSET_Z_KEY)
- volume->source.mesh_offset.z() = ::atof(metadata.value.c_str());
- else if (metadata.key == SOURCE_IN_INCHES_KEY)
- volume->source.is_converted_from_inches = metadata.value == "1";
- else if (metadata.key == SOURCE_IN_METERS_KEY)
- volume->source.is_converted_from_meters = metadata.value == "1";
-#if ENABLE_RELOAD_FROM_DISK_REWORK
- else if (metadata.key == SOURCE_IS_BUILTIN_VOLUME_KEY)
- volume->source.is_from_builtin_objects = metadata.value == "1";
-#endif // ENABLE_RELOAD_FROM_DISK_REWORK
- else
- volume->config.set_deserialize(metadata.key, metadata.value, config_substitutions);
- }
-
- // this may happen for 3mf saved by 3rd part softwares
- if (volume->name.empty()) {
- volume->name = object.name;
- if (renamed_volumes_count > 0)
- volume->name += "_" + std::to_string(renamed_volumes_count + 1);
- ++renamed_volumes_count;
- }
- }
-
- return true;
- }
-
- void XMLCALL _3MF_Importer::_handle_start_model_xml_element(void* userData, const char* name, const char** attributes)
- {
- _3MF_Importer* importer = (_3MF_Importer*)userData;
- if (importer != nullptr)
- importer->_handle_start_model_xml_element(name, attributes);
- }
-
- void XMLCALL _3MF_Importer::_handle_end_model_xml_element(void* userData, const char* name)
- {
- _3MF_Importer* importer = (_3MF_Importer*)userData;
- if (importer != nullptr)
- importer->_handle_end_model_xml_element(name);
- }
-
- void XMLCALL _3MF_Importer::_handle_model_xml_characters(void* userData, const XML_Char* s, int len)
- {
- _3MF_Importer* importer = (_3MF_Importer*)userData;
- if (importer != nullptr)
- importer->_handle_model_xml_characters(s, len);
- }
-
- void XMLCALL _3MF_Importer::_handle_start_config_xml_element(void* userData, const char* name, const char** attributes)
- {
- _3MF_Importer* importer = (_3MF_Importer*)userData;
- if (importer != nullptr)
- importer->_handle_start_config_xml_element(name, attributes);
- }
-
- void XMLCALL _3MF_Importer::_handle_end_config_xml_element(void* userData, const char* name)
- {
- _3MF_Importer* importer = (_3MF_Importer*)userData;
- if (importer != nullptr)
- importer->_handle_end_config_xml_element(name);
- }
-
- class _3MF_Exporter : public _3MF_Base
- {
- struct BuildItem
- {
- unsigned int id;
- Transform3d transform;
- bool printable;
-
- BuildItem(unsigned int id, const Transform3d& transform, const bool printable)
- : id(id)
- , transform(transform)
- , printable(printable)
- {
- }
- };
-
- struct Offsets
- {
- unsigned int first_vertex_id;
- unsigned int first_triangle_id;
- unsigned int last_triangle_id;
-
- Offsets(unsigned int first_vertex_id)
- : first_vertex_id(first_vertex_id)
- , first_triangle_id(-1)
- , last_triangle_id(-1)
- {
- }
- };
-
- typedef std::map VolumeToOffsetsMap;
-
- struct ObjectData
- {
- ModelObject* object;
- VolumeToOffsetsMap volumes_offsets;
-
- explicit ObjectData(ModelObject* object)
- : object(object)
- {
- }
- };
-
- typedef std::vector BuildItemsList;
- typedef std::map IdToObjectDataMap;
-
- bool m_fullpath_sources{ true };
- bool m_zip64 { true };
-
- public:
- bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data, bool zip64);
-
- private:
- bool _save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data);
- bool _add_content_types_file_to_archive(mz_zip_archive& archive);
- bool _add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data);
- bool _add_relationships_file_to_archive(mz_zip_archive& archive);
- bool _add_model_file_to_archive(const std::string& filename, mz_zip_archive& archive, const Model& model, IdToObjectDataMap& objects_data);
- bool _add_object_to_model_stream(mz_zip_writer_staged_context &context, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets);
- bool _add_mesh_to_object_stream(mz_zip_writer_staged_context &context, ModelObject& object, VolumeToOffsetsMap& volumes_offsets);
- bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items);
- bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
- bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model);
- bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
- bool _add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model);
- bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config);
- bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data);
- bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config);
- };
-
- bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data, bool zip64)
- {
- clear_errors();
- m_fullpath_sources = fullpath_sources;
- m_zip64 = zip64;
- return _save_model_to_file(filename, model, config, thumbnail_data);
- }
-
- bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data)
- {
- mz_zip_archive archive;
- mz_zip_zero_struct(&archive);
-
- if (!open_zip_writer(&archive, filename)) {
- add_error("Unable to open the file");
- return false;
- }
-
- // Adds content types file ("[Content_Types].xml";).
- // The content of this file is the same for each PrusaSlicer 3mf.
- if (!_add_content_types_file_to_archive(archive)) {
- close_zip_writer(&archive);
- boost::filesystem::remove(filename);
- return false;
- }
-
- if (thumbnail_data != nullptr && thumbnail_data->is_valid()) {
- // Adds the file Metadata/thumbnail.png.
- if (!_add_thumbnail_file_to_archive(archive, *thumbnail_data)) {
- close_zip_writer(&archive);
- boost::filesystem::remove(filename);
- return false;
- }
- }
-
- // Adds relationships file ("_rels/.rels").
- // The content of this file is the same for each PrusaSlicer 3mf.
- // The relationshis file contains a reference to the geometry file "3D/3dmodel.model", the name was chosen to be compatible with CURA.
- if (!_add_relationships_file_to_archive(archive)) {
- close_zip_writer(&archive);
- boost::filesystem::remove(filename);
- return false;
- }
-
- // Adds model file ("3D/3dmodel.model").
- // This is the one and only file that contains all the geometry (vertices and triangles) of all ModelVolumes.
- IdToObjectDataMap objects_data;
- if (!_add_model_file_to_archive(filename, archive, model, objects_data)) {
- close_zip_writer(&archive);
- boost::filesystem::remove(filename);
- return false;
- }
-
- // Adds layer height profile file ("Metadata/Slic3r_PE_layer_heights_profile.txt").
- // All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
- // The index differes from the index of an object ID of an object instance of a 3MF file!
- if (!_add_layer_height_profile_file_to_archive(archive, model)) {
- close_zip_writer(&archive);
- boost::filesystem::remove(filename);
- return false;
- }
-
- // Adds layer config ranges file ("Metadata/Slic3r_PE_layer_config_ranges.txt").
- // All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
- // The index differes from the index of an object ID of an object instance of a 3MF file!
- if (!_add_layer_config_ranges_file_to_archive(archive, model)) {
- close_zip_writer(&archive);
- boost::filesystem::remove(filename);
- return false;
- }
-
- // Adds sla support points file ("Metadata/Slic3r_PE_sla_support_points.txt").
- // All sla support points of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
- // The index differes from the index of an object ID of an object instance of a 3MF file!
- if (!_add_sla_support_points_file_to_archive(archive, model)) {
- close_zip_writer(&archive);
- boost::filesystem::remove(filename);
- return false;
- }
-
- if (!_add_sla_drain_holes_file_to_archive(archive, model)) {
- close_zip_writer(&archive);
- boost::filesystem::remove(filename);
- return false;
- }
-
-
- // Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml").
- // All custom gcode per height of whole Model are stored here
- if (!_add_custom_gcode_per_print_z_file_to_archive(archive, model, config)) {
- close_zip_writer(&archive);
- boost::filesystem::remove(filename);
- return false;
- }
-
- // Adds slic3r print config file ("Metadata/Slic3r_PE.config").
- // This file contains the content of FullPrintConfing / SLAFullPrintConfig.
- if (config != nullptr) {
- if (!_add_print_config_file_to_archive(archive, *config)) {
- close_zip_writer(&archive);
- boost::filesystem::remove(filename);
- return false;
- }
- }
-
- // Adds slic3r model config file ("Metadata/Slic3r_PE_model.config").
- // This file contains all the attributes of all ModelObjects and their ModelVolumes (names, parameter overrides).
- // As there is just a single Indexed Triangle Set data stored per ModelObject, offsets of volumes into their respective Indexed Triangle Set data
- // is stored here as well.
- if (!_add_model_config_file_to_archive(archive, model, objects_data)) {
- close_zip_writer(&archive);
- boost::filesystem::remove(filename);
- return false;
- }
-
- if (!mz_zip_writer_finalize_archive(&archive)) {
- close_zip_writer(&archive);
- boost::filesystem::remove(filename);
- add_error("Unable to finalize the archive");
- return false;
- }
-
- close_zip_writer(&archive);
-
- return true;
- }
-
- bool _3MF_Exporter::_add_content_types_file_to_archive(mz_zip_archive& archive)
- {
- std::stringstream stream;
- stream << "\n";
- stream << "\n";
- stream << " \n";
- stream << " \n";
- stream << " \n";
- stream << "";
-
- std::string out = stream.str();
-
- if (!mz_zip_writer_add_mem(&archive, CONTENT_TYPES_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
- add_error("Unable to add content types file to archive");
- return false;
- }
-
- return true;
- }
-
- bool _3MF_Exporter::_add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data)
- {
- bool res = false;
-
- size_t png_size = 0;
- void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)thumbnail_data.pixels.data(), thumbnail_data.width, thumbnail_data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1);
- if (png_data != nullptr) {
- res = mz_zip_writer_add_mem(&archive, THUMBNAIL_FILE.c_str(), (const void*)png_data, png_size, MZ_DEFAULT_COMPRESSION);
- mz_free(png_data);
- }
-
- if (!res)
- add_error("Unable to add thumbnail file to archive");
-
- return res;
- }
-
- bool _3MF_Exporter::_add_relationships_file_to_archive(mz_zip_archive& archive)
- {
- std::stringstream stream;
- stream << "\n";
- stream << "\n";
- stream << " \n";
- stream << " \n";
- stream << "";
-
- std::string out = stream.str();
-
- if (!mz_zip_writer_add_mem(&archive, RELATIONSHIPS_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
- add_error("Unable to add relationships file to archive");
- return false;
- }
-
- return true;
- }
-
- static void reset_stream(std::stringstream &stream)
- {
- stream.str("");
- stream.clear();
- // https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10
- // Conversion of a floating-point value to text and back is exact as long as at least max_digits10 were used (9 for float, 17 for double).
- // It is guaranteed to produce the same floating-point value, even though the intermediate text representation is not exact.
- // The default value of std::stream precision is 6 digits only!
- stream << std::setprecision(std::numeric_limits::max_digits10);
- }
-
- bool _3MF_Exporter::_add_model_file_to_archive(const std::string& filename, mz_zip_archive& archive, const Model& model, IdToObjectDataMap& objects_data)
- {
- mz_zip_writer_staged_context context;
- if (!mz_zip_writer_add_staged_open(&archive, &context, MODEL_FILE.c_str(),
- m_zip64 ?
- // Maximum expected and allowed 3MF file size is 16GiB.
- // This switches the ZIP file to a 64bit mode, which adds a tiny bit of overhead to file records.
- (uint64_t(1) << 30) * 16 :
- // Maximum expected 3MF file size is 4GB-1. This is a workaround for interoperability with Windows 10 3D model fixing API, see
- // GH issue #6193.
- (uint64_t(1) << 32) - 1,
- nullptr, nullptr, 0, MZ_DEFAULT_COMPRESSION, nullptr, 0, nullptr, 0)) {
- add_error("Unable to add model file to archive");
- return false;
- }
-
- {
- std::stringstream stream;
- reset_stream(stream);
- stream << "\n";
- stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
- stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "" << METADATA_TAG << ">\n";
-
- if (model.is_fdm_support_painted())
- stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION << "\">" << FDM_SUPPORTS_PAINTING_VERSION << "" << METADATA_TAG << ">\n";
-
- if (model.is_seam_painted())
- stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_SEAM_PAINTING_VERSION << "\">" << SEAM_PAINTING_VERSION << "" << METADATA_TAG << ">\n";
-
- if (model.is_mm_painted())
- stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_MM_PAINTING_VERSION << "\">" << MM_PAINTING_VERSION << "" << METADATA_TAG << ">\n";
-
- std::string name = xml_escape(boost::filesystem::path(filename).stem().string());
- stream << " <" << METADATA_TAG << " name=\"Title\">" << name << "" << METADATA_TAG << ">\n";
- stream << " <" << METADATA_TAG << " name=\"Designer\">" << "" << METADATA_TAG << ">\n";
- stream << " <" << METADATA_TAG << " name=\"Description\">" << name << "" << METADATA_TAG << ">\n";
- stream << " <" << METADATA_TAG << " name=\"Copyright\">" << "" << METADATA_TAG << ">\n";
- stream << " <" << METADATA_TAG << " name=\"LicenseTerms\">" << "" << METADATA_TAG << ">\n";
- stream << " <" << METADATA_TAG << " name=\"Rating\">" << "" << METADATA_TAG << ">\n";
- std::string date = Slic3r::Utils::utc_timestamp(Slic3r::Utils::get_current_time_utc());
- // keep only the date part of the string
- date = date.substr(0, 10);
- stream << " <" << METADATA_TAG << " name=\"CreationDate\">" << date << "" << METADATA_TAG << ">\n";
- stream << " <" << METADATA_TAG << " name=\"ModificationDate\">" << date << "" << METADATA_TAG << ">\n";
- stream << " <" << METADATA_TAG << " name=\"Application\">" << SLIC3R_APP_KEY << "-" << SLIC3R_VERSION << "" << METADATA_TAG << ">\n";
- stream << " <" << RESOURCES_TAG << ">\n";
- std::string buf = stream.str();
- if (! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) {
- add_error("Unable to add model file to archive");
- return false;
- }
- }
-
- // Instance transformations, indexed by the 3MF object ID (which is a linear serialization of all instances of all ModelObjects).
- BuildItemsList build_items;
-
- // The object_id here is a one based identifier of the first instance of a ModelObject in the 3MF file, where
- // all the object instances of all ModelObjects are stored and indexed in a 1 based linear fashion.
- // Therefore the list of object_ids here may not be continuous.
- unsigned int object_id = 1;
- for (ModelObject* obj : model.objects) {
- if (obj == nullptr)
- continue;
-
- // Index of an object in the 3MF file corresponding to the 1st instance of a ModelObject.
- unsigned int curr_id = object_id;
- IdToObjectDataMap::iterator object_it = objects_data.insert({ curr_id, ObjectData(obj) }).first;
- // Store geometry of all ModelVolumes contained in a single ModelObject into a single 3MF indexed triangle set object.
- // object_it->second.volumes_offsets will contain the offsets of the ModelVolumes in that single indexed triangle set.
- // object_id will be increased to point to the 1st instance of the next ModelObject.
- if (!_add_object_to_model_stream(context, object_id, *obj, build_items, object_it->second.volumes_offsets)) {
- add_error("Unable to add object to archive");
- mz_zip_writer_add_staged_finish(&context);
- return false;
- }
- }
-
- {
- std::stringstream stream;
- reset_stream(stream);
- stream << " " << RESOURCES_TAG << ">\n";
-
- // Store the transformations of all the ModelInstances of all ModelObjects, indexed in a linear fashion.
- if (!_add_build_to_model_stream(stream, build_items)) {
- add_error("Unable to add build to archive");
- mz_zip_writer_add_staged_finish(&context);
- return false;
- }
-
- stream << "" << MODEL_TAG << ">\n";
-
- std::string buf = stream.str();
-
- if ((! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) ||
- ! mz_zip_writer_add_staged_finish(&context)) {
- add_error("Unable to add model file to archive");
- return false;
- }
- }
-
- return true;
- }
-
- bool _3MF_Exporter::_add_object_to_model_stream(mz_zip_writer_staged_context &context, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets)
- {
- std::stringstream stream;
- reset_stream(stream);
- unsigned int id = 0;
- for (const ModelInstance* instance : object.instances) {
- assert(instance != nullptr);
- if (instance == nullptr)
- continue;
-
- unsigned int instance_id = object_id + id;
- stream << " <" << OBJECT_TAG << " id=\"" << instance_id << "\" type=\"model\">\n";
-
- if (id == 0) {
- std::string buf = stream.str();
- reset_stream(stream);
- if ((! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) ||
- ! _add_mesh_to_object_stream(context, object, volumes_offsets)) {
- add_error("Unable to add mesh to archive");
- return false;
- }
- }
- else {
- stream << " <" << COMPONENTS_TAG << ">\n";
- stream << " <" << COMPONENT_TAG << " objectid=\"" << object_id << "\"/>\n";
- stream << " " << COMPONENTS_TAG << ">\n";
- }
-
- Transform3d t = instance->get_matrix();
- // instance_id is just a 1 indexed index in build_items.
- assert(instance_id == build_items.size() + 1);
- build_items.emplace_back(instance_id, t, instance->printable);
-
- stream << " " << OBJECT_TAG << ">\n";
-
- ++id;
- }
-
- object_id += id;
- std::string buf = stream.str();
- return buf.empty() || mz_zip_writer_add_staged_data(&context, buf.data(), buf.size());
- }
-
-#if EXPORT_3MF_USE_SPIRIT_KARMA_FP
- template
- struct coordinate_policy_fixed : boost::spirit::karma::real_policies
- {
- static int floatfield(Num n) { return fmtflags::fixed; }
- // Number of decimal digits to maintain float accuracy when storing into a text file and parsing back.
- static unsigned precision(Num /* n */) { return std::numeric_limits::max_digits10 + 1; }
- // No trailing zeros, thus for fmtflags::fixed usually much less than max_digits10 decimal numbers will be produced.
- static bool trailing_zeros(Num /* n */) { return false; }
- };
- template
- struct coordinate_policy_scientific : coordinate_policy_fixed
- {
- static int floatfield(Num n) { return fmtflags::scientific; }
- };
- // Define a new generator type based on the new coordinate policy.
- using coordinate_type_fixed = boost::spirit::karma::real_generator>;
- using coordinate_type_scientific = boost::spirit::karma::real_generator>;
-#endif // EXPORT_3MF_USE_SPIRIT_KARMA_FP
-
- bool _3MF_Exporter::_add_mesh_to_object_stream(mz_zip_writer_staged_context &context, ModelObject& object, VolumeToOffsetsMap& volumes_offsets)
- {
- std::string output_buffer;
- output_buffer += " <";
- output_buffer += MESH_TAG;
- output_buffer += ">\n <";
- output_buffer += VERTICES_TAG;
- output_buffer += ">\n";
-
- auto flush = [this, &output_buffer, &context](bool force = false) {
- if ((force && ! output_buffer.empty()) || output_buffer.size() >= 65536 * 16) {
- if (! mz_zip_writer_add_staged_data(&context, output_buffer.data(), output_buffer.size())) {
- add_error("Error during writing or compression");
- return false;
- }
- output_buffer.clear();
- }
- return true;
- };
-
- auto format_coordinate = [](float f, char *buf) -> char* {
- assert(is_decimal_separator_point());
-#if EXPORT_3MF_USE_SPIRIT_KARMA_FP
- // Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
- // https://github.com/boostorg/spirit/pull/586
- // where the exported string is one digit shorter than it should be to guarantee lossless round trip.
- // The code is left here for the ocasion boost guys improve.
- coordinate_type_fixed const coordinate_fixed = coordinate_type_fixed();
- coordinate_type_scientific const coordinate_scientific = coordinate_type_scientific();
- // Format "f" in a fixed format.
- char *ptr = buf;
- boost::spirit::karma::generate(ptr, coordinate_fixed, f);
- // Format "f" in a scientific format.
- char *ptr2 = ptr;
- boost::spirit::karma::generate(ptr2, coordinate_scientific, f);
- // Return end of the shorter string.
- auto len2 = ptr2 - ptr;
- if (ptr - buf > len2) {
- // Move the shorter scientific form to the front.
- memcpy(buf, ptr, len2);
- ptr = buf + len2;
- }
- // Return pointer to the end.
- return ptr;
-#else
- // Round-trippable float, shortest possible.
- return buf + sprintf(buf, "%.9g", f);
-#endif
- };
-
- char buf[256];
- unsigned int vertices_count = 0;
- for (ModelVolume* volume : object.volumes) {
- if (volume == nullptr)
- continue;
-
- volumes_offsets.insert({ volume, Offsets(vertices_count) });
-
- const indexed_triangle_set &its = volume->mesh().its;
- if (its.vertices.empty()) {
- add_error("Found invalid mesh");
- return false;
- }
-
- vertices_count += (int)its.vertices.size();
-
- const Transform3d& matrix = volume->get_matrix();
-
- for (size_t i = 0; i < its.vertices.size(); ++i) {
- Vec3f v = (matrix * its.vertices[i].cast()).cast();
- char *ptr = buf;
- boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << VERTEX_TAG << " x=\"");
- ptr = format_coordinate(v.x(), ptr);
- boost::spirit::karma::generate(ptr, "\" y=\"");
- ptr = format_coordinate(v.y(), ptr);
- boost::spirit::karma::generate(ptr, "\" z=\"");
- ptr = format_coordinate(v.z(), ptr);
- boost::spirit::karma::generate(ptr, "\"/>\n");
- *ptr = '\0';
- output_buffer += buf;
- if (! flush())
- return false;
- }
- }
-
- output_buffer += " ";
- output_buffer += VERTICES_TAG;
- output_buffer += ">\n <";
- output_buffer += TRIANGLES_TAG;
- output_buffer += ">\n";
-
- unsigned int triangles_count = 0;
- for (ModelVolume* volume : object.volumes) {
- if (volume == nullptr)
- continue;
-
- bool is_left_handed = volume->is_left_handed();
- VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume);
- assert(volume_it != volumes_offsets.end());
-
- const indexed_triangle_set &its = volume->mesh().its;
-
- // updates triangle offsets
- volume_it->second.first_triangle_id = triangles_count;
- triangles_count += (int)its.indices.size();
- volume_it->second.last_triangle_id = triangles_count - 1;
-
- for (int i = 0; i < int(its.indices.size()); ++ i) {
- {
- const Vec3i &idx = its.indices[i];
- char *ptr = buf;
- boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
- " v1=\"" << boost::spirit::int_ <<
- "\" v2=\"" << boost::spirit::int_ <<
- "\" v3=\"" << boost::spirit::int_ << "\"",
- idx[is_left_handed ? 2 : 0] + volume_it->second.first_vertex_id,
- idx[1] + volume_it->second.first_vertex_id,
- idx[is_left_handed ? 0 : 2] + volume_it->second.first_vertex_id);
- *ptr = '\0';
- output_buffer += buf;
- }
-
- std::string custom_supports_data_string = volume->supported_facets.get_triangle_as_string(i);
- if (! custom_supports_data_string.empty()) {
- output_buffer += " ";
- output_buffer += CUSTOM_SUPPORTS_ATTR;
- output_buffer += "=\"";
- output_buffer += custom_supports_data_string;
- output_buffer += "\"";
- }
-
- std::string custom_seam_data_string = volume->seam_facets.get_triangle_as_string(i);
- if (! custom_seam_data_string.empty()) {
- output_buffer += " ";
- output_buffer += CUSTOM_SEAM_ATTR;
- output_buffer += "=\"";
- output_buffer += custom_seam_data_string;
- output_buffer += "\"";
- }
-
- std::string mmu_painting_data_string = volume->mmu_segmentation_facets.get_triangle_as_string(i);
- if (! mmu_painting_data_string.empty()) {
- output_buffer += " ";
- output_buffer += MMU_SEGMENTATION_ATTR;
- output_buffer += "=\"";
- output_buffer += mmu_painting_data_string;
- output_buffer += "\"";
- }
-
- output_buffer += "/>\n";
-
- if (! flush())
- return false;
- }
- }
-
- output_buffer += " ";
- output_buffer += TRIANGLES_TAG;
- output_buffer += ">\n ";
- output_buffer += MESH_TAG;
- output_buffer += ">\n";
-
- // Force flush.
- return flush(true);
- }
-
- bool _3MF_Exporter::_add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items)
- {
- // This happens for empty projects
- if (build_items.size() == 0) {
- add_error("No build item found");
- return true;
- }
-
- stream << " <" << BUILD_TAG << ">\n";
-
- for (const BuildItem& item : build_items) {
- stream << " <" << ITEM_TAG << " " << OBJECTID_ATTR << "=\"" << item.id << "\" " << TRANSFORM_ATTR << "=\"";
- for (unsigned c = 0; c < 4; ++c) {
- for (unsigned r = 0; r < 3; ++r) {
- stream << item.transform(r, c);
- if (r != 2 || c != 3)
- stream << " ";
- }
- }
- stream << "\" " << PRINTABLE_ATTR << "=\"" << item.printable << "\"/>\n";
- }
-
- stream << " " << BUILD_TAG << ">\n";
-
- return true;
- }
-
- bool _3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model)
- {
- assert(is_decimal_separator_point());
- std::string out = "";
- char buffer[1024];
-
- unsigned int count = 0;
- for (const ModelObject* object : model.objects) {
- ++count;
- const std::vector& layer_height_profile = object->layer_height_profile.get();
- if (layer_height_profile.size() >= 4 && layer_height_profile.size() % 2 == 0) {
- sprintf(buffer, "object_id=%d|", count);
- out += buffer;
-
- // Store the layer height profile as a single semicolon separated list.
- for (size_t i = 0; i < layer_height_profile.size(); ++i) {
- sprintf(buffer, (i == 0) ? "%f" : ";%f", layer_height_profile[i]);
- out += buffer;
- }
-
- out += "\n";
- }
- }
-
- if (!out.empty()) {
- if (!mz_zip_writer_add_mem(&archive, LAYER_HEIGHTS_PROFILE_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
- add_error("Unable to add layer heights profile file to archive");
- return false;
- }
- }
-
- return true;
- }
-
- bool _3MF_Exporter::_add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model)
- {
- std::string out = "";
- pt::ptree tree;
-
- unsigned int object_cnt = 0;
- for (const ModelObject* object : model.objects) {
- object_cnt++;
- const t_layer_config_ranges& ranges = object->layer_config_ranges;
- if (!ranges.empty())
- {
- pt::ptree& obj_tree = tree.add("objects.object","");
-
- obj_tree.put(".id", object_cnt);
-
- // Store the layer config ranges.
- for (const auto& range : ranges) {
- pt::ptree& range_tree = obj_tree.add("range", "");
-
- // store minX and maxZ
- range_tree.put(".min_z", range.first.first);
- range_tree.put(".max_z", range.first.second);
-
- // store range configuration
- const ModelConfig& config = range.second;
- for (const std::string& opt_key : config.keys()) {
- pt::ptree& opt_tree = range_tree.add("option", config.opt_serialize(opt_key));
- opt_tree.put(".opt_key", opt_key);
- }
- }
- }
- }
-
- if (!tree.empty()) {
- std::ostringstream oss;
- pt::write_xml(oss, tree);
- out = oss.str();
-
- // Post processing("beautification") of the output string for a better preview
- boost::replace_all(out, ">");
- // OR just
- boost::replace_all(out, "><", ">\n<");
- }
-
- if (!out.empty()) {
- if (!mz_zip_writer_add_mem(&archive, LAYER_CONFIG_RANGES_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
- add_error("Unable to add layer heights profile file to archive");
- return false;
- }
- }
-
- return true;
- }
-
- bool _3MF_Exporter::_add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model)
- {
- assert(is_decimal_separator_point());
- std::string out = "";
- char buffer[1024];
-
- unsigned int count = 0;
- for (const ModelObject* object : model.objects) {
- ++count;
- const std::vector& sla_support_points = object->sla_support_points;
- if (!sla_support_points.empty()) {
- sprintf(buffer, "object_id=%d|", count);
- out += buffer;
-
- // Store the layer height profile as a single space separated list.
- for (size_t i = 0; i < sla_support_points.size(); ++i) {
- sprintf(buffer, (i==0 ? "%f %f %f %f %f" : " %f %f %f %f %f"), sla_support_points[i].pos(0), sla_support_points[i].pos(1), sla_support_points[i].pos(2), sla_support_points[i].head_front_radius, (float)sla_support_points[i].is_new_island);
- out += buffer;
- }
- out += "\n";
- }
- }
-
- if (!out.empty()) {
- // Adds version header at the beginning:
- out = std::string("support_points_format_version=") + std::to_string(support_points_format_version) + std::string("\n") + out;
-
- if (!mz_zip_writer_add_mem(&archive, SLA_SUPPORT_POINTS_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
- add_error("Unable to add sla support points file to archive");
- return false;
- }
- }
- return true;
- }
-
- bool _3MF_Exporter::_add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model)
- {
- assert(is_decimal_separator_point());
- const char *const fmt = "object_id=%d|";
- std::string out;
-
- unsigned int count = 0;
- for (const ModelObject* object : model.objects) {
- ++count;
- sla::DrainHoles drain_holes = object->sla_drain_holes;
-
- // The holes were placed 1mm above the mesh in the first implementation.
- // This was a bad idea and the reference point was changed in 2.3 so
- // to be on the mesh exactly. The elevated position is still saved
- // in 3MFs for compatibility reasons.
- for (sla::DrainHole& hole : drain_holes) {
- hole.pos -= hole.normal.normalized();
- hole.height += 1.f;
- }
-
- if (!drain_holes.empty()) {
- out += string_printf(fmt, count);
-
- // Store the layer height profile as a single space separated list.
- for (size_t i = 0; i < drain_holes.size(); ++i)
- out += string_printf((i == 0 ? "%f %f %f %f %f %f %f %f" : " %f %f %f %f %f %f %f %f"),
- drain_holes[i].pos(0),
- drain_holes[i].pos(1),
- drain_holes[i].pos(2),
- drain_holes[i].normal(0),
- drain_holes[i].normal(1),
- drain_holes[i].normal(2),
- drain_holes[i].radius,
- drain_holes[i].height);
-
- out += "\n";
- }
- }
-
- if (!out.empty()) {
- // Adds version header at the beginning:
- out = std::string("drain_holes_format_version=") + std::to_string(drain_holes_format_version) + std::string("\n") + out;
-
- if (!mz_zip_writer_add_mem(&archive, SLA_DRAIN_HOLES_FILE.c_str(), static_cast(out.data()), out.length(), mz_uint(MZ_DEFAULT_COMPRESSION))) {
- add_error("Unable to add sla support points file to archive");
- return false;
- }
- }
- return true;
- }
-
- bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config)
- {
- assert(is_decimal_separator_point());
- char buffer[1024];
- sprintf(buffer, "; %s\n\n", header_slic3r_generated().c_str());
- std::string out = buffer;
-
- for (const std::string &key : config.keys())
- if (key != "compatible_printers")
- out += "; " + key + " = " + config.opt_serialize(key) + "\n";
-
- if (!out.empty()) {
- if (!mz_zip_writer_add_mem(&archive, PRINT_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
- add_error("Unable to add print config file to archive");
- return false;
- }
- }
-
- return true;
- }
-
- bool _3MF_Exporter::_add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data)
- {
- std::stringstream stream;
- // Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back
- // when loaded as accurately as possible.
- stream << std::setprecision(std::numeric_limits::max_digits10);
- stream << "\n";
- stream << "<" << CONFIG_TAG << ">\n";
-
- for (const IdToObjectDataMap::value_type& obj_metadata : objects_data) {
- const ModelObject* obj = obj_metadata.second.object;
- if (obj != nullptr) {
- // Output of instances count added because of github #3435, currently not used by PrusaSlicer
- stream << " <" << OBJECT_TAG << " " << ID_ATTR << "=\"" << obj_metadata.first << "\" " << INSTANCESCOUNT_ATTR << "=\"" << obj->instances.size() << "\">\n";
-
- // stores object's name
- if (!obj->name.empty())
- stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << xml_escape(obj->name) << "\"/>\n";
-
- // stores object's config data
- for (const std::string& key : obj->config.keys()) {
- stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.opt_serialize(key) << "\"/>\n";
- }
-
- for (const ModelVolume* volume : obj_metadata.second.object->volumes) {
- if (volume != nullptr) {
- const VolumeToOffsetsMap& offsets = obj_metadata.second.volumes_offsets;
- VolumeToOffsetsMap::const_iterator it = offsets.find(volume);
- if (it != offsets.end()) {
- // stores volume's offsets
- stream << " <" << VOLUME_TAG << " ";
- stream << FIRST_TRIANGLE_ID_ATTR << "=\"" << it->second.first_triangle_id << "\" ";
- stream << LAST_TRIANGLE_ID_ATTR << "=\"" << it->second.last_triangle_id << "\">\n";
-
- // stores volume's name
- if (!volume->name.empty())
- stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
-
- // stores volume's modifier field (legacy, to support old slicers)
- if (volume->is_modifier())
- stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
- // stores volume's type (overrides the modifier field above)
- stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " <<
- VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";
-
- // stores volume's local matrix
- stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MATRIX_KEY << "\" " << VALUE_ATTR << "=\"";
- const Transform3d matrix = volume->get_matrix() * volume->source.transform.get_matrix();
- for (int r = 0; r < 4; ++r) {
- for (int c = 0; c < 4; ++c) {
- stream << matrix(r, c);
- if (r != 3 || c != 3)
- stream << " ";
- }
- }
- stream << "\"/>\n";
-
- // stores volume's source data
- {
- std::string input_file = xml_escape(m_fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string());
- std::string prefix = std::string(" <") + METADATA_TAG + " " + TYPE_ATTR + "=\"" + VOLUME_TYPE + "\" " + KEY_ATTR + "=\"";
- if (! volume->source.input_file.empty()) {
- stream << prefix << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n";
- stream << prefix << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n";
- stream << prefix << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n";
- stream << prefix << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
- stream << prefix << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n";
- stream << prefix << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
- }
- assert(! volume->source.is_converted_from_inches || ! volume->source.is_converted_from_meters);
- if (volume->source.is_converted_from_inches)
- stream << prefix << SOURCE_IN_INCHES_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
- else if (volume->source.is_converted_from_meters)
- stream << prefix << SOURCE_IN_METERS_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
-#if ENABLE_RELOAD_FROM_DISK_REWORK
- if (volume->source.is_from_builtin_objects)
- stream << prefix << SOURCE_IS_BUILTIN_VOLUME_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
-#endif // ENABLE_RELOAD_FROM_DISK_REWORK
- }
-
- // stores volume's config data
- for (const std::string& key : volume->config.keys()) {
- stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n";
- }
-
- // stores mesh's statistics
- const RepairedMeshErrors& stats = volume->mesh().stats().repaired_errors;
- stream << " <" << MESH_TAG << " ";
- stream << MESH_STAT_EDGES_FIXED << "=\"" << stats.edges_fixed << "\" ";
- stream << MESH_STAT_DEGENERATED_FACETS << "=\"" << stats.degenerate_facets << "\" ";
- stream << MESH_STAT_FACETS_REMOVED << "=\"" << stats.facets_removed << "\" ";
- stream << MESH_STAT_FACETS_RESERVED << "=\"" << stats.facets_reversed << "\" ";
- stream << MESH_STAT_BACKWARDS_EDGES << "=\"" << stats.backwards_edges << "\"/>\n";
-
- stream << " " << VOLUME_TAG << ">\n";
- }
- }
- }
-
- stream << " " << OBJECT_TAG << ">\n";
- }
- }
-
- stream << "" << CONFIG_TAG << ">\n";
-
- std::string out = stream.str();
-
- if (!mz_zip_writer_add_mem(&archive, MODEL_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
- add_error("Unable to add model config file to archive");
- return false;
- }
-
- return true;
- }
-
-bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config)
-{
- std::string out = "";
-
- if (!model.custom_gcode_per_print_z.gcodes.empty()) {
- pt::ptree tree;
- pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", "");
-
- for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes) {
- pt::ptree& code_tree = main_tree.add("code", "");
-
- // store data of custom_gcode_per_print_z
- code_tree.put(".print_z" , code.print_z );
- code_tree.put(".type" , static_cast(code.type));
- code_tree.put(".extruder" , code.extruder );
- code_tree.put(".color" , code.color );
- code_tree.put(".extra" , code.extra );
-
- // add gcode field data for the old version of the PrusaSlicer
- std::string gcode = code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
- code.type == CustomGCode::PausePrint ? config->opt_string("pause_print_gcode") :
- code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
- code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
- code_tree.put(".gcode" , gcode );
- }
-
- pt::ptree& mode_tree = main_tree.add("mode", "");
- // store mode of a custom_gcode_per_print_z
- mode_tree.put(".value", model.custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
- model.custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
- CustomGCode::MultiExtruderMode);
-
- if (!tree.empty()) {
- std::ostringstream oss;
- boost::property_tree::write_xml(oss, tree);
- out = oss.str();
-
- // Post processing("beautification") of the output string
- boost::replace_all(out, "><", ">\n<");
- }
- }
-
- if (!out.empty()) {
- if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_PRINT_Z_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
- add_error("Unable to add custom Gcodes per print_z file to archive");
- return false;
- }
- }
-
- return true;
-}
-
-// Perform conversions based on the config values available.
-//FIXME provide a version of PrusaSlicer that stored the project file (3MF).
-static void handle_legacy_project_loaded(unsigned int version_project_file, DynamicPrintConfig& config)
-{
- if (! config.has("brim_separation")) {
- if (auto *opt_elephant_foot = config.option("elefant_foot_compensation", false); opt_elephant_foot) {
- // Conversion from older PrusaSlicer which applied brim separation equal to elephant foot compensation.
- auto *opt_brim_separation = config.option("brim_separation", true);
- opt_brim_separation->value = opt_elephant_foot->value;
- }
- }
-}
-
-bool is_project_3mf(const std::string& filename)
-{
- mz_zip_archive archive;
- mz_zip_zero_struct(&archive);
-
- if (!open_zip_reader(&archive, filename))
- return false;
-
- mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
-
- // loop the entries to search for config
- mz_zip_archive_file_stat stat;
- bool config_found = false;
- for (mz_uint i = 0; i < num_entries; ++i) {
- if (mz_zip_reader_file_stat(&archive, i, &stat)) {
- std::string name(stat.m_filename);
- std::replace(name.begin(), name.end(), '\\', '/');
-
- if (boost::algorithm::iequals(name, PRINT_CONFIG_FILE)) {
- config_found = true;
- break;
- }
- }
- }
-
- close_zip_reader(&archive);
-
- return config_found;
-}
-
-bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Model* model, bool check_version)
-{
- if (path == nullptr || model == nullptr)
- return false;
-
- // All import should use "C" locales for number formatting.
- CNumericLocalesSetter locales_setter;
- _3MF_Importer importer;
- bool res = importer.load_model_from_file(path, *model, config, config_substitutions, check_version);
- importer.log_errors();
- handle_legacy_project_loaded(importer.version(), config);
- return res;
-}
-
-bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data, bool zip64)
-{
- // All export should use "C" locales for number formatting.
- CNumericLocalesSetter locales_setter;
-
- if (path == nullptr || model == nullptr)
- return false;
-
- _3MF_Exporter exporter;
- bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources, thumbnail_data, zip64);
- if (!res)
- exporter.log_errors();
-
- return res;
-}
-} // namespace Slic3r
+#include "../libslic3r.h"
+#include "../Exception.hpp"
+#include "../Model.hpp"
+#include "../Utils.hpp"
+#include "../LocalesUtils.hpp"
+#include "../GCode.hpp"
+#include "../Geometry.hpp"
+#include "../GCode/ThumbnailData.hpp"
+#include "../Semver.hpp"
+#include "../Time.hpp"
+
+#include "../I18N.hpp"
+
+#include "3mf.hpp"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include