Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_gcode_viewer

This commit is contained in:
Enrico Turri 2020-02-14 08:32:06 +01:00
commit ebbebe3727
241 changed files with 43747 additions and 15023 deletions

View file

@ -104,7 +104,7 @@ list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/)
enable_testing () enable_testing ()
# Enable C++11 language standard. # Enable C++17 language standard.
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@ -392,8 +392,7 @@ if (NOT GLEW_FOUND)
endif () endif ()
# Find the Cereal serialization library # Find the Cereal serialization library
add_library(cereal INTERFACE) find_package(cereal REQUIRED)
target_include_directories(cereal INTERFACE include)
# l10n # l10n
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization") set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization")
@ -426,6 +425,12 @@ if(SLIC3R_STATIC)
set(USE_BLOSC TRUE) set(USE_BLOSC TRUE)
endif() endif()
find_package(OpenVDB 5.0 REQUIRED COMPONENTS openvdb)
if(OpenVDB_FOUND)
slic3r_remap_configs(IlmBase::Half RelWithDebInfo Release)
slic3r_remap_configs(Blosc::blosc RelWithDebInfo Release)
endif()
set(TOP_LEVEL_PROJECT_DIR ${PROJECT_SOURCE_DIR}) set(TOP_LEVEL_PROJECT_DIR ${PROJECT_SOURCE_DIR})
function(prusaslicer_copy_dlls target) function(prusaslicer_copy_dlls target)
if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
@ -434,23 +439,30 @@ function(prusaslicer_copy_dlls target)
set(_bits 32) set(_bits 32)
endif () endif ()
get_target_property(_out_dir ${target} BINARY_DIR) get_property(_is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
get_target_property(_alt_out_dir ${target} RUNTIME_OUTPUT_DIRECTORY)
if (_alt_out_dir)
set (_out_dir "${_alt_out_dir}")
elseif (_is_multi)
set (_out_dir "$<TARGET_PROPERTY:${target},BINARY_DIR>/$<CONFIG>")
else ()
set (_out_dir "$<TARGET_PROPERTY:${target},BINARY_DIR>")
endif ()
# This has to be a separate target due to the windows command line lenght limits # This has to be a separate target due to the windows command line lenght limits
add_custom_command(TARGET ${target} POST_BUILD add_custom_command(TARGET ${target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${TOP_LEVEL_PROJECT_DIR}/deps/GMP/gmp/lib/win${_bits}/libgmp-10.dll ${_out_dir}/ COMMAND ${CMAKE_COMMAND} -E copy ${TOP_LEVEL_PROJECT_DIR}/deps/GMP/gmp/lib/win${_bits}/libgmp-10.dll ${_out_dir}
COMMENT "Copy gmp runtime to build tree" COMMENT "Copy gmp runtime to build tree"
VERBATIM) VERBATIM)
add_custom_command(TARGET ${target} POST_BUILD add_custom_command(TARGET ${target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${TOP_LEVEL_PROJECT_DIR}/deps/MPFR/mpfr/lib/win${_bits}/libmpfr-4.dll ${_out_dir}/ COMMAND ${CMAKE_COMMAND} -E copy ${TOP_LEVEL_PROJECT_DIR}/deps/MPFR/mpfr/lib/win${_bits}/libmpfr-4.dll ${_out_dir}
COMMENT "Copy mpfr runtime to build tree" COMMENT "Copy mpfr runtime to build tree"
VERBATIM) VERBATIM)
endfunction() endfunction()
#find_package(OpenVDB 5.0 COMPONENTS openvdb)
#slic3r_remap_configs(IlmBase::Half RelWithDebInfo Release)
# libslic3r, PrusaSlicer GUI and the PrusaSlicer executable. # libslic3r, PrusaSlicer GUI and the PrusaSlicer executable.
add_subdirectory(src) add_subdirectory(src)

View file

@ -0,0 +1,106 @@
# atomic builtins are required for threading support.
INCLUDE(CheckCXXSourceCompiles)
INCLUDE(CheckLibraryExists)
# Sometimes linking against libatomic is required for atomic ops, if
# the platform doesn't support lock-free atomics.
function(check_working_cxx_atomics varname)
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11")
CHECK_CXX_SOURCE_COMPILES("
#include <atomic>
std::atomic<int> x;
int main() {
return x;
}
" ${varname})
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
endfunction(check_working_cxx_atomics)
function(check_working_cxx_atomics64 varname)
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}")
CHECK_CXX_SOURCE_COMPILES("
#include <atomic>
#include <cstdint>
std::atomic<uint64_t> x (0);
int main() {
uint64_t i = x.load(std::memory_order_relaxed);
return 0;
}
" ${varname})
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
endfunction(check_working_cxx_atomics64)
# This isn't necessary on MSVC, so avoid command-line switch annoyance
# by only running on GCC-like hosts.
if (LLVM_COMPILER_IS_GCC_COMPATIBLE)
# First check if atomics work without the library.
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB)
# If not, check if the library exists, and atomics work with it.
if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC)
if( HAVE_LIBATOMIC )
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB)
if (NOT HAVE_CXX_ATOMICS_WITH_LIB)
message(FATAL_ERROR "Host compiler must support std::atomic!")
endif()
else()
message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.")
endif()
endif()
endif()
# Check for 64 bit atomic operations.
if(MSVC)
set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True)
else()
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB)
endif()
# If not, check if the library exists, and atomics work with it.
if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64)
if(HAVE_CXX_LIBATOMICS64)
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB)
if (NOT HAVE_CXX_ATOMICS64_WITH_LIB)
message(FATAL_ERROR "Host compiler must support 64-bit std::atomic!")
endif()
else()
message(FATAL_ERROR "Host compiler appears to require libatomic for 64-bit operations, but cannot find it.")
endif()
endif()
## TODO: This define is only used for the legacy atomic operations in
## llvm's Atomic.h, which should be replaced. Other code simply
## assumes C++11 <atomic> works.
CHECK_CXX_SOURCE_COMPILES("
#ifdef _MSC_VER
#include <windows.h>
#endif
int main() {
#ifdef _MSC_VER
volatile LONG val = 1;
MemoryBarrier();
InterlockedCompareExchange(&val, 0, 1);
InterlockedIncrement(&val);
InterlockedDecrement(&val);
#else
volatile unsigned long val = 1;
__sync_synchronize();
__sync_val_compare_and_swap(&val, 1, 0);
__sync_add_and_fetch(&val, 1);
__sync_sub_and_fetch(&val, 1);
#endif
return 0;
}
" LLVM_HAS_ATOMICS)
if( NOT LLVM_HAS_ATOMICS )
message(STATUS "Warning: LLVM will be built thread-unsafe because atomic builtins are missing")
endif()

View file

@ -108,6 +108,18 @@ if(POLICY CMP0074)
cmake_policy(SET CMP0074 NEW) cmake_policy(SET CMP0074 NEW)
endif() endif()
if(OpenVDB_FIND_QUIETLY)
set (_quiet "QUIET")
else()
set (_quiet "")
endif()
if(OpenVDB_FIND_REQUIRED)
set (_required "REQUIRED")
else()
set (_required "")
endif()
# Include utility functions for version information # Include utility functions for version information
include(${CMAKE_CURRENT_LIST_DIR}/OpenVDBUtils.cmake) include(${CMAKE_CURRENT_LIST_DIR}/OpenVDBUtils.cmake)
@ -146,7 +158,7 @@ set(_OPENVDB_ROOT_SEARCH_DIR "")
# Additionally try and use pkconfig to find OpenVDB # Additionally try and use pkconfig to find OpenVDB
find_package(PkgConfig) find_package(PkgConfig ${_quiet} )
pkg_check_modules(PC_OpenVDB QUIET OpenVDB) pkg_check_modules(PC_OpenVDB QUIET OpenVDB)
# ------------------------------------------------------------------------ # ------------------------------------------------------------------------
@ -221,21 +233,26 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS})
) )
if (_is_multi) if (_is_multi)
list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE} ${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}) list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE})
if (MSVC OR OpenVDB_${COMPONENT}_LIBRARY_DEBUG)
list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY_DEBUG})
endif ()
list(FIND CMAKE_CONFIGURATION_TYPES "Debug" _has_debug) list(FIND CMAKE_CONFIGURATION_TYPES "Debug" _has_debug)
if(OpenVDB_${COMPONENT}_LIBRARY_RELEASE AND (_has_debug LESS 0 OR OpenVDB_${COMPONENT}_LIBRARY_DEBUG)) if(OpenVDB_${COMPONENT}_LIBRARY_RELEASE AND (NOT MSVC OR _has_debug LESS 0 OR OpenVDB_${COMPONENT}_LIBRARY_DEBUG))
set(OpenVDB_${COMPONENT}_FOUND TRUE) set(OpenVDB_${COMPONENT}_FOUND TRUE)
else() else()
set(OpenVDB_${COMPONENT}_FOUND FALSE) set(OpenVDB_${COMPONENT}_FOUND FALSE)
endif() endif()
set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE})
else () else ()
string(TOUPPER "${CMAKE_BUILD_TYPE}" _BUILD_TYPE) string(TOUPPER "${CMAKE_BUILD_TYPE}" _BUILD_TYPE)
set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_${_BUILD_TYPE}}) set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_${_BUILD_TYPE}})
if (NOT MSVC AND NOT OpenVDB_${COMPONENT}_LIBRARY) if (NOT OpenVDB_${COMPONENT}_LIBRARY)
set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}) set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE})
endif () endif ()
@ -247,6 +264,7 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS})
set(OpenVDB_${COMPONENT}_FOUND FALSE) set(OpenVDB_${COMPONENT}_FOUND FALSE)
endif() endif()
endif () endif ()
endforeach() endforeach()
if(UNIX AND OPENVDB_USE_STATIC_LIBS) if(UNIX AND OPENVDB_USE_STATIC_LIBS)
@ -280,7 +298,7 @@ OPENVDB_ABI_VERSION_FROM_PRINT(
ABI OpenVDB_ABI ABI OpenVDB_ABI
) )
if(NOT OpenVDB_FIND_QUIET) if(NOT OpenVDB_FIND_QUIETLY)
if(NOT OpenVDB_ABI) if(NOT OpenVDB_ABI)
message(WARNING "Unable to determine OpenVDB ABI version from OpenVDB " message(WARNING "Unable to determine OpenVDB ABI version from OpenVDB "
"installation. The library major version \"${OpenVDB_MAJOR_VERSION}\" " "installation. The library major version \"${OpenVDB_MAJOR_VERSION}\" "
@ -298,7 +316,17 @@ endif()
# Add standard dependencies # Add standard dependencies
find_package(IlmBase COMPONENTS Half) macro(just_fail msg)
set(OpenVDB_FOUND FALSE)
if(OpenVDB_FIND_REQUIRED)
message(FATAL_ERROR ${msg})
elseif(NOT OpenVDB_FIND_QUIETLY)
message(WARNING ${msg})
endif()
return()
endmacro()
find_package(IlmBase QUIET COMPONENTS Half)
if(NOT IlmBase_FOUND) if(NOT IlmBase_FOUND)
pkg_check_modules(IlmBase QUIET IlmBase) pkg_check_modules(IlmBase QUIET IlmBase)
endif() endif()
@ -306,20 +334,20 @@ if (IlmBase_FOUND AND NOT TARGET IlmBase::Half)
message(STATUS "Falling back to IlmBase found by pkg-config...") message(STATUS "Falling back to IlmBase found by pkg-config...")
find_library(IlmHalf_LIBRARY NAMES Half) find_library(IlmHalf_LIBRARY NAMES Half)
if(IlmHalf_LIBRARY-NOTFOUND) if(IlmHalf_LIBRARY-NOTFOUND OR NOT IlmBase_INCLUDE_DIRS)
message(FATAL_ERROR "IlmBase::Half can not be found!") just_fail("IlmBase::Half can not be found!")
endif() endif()
add_library(IlmBase::Half UNKNOWN IMPORTED) add_library(IlmBase::Half UNKNOWN IMPORTED)
set_target_properties(IlmBase::Half PROPERTIES set_target_properties(IlmBase::Half PROPERTIES
IMPORTED_LOCATION "${IlmHalf_LIBRARY}" IMPORTED_LOCATION "${IlmHalf_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES ${IlmBase_INCLUDE_DIRS}) INTERFACE_INCLUDE_DIRECTORIES "${IlmBase_INCLUDE_DIRS}")
elseif(NOT IlmBase_FOUND) elseif(NOT IlmBase_FOUND)
message(FATAL_ERROR "IlmBase::Half can not be found!") just_fail("IlmBase::Half can not be found!")
endif() endif()
find_package(TBB REQUIRED COMPONENTS tbb) find_package(TBB ${_quiet} ${_required} COMPONENTS tbb)
find_package(ZLIB REQUIRED) find_package(ZLIB ${_quiet} ${_required})
find_package(Boost REQUIRED COMPONENTS iostreams system) find_package(Boost ${_quiet} ${_required} COMPONENTS iostreams system )
# Use GetPrerequisites to see which libraries this OpenVDB lib has linked to # Use GetPrerequisites to see which libraries this OpenVDB lib has linked to
# which we can query for optional deps. This basically runs ldd/otoll/objdump # which we can query for optional deps. This basically runs ldd/otoll/objdump
@ -380,7 +408,7 @@ unset(_OPENVDB_PREREQUISITE_LIST)
unset(_HAS_DEP) unset(_HAS_DEP)
if(OpenVDB_USES_BLOSC) if(OpenVDB_USES_BLOSC)
find_package(Blosc ) find_package(Blosc QUIET)
if(NOT Blosc_FOUND OR NOT TARGET Blosc::blosc) if(NOT Blosc_FOUND OR NOT TARGET Blosc::blosc)
message(STATUS "find_package could not find Blosc. Using fallback blosc search...") message(STATUS "find_package could not find Blosc. Using fallback blosc search...")
find_path(Blosc_INCLUDE_DIR blosc.h) find_path(Blosc_INCLUDE_DIR blosc.h)
@ -392,25 +420,25 @@ if(OpenVDB_USES_BLOSC)
IMPORTED_LOCATION "${Blosc_LIBRARY}" IMPORTED_LOCATION "${Blosc_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES ${Blosc_INCLUDE_DIR}) INTERFACE_INCLUDE_DIRECTORIES ${Blosc_INCLUDE_DIR})
elseif() elseif()
message(FATAL_ERROR "Blosc library can not be found!") just_fail("Blosc library can not be found!")
endif() endif()
endif() endif()
endif() endif()
if(OpenVDB_USES_LOG4CPLUS) if(OpenVDB_USES_LOG4CPLUS)
find_package(Log4cplus REQUIRED) find_package(Log4cplus ${_quiet} ${_required})
endif() endif()
if(OpenVDB_USES_ILM) if(OpenVDB_USES_ILM)
find_package(IlmBase REQUIRED) find_package(IlmBase ${_quiet} ${_required})
endif() endif()
if(OpenVDB_USES_EXR) if(OpenVDB_USES_EXR)
find_package(OpenEXR REQUIRED) find_package(OpenEXR ${_quiet} ${_required})
endif() endif()
if(UNIX) if(UNIX)
find_package(Threads REQUIRED) find_package(Threads ${_quiet} ${_required})
endif() endif()
# Set deps. Note that the order here is important. If we're building against # Set deps. Note that the order here is important. If we're building against
@ -493,24 +521,33 @@ list(REMOVE_DUPLICATES OpenVDB_LIBRARY_DIRS)
foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS})
if(NOT TARGET OpenVDB::${COMPONENT}) if(NOT TARGET OpenVDB::${COMPONENT})
if (${COMPONENT} STREQUAL openvdb)
include (${CMAKE_CURRENT_LIST_DIR}/CheckAtomic.cmake)
set(_LINK_LIBS ${_OPENVDB_VISIBLE_DEPENDENCIES} ${CMAKE_REQUIRED_LIBRARIES})
else ()
set(_LINK_LIBS _OPENVDB_VISIBLE_DEPENDENCIES)
endif ()
add_library(OpenVDB::${COMPONENT} UNKNOWN IMPORTED) add_library(OpenVDB::${COMPONENT} UNKNOWN IMPORTED)
set_target_properties(OpenVDB::${COMPONENT} PROPERTIES set_target_properties(OpenVDB::${COMPONENT} PROPERTIES
INTERFACE_COMPILE_OPTIONS "${OpenVDB_DEFINITIONS}" INTERFACE_COMPILE_OPTIONS "${OpenVDB_DEFINITIONS}"
INTERFACE_INCLUDE_DIRECTORIES "${OpenVDB_INCLUDE_DIR}" INTERFACE_INCLUDE_DIRECTORIES "${OpenVDB_INCLUDE_DIR}"
IMPORTED_LINK_DEPENDENT_LIBRARIES "${_OPENVDB_HIDDEN_DEPENDENCIES}" # non visible deps IMPORTED_LINK_DEPENDENT_LIBRARIES "${_OPENVDB_HIDDEN_DEPENDENCIES}" # non visible deps
INTERFACE_LINK_LIBRARIES "${_OPENVDB_VISIBLE_DEPENDENCIES}" # visible deps (headers) INTERFACE_LINK_LIBRARIES "${_LINK_LIBS}" # visible deps (headers)
INTERFACE_COMPILE_FEATURES cxx_std_11 INTERFACE_COMPILE_FEATURES cxx_std_11
IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}"
) )
if (_is_multi) if (_is_multi)
set_target_properties(OpenVDB::${COMPONENT} PROPERTIES set_target_properties(OpenVDB::${COMPONENT} PROPERTIES
IMPORTED_LOCATION_RELEASE "${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}" IMPORTED_LOCATION_RELEASE "${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}"
)
if (MSVC OR OpenVDB_${COMPONENT}_LIBRARY_DEBUG)
set_target_properties(OpenVDB::${COMPONENT} PROPERTIES
IMPORTED_LOCATION_DEBUG "${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}" IMPORTED_LOCATION_DEBUG "${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}"
) )
else () endif ()
set_target_properties(OpenVDB::${COMPONENT} PROPERTIES
IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}"
)
endif () endif ()
if (OPENVDB_USE_STATIC_LIBS) if (OPENVDB_USE_STATIC_LIBS)
@ -521,7 +558,7 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS})
endif() endif()
endforeach() endforeach()
if(OpenVDB_FOUND AND NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY) if(OpenVDB_FOUND AND NOT OpenVDB_FIND_QUIETLY)
message(STATUS "OpenVDB libraries: ${OpenVDB_LIBRARIES}") message(STATUS "OpenVDB libraries: ${OpenVDB_LIBRARIES}")
endif() endif()

View file

@ -0,0 +1,26 @@
set(_q "")
if(${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
set(_q QUIET)
set(_quietly TRUE)
endif()
find_package(${CMAKE_FIND_PACKAGE_NAME} ${${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION} CONFIG ${_q})
if (NOT ${CMAKE_FIND_PACKAGE_NAME}_FOUND)
# Fall-back solution to find the Cereal serialization library header file
include(CheckIncludeFileCXX)
add_library(cereal INTERFACE)
target_include_directories(cereal INTERFACE include)
if (_quietly)
set(CMAKE_REQUIRED_QUIET ON)
endif()
CHECK_INCLUDE_FILE_CXX("cereal/cereal.hpp" HAVE_CEREAL_H)
if (NOT HAVE_CEREAL_H)
if (${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
message(FATAL_ERROR "Cereal library not found. Please install the dependency.")
elseif(NOT _quietly)
message(WARNING "Cereal library not found.")
endif()
endif ()
endif()

View file

@ -125,7 +125,9 @@ function(OPENVDB_ABI_VERSION_FROM_PRINT OPENVDB_PRINT)
cmake_parse_arguments(_VDB "QUIET" "ABI" "" ${ARGN}) cmake_parse_arguments(_VDB "QUIET" "ABI" "" ${ARGN})
if(NOT EXISTS ${OPENVDB_PRINT}) if(NOT EXISTS ${OPENVDB_PRINT})
if(NOT OpenVDB_FIND_QUIETLY)
message(WARNING "vdb_print not found! ${OPENVDB_PRINT}") message(WARNING "vdb_print not found! ${OPENVDB_PRINT}")
endif()
return() return()
endif() endif()
@ -148,7 +150,9 @@ function(OPENVDB_ABI_VERSION_FROM_PRINT OPENVDB_PRINT)
endif() endif()
if(${_VDB_PRINT_RETURN_STATUS}) if(${_VDB_PRINT_RETURN_STATUS})
if(NOT OpenVDB_FIND_QUIETLY)
message(WARNING "vdb_print returned with status ${_VDB_PRINT_RETURN_STATUS}") message(WARNING "vdb_print returned with status ${_VDB_PRINT_RETURN_STATUS}")
endif()
return() return()
endif() endif()

3
deps/GMP/GMP.cmake vendored
View file

@ -18,7 +18,8 @@ if (MSVC)
else () else ()
ExternalProject_Add(dep_GMP ExternalProject_Add(dep_GMP
URL https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2 # URL https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2
URL https://gmplib.org/download/gmp/gmp-6.2.0.tar.lz
BUILD_IN_SOURCE ON BUILD_IN_SOURCE ON
CONFIGURE_COMMAND ./configure --enable-shared=no --enable-cxx=yes --enable-static=yes "--prefix=${DESTDIR}/usr/local" --with-pic CONFIGURE_COMMAND ./configure --enable-shared=no --enable-cxx=yes --enable-static=yes "--prefix=${DESTDIR}/usr/local" --with-pic
BUILD_COMMAND make -j BUILD_COMMAND make -j

View file

@ -1,24 +1,25 @@
From dbe038fce8a15ddc9a5c83ec5156d7bc9e178015 Mon Sep 17 00:00:00 2001 From d359098d9989ac7dbd149611d6ac941529fb4157 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com> From: tamasmeszaros <meszaros.q@gmail.com>
Date: Wed, 16 Oct 2019 17:42:50 +0200 Date: Thu, 23 Jan 2020 17:17:36 +0100
Subject: [PATCH] Build fixes for PrusaSlicer integration Subject: [PATCH] openvdb-mods
Signed-off-by: tamasmeszaros <meszaros.q@gmail.com>
--- ---
CMakeLists.txt | 3 - CMakeLists.txt | 3 -
cmake/FindBlosc.cmake | 218 --------------- cmake/CheckAtomic.cmake | 106 ++++++
cmake/FindBlosc.cmake | 218 ------------
cmake/FindCppUnit.cmake | 4 +- cmake/FindCppUnit.cmake | 4 +-
cmake/FindIlmBase.cmake | 337 ---------------------- cmake/FindIlmBase.cmake | 337 ------------------
cmake/FindOpenEXR.cmake | 329 ---------------------- cmake/FindOpenEXR.cmake | 329 ------------------
cmake/FindOpenVDB.cmake | 19 +- cmake/FindOpenVDB.cmake | 19 +-
cmake/FindTBB.cmake | 605 ++++++++++++++++++++-------------------- cmake/FindTBB.cmake | 599 ++++++++++++++++----------------
openvdb/CMakeLists.txt | 13 +- openvdb/CMakeLists.txt | 16 +-
openvdb/Grid.cc | 3 + openvdb/Grid.cc | 3 +
openvdb/PlatformConfig.h | 9 +- openvdb/PlatformConfig.h | 9 +-
openvdb/cmd/CMakeLists.txt | 4 +- openvdb/cmd/CMakeLists.txt | 4 +-
openvdb/unittest/CMakeLists.txt | 3 +- openvdb/unittest/CMakeLists.txt | 3 +-
openvdb/unittest/TestFile.cc | 2 +- openvdb/unittest/TestFile.cc | 2 +-
13 files changed, 336 insertions(+), 1213 deletions(-) 14 files changed, 442 insertions(+), 1210 deletions(-)
create mode 100644 cmake/CheckAtomic.cmake
delete mode 100644 cmake/FindBlosc.cmake delete mode 100644 cmake/FindBlosc.cmake
delete mode 100644 cmake/FindIlmBase.cmake delete mode 100644 cmake/FindIlmBase.cmake
delete mode 100644 cmake/FindOpenEXR.cmake delete mode 100644 cmake/FindOpenEXR.cmake
@ -40,6 +41,119 @@ index 580b353..6d364c1 100644
cmake/FindOpenVDB.cmake cmake/FindOpenVDB.cmake
cmake/FindTBB.cmake cmake/FindTBB.cmake
cmake/OpenVDBGLFW3Setup.cmake cmake/OpenVDBGLFW3Setup.cmake
diff --git a/cmake/CheckAtomic.cmake b/cmake/CheckAtomic.cmake
new file mode 100644
index 0000000..c045e30
--- /dev/null
+++ b/cmake/CheckAtomic.cmake
@@ -0,0 +1,106 @@
+# atomic builtins are required for threading support.
+
+INCLUDE(CheckCXXSourceCompiles)
+INCLUDE(CheckLibraryExists)
+
+# Sometimes linking against libatomic is required for atomic ops, if
+# the platform doesn't support lock-free atomics.
+
+function(check_working_cxx_atomics varname)
+ set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
+ set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11")
+ CHECK_CXX_SOURCE_COMPILES("
+#include <atomic>
+std::atomic<int> x;
+int main() {
+ return x;
+}
+" ${varname})
+ set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
+endfunction(check_working_cxx_atomics)
+
+function(check_working_cxx_atomics64 varname)
+ set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
+ set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}")
+ CHECK_CXX_SOURCE_COMPILES("
+#include <atomic>
+#include <cstdint>
+std::atomic<uint64_t> x (0);
+int main() {
+ uint64_t i = x.load(std::memory_order_relaxed);
+ return 0;
+}
+" ${varname})
+ set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
+endfunction(check_working_cxx_atomics64)
+
+
+# This isn't necessary on MSVC, so avoid command-line switch annoyance
+# by only running on GCC-like hosts.
+if (LLVM_COMPILER_IS_GCC_COMPATIBLE)
+ # First check if atomics work without the library.
+ check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB)
+ # If not, check if the library exists, and atomics work with it.
+ if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
+ check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC)
+ if( HAVE_LIBATOMIC )
+ list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
+ check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB)
+ if (NOT HAVE_CXX_ATOMICS_WITH_LIB)
+ message(FATAL_ERROR "Host compiler must support std::atomic!")
+ endif()
+ else()
+ message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.")
+ endif()
+ endif()
+endif()
+
+# Check for 64 bit atomic operations.
+if(MSVC)
+ set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True)
+else()
+ check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB)
+endif()
+
+# If not, check if the library exists, and atomics work with it.
+if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
+ check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64)
+ if(HAVE_CXX_LIBATOMICS64)
+ list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
+ check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB)
+ if (NOT HAVE_CXX_ATOMICS64_WITH_LIB)
+ message(FATAL_ERROR "Host compiler must support 64-bit std::atomic!")
+ endif()
+ else()
+ message(FATAL_ERROR "Host compiler appears to require libatomic for 64-bit operations, but cannot find it.")
+ endif()
+endif()
+
+## TODO: This define is only used for the legacy atomic operations in
+## llvm's Atomic.h, which should be replaced. Other code simply
+## assumes C++11 <atomic> works.
+CHECK_CXX_SOURCE_COMPILES("
+#ifdef _MSC_VER
+#include <windows.h>
+#endif
+int main() {
+#ifdef _MSC_VER
+ volatile LONG val = 1;
+ MemoryBarrier();
+ InterlockedCompareExchange(&val, 0, 1);
+ InterlockedIncrement(&val);
+ InterlockedDecrement(&val);
+#else
+ volatile unsigned long val = 1;
+ __sync_synchronize();
+ __sync_val_compare_and_swap(&val, 1, 0);
+ __sync_add_and_fetch(&val, 1);
+ __sync_sub_and_fetch(&val, 1);
+#endif
+ return 0;
+ }
+" LLVM_HAS_ATOMICS)
+
+if( NOT LLVM_HAS_ATOMICS )
+ message(STATUS "Warning: LLVM will be built thread-unsafe because atomic builtins are missing")
+endif()
\ No newline at end of file
diff --git a/cmake/FindBlosc.cmake b/cmake/FindBlosc.cmake diff --git a/cmake/FindBlosc.cmake b/cmake/FindBlosc.cmake
deleted file mode 100644 deleted file mode 100644
index 5aacfdd..0000000 index 5aacfdd..0000000
@ -965,7 +1079,7 @@ index 339c1a2..0000000
- message(FATAL_ERROR "Unable to find OpenEXR") - message(FATAL_ERROR "Unable to find OpenEXR")
-endif() -endif()
diff --git a/cmake/FindOpenVDB.cmake b/cmake/FindOpenVDB.cmake diff --git a/cmake/FindOpenVDB.cmake b/cmake/FindOpenVDB.cmake
index 63a2eda..6211071 100644 index 63a2eda..d9f6d07 100644
--- a/cmake/FindOpenVDB.cmake --- a/cmake/FindOpenVDB.cmake
+++ b/cmake/FindOpenVDB.cmake +++ b/cmake/FindOpenVDB.cmake
@@ -244,7 +244,7 @@ set(OpenVDB_LIB_COMPONENTS "") @@ -244,7 +244,7 @@ set(OpenVDB_LIB_COMPONENTS "")
@ -1012,7 +1126,7 @@ index 63a2eda..6211071 100644
endforeach() endforeach()
diff --git a/cmake/FindTBB.cmake b/cmake/FindTBB.cmake diff --git a/cmake/FindTBB.cmake b/cmake/FindTBB.cmake
index bdf9c81..c6bdec9 100644 index bdf9c81..06093a4 100644
--- a/cmake/FindTBB.cmake --- a/cmake/FindTBB.cmake
+++ b/cmake/FindTBB.cmake +++ b/cmake/FindTBB.cmake
@@ -1,333 +1,332 @@ @@ -1,333 +1,332 @@
@ -1022,35 +1136,21 @@ index bdf9c81..c6bdec9 100644
-# All rights reserved. This software is distributed under the -# All rights reserved. This software is distributed under the
-# Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) -# Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+# Copyright (c) 2015 Justus Calvin +# Copyright (c) 2015 Justus Calvin
+# #
-# Redistributions of source code must retain the above copyright
-# and license notice and the following restrictions and disclaimer.
+# Permission is hereby granted, free of charge, to any person obtaining a copy +# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal +# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights +# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is +# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions: +# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
#
-# Redistributions of source code must retain the above copyright
-# and license notice and the following restrictions and disclaimer.
+# FindTBB
+# -------
# #
-# * Neither the name of DreamWorks Animation nor the names of -# * Neither the name of DreamWorks Animation nor the names of
-# its contributors may be used to endorse or promote products derived -# its contributors may be used to endorse or promote products derived
-# from this software without specific prior written permission. -# from this software without specific prior written permission.
+# Find TBB include directories and libraries. +# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
# #
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@ -1065,7 +1165,14 @@ index bdf9c81..c6bdec9 100644
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-# IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE -# IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
-# LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. -# LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+# Usage: +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
# #
-#[=======================================================================[.rst: -#[=======================================================================[.rst:
- -
@ -1142,6 +1249,13 @@ index bdf9c81..c6bdec9 100644
-if(POLICY CMP0057) -if(POLICY CMP0057)
- cmake_policy(SET CMP0057 NEW) - cmake_policy(SET CMP0057 NEW)
-endif() -endif()
+# FindTBB
+# -------
+#
+# Find TBB include directories and libraries.
+#
+# Usage:
+#
+# find_package(TBB [major[.minor]] [EXACT] +# find_package(TBB [major[.minor]] [EXACT]
+# [QUIET] [REQUIRED] +# [QUIET] [REQUIRED]
+# [[COMPONENTS] [components...]] +# [[COMPONENTS] [components...]]
@ -1244,12 +1358,10 @@ index bdf9c81..c6bdec9 100644
- set(TBB_FIND_COMPONENTS ${_TBB_COMPONENT_LIST}) - set(TBB_FIND_COMPONENTS ${_TBB_COMPONENT_LIST})
-endif() -endif()
+include(FindPackageHandleStandardArgs) +include(FindPackageHandleStandardArgs)
+
+find_package(Threads QUIET REQUIRED)
-# Append TBB_ROOT or $ENV{TBB_ROOT} if set (prioritize the direct cmake var) -# Append TBB_ROOT or $ENV{TBB_ROOT} if set (prioritize the direct cmake var)
-set(_TBB_ROOT_SEARCH_DIR "") -set(_TBB_ROOT_SEARCH_DIR "")
+if(NOT TBB_FOUND) +find_package(Threads QUIET REQUIRED)
-if(TBB_ROOT) -if(TBB_ROOT)
- list(APPEND _TBB_ROOT_SEARCH_DIR ${TBB_ROOT}) - list(APPEND _TBB_ROOT_SEARCH_DIR ${TBB_ROOT})
@ -1257,41 +1369,9 @@ index bdf9c81..c6bdec9 100644
- set(_ENV_TBB_ROOT $ENV{TBB_ROOT}) - set(_ENV_TBB_ROOT $ENV{TBB_ROOT})
- if(_ENV_TBB_ROOT) - if(_ENV_TBB_ROOT)
- list(APPEND _TBB_ROOT_SEARCH_DIR ${_ENV_TBB_ROOT}) - list(APPEND _TBB_ROOT_SEARCH_DIR ${_ENV_TBB_ROOT})
+ ################################## - endif()
+ # Check the build type
+ ##################################
+
+ if(NOT DEFINED TBB_USE_DEBUG_BUILD)
+ if(CMAKE_BUILD_TYPE MATCHES "(Debug|DEBUG|debug)")
+ set(TBB_BUILD_TYPE DEBUG)
+ else()
+ set(TBB_BUILD_TYPE RELEASE)
+ endif()
+ elseif(TBB_USE_DEBUG_BUILD)
+ set(TBB_BUILD_TYPE DEBUG)
+ else()
+ set(TBB_BUILD_TYPE RELEASE)
endif()
-endif() -endif()
+ +if(NOT TBB_FOUND)
+ ##################################
+ # Set the TBB search directories
+ ##################################
+
+ # Define search paths based on user input and environment variables
+ set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT})
+
+ # Define the search directories based on the current platform
+ if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB"
+ "C:/Program Files (x86)/Intel/TBB")
+
+ # Set the target architecture
+ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(TBB_ARCHITECTURE "intel64")
+ else()
+ set(TBB_ARCHITECTURE "ia32")
+ endif()
-# Additionally try and use pkconfig to find Tbb -# Additionally try and use pkconfig to find Tbb
- -
@ -1339,6 +1419,57 @@ index bdf9c81..c6bdec9 100644
- -
- set(Tbb_VERSION ${Tbb_VERSION_MAJOR}.${Tbb_VERSION_MINOR}) - set(Tbb_VERSION ${Tbb_VERSION_MAJOR}.${Tbb_VERSION_MINOR})
-endif() -endif()
+ ##################################
+ # Check the build type
+ ##################################
+
+ if(NOT DEFINED TBB_USE_DEBUG_BUILD)
+ if(CMAKE_BUILD_TYPE MATCHES "(Debug|DEBUG|debug)")
+ set(TBB_BUILD_TYPE DEBUG)
+ else()
+ set(TBB_BUILD_TYPE RELEASE)
+ endif()
+ elseif(TBB_USE_DEBUG_BUILD)
+ set(TBB_BUILD_TYPE DEBUG)
+ else()
+ set(TBB_BUILD_TYPE RELEASE)
+ endif()
-# ------------------------------------------------------------------------
-# Search for TBB lib DIR
-# ------------------------------------------------------------------------
+ ##################################
+ # Set the TBB search directories
+ ##################################
-set(_TBB_LIBRARYDIR_SEARCH_DIRS "")
+ # Define search paths based on user input and environment variables
+ set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT})
-# Append to _TBB_LIBRARYDIR_SEARCH_DIRS in priority order
+ # Define the search directories based on the current platform
+ if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB"
+ "C:/Program Files (x86)/Intel/TBB")
-set(_TBB_LIBRARYDIR_SEARCH_DIRS "")
-list(APPEND _TBB_LIBRARYDIR_SEARCH_DIRS
- ${TBB_LIBRARYDIR}
- ${_TBB_ROOT_SEARCH_DIR}
- ${PC_Tbb_LIBRARY_DIRS}
- ${SYSTEM_LIBRARY_PATHS}
-)
+ # Set the target architecture
+ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(TBB_ARCHITECTURE "intel64")
+ else()
+ set(TBB_ARCHITECTURE "ia32")
+ endif()
-set(TBB_PATH_SUFFIXES
- lib64
- lib
-)
+ # Set the TBB search library path search suffix based on the version of VC + # Set the TBB search library path search suffix based on the version of VC
+ if(WINDOWS_STORE) + if(WINDOWS_STORE)
+ set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11_ui") + set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11_ui")
@ -1352,104 +1483,16 @@ index bdf9c81..c6bdec9 100644
+ set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10") + set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10")
+ endif() + endif()
-# ------------------------------------------------------------------------ -# platform branching
-# Search for TBB lib DIR
-# ------------------------------------------------------------------------
+ # Add the library path search suffix for the VC independent version of TBB + # Add the library path search suffix for the VC independent version of TBB
+ list(APPEND TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc_mt") + list(APPEND TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc_mt")
+
+ elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+ # OS X
+ set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
+
+ # TODO: Check to see which C++ library is being used by the compiler.
+ if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0)
+ # The default C++ library on OS X 10.9 and later is libc++
+ set(TBB_LIB_PATH_SUFFIX "lib/libc++" "lib")
+ else()
+ set(TBB_LIB_PATH_SUFFIX "lib")
+ endif()
+ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ # Linux
+ set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
+
+ # TODO: Check compiler version to see the suffix should be <arch>/gcc4.1 or
+ # <arch>/gcc4.1. For now, assume that the compiler is more recent than
+ # gcc 4.4.x or later.
+ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4")
+ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$")
+ set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4")
+ endif()
+ endif()
+
+ ##################################
+ # Find the TBB include dir
+ ##################################
+
+ find_path(TBB_INCLUDE_DIRS tbb/tbb.h
+ HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR}
+ PATHS ${TBB_DEFAULT_SEARCH_DIR}
+ PATH_SUFFIXES include)
+
+ ##################################
+ # Set version strings
+ ##################################
+
+ if(TBB_INCLUDE_DIRS)
+ file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file)
+ string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1"
+ TBB_VERSION_MAJOR "${_tbb_version_file}")
+ string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1"
+ TBB_VERSION_MINOR "${_tbb_version_file}")
+ string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1"
+ TBB_INTERFACE_VERSION "${_tbb_version_file}")
+ set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}")
+ endif()
-set(_TBB_LIBRARYDIR_SEARCH_DIRS "")
+ ##################################
+ # Find TBB components
+ ##################################
-# Append to _TBB_LIBRARYDIR_SEARCH_DIRS in priority order
+ if(TBB_VERSION VERSION_LESS 4.3)
+ set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb)
+ else()
+ set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb)
+ endif()
-set(_TBB_LIBRARYDIR_SEARCH_DIRS "")
-list(APPEND _TBB_LIBRARYDIR_SEARCH_DIRS
- ${TBB_LIBRARYDIR}
- ${_TBB_ROOT_SEARCH_DIR}
- ${PC_Tbb_LIBRARY_DIRS}
- ${SYSTEM_LIBRARY_PATHS}
-)
+ if(TBB_STATIC)
+ set(TBB_STATIC_SUFFIX "_static")
+ endif()
-set(TBB_PATH_SUFFIXES
- lib64
- lib
-)
+ # Find each component
+ foreach(_comp ${TBB_SEARCH_COMPOMPONENTS})
+ if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};")
-# platform branching
+ unset(TBB_${_comp}_LIBRARY_DEBUG CACHE)
+ unset(TBB_${_comp}_LIBRARY_RELEASE CACHE)
-if(UNIX) -if(UNIX)
- list(INSERT TBB_PATH_SUFFIXES 0 lib/x86_64-linux-gnu) - list(INSERT TBB_PATH_SUFFIXES 0 lib/x86_64-linux-gnu)
-endif() -endif()
+ # Search for the libraries + elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+ find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX} + # OS X
+ HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} + set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
+ PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
+ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
-if(APPLE) -if(APPLE)
- if(TBB_FOR_CLANG) - if(TBB_FOR_CLANG)
@ -1471,29 +1514,33 @@ index bdf9c81..c6bdec9 100644
- list(GET GCC_VERSION_COMPONENTS 0 GCC_MAJOR) - list(GET GCC_VERSION_COMPONENTS 0 GCC_MAJOR)
- list(GET GCC_VERSION_COMPONENTS 1 GCC_MINOR) - list(GET GCC_VERSION_COMPONENTS 1 GCC_MINOR)
- list(INSERT TBB_PATH_SUFFIXES 0 lib/intel64/gcc${GCC_MAJOR}.${GCC_MINOR}) - list(INSERT TBB_PATH_SUFFIXES 0 lib/intel64/gcc${GCC_MAJOR}.${GCC_MINOR})
- else() + # TODO: Check to see which C++ library is being used by the compiler.
+ if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0)
+ # The default C++ library on OS X 10.9 and later is libc++
+ set(TBB_LIB_PATH_SUFFIX "lib/libc++" "lib")
else()
- list(INSERT TBB_PATH_SUFFIXES 0 lib/intel64/gcc4.4) - list(INSERT TBB_PATH_SUFFIXES 0 lib/intel64/gcc4.4)
- endif() + set(TBB_LIB_PATH_SUFFIX "lib")
- endif() + endif()
+ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ # Linux
+ set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
+
+ # TODO: Check compiler version to see the suffix should be <arch>/gcc4.1 or
+ # <arch>/gcc4.1. For now, assume that the compiler is more recent than
+ # gcc 4.4.x or later.
+ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4")
+ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$")
+ set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4")
endif()
endif()
-endif() -endif()
+ find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}${TBB_STATIC_SUFFIX}_debug -
+ HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
+ PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
+ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
-if(UNIX AND TBB_USE_STATIC_LIBS) -if(UNIX AND TBB_USE_STATIC_LIBS)
- set(_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) - set(_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
- set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") - set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
-endif() -endif()
+ if(TBB_${_comp}_LIBRARY_DEBUG)
+ list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}")
+ endif()
+ if(TBB_${_comp}_LIBRARY_RELEASE)
+ list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}")
+ endif()
+ if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY)
+ set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}")
+ endif()
-set(Tbb_LIB_COMPONENTS "") -set(Tbb_LIB_COMPONENTS "")
- -
@ -1516,39 +1563,44 @@ index bdf9c81..c6bdec9 100644
- # Extract the directory and apply the matched text (in brackets) - # Extract the directory and apply the matched text (in brackets)
- get_filename_component(Tbb_${COMPONENT}_DIR "${Tbb_${COMPONENT}_LIBRARY}" DIRECTORY) - get_filename_component(Tbb_${COMPONENT}_DIR "${Tbb_${COMPONENT}_LIBRARY}" DIRECTORY)
- set(Tbb_${COMPONENT}_LIBRARY "${Tbb_${COMPONENT}_DIR}/${CMAKE_MATCH_1}") - set(Tbb_${COMPONENT}_LIBRARY "${Tbb_${COMPONENT}_DIR}/${CMAKE_MATCH_1}")
+ if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}")
+ set(TBB_${_comp}_FOUND TRUE)
+ else()
+ set(TBB_${_comp}_FOUND FALSE)
endif()
+
+ # Mark internal variables as advanced
+ mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE)
+ mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG)
+ mark_as_advanced(TBB_${_comp}_LIBRARY)
+
endif()
- endif() - endif()
+ endforeach() - endif()
+ ##################################
+ # Find the TBB include dir
+ ##################################
+
+ find_path(TBB_INCLUDE_DIRS tbb/tbb.h
+ HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR}
+ PATHS ${TBB_DEFAULT_SEARCH_DIR}
+ PATH_SUFFIXES include)
+
+ ##################################
+ # Set version strings
+ ##################################
+
+ if(TBB_INCLUDE_DIRS)
+ file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file)
+ string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1"
+ TBB_VERSION_MAJOR "${_tbb_version_file}")
+ string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1"
+ TBB_VERSION_MINOR "${_tbb_version_file}")
+ string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1"
+ TBB_INTERFACE_VERSION "${_tbb_version_file}")
+ set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}")
endif()
- list(APPEND Tbb_LIB_COMPONENTS ${Tbb_${COMPONENT}_LIBRARY}) - list(APPEND Tbb_LIB_COMPONENTS ${Tbb_${COMPONENT}_LIBRARY})
+ ################################## + ##################################
+ # Set compile flags and libraries + # Find TBB components
+ ################################## + ##################################
- if(Tbb_${COMPONENT}_LIBRARY) - if(Tbb_${COMPONENT}_LIBRARY)
- set(TBB_${COMPONENT}_FOUND TRUE) - set(TBB_${COMPONENT}_FOUND TRUE)
- else() + if(TBB_VERSION VERSION_LESS 4.3)
+ set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb)
else()
- set(TBB_${COMPONENT}_FOUND FALSE) - set(TBB_${COMPONENT}_FOUND FALSE)
+ set(TBB_DEFINITIONS_RELEASE "") + set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb)
+ set(TBB_DEFINITIONS_DEBUG "TBB_USE_DEBUG=1")
+
+ if(TBB_LIBRARIES_${TBB_BUILD_TYPE})
+ set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}")
+ endif()
+
+ if(NOT MSVC AND NOT TBB_LIBRARIES)
+ set(TBB_LIBRARIES ${TBB_LIBRARIES_RELEASE})
endif() endif()
-endforeach() -endforeach()
@ -1556,61 +1608,51 @@ index bdf9c81..c6bdec9 100644
- set(CMAKE_FIND_LIBRARY_SUFFIXES ${_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) - set(CMAKE_FIND_LIBRARY_SUFFIXES ${_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
- unset(_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES) - unset(_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES)
-endif() -endif()
+ set(TBB_DEFINITIONS "") + if(TBB_STATIC)
+ if (MSVC AND TBB_STATIC) + set(TBB_STATIC_SUFFIX "_static")
+ set(TBB_DEFINITIONS __TBB_NO_IMPLICIT_LINKAGE)
+ endif ()
+
+ unset (TBB_STATIC_SUFFIX)
+
+ find_package_handle_standard_args(TBB
+ REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES
+ FAIL_MESSAGE "TBB library cannot be found. Consider set TBBROOT environment variable."
+ HANDLE_COMPONENTS
+ VERSION_VAR TBB_VERSION)
+
+ ##################################
+ # Create targets
+ ##################################
+
+ if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND)
+ add_library(TBB::tbb UNKNOWN IMPORTED)
+ set_target_properties(TBB::tbb PROPERTIES
+ INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS}"
+ INTERFACE_LINK_LIBRARIES "Threads::Threads;${CMAKE_DL_LIBS}"
+ INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS}
+ IMPORTED_LOCATION ${TBB_LIBRARIES})
+ if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG)
+ set_target_properties(TBB::tbb PROPERTIES
+ INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS};$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:${TBB_DEFINITIONS_DEBUG}>;$<$<CONFIG:Release>:${TBB_DEFINITIONS_RELEASE}>"
+ IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG}
+ IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_RELEASE}
+ IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE}
+ IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE}
+ )
+ endif()
+ endif() + endif()
-# ------------------------------------------------------------------------ -# ------------------------------------------------------------------------
-# Cache and set TBB_FOUND -# Cache and set TBB_FOUND
-# ------------------------------------------------------------------------ -# ------------------------------------------------------------------------
+ mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES) + # Find each component
+ foreach(_comp ${TBB_SEARCH_COMPOMPONENTS})
+ if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};")
+ +
+ unset(TBB_ARCHITECTURE) + unset(TBB_${_comp}_LIBRARY_DEBUG CACHE)
+ unset(TBB_BUILD_TYPE) + unset(TBB_${_comp}_LIBRARY_RELEASE CACHE)
+ unset(TBB_LIB_PATH_SUFFIX)
+ unset(TBB_DEFAULT_SEARCH_DIR)
+ +
+ if(TBB_DEBUG) + # Search for the libraries
+ message(STATUS " TBB_FOUND = ${TBB_FOUND}") + find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX}
+ message(STATUS " TBB_INCLUDE_DIRS = ${TBB_INCLUDE_DIRS}") + HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
+ message(STATUS " TBB_DEFINITIONS = ${TBB_DEFINITIONS}") + PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
+ message(STATUS " TBB_LIBRARIES = ${TBB_LIBRARIES}") + PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
+ message(STATUS " TBB_DEFINITIONS_DEBUG = ${TBB_DEFINITIONS_DEBUG}") +
+ message(STATUS " TBB_LIBRARIES_DEBUG = ${TBB_LIBRARIES_DEBUG}") + find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}${TBB_STATIC_SUFFIX}_debug
+ message(STATUS " TBB_DEFINITIONS_RELEASE = ${TBB_DEFINITIONS_RELEASE}") + HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
+ message(STATUS " TBB_LIBRARIES_RELEASE = ${TBB_LIBRARIES_RELEASE}") + PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
+ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
+
+ if(TBB_${_comp}_LIBRARY_DEBUG)
+ list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}")
+ endif() + endif()
+ if(TBB_${_comp}_LIBRARY_RELEASE)
+ list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}")
+ endif()
+ if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY)
+ set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}")
+ endif()
+
+ if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}")
+ set(TBB_${_comp}_FOUND TRUE)
+ else()
+ set(TBB_${_comp}_FOUND FALSE)
+ endif()
+
+ # Mark internal variables as advanced
+ mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE)
+ mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG)
+ mark_as_advanced(TBB_${_comp}_LIBRARY)
-include(FindPackageHandleStandardArgs) -include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(TBB -find_package_handle_standard_args(TBB
@ -1646,13 +1688,82 @@ index bdf9c81..c6bdec9 100644
- INTERFACE_COMPILE_OPTIONS "${Tbb_DEFINITIONS}" - INTERFACE_COMPILE_OPTIONS "${Tbb_DEFINITIONS}"
- INTERFACE_INCLUDE_DIRECTORIES "${Tbb_INCLUDE_DIR}" - INTERFACE_INCLUDE_DIRECTORIES "${Tbb_INCLUDE_DIR}"
- ) - )
- endif() endif()
- endforeach() endforeach()
-elseif(TBB_FIND_REQUIRED) -elseif(TBB_FIND_REQUIRED)
- message(FATAL_ERROR "Unable to find TBB") - message(FATAL_ERROR "Unable to find TBB")
+
+ ##################################
+ # Set compile flags and libraries
+ ##################################
+
+ set(TBB_DEFINITIONS_RELEASE "")
+ set(TBB_DEFINITIONS_DEBUG "TBB_USE_DEBUG=1")
+
+ if(TBB_LIBRARIES_${TBB_BUILD_TYPE})
+ set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}")
+ endif()
+
+ if(NOT MSVC AND NOT TBB_LIBRARIES)
+ set(TBB_LIBRARIES ${TBB_LIBRARIES_RELEASE})
+ endif()
+
+ set(TBB_DEFINITIONS "")
+ if (MSVC AND TBB_STATIC)
+ set(TBB_DEFINITIONS __TBB_NO_IMPLICIT_LINKAGE)
+ endif ()
+
+ unset (TBB_STATIC_SUFFIX)
+
+ find_package_handle_standard_args(TBB
+ REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES
+ FAIL_MESSAGE "TBB library cannot be found. Consider set TBBROOT environment variable."
+ HANDLE_COMPONENTS
+ VERSION_VAR TBB_VERSION)
+
+ ##################################
+ # Create targets
+ ##################################
+
+ if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND)
+ add_library(TBB::tbb UNKNOWN IMPORTED)
+ set_target_properties(TBB::tbb PROPERTIES
+ INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS}"
+ INTERFACE_LINK_LIBRARIES "Threads::Threads;${CMAKE_DL_LIBS}"
+ INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS}
+ IMPORTED_LOCATION ${TBB_LIBRARIES})
+ if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG)
+ set_target_properties(TBB::tbb PROPERTIES
+ INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS};$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:${TBB_DEFINITIONS_DEBUG}>;$<$<CONFIG:Release>:${TBB_DEFINITIONS_RELEASE}>"
+ IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG}
+ IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_RELEASE}
+ IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE}
+ IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE}
+ )
+ endif()
+ endif()
+
+ mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES)
+
+ unset(TBB_ARCHITECTURE)
+ unset(TBB_BUILD_TYPE)
+ unset(TBB_LIB_PATH_SUFFIX)
+ unset(TBB_DEFAULT_SEARCH_DIR)
+
+ if(TBB_DEBUG)
+ message(STATUS " TBB_FOUND = ${TBB_FOUND}")
+ message(STATUS " TBB_INCLUDE_DIRS = ${TBB_INCLUDE_DIRS}")
+ message(STATUS " TBB_DEFINITIONS = ${TBB_DEFINITIONS}")
+ message(STATUS " TBB_LIBRARIES = ${TBB_LIBRARIES}")
+ message(STATUS " TBB_DEFINITIONS_DEBUG = ${TBB_DEFINITIONS_DEBUG}")
+ message(STATUS " TBB_LIBRARIES_DEBUG = ${TBB_LIBRARIES_DEBUG}")
+ message(STATUS " TBB_DEFINITIONS_RELEASE = ${TBB_DEFINITIONS_RELEASE}")
+ message(STATUS " TBB_LIBRARIES_RELEASE = ${TBB_LIBRARIES_RELEASE}")
+ endif()
+
endif() endif()
diff --git a/openvdb/CMakeLists.txt b/openvdb/CMakeLists.txt diff --git a/openvdb/CMakeLists.txt b/openvdb/CMakeLists.txt
index 89301bd..df27aae 100644 index 89301bd..6a3c90c 100644
--- a/openvdb/CMakeLists.txt --- a/openvdb/CMakeLists.txt
+++ b/openvdb/CMakeLists.txt +++ b/openvdb/CMakeLists.txt
@@ -78,7 +78,7 @@ else() @@ -78,7 +78,7 @@ else()
@ -1664,7 +1775,21 @@ index 89301bd..df27aae 100644
message(DEPRECATION "Support for TBB versions < ${FUTURE_MINIMUM_TBB_VERSION} " message(DEPRECATION "Support for TBB versions < ${FUTURE_MINIMUM_TBB_VERSION} "
"is deprecated and will be removed.") "is deprecated and will be removed.")
endif() endif()
@@ -185,11 +185,6 @@ if(WIN32) @@ -129,10 +129,13 @@ endif()
# include paths from shared installs (including houdini) may pull in the wrong
# headers
+include (CheckAtomic)
+
set(OPENVDB_CORE_DEPENDENT_LIBS
Boost::iostreams
Boost::system
IlmBase::Half
+ ${CMAKE_REQUIRED_LIBRARIES}
)
if(USE_EXR)
@@ -185,11 +188,6 @@ if(WIN32)
endif() endif()
endif() endif()
@ -1676,7 +1801,7 @@ index 89301bd..df27aae 100644
##### Core library configuration ##### Core library configuration
set(OPENVDB_LIBRARY_SOURCE_FILES set(OPENVDB_LIBRARY_SOURCE_FILES
@@ -374,10 +369,16 @@ set(OPENVDB_LIBRARY_UTIL_INCLUDE_FILES @@ -374,10 +372,16 @@ set(OPENVDB_LIBRARY_UTIL_INCLUDE_FILES
if(OPENVDB_CORE_SHARED) if(OPENVDB_CORE_SHARED)
add_library(openvdb_shared SHARED ${OPENVDB_LIBRARY_SOURCE_FILES}) add_library(openvdb_shared SHARED ${OPENVDB_LIBRARY_SOURCE_FILES})
@ -1779,5 +1904,5 @@ index df51830..0ab0c12 100644
/// @todo This changes the compressor setting globally. /// @todo This changes the compressor setting globally.
if (blosc_set_compressor(compname) < 0) continue; if (blosc_set_compressor(compname) < 0) continue;
-- --
2.16.2.windows.1 2.17.1

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="edit_x5F_Gcode">
<g>
<path fill="#ED6B21" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
</g>
<g>
<path fill="#ED6B21" d="M7.97,7.47h2.65v2.05c0,1.73-0.82,2.48-2.69,2.48S5.3,11.25,5.3,9.55V6.39c0-1.61,0.73-2.36,2.63-2.36
s2.69,0.67,2.69,2.36H9.21c0-0.74-0.18-1.11-1.28-1.11c-1.02,0-1.22,0.46-1.22,1.18v3.09c0,0.75,0.19,1.18,1.22,1.18
c1.02,0,1.38-0.43,1.38-1.21V8.75H7.97V7.47z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 808 B

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="error_tick">
<path fill="#ED6B21" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
<path fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-width="2" d="M8 4 L8 9" />
<circle fill="#ED6B21" cx="8" cy="12" r="1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 640 B

View file

@ -7,9 +7,9 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
id="svg8" id="svg8"
version="1.1" version="1.1"
viewBox="0 0 210 297" viewBox="0 0 3.7041666 3.7041667"
height="297mm" height="3.7041667mm"
width="210mm"> width="3.7041667mm">
<defs <defs
id="defs2"> id="defs2">
<clipPath <clipPath
@ -19,6 +19,20 @@
d="M 0,800 H 800 V 0 H 0 Z" d="M 0,800 H 800 V 0 H 0 Z"
id="path3731" /> id="path3731" />
</clipPath> </clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath4697">
<path
d="M 0,800 H 800 V 0 H 0 Z"
id="path4695" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath4697-9">
<path
d="M 0,800 H 800 V 0 H 0 Z"
id="path4695-2" />
</clipPath>
</defs> </defs>
<metadata <metadata
id="metadata5"> id="metadata5">
@ -33,111 +47,120 @@
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
<g <g
transform="translate(294.04601,-97.736694)"
id="layer1"> id="layer1">
<g <g
id="g3721" transform="translate(-265.33929,12.851203)"
transform="matrix(0.35277777,0,0,-0.35277777,-27.74952,290.88146)"> id="layer1-0">
<g
transform="matrix(1.31769,0,0,1.31769,-167.28747,-111.35623)"
id="layer1-3">
<g
id="g4685"
transform="matrix(0.00352778,0,0,-0.00352778,105.26858,151.76571)">
<path <path
d="M 381.663,302.607 H 558.791 V 65.846 H 381.663 Z" d="M 381.663,302.607 H 558.791 V 65.846 H 381.663 Z"
style="fill:#d8d8db;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#d8d8db;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3723" /> id="path4687" />
<path <path
d="m 470.227,302.607 h 95.411 V 65.846 h -95.411 z" d="m 470.227,302.607 h 95.411 V 65.846 h -95.411 z"
style="fill:#f7f7f8;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#f7f7f8;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3725" /> id="path4689" />
<g <g
id="g3727"> id="g4691">
<g <g
id="g3729" id="g4693"
clip-path="url(#clipPath3733)"> clip-path="url(#clipPath4697-9)">
<g <g
id="g3735" id="g4699"
transform="translate(380.7793,225.1963)"> transform="translate(380.7793,225.1963)">
<path <path
d="m 0,0 h 174.037 c 39.032,0 70.675,31.643 70.675,70.675 v 372.92 c 0,39.033 -31.643,70.675 -70.675,70.675 H 0 c -39.033,0 -70.675,-31.642 -70.675,-70.675 V 70.675 C -70.675,31.643 -39.033,0 0,0" d="m 0,0 h 174.037 c 39.032,0 70.675,31.643 70.675,70.675 v 372.92 c 0,39.033 -31.643,70.675 -70.675,70.675 H 0 c -39.033,0 -70.675,-31.642 -70.675,-70.675 V 70.675 C -70.675,31.643 -39.033,0 0,0"
style="fill:#e96700;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#e96700;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3737" /> id="path4701" />
</g> </g>
<g <g
id="g3739" id="g4703"
transform="translate(377.0244,168.7666)"> transform="translate(377.0244,168.7666)">
<path <path
d="m 0,0 h -237.865 c -8.782,0 -15.902,7.12 -15.902,15.902 v 365.743 c 0,8.782 7.12,15.901 15.902,15.901 H 28.933 c 8.782,0 15.902,-7.119 15.902,-15.901 V 56.43 C 38.907,48.507 9.055,8.503 0,0" d="m 0,0 h -237.865 c -8.782,0 -15.902,7.12 -15.902,15.902 v 365.743 c 0,8.782 7.12,15.901 15.902,15.901 H 28.933 c 8.782,0 15.902,-7.119 15.902,-15.901 V 56.43 C 38.907,48.507 9.055,8.503 0,0"
style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3741" /> id="path4705" />
</g> </g>
<g <g
id="g3743" id="g4707"
transform="translate(421.8594,225.1963)"> transform="translate(421.8594,225.1963)">
<path <path
d="M 0,0 C -7.399,-10.049 -35.78,-47.927 -44.835,-56.43 H -282.7" d="M 0,0 C -7.399,-10.049 -35.78,-47.927 -44.835,-56.43 H -282.7"
style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3745" /> id="path4709" />
</g> </g>
<g <g
id="g3747" id="g4711"
transform="translate(421.8594,225.1963)"> transform="translate(421.8594,225.1963)">
<path <path
d="M 0,0 C -5.876,-7.899 -35.78,-47.927 -44.835,-56.43" d="M 0,0 C -5.876,-7.899 -35.78,-47.927 -44.835,-56.43"
style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3749" /> id="path4713" />
</g> </g>
<g <g
id="g3751" id="g4715"
transform="translate(333.957,198.1406)"> transform="translate(333.957,198.1406)">
<path <path
d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0" d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0"
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3753" /> id="path4717" />
</g> </g>
<g <g
id="g3755" id="g4719"
transform="translate(297.957,198.1406)"> transform="translate(297.957,198.1406)">
<path <path
d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0" d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0"
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3757" /> id="path4721" />
</g> </g>
<g <g
id="g3759" id="g4723"
transform="translate(261.957,198.1406)"> transform="translate(261.957,198.1406)">
<path <path
d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0" d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0"
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3761" /> id="path4725" />
</g> </g>
<g <g
id="g3763" id="g4727"
transform="translate(225.957,198.1406)"> transform="translate(225.957,198.1406)">
<path <path
d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0" d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0"
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3765" /> id="path4729" />
</g> </g>
<g <g
id="g3767" id="g4731"
transform="translate(189.957,198.1406)"> transform="translate(189.957,198.1406)">
<path <path
d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0" d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0"
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3769" /> id="path4733" />
</g> </g>
<g <g
id="g3771" id="g4735"
transform="translate(369.2207,225.1963)"> transform="translate(369.2207,225.1963)">
<path <path
d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 54.22 c 0,2.196 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.78 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0" d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 54.22 c 0,2.196 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.78 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0"
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3773" /> id="path4737" />
</g> </g>
<path <path
d="m 450.019,104.056 h -50.577 v 33.128 h 50.577 z" d="m 450.019,104.056 h -50.577 v 33.128 h 50.577 z"
style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3775" /> id="path4739" />
<path <path
d="m 543.221,104.056 h -50.576 v 33.128 h 50.576 z" d="m 543.221,104.056 h -50.576 v 33.128 h 50.576 z"
style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3777" /> id="path4741" />
</g>
</g>
</g> </g>
</g> </g>
</g> </g>

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Before After
Before After

View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
<g id="hollow">
<g>
<g>
<path fill="#FFFFFF" d="M64,121.87c-0.39,0-0.77-0.11-1.11-0.34l-17.93-11.95c-0.92-0.61-1.17-1.85-0.55-2.77
s1.86-1.17,2.77-0.55L64,117.47l17.03-11.35c0.92-0.61,2.16-0.36,2.77,0.55c0.61,0.92,0.36,2.16-0.55,2.77l-18.14,12.09
C64.77,121.76,64.39,121.87,64,121.87z"/>
</g>
<g>
<path fill="#FFFFFF" d="M90.96,103.89c-0.65,0-1.28-0.31-1.67-0.89c-0.61-0.92-0.36-2.16,0.55-2.77l20.04-13.36V41.13L64,10.53
L18.11,41.13v45.75l20.03,13.36c0.92,0.61,1.17,1.85,0.55,2.77c-0.61,0.92-1.85,1.17-2.77,0.55L15,89.61
c-0.56-0.37-0.89-1-0.89-1.66V40.06c0-0.67,0.33-1.29,0.89-1.66L62.89,6.47c0.67-0.45,1.55-0.45,2.22,0L113,38.39
c0.56,0.37,0.89,1,0.89,1.66v47.89c0,0.67-0.33,1.29-0.89,1.66l-20.93,13.95C91.73,103.79,91.34,103.89,90.96,103.89z"/>
</g>
</g>
<g>
<g>
<path fill="#ED6B21" d="M67.33,23.81c-0.38,0-0.77-0.11-1.11-0.34L64,22l-2.22,1.48c-0.92,0.61-2.16,0.36-2.77-0.55
c-0.61-0.92-0.36-2.16,0.55-2.77l3.33-2.22c0.67-0.45,1.55-0.45,2.22,0l3.33,2.22c0.92,0.61,1.17,1.85,0.55,2.77
C68.61,23.5,67.97,23.81,67.33,23.81z"/>
</g>
<g>
<path fill="#ED6B21" d="M36.09,40.2c-0.65,0-1.28-0.31-1.67-0.89c-0.61-0.92-0.36-2.16,0.55-2.77l5.46-3.64
c0.92-0.61,2.16-0.36,2.77,0.55c0.61,0.92,0.36,2.16-0.55,2.77l-5.46,3.64C36.86,40.09,36.48,40.2,36.09,40.2z M48.38,32.01
c-0.65,0-1.28-0.31-1.67-0.89c-0.61-0.92-0.36-2.16,0.55-2.77l5.46-3.64c0.92-0.61,2.16-0.36,2.77,0.55s0.36,2.16-0.55,2.77
l-5.46,3.64C49.15,31.9,48.77,32.01,48.38,32.01z"/>
</g>
<g>
<path fill="#ED6B21" d="M25.94,50.97c-1.1,0-2-0.9-2-2v-4c0-0.67,0.33-1.29,0.89-1.66l3.33-2.22c0.92-0.62,2.16-0.36,2.77,0.55
s0.36,2.16-0.55,2.77l-2.44,1.62v2.93C27.94,50.07,27.04,50.97,25.94,50.97z"/>
</g>
<g>
<path fill="#ED6B21" d="M25.94,70.29c-1.1,0-2-0.9-2-2v-8.59c0-1.1,0.9-2,2-2s2,0.9,2,2v8.59C27.94,69.4,27.04,70.29,25.94,70.29
z"/>
</g>
<g>
<path fill="#ED6B21" d="M29.26,87.25c-0.38,0-0.77-0.11-1.11-0.34l-3.33-2.22c-0.56-0.37-0.89-1-0.89-1.66v-4c0-1.1,0.9-2,2-2
s2,0.9,2,2v2.93l2.44,1.62c0.92,0.61,1.17,1.85,0.55,2.77C30.54,86.94,29.91,87.25,29.26,87.25z"/>
</g>
<g>
<path fill="#ED6B21" d="M53.84,103.64c-0.38,0-0.77-0.11-1.11-0.34l-5.46-3.64c-0.92-0.61-1.17-1.85-0.55-2.77
c0.61-0.92,1.85-1.17,2.77-0.55l5.46,3.64c0.92,0.61,1.17,1.85,0.55,2.77C55.12,103.32,54.49,103.64,53.84,103.64z M41.55,95.44
c-0.38,0-0.77-0.11-1.11-0.34l-5.46-3.64c-0.92-0.61-1.17-1.85-0.55-2.77c0.61-0.92,1.86-1.17,2.77-0.55l5.46,3.64
c0.92,0.61,1.17,1.85,0.55,2.77C42.83,95.13,42.2,95.44,41.55,95.44z"/>
</g>
<g>
<path fill="#ED6B21" d="M64,110.41c-0.39,0-0.77-0.11-1.11-0.34l-3.33-2.22c-0.92-0.61-1.17-1.85-0.55-2.77
c0.61-0.92,1.85-1.17,2.77-0.55L64,106l2.22-1.48c0.92-0.61,2.16-0.37,2.77,0.55c0.61,0.92,0.36,2.16-0.55,2.77l-3.33,2.22
C64.77,110.29,64.39,110.41,64,110.41z"/>
</g>
<g>
<path fill="#ED6B21" d="M74.16,103.64c-0.65,0-1.28-0.31-1.67-0.89c-0.61-0.92-0.36-2.16,0.55-2.77l5.46-3.64
c0.92-0.61,2.16-0.36,2.77,0.55c0.61,0.92,0.36,2.16-0.55,2.77l-5.46,3.64C74.92,103.53,74.54,103.64,74.16,103.64z M86.45,95.44
c-0.65,0-1.28-0.31-1.67-0.89c-0.61-0.92-0.36-2.16,0.55-2.77l5.46-3.64c0.92-0.61,2.16-0.36,2.77,0.55
c0.61,0.92,0.36,2.16-0.55,2.77l-5.46,3.64C87.21,95.33,86.83,95.44,86.45,95.44z"/>
</g>
<g>
<path fill="#ED6B21" d="M98.74,87.25c-0.65,0-1.28-0.31-1.67-0.89c-0.61-0.92-0.36-2.16,0.55-2.77l2.44-1.62v-2.93
c0-1.1,0.9-2,2-2s2,0.9,2,2v4c0,0.67-0.33,1.29-0.89,1.66l-3.33,2.22C99.5,87.14,99.12,87.25,98.74,87.25z"/>
</g>
<g>
<path fill="#ED6B21" d="M102.06,70.29c-1.1,0-2-0.9-2-2v-8.59c0-1.1,0.9-2,2-2s2,0.9,2,2v8.59
C104.06,69.4,103.17,70.29,102.06,70.29z"/>
</g>
<g>
<path fill="#ED6B21" d="M102.06,50.97c-1.1,0-2-0.9-2-2v-2.93l-2.44-1.62c-0.92-0.61-1.17-1.85-0.55-2.77s1.85-1.17,2.77-0.55
l3.33,2.22c0.56,0.37,0.89,1,0.89,1.66v4C104.06,50.07,103.17,50.97,102.06,50.97z"/>
</g>
<g>
<path fill="#ED6B21" d="M91.91,40.2c-0.38,0-0.77-0.11-1.11-0.34l-5.46-3.64c-0.92-0.61-1.17-1.85-0.55-2.77
c0.61-0.92,1.85-1.17,2.77-0.55l5.46,3.64c0.92,0.61,1.17,1.85,0.55,2.77C93.19,39.89,92.55,40.2,91.91,40.2z M79.62,32.01
c-0.38,0-0.77-0.11-1.11-0.34l-5.46-3.64c-0.92-0.61-1.17-1.85-0.55-2.77s1.85-1.17,2.77-0.55l5.46,3.64
c0.92,0.61,1.17,1.85,0.55,2.77C80.9,31.69,80.26,32.01,79.62,32.01z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="support">
<polygon fill="none" stroke="#808080" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="8,1
2.31,4.79 2.31,8.57 2.31,8.79 2.31,10.47 8,14.25 13.69,10.47 13.69,8.79 13.69,8.57 13.69,4.79 "/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="12.69" y1="15" x2="12.69" y2="12.44"/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="13.87" y1="15" x2="13.87" y2="11.64"/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="2.13" y1="15" x2="2.13" y2="11.64"/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="3.33" y1="15" x2="3.33" y2="12.44"/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="4.51" y1="15" x2="4.51" y2="13.22"/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="5.66" y1="15" x2="5.66" y2="14"/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="10.34" y1="15" x2="10.34" y2="14"/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="11.5" y1="15" x2="11.5" y2="13.22"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="hex_x5F_yellow">
<g>
<polygon fill="#FFDC00" points="8,1 2,5 2,7 2,11 8,15 14,11 14,7 14,5 "/>
</g>
<g>
<path fill="#FFD200" d="M8,1L2.94,4.38c4.13,1.04,7.4,4.21,8.58,8.28L14,11V7V5L8,1z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 562 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="hex_x5F_red">
<g>
<polygon fill="#E70000" points="8,1 2,5 2,7 2,11 8,15 14,11 14,7 14,5 "/>
</g>
<g>
<path fill="#D30000" d="M8,1L2.94,4.38c4.13,1.04,7.4,4.21,8.58,8.28L14,11V7V5L8,1z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 559 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="hex_x5F_green">
<g>
<polygon fill="#7DF028" points="8,1 2,5 2,7 2,11 8,15 14,11 14,7 14,5 "/>
</g>
<g>
<path fill="#7DDC28" d="M8,1L2.94,4.38c4.13,1.04,7.4,4.21,8.58,8.28L14,11V7V5L8,1z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 561 B

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="pause_x5F_print">
<g>
<path fill="#ED6B21" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
</g>
<g>
<path fill="#ED6B21" d="M6,11.71c-0.39,0-0.71-0.32-0.71-0.71V5c0-0.39,0.32-0.71,0.71-0.71S6.71,4.61,6.71,5v6
C6.71,11.39,6.39,11.71,6,11.71z"/>
</g>
<g>
<path fill="#ED6B21" d="M10,11.71c-0.39,0-0.71-0.32-0.71-0.71V5c0-0.39,0.32-0.71,0.71-0.71S10.71,4.61,10.71,5v6
C10.71,11.39,10.39,11.71,10,11.71z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 833 B

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="sla">
<rect x="3" y="11" fill="#808080" width="1" height="4"/>
<rect x="12" y="11" fill="#808080" width="1" height="4"/>
<rect x="7.5" y="6.5" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 19.5 3.5)" fill="#808080" width="1" height="10"/>
<rect x="7.5" y="9.5" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 22.5 6.5)" fill="#808080" width="1" height="10"/>
<rect x="10.5" y="11.5" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 24.5 1.5)" fill="#808080" width="2" height="3"/>
<rect x="3.5" y="11.5" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 17.5 8.5)" fill="#808080" width="2" height="3"/>
<rect x="3" y="1" fill="#ED6B21" width="10" height="10"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="support">
<polygon fill="none" stroke="#808080" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="8,1
2.31,4.79 2.31,8.57 2.31,8.79 2.31,10.47 8,14.25 13.69,10.47 13.69,8.79 13.69,8.57 13.69,4.79 "/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="12.69" y1="15" x2="12.69" y2="12.44"/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="13.87" y1="15" x2="13.87" y2="11.64"/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="2.13" y1="15" x2="2.13" y2="11.64"/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="3.33" y1="15" x2="3.33" y2="12.44"/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="4.51" y1="15" x2="4.51" y2="13.22"/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="5.66" y1="15" x2="5.66" y2="14"/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="10.34" y1="15" x2="10.34" y2="14"/>
<line fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="11.5" y1="15" x2="11.5" y2="13.22"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="sla">
<rect x="3" y="11" fill="#FFFFFF" width="1" height="4"/>
<rect x="12" y="11" fill="#FFFFFF" width="1" height="4"/>
<rect x="7.5" y="6.5" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 19.5 3.5)" fill="#FFFFFF" width="1" height="10"/>
<rect x="7.5" y="9.5" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 22.5 6.5)" fill="#FFFFFF" width="1" height="10"/>
<rect x="10.5" y="11.5" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 24.5 1.5)" fill="#FFFFFF" width="2" height="3"/>
<rect x="3.5" y="11.5" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 17.5 8.5)" fill="#FFFFFF" width="2" height="3"/>
<rect x="3" y="1" fill="#ED6B21" width="10" height="10"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -8,12 +8,15 @@ src/slic3r/GUI/ButtonsDescription.cpp
src/slic3r/GUI/ConfigManipulation.cpp src/slic3r/GUI/ConfigManipulation.cpp
src/slic3r/GUI/ConfigSnapshotDialog.cpp src/slic3r/GUI/ConfigSnapshotDialog.cpp
src/slic3r/GUI/ConfigWizard.cpp src/slic3r/GUI/ConfigWizard.cpp
src/slic3r/GUI/DoubleSlider.cpp
src/slic3r/GUI/ExtruderSequenceDialog.cpp
src/slic3r/GUI/Field.cpp src/slic3r/GUI/Field.cpp
src/slic3r/GUI/FirmwareDialog.cpp src/slic3r/GUI/FirmwareDialog.cpp
src/slic3r/GUI/GLCanvas3D.cpp src/slic3r/GUI/GLCanvas3D.cpp
src/slic3r/GUI/GLCanvas3DManager.cpp src/slic3r/GUI/GLCanvas3DManager.cpp
src/slic3r/GUI/Gizmos/GLGizmoCut.cpp src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
src/slic3r/GUI/Gizmos/GLGizmoMove.cpp src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
src/slic3r/GUI/Gizmos/GLGizmoScale.cpp src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
@ -26,10 +29,12 @@ src/slic3r/GUI/GUI_ObjectList.cpp
src/slic3r/GUI/GUI_ObjectManipulation.cpp src/slic3r/GUI/GUI_ObjectManipulation.cpp
src/slic3r/GUI/GUI_ObjectSettings.cpp src/slic3r/GUI/GUI_ObjectSettings.cpp
src/slic3r/GUI/GUI_Preview.cpp src/slic3r/GUI/GUI_Preview.cpp
src/slic3r/GUI/Job.hpp
src/slic3r/GUI/KBShortcutsDialog.cpp src/slic3r/GUI/KBShortcutsDialog.cpp
src/slic3r/GUI/MainFrame.cpp src/slic3r/GUI/MainFrame.cpp
src/slic3r/GUI/Mouse3DController.cpp src/slic3r/GUI/Mouse3DController.cpp
src/slic3r/GUI/MsgDialog.cpp src/slic3r/GUI/MsgDialog.cpp
src/slic3r/GUI/ObjectDataViewModel.cpp
src/slic3r/GUI/OptionsGroup.cpp src/slic3r/GUI/OptionsGroup.cpp
src/slic3r/GUI/Plater.cpp src/slic3r/GUI/Plater.cpp
src/slic3r/GUI/Preferences.cpp src/slic3r/GUI/Preferences.cpp
@ -46,21 +51,23 @@ src/slic3r/GUI/Tab.hpp
src/slic3r/GUI/UpdateDialogs.cpp src/slic3r/GUI/UpdateDialogs.cpp
src/slic3r/GUI/WipeTowerDialog.cpp src/slic3r/GUI/WipeTowerDialog.cpp
src/slic3r/GUI/wxExtensions.cpp src/slic3r/GUI/wxExtensions.cpp
src/slic3r/GUI/ExtruderSequenceDialog.cpp
src/slic3r/Utils/Duet.cpp
src/slic3r/Utils/OctoPrint.cpp
src/slic3r/Utils/FlashAir.cpp
src/slic3r/Utils/AstroBox.cpp src/slic3r/Utils/AstroBox.cpp
src/slic3r/Utils/PresetUpdater.cpp src/slic3r/Utils/Duet.cpp
src/slic3r/Utils/FixModelByWin10.cpp src/slic3r/Utils/FixModelByWin10.cpp
src/libslic3r/SLA/SLAPad.cpp src/slic3r/Utils/FlashAir.cpp
src/slic3r/Utils/OctoPrint.cpp
src/slic3r/Utils/PresetUpdater.cpp
src/libslic3r/SLA/Pad.cpp
src/libslic3r/SLA/Hollowing.cpp
src/libslic3r/Zipper.cpp src/libslic3r/Zipper.cpp
src/libslic3r/GCode.cpp src/libslic3r/GCode.cpp
src/libslic3r/ExtrusionEntity.cpp src/libslic3r/ExtrusionEntity.cpp
src/libslic3r/Flow.cpp
src/libslic3r/Format/3mf.cpp src/libslic3r/Format/3mf.cpp
src/libslic3r/Format/AMF.cpp src/libslic3r/Format/AMF.cpp
src/libslic3r/Print.cpp src/libslic3r/Print.cpp
src/libslic3r/SLAPrint.cpp src/libslic3r/SLAPrint.cpp
src/libslic3r/SLAPrintSteps.cpp
src/libslic3r/PrintBase.cpp src/libslic3r/PrintBase.cpp
src/libslic3r/PrintConfig.cpp src/libslic3r/PrintConfig.cpp
src/libslic3r/PrintObject.cpp src/libslic3r/PrintObject.cpp

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,8 @@
min_slic3r_version = 2.2.0-alpha3
0.0.2-alpha1 Extended list of default filaments to be installed
0.0.2-alpha0 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer.
# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer,
# so they will see the print bed.
max_slic3r_version = 2.2.0-alpha2
min_slic3r_version = 2.2.0-alpha0
0.0.1 Initial version

View file

@ -5,7 +5,7 @@
name = Creality name = Creality
# Configuration version of this file. Config file will only be installed, if the config_version differs. # 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. # This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 0.0.1 config_version = 0.0.2-alpha1
# Where to get the updates from? # Where to get the updates from?
config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Creality/ config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Creality/
# changelog_url = http://files.prusa3d.com/?latest=slicer-profiles&lng=%1% # changelog_url = http://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@ -20,7 +20,7 @@ variants = 0.4
technology = FFF technology = FFF
bed_model = ender3_bed.stl bed_model = ender3_bed.stl
bed_texture = ender3.svg bed_texture = ender3.svg
default_materials = Creality PLA @ENDER3; Prusament PLA @ENDER3 default_materials = Generic PLA @ENDER3; Generic PETG @ENDER3; Generic ABS @ENDER3; Prusament PLA @ENDER3; Prusament PETG @ENDER3
# All presets starting with asterisk, for example *common*, are intermediate and they will # All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface. # not make it into the user interface.

View file

@ -1,3 +1,9 @@
min_slic3r_version = 2.2.0-alpha3
1.1.1-alpha4 Extended list of default filaments to be installed, top/bottom_solid_min_thickness defined, infill_acceleration changed etc
1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer.
# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer,
# so they will see the print bed.
max_slic3r_version = 2.2.0-alpha2
min_slic3r_version = 2.2.0-alpha0 min_slic3r_version = 2.2.0-alpha0
1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles. 1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles.
1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0 1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0

View file

@ -5,7 +5,7 @@
name = Prusa Research name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs. # 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. # This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 1.1.1-alpha2 config_version = 1.1.1-alpha4
# Where to get the updates from? # Where to get the updates from?
config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
changelog_url = http://files.prusa3d.com/?latest=slicer-profiles&lng=%1% changelog_url = http://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@ -24,7 +24,7 @@ technology = FFF
family = MINI family = MINI
bed_model = mini_bed.stl bed_model = mini_bed.stl
bed_texture = mini.svg bed_texture = mini.svg
default_materials = Prusament PLA; Prusament PETG @MINI default_materials = Generic PLA; Generic ABS @MINI; Generic PETG @MINI; Prusament PLA; Prusament PETG @MINI; Prusament ASA @MINI
[printer_model:MK3S] [printer_model:MK3S]
name = Original Prusa i3 MK3S name = Original Prusa i3 MK3S
@ -33,7 +33,7 @@ technology = FFF
family = MK3 family = MK3
bed_model = mk3_bed.stl bed_model = mk3_bed.stl
bed_texture = mk3.svg bed_texture = mk3.svg
default_materials = Prusament PLA; Prusament PETG default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA
[printer_model:MK3] [printer_model:MK3]
name = Original Prusa i3 MK3 name = Original Prusa i3 MK3
@ -42,7 +42,7 @@ technology = FFF
family = MK3 family = MK3
bed_model = mk3_bed.stl bed_model = mk3_bed.stl
bed_texture = mk3.svg bed_texture = mk3.svg
default_materials = Prusament PLA; Prusament PETG default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA
[printer_model:MK3SMMU2S] [printer_model:MK3SMMU2S]
name = Original Prusa i3 MK3S MMU2S name = Original Prusa i3 MK3S MMU2S
@ -51,7 +51,7 @@ technology = FFF
family = MK3 family = MK3
bed_model = mk3_bed.stl bed_model = mk3_bed.stl
bed_texture = mk3.svg bed_texture = mk3.svg
default_materials = Prusament PLA @MMU2; Prusament PETG @MMU2 default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA @MMU2; Prusament PETG @MMU2; Prusament ASA @MMU2; Verbatim BVOH @MMU2
[printer_model:MK3MMU2] [printer_model:MK3MMU2]
name = Original Prusa i3 MK3 MMU2 name = Original Prusa i3 MK3 MMU2
@ -60,7 +60,7 @@ technology = FFF
family = MK3 family = MK3
bed_model = mk3_bed.stl bed_model = mk3_bed.stl
bed_texture = mk3.svg bed_texture = mk3.svg
default_materials = Prusament PLA @MMU2; Prusament PETG @MMU2 default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA @MMU2; Prusament PETG @MMU2; Prusament ASA @MMU2; Verbatim BVOH @MMU2
[printer_model:MK2.5S] [printer_model:MK2.5S]
name = Original Prusa i3 MK2.5S name = Original Prusa i3 MK2.5S
@ -69,7 +69,7 @@ technology = FFF
family = MK2.5 family = MK2.5
bed_model = mk3_bed.stl bed_model = mk3_bed.stl
bed_texture = mk3.svg bed_texture = mk3.svg
default_materials = Prusament PLA; Prusament PETG default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA
[printer_model:MK2.5] [printer_model:MK2.5]
name = Original Prusa i3 MK2.5 name = Original Prusa i3 MK2.5
@ -78,7 +78,7 @@ technology = FFF
family = MK2.5 family = MK2.5
bed_model = mk3_bed.stl bed_model = mk3_bed.stl
bed_texture = mk3.svg bed_texture = mk3.svg
default_materials = Prusament PLA; Prusament PETG default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA
[printer_model:MK2.5SMMU2S] [printer_model:MK2.5SMMU2S]
name = Original Prusa i3 MK2.5S MMU2S name = Original Prusa i3 MK2.5S MMU2S
@ -87,7 +87,7 @@ technology = FFF
family = MK2.5 family = MK2.5
bed_model = mk3_bed.stl bed_model = mk3_bed.stl
bed_texture = mk3.svg bed_texture = mk3.svg
default_materials = Prusament PLA @MMU2; Prusament PETG @MMU2 default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA @MMU2; Prusament PETG @MMU2; Prusament ASA @MMU2; Verbatim BVOH @MMU2
[printer_model:MK2.5MMU2] [printer_model:MK2.5MMU2]
name = Original Prusa i3 MK2.5 MMU2 name = Original Prusa i3 MK2.5 MMU2
@ -96,7 +96,7 @@ technology = FFF
family = MK2.5 family = MK2.5
bed_model = mk3_bed.stl bed_model = mk3_bed.stl
bed_texture = mk3.svg bed_texture = mk3.svg
default_materials = Prusament PLA @MMU2; Prusament PETG @MMU2 default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA @MMU2; Prusament PETG @MMU2; Prusament ASA @MMU2; Verbatim BVOH @MMU2
[printer_model:MK2S] [printer_model:MK2S]
name = Original Prusa i3 MK2S name = Original Prusa i3 MK2S
@ -105,7 +105,7 @@ technology = FFF
family = MK2 family = MK2
bed_model = mk2_bed.stl bed_model = mk2_bed.stl
bed_texture = mk2.svg bed_texture = mk2.svg
default_materials = Prusament PLA; Prusament PETG default_materials = Generic PLA; Generic ABS; Generic PETG; Prusament PLA; Prusament PETG; Prusament ASA
[printer_model:MK2SMM] [printer_model:MK2SMM]
name = Original Prusa i3 MK2S MMU1 name = Original Prusa i3 MK2S MMU1
@ -114,7 +114,7 @@ technology = FFF
family = MK2 family = MK2
bed_model = mk2_bed.stl bed_model = mk2_bed.stl
bed_texture = mk2.svg bed_texture = mk2.svg
default_materials = Prusament PLA; Prusament PETG @MMU1 default_materials = Generic PLA; Generic ABS; Generic PETG @MMU1; Prusament PLA; Prusament PETG @MMU1; Prusament ASA
[printer_model:SL1] [printer_model:SL1]
name = Original Prusa SL1 name = Original Prusa SL1
@ -123,7 +123,7 @@ technology = SLA
family = SL1 family = SL1
bed_model = sl1_bed.stl bed_model = sl1_bed.stl
bed_texture = sl1.svg bed_texture = sl1.svg
default_materials = Prusa Transparent Tough @0.05 default_materials = Prusa Orange Tough @0.05
# All presets starting with asterisk, for example *common*, are intermediate and they will # All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface. # not make it into the user interface.
@ -226,6 +226,8 @@ wipe_tower_width = 60
wipe_tower_x = 170 wipe_tower_x = 170
wipe_tower_y = 140 wipe_tower_y = 140
xy_size_compensation = 0 xy_size_compensation = 0
top_solid_min_thickness = 0.6
bottom_solid_min_thickness = 0.5
[print:*MK3*] [print:*MK3*]
fill_pattern = grid fill_pattern = grid
@ -359,9 +361,9 @@ output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_heig
external_perimeter_extrusion_width = 0.65 external_perimeter_extrusion_width = 0.65
extrusion_width = 0.65 extrusion_width = 0.65
first_layer_extrusion_width = 0.65 first_layer_extrusion_width = 0.65
infill_extrusion_width = 0.7 infill_extrusion_width = 0.65
perimeter_extrusion_width = 0.65 perimeter_extrusion_width = 0.65
solid_infill_extrusion_width = 0.7 solid_infill_extrusion_width = 0.65
top_infill_extrusion_width = 0.6 top_infill_extrusion_width = 0.6
support_material_extrusion_width = 0.55 support_material_extrusion_width = 0.55
bridge_flow_ratio = 0.95 bridge_flow_ratio = 0.95
@ -555,7 +557,7 @@ inherits = *0.10mm*; *MK3*
bridge_speed = 30 bridge_speed = 30
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material
external_perimeter_speed = 25 external_perimeter_speed = 25
infill_acceleration = 1250 infill_acceleration = 1000
infill_speed = 80 infill_speed = 80
max_print_speed = 200 max_print_speed = 200
perimeter_speed = 45 perimeter_speed = 45
@ -655,7 +657,7 @@ inherits = *0.15mm*; *MK3*
bridge_speed = 30 bridge_speed = 30
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
external_perimeter_speed = 25 external_perimeter_speed = 25
infill_acceleration = 1250 infill_acceleration = 1000
infill_speed = 80 infill_speed = 80
max_print_speed = 200 max_print_speed = 200
perimeter_speed = 45 perimeter_speed = 45
@ -670,7 +672,7 @@ inherits = *0.15mm*; *MK3*
bridge_speed = 30 bridge_speed = 30
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
external_perimeter_speed = 35 external_perimeter_speed = 35
infill_acceleration = 1250 infill_acceleration = 1000
infill_speed = 200 infill_speed = 200
max_print_speed = 200 max_print_speed = 200
perimeter_speed = 60 perimeter_speed = 60
@ -735,7 +737,7 @@ inherits = *0.15mm*; *0.6nozzleMK3*; *MK306*
# alias = 0.15mm DETAIL # alias = 0.15mm DETAIL
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
external_perimeter_speed = 35 external_perimeter_speed = 35
infill_acceleration = 1250 infill_acceleration = 1000
infill_speed = 70 infill_speed = 70
max_print_speed = 100 max_print_speed = 100
perimeter_speed = 45 perimeter_speed = 45
@ -780,7 +782,7 @@ inherits = *0.20mm*; *MK3*
bridge_speed = 30 bridge_speed = 30
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
external_perimeter_speed = 25 external_perimeter_speed = 25
infill_acceleration = 1250 infill_acceleration = 1000
infill_speed = 80 infill_speed = 80
max_print_speed = 200 max_print_speed = 200
perimeter_speed = 45 perimeter_speed = 45
@ -795,7 +797,7 @@ inherits = *0.20mm*; *MK3*
bridge_speed = 30 bridge_speed = 30
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
external_perimeter_speed = 35 external_perimeter_speed = 35
infill_acceleration = 1250 infill_acceleration = 1000
infill_speed = 200 infill_speed = 200
max_print_speed = 200 max_print_speed = 200
perimeter_speed = 60 perimeter_speed = 60
@ -860,7 +862,7 @@ inherits = *0.20mm*; *0.6nozzleMK3*; *MK306*
# alias = 0.20mm DETAIL # alias = 0.20mm DETAIL
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
external_perimeter_speed = 35 external_perimeter_speed = 35
infill_acceleration = 1250 infill_acceleration = 1000
infill_speed = 70 infill_speed = 70
max_print_speed = 100 max_print_speed = 100
perimeter_speed = 45 perimeter_speed = 45
@ -905,7 +907,7 @@ inherits = *0.30mm*; *0.6nozzleMK3*; *MK306*
# alias = 0.30mm QUALITY # alias = 0.30mm QUALITY
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
external_perimeter_speed = 35 external_perimeter_speed = 35
infill_acceleration = 1250 infill_acceleration = 1000
infill_speed = 70 infill_speed = 70
max_print_speed = 100 max_print_speed = 100
perimeter_speed = 45 perimeter_speed = 45
@ -942,7 +944,7 @@ bottom_solid_layers = 3
bridge_speed = 30 bridge_speed = 30
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
external_perimeter_speed = 35 external_perimeter_speed = 35
infill_acceleration = 1250 infill_acceleration = 1000
infill_speed = 85 infill_speed = 85
max_print_speed = 200 max_print_speed = 200
perimeter_speed = 50 perimeter_speed = 50
@ -1027,7 +1029,7 @@ inherits = *0.35mm*; *0.6nozzleMK3*; *MK306*
# alias = 0.35mm SPEED # alias = 0.35mm SPEED
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
external_perimeter_speed = 35 external_perimeter_speed = 35
infill_acceleration = 1250 infill_acceleration = 1000
infill_speed = 70 infill_speed = 70
max_print_speed = 100 max_print_speed = 100
perimeter_speed = 45 perimeter_speed = 45
@ -1035,6 +1037,8 @@ solid_infill_speed = 70
top_solid_infill_speed = 45 top_solid_infill_speed = 45
external_perimeter_extrusion_width = 0.68 external_perimeter_extrusion_width = 0.68
perimeter_extrusion_width = 0.68 perimeter_extrusion_width = 0.68
infill_extrusion_width = 0.68
solid_infill_extrusion_width = 0.68
# XXXXXXXXXXXXXXXXXXXX # XXXXXXXXXXXXXXXXXXXX
# XXX--- 0.40mm ---XXX # XXX--- 0.40mm ---XXX
@ -1063,7 +1067,7 @@ inherits = *0.40mm*; *0.6nozzleMK3*; *MK306*
# alias = 0.40mm DRAFT # alias = 0.40mm DRAFT
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
external_perimeter_speed = 35 external_perimeter_speed = 35
infill_acceleration = 1250 infill_acceleration = 1000
infill_speed = 70 infill_speed = 70
max_print_speed = 100 max_print_speed = 100
perimeter_speed = 45 perimeter_speed = 45
@ -1185,6 +1189,7 @@ top_infill_extrusion_width = 0.4
small_perimeter_speed = 15 small_perimeter_speed = 15
perimeter_extrusion_width = 0.4 perimeter_extrusion_width = 0.4
external_perimeter_extrusion_width = 0.4 external_perimeter_extrusion_width = 0.4
support_material_xy_spacing = 60%
[print:0.07mm ULTRADETAIL @MINI] [print:0.07mm ULTRADETAIL @MINI]
inherits = *0.07mm*; *MINI* inherits = *0.07mm*; *MINI*
@ -1196,6 +1201,7 @@ top_infill_extrusion_width = 0.4
small_perimeter_speed = 15 small_perimeter_speed = 15
perimeter_extrusion_width = 0.4 perimeter_extrusion_width = 0.4
external_perimeter_extrusion_width = 0.4 external_perimeter_extrusion_width = 0.4
support_material_xy_spacing = 60%
[print:0.10mm DETAIL @MINI] [print:0.10mm DETAIL @MINI]
inherits = *0.10mm*; *MINI* inherits = *0.10mm*; *MINI*
@ -1212,6 +1218,7 @@ fill_pattern = gyroid
fill_density = 15% fill_density = 15%
perimeters = 3 perimeters = 3
bridge_acceleration = 1000 bridge_acceleration = 1000
support_material_xy_spacing = 60%
[print:0.15mm QUALITY @MINI] [print:0.15mm QUALITY @MINI]
inherits = *0.15mm*; *MINI* inherits = *0.15mm*; *MINI*
@ -1226,6 +1233,7 @@ top_solid_infill_speed = 40
fill_pattern = gyroid fill_pattern = gyroid
fill_density = 15% fill_density = 15%
bridge_flow_ratio = 0.85 bridge_flow_ratio = 0.85
support_material_xy_spacing = 60%
[print:0.15mm SPEED @MINI] [print:0.15mm SPEED @MINI]
inherits = *0.15mm*; *MINI* inherits = *0.15mm*; *MINI*
@ -1238,6 +1246,7 @@ infill_speed = 140
solid_infill_speed = 140 solid_infill_speed = 140
top_solid_infill_speed = 40 top_solid_infill_speed = 40
bridge_flow_ratio = 0.85 bridge_flow_ratio = 0.85
support_material_xy_spacing = 60%
[print:0.20mm QUALITY @MINI] [print:0.20mm QUALITY @MINI]
inherits = *0.20mm*; *MINI* inherits = *0.20mm*; *MINI*
@ -1251,6 +1260,7 @@ solid_infill_speed = 80
top_solid_infill_speed = 40 top_solid_infill_speed = 40
fill_pattern = gyroid fill_pattern = gyroid
fill_density = 15% fill_density = 15%
support_material_xy_spacing = 60%
[print:0.20mm SPEED @MINI] [print:0.20mm SPEED @MINI]
inherits = *0.20mm*; *MINI* inherits = *0.20mm*; *MINI*
@ -1263,6 +1273,7 @@ infill_speed = 140
max_print_speed = 150 max_print_speed = 150
solid_infill_speed = 140 solid_infill_speed = 140
top_solid_infill_speed = 40 top_solid_infill_speed = 40
support_material_xy_spacing = 60%
[print:0.25mm DRAFT @MINI] [print:0.25mm DRAFT @MINI]
inherits = *0.25mm*; *MINI* inherits = *0.25mm*; *MINI*
@ -1279,6 +1290,7 @@ first_layer_extrusion_width = 0.42
infill_extrusion_width = 0.45 infill_extrusion_width = 0.45
solid_infill_extrusion_width = 0.45 solid_infill_extrusion_width = 0.45
top_infill_extrusion_width = 0.4 top_infill_extrusion_width = 0.4
support_material_xy_spacing = 60%
# 0.25mm nozzle # 0.25mm nozzle
@ -1551,7 +1563,7 @@ bridge_fan_speed = 80
compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MK2SMM" and printer_model!="MINI" and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MK2SMM" and printer_model!="MINI" and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material)
cooling = 0 cooling = 0
disable_fan_first_layers = 3 disable_fan_first_layers = 3
extrusion_multiplier = 1.2 extrusion_multiplier = 1.15
fan_always_on = 0 fan_always_on = 0
fan_below_layer_time = 100 fan_below_layer_time = 100
filament_colour = #008000 filament_colour = #008000
@ -1626,7 +1638,7 @@ filament_density = 1.24
inherits = *PLA* inherits = *PLA*
filament_vendor = ColorFabb filament_vendor = ColorFabb
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
extrusion_multiplier = 1.2 extrusion_multiplier = 1.1
filament_cost = 58.30 filament_cost = 58.30
filament_density = 1.15 filament_density = 1.15
filament_colour = #dfc287 filament_colour = #dfc287
@ -1634,12 +1646,13 @@ filament_max_volumetric_speed = 9
first_layer_temperature = 200 first_layer_temperature = 200
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode" start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode"
temperature = 200 temperature = 200
filament_retract_lift = 0.2
[filament:ColorFabb corkFill] [filament:ColorFabb corkFill]
inherits = *PLA* inherits = *PLA*
filament_vendor = ColorFabb filament_vendor = ColorFabb
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
extrusion_multiplier = 1.2 extrusion_multiplier = 1.1
filament_cost = 58.30 filament_cost = 58.30
filament_density = 1.18 filament_density = 1.18
filament_colour = #634d33 filament_colour = #634d33
@ -1647,6 +1660,7 @@ filament_max_volumetric_speed = 6
first_layer_temperature = 220 first_layer_temperature = 220
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode" start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode"
temperature = 220 temperature = 220
filament_retract_lift = 0.2
[filament:ColorFabb XT] [filament:ColorFabb XT]
inherits = *PET* inherits = *PET*
@ -1788,7 +1802,7 @@ temperature = 275
inherits = *PLA* inherits = *PLA*
filament_vendor = Fillamentum filament_vendor = Fillamentum
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
extrusion_multiplier = 1.2 extrusion_multiplier = 1.1
filament_cost = 68 filament_cost = 68
filament_density = 1.15 filament_density = 1.15
filament_colour = #804040 filament_colour = #804040
@ -1796,6 +1810,21 @@ filament_max_volumetric_speed = 10
first_layer_temperature = 190 first_layer_temperature = 190
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode" start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode"
temperature = 190 temperature = 190
filament_retract_lift = 0.2
[filament:Smartfil Wood]
inherits = *PLA*
filament_vendor = Smart Materials 3D
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
extrusion_multiplier = 1.1
filament_cost = 68
filament_density = 1.58
filament_colour = #804040
filament_max_volumetric_speed = 9
first_layer_temperature = 220
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode"
temperature = 220
filament_retract_lift = 0.2
[filament:Generic ABS] [filament:Generic ABS]
inherits = *ABS* inherits = *ABS*
@ -1855,7 +1884,7 @@ inherits = *FLEX*
filament_vendor = SainSmart filament_vendor = SainSmart
fan_always_on = 1 fan_always_on = 1
filament_max_volumetric_speed = 2.5 filament_max_volumetric_speed = 2.5
extrusion_multiplier = 1.15 extrusion_multiplier = 1.1
first_layer_temperature = 230 first_layer_temperature = 230
first_layer_bed_temperature = 50 first_layer_bed_temperature = 50
temperature = 230 temperature = 230
@ -1881,7 +1910,7 @@ inherits = *FLEX*
filament_vendor = Filatech filament_vendor = Filatech
fan_always_on = 1 fan_always_on = 1
filament_max_volumetric_speed = 2.5 filament_max_volumetric_speed = 2.5
extrusion_multiplier = 1.15 extrusion_multiplier = 1.1
first_layer_temperature = 230 first_layer_temperature = 230
first_layer_bed_temperature = 50 first_layer_bed_temperature = 50
temperature = 230 temperature = 230
@ -1914,6 +1943,7 @@ first_layer_bed_temperature = 100
first_layer_temperature = 270 first_layer_temperature = 270
temperature = 270 temperature = 270
bridge_fan_speed = 0 bridge_fan_speed = 0
filament_max_volumetric_speed = 8
[filament:PrimaSelect PVA+] [filament:PrimaSelect PVA+]
inherits = *PLA* inherits = *PLA*
@ -2009,6 +2039,25 @@ min_fan_speed = 20
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode" start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode"
temperature = 220 temperature = 220
[filament:Generic HIPS]
inherits = *ABS*
filament_vendor = Generic
filament_cost = 27.3
filament_density = 1.04
bridge_fan_speed = 50
cooling = 1
extrusion_multiplier = 1
fan_always_on = 1
fan_below_layer_time = 10
filament_colour = #FFFFD7
filament_soluble = 1
filament_type = HIPS
first_layer_temperature = 230
max_fan_speed = 20
min_fan_speed = 20
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/}0{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}0{else}10{endif}; Filament gcode"
temperature = 230
[filament:Prusa PETG] [filament:Prusa PETG]
inherits = *PET* inherits = *PET*
filament_vendor = Made for Prusa filament_vendor = Made for Prusa
@ -2753,6 +2802,7 @@ filament_density = 1.07
inherits = Polymaker PC-Max; *ABSMINI* inherits = Polymaker PC-Max; *ABSMINI*
# alias = Polymaker PC-Max # alias = Polymaker PC-Max
filament_type = PC filament_type = PC
filament_max_volumetric_speed = 7
bed_temperature = 100 bed_temperature = 100
filament_colour = #3A80CA filament_colour = #3A80CA
first_layer_bed_temperature = 100 first_layer_bed_temperature = 100
@ -2777,7 +2827,7 @@ filament_cost = 27.3
filament_density = 1.04 filament_density = 1.04
bridge_fan_speed = 50 bridge_fan_speed = 50
cooling = 1 cooling = 1
extrusion_multiplier = 0.9 extrusion_multiplier = 1
fan_always_on = 1 fan_always_on = 1
fan_below_layer_time = 10 fan_below_layer_time = 10
filament_colour = #FFFFD7 filament_colour = #FFFFD7
@ -2963,6 +3013,13 @@ initial_exposure_time = 30
material_type = Tough material_type = Tough
material_vendor = 3DM material_vendor = 3DM
[sla_material:3DM-TOUGH Clear 0.025]
inherits = *common 0.025*
exposure_time = 9
initial_exposure_time = 30
material_type = Tough
material_vendor = 3DM
[sla_material:BlueCast Phrozen Wax @0.025] [sla_material:BlueCast Phrozen Wax @0.025]
inherits = *common 0.025* inherits = *common 0.025*
exposure_time = 15 exposure_time = 15
@ -3323,6 +3380,13 @@ initial_exposure_time = 30
material_type = Tough material_type = Tough
material_vendor = 3DM material_vendor = 3DM
[sla_material:3DM-TOUGH Clear 0.05]
inherits = *common 0.05*
exposure_time = 15
initial_exposure_time = 30
material_type = Tough
material_vendor = 3DM
[sla_material:FTD Ash Grey @0.05] [sla_material:FTD Ash Grey @0.05]
inherits = *common 0.05* inherits = *common 0.05*
exposure_time = 9 exposure_time = 9
@ -3835,18 +3899,21 @@ min_layer_height = 0.1
inherits = Original Prusa i3 MK2S inherits = Original Prusa i3 MK2S
printer_model = MK2.5 printer_model = MK2.5
remaining_times = 1 remaining_times = 1
machine_max_jerk_e = 4.5
start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.1 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.1 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
[printer:Original Prusa i3 MK2.5 0.25 nozzle] [printer:Original Prusa i3 MK2.5 0.25 nozzle]
inherits = Original Prusa i3 MK2S 0.25 nozzle inherits = Original Prusa i3 MK2S 0.25 nozzle
printer_model = MK2.5 printer_model = MK2.5
remaining_times = 1 remaining_times = 1
machine_max_jerk_e = 4.5
start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.1 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.1 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
[printer:Original Prusa i3 MK2.5 0.6 nozzle] [printer:Original Prusa i3 MK2.5 0.6 nozzle]
inherits = Original Prusa i3 MK2S 0.6 nozzle inherits = Original Prusa i3 MK2S 0.6 nozzle
printer_model = MK2.5 printer_model = MK2.5
remaining_times = 1 remaining_times = 1
machine_max_jerk_e = 4.5
start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.1 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.8.1 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
[printer:Original Prusa i3 MK2.5 MMU2 Single] [printer:Original Prusa i3 MK2.5 MMU2 Single]
@ -3867,7 +3934,7 @@ machine_max_feedrate_e = 120
machine_max_feedrate_x = 500 machine_max_feedrate_x = 500
machine_max_feedrate_y = 500 machine_max_feedrate_y = 500
machine_max_feedrate_z = 12 machine_max_feedrate_z = 12
machine_max_jerk_e = 2.5 machine_max_jerk_e = 4.5
machine_max_jerk_x = 10 machine_max_jerk_x = 10
machine_max_jerk_y = 10 machine_max_jerk_y = 10
machine_max_jerk_z = 0.2 machine_max_jerk_z = 0.2
@ -3904,7 +3971,7 @@ machine_max_feedrate_e = 120
machine_max_feedrate_x = 500 machine_max_feedrate_x = 500
machine_max_feedrate_y = 500 machine_max_feedrate_y = 500
machine_max_feedrate_z = 12 machine_max_feedrate_z = 12
machine_max_jerk_e = 2.5 machine_max_jerk_e = 4.5
machine_max_jerk_x = 10 machine_max_jerk_x = 10
machine_max_jerk_y = 10 machine_max_jerk_y = 10
machine_max_jerk_z = 0.2 machine_max_jerk_z = 0.2
@ -3954,7 +4021,7 @@ machine_max_feedrate_e = 120
machine_max_feedrate_x = 500 machine_max_feedrate_x = 500
machine_max_feedrate_y = 500 machine_max_feedrate_y = 500
machine_max_feedrate_z = 12 machine_max_feedrate_z = 12
machine_max_jerk_e = 2.5 machine_max_jerk_e = 4.5
machine_max_jerk_x = 10 machine_max_jerk_x = 10
machine_max_jerk_y = 10 machine_max_jerk_y = 10
machine_max_jerk_z = 0.2 machine_max_jerk_z = 0.2
@ -4003,7 +4070,7 @@ machine_max_feedrate_e = 120
machine_max_feedrate_x = 500 machine_max_feedrate_x = 500
machine_max_feedrate_y = 500 machine_max_feedrate_y = 500
machine_max_feedrate_z = 12 machine_max_feedrate_z = 12
machine_max_jerk_e = 2.5 machine_max_jerk_e = 4.5
machine_max_jerk_x = 10 machine_max_jerk_x = 10
machine_max_jerk_y = 10 machine_max_jerk_y = 10
machine_max_jerk_z = 0.2 machine_max_jerk_z = 0.2
@ -4053,7 +4120,7 @@ machine_max_feedrate_e = 120,120
machine_max_feedrate_x = 200,100 machine_max_feedrate_x = 200,100
machine_max_feedrate_y = 200,100 machine_max_feedrate_y = 200,100
machine_max_feedrate_z = 12,12 machine_max_feedrate_z = 12,12
machine_max_jerk_e = 1.5,1.5 machine_max_jerk_e = 4.5,4.5
machine_max_jerk_x = 8,8 machine_max_jerk_x = 8,8
machine_max_jerk_y = 8,8 machine_max_jerk_y = 8,8
machine_max_jerk_z = 0.4,0.4 machine_max_jerk_z = 0.4,0.4
@ -4261,7 +4328,7 @@ retract_lift_below = 179
retract_layer_change = 0 retract_layer_change = 0
silent_mode = 0 silent_mode = 0
remaining_times = 1 remaining_times = 1
start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM92 E317 ; set steps/unit for extruder\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0.0\nG1 Y-2.0 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110.0 E8.0 F900\nG1 X40.0 E10.0 F700\nG92 E0.0\n\nM221 S95 ; set flow start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0.0\nG1 Y-2.0 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110.0 E8.0 F900\nG1 X40.0 E10.0 F700\nG92 E0.0\n\nM221 S95 ; set flow
end_gcode = G1 E-1 F2100 ; retract\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+5, max_print_height)}{endif} F720 ; Move print head up\nG1 X178 Y180 F4200 ; park print head\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM221 S100 ; reset flow\nM84 ; disable motors end_gcode = G1 E-1 F2100 ; retract\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+5, max_print_height)}{endif} F720 ; Move print head up\nG1 X178 Y180 F4200 ; park print head\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM221 S100 ; reset flow\nM84 ; disable motors
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_PRUSA3D\nPRINTER_MODEL_MINI\n 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_PRUSA3D\nPRINTER_MODEL_MINI\n
extruder_colour = extruder_colour =
@ -4280,7 +4347,7 @@ deretract_speed = 40
wipe = 1 wipe = 1
retract_before_wipe = 70% retract_before_wipe = 70%
retract_before_travel = 1 retract_before_travel = 1
start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM92 E317 ; set steps/unit for extruder\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0.0\nG1 Y-2.0 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110.0 E8.0 F600\nG1 X40.0 E10.0 F400\nG92 E0.0\n\nM221 S95 ; set flow start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0.0\nG1 Y-2.0 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110.0 E8.0 F600\nG1 X40.0 E10.0 F400\nG92 E0.0\n\nM221 S95 ; set flow
[printer:Original Prusa MINI 0.6 nozzle] [printer:Original Prusa MINI 0.6 nozzle]
inherits = Original Prusa MINI inherits = Original Prusa MINI
@ -4295,7 +4362,7 @@ retract_speed = 70
deretract_speed = 40 deretract_speed = 40
wipe = 1 wipe = 1
retract_before_wipe = 70% retract_before_wipe = 70%
retract_before_travel = 1 retract_before_travel = 1.5
[printer:Original Prusa SL1] [printer:Original Prusa SL1]
printer_technology = SLA printer_technology = SLA

View file

@ -1,3 +1,4 @@
#add_subdirectory(slasupporttree) #add_subdirectory(slasupporttree)
#add_subdirectory(openvdb) #add_subdirectory(openvdb)
add_subdirectory(meshboolean) add_subdirectory(meshboolean)
add_subdirectory(opencsg)

View file

@ -1,12 +1,6 @@
if (SLIC3R_STATIC)
set(CGAL_Boost_USE_STATIC_LIBS ON)
endif ()
find_package(CGAL REQUIRED)
add_executable(meshboolean MeshBoolean.cpp) add_executable(meshboolean MeshBoolean.cpp)
target_link_libraries(meshboolean libslic3r CGAL::CGAL) target_link_libraries(meshboolean libslic3r)
if (WIN32) if (WIN32)
prusaslicer_copy_dlls(meshboolean) prusaslicer_copy_dlls(meshboolean)

View file

@ -1,85 +1,44 @@
#include <libslic3r/TriangleMesh.hpp>
#undef PI
#include <igl/readOFF.h>
//#undef IGL_STATIC_LIBRARY
#include <igl/copyleft/cgal/mesh_boolean.h>
#include <Eigen/Core>
#include <iostream> #include <iostream>
#include <vector>
#include <admesh/stl.h> #include <libslic3r/TriangleMesh.hpp>
#include <libslic3r/Model.hpp>
#include <libslic3r/SLAPrint.hpp>
#include <libslic3r/SLAPrintSteps.hpp>
#include <libslic3r/MeshBoolean.hpp>
#include <libnest2d/tools/benchmark.h>
#include <boost/nowide/cstdio.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
namespace Slic3r {
bool its_write_obj(const Eigen::MatrixXd &V, Eigen::MatrixXi &F, const char *file)
{
FILE *fp = boost::nowide::fopen(file, "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error) << "stl_write_obj: Couldn't open " << file << " for writing";
return false;
}
for (size_t i = 0; i < V.rows(); ++ i)
fprintf(fp, "v %lf %lf %lf\n", V(i, 0), V(i, 1), V(i, 2));
for (size_t i = 0; i < F.rows(); ++ i)
fprintf(fp, "f %d %d %d\n", F(i, 0) + 1, F(i, 1) + 1, F(i, 2) + 1);
fclose(fp);
return true;
}
void mesh_boolean_test(const std::string &fname)
{
using namespace Eigen;
using namespace std;
// igl::readOFF(TUTORIAL_SHARED_PATH "/cheburashka.off",VA,FA);
// igl::readOFF(TUTORIAL_SHARED_PATH "/decimated-knight.off",VB,FB);
// Plot the mesh with pseudocolors
// igl::opengl::glfw::Viewer viewer;
// Initialize
// update(viewer);
//igl::copyleft::cgal::mesh_boolean(VA,FA,VB,FB,boolean_type,VC,FC,J);
std::cout << fname.c_str() << std::endl;
TriangleMesh mesh;
mesh.ReadSTLFile(fname.c_str());
mesh.repair(true);
its_write_obj(mesh.its, (fname + "-imported0.obj").c_str());
Eigen::MatrixXd VA,VB,VC;
Eigen::VectorXi J,I;
Eigen::MatrixXi FA,FB,FC;
igl::MeshBooleanType boolean_type(igl::MESH_BOOLEAN_TYPE_UNION);
typedef Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXfUnaligned;
typedef Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXiUnaligned;
Eigen::MatrixXd V = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), mesh.its.vertices.size(), 3).cast<double>();
Eigen::MatrixXi F = MapMatrixXiUnaligned(mesh.its.indices.front().data(), mesh.its.indices.size(), 3);
its_write_obj(V, F, (fname + "-imported.obj").c_str());
// Self-union to clean up
igl::copyleft::cgal::mesh_boolean(V, F, Eigen::MatrixXd(), Eigen::MatrixXi(), boolean_type, VC, FC);
its_write_obj(VC, FC, (fname + "-fixed.obj").c_str());
}
} // namespace Slic3r
int main(const int argc, const char * argv[]) int main(const int argc, const char * argv[])
{ {
if (argc < 1) return -1; using namespace Slic3r;
Slic3r::mesh_boolean_test(argv[1]); if (argc <= 1) {
std::cout << "Usage: meshboolean <input_file.3mf>" << std::endl;
return EXIT_FAILURE;
}
TriangleMesh input;
input.ReadSTLFile(argv[1]);
input.repair();
Benchmark bench;
bench.start();
bool fckd = MeshBoolean::cgal::does_self_intersect(input);
bench.stop();
std::cout << "Self intersect test: " << fckd << " duration: " << bench.getElapsedSec() << std::endl;
bench.start();
MeshBoolean::self_union(input);
bench.stop();
std::cout << "Self union duration: " << bench.getElapsedSec() << std::endl;
return 0; return 0;
} }

View file

@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.0)
project(OpenCSG-example)
add_executable(opencsg_example WIN32
main.cpp
Engine.hpp Engine.cpp
ShaderCSGDisplay.hpp ShaderCSGDisplay.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/ProgressStatusBar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.hpp
${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.cpp)
find_package(wxWidgets 3.1 REQUIRED COMPONENTS core base gl html)
find_package(OpenGL REQUIRED)
find_package(GLEW REQUIRED)
find_package(OpenCSG REQUIRED)
include(${wxWidgets_USE_FILE})
target_link_libraries(opencsg_example libslic3r)
target_include_directories(opencsg_example PRIVATE ${wxWidgets_INCLUDE_DIRS})
target_compile_definitions(opencsg_example PRIVATE ${wxWidgets_DEFINITIONS})
slic3r_remap_configs(OpenCSG::opencsg RelWithDebInfo Release)
target_link_libraries(opencsg_example ${wxWidgets_LIBRARIES}
OpenCSG::opencsg
GLEW::GLEW
OpenGL::GL
#-lXrandr -lXext -lX11
)

View file

@ -0,0 +1,498 @@
#include "Engine.hpp"
#include <libslic3r/Utils.hpp>
#include <libslic3r/SLAPrint.hpp>
#include <GL/glew.h>
#include <boost/log/trivial.hpp>
#ifndef NDEBUG
#define HAS_GLSAFE
#endif
#ifdef HAS_GLSAFE
extern void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char *function_name);
inline void glAssertRecentCall() { glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); }
#define glsafe(cmd) do { cmd; glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } while (false)
#define glcheck() do { glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } while (false)
void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char *function_name)
{
GLenum err = glGetError();
if (err == GL_NO_ERROR)
return;
const char *sErr = 0;
switch (err) {
case GL_INVALID_ENUM: sErr = "Invalid Enum"; break;
case GL_INVALID_VALUE: sErr = "Invalid Value"; break;
// be aware that GL_INVALID_OPERATION is generated if glGetError is executed between the execution of glBegin and the corresponding execution of glEnd
case GL_INVALID_OPERATION: sErr = "Invalid Operation"; break;
case GL_STACK_OVERFLOW: sErr = "Stack Overflow"; break;
case GL_STACK_UNDERFLOW: sErr = "Stack Underflow"; break;
case GL_OUT_OF_MEMORY: sErr = "Out Of Memory"; break;
default: sErr = "Unknown"; break;
}
BOOST_LOG_TRIVIAL(error) << "OpenGL error in " << file_name << ":" << line << ", function " << function_name << "() : " << (int)err << " - " << sErr;
assert(false);
}
#else
inline void glAssertRecentCall() { }
#define glsafe(cmd) cmd
#define glcheck()
#endif
namespace Slic3r { namespace GL {
Scene::Scene() = default;
Scene::~Scene() = default;
void CSGDisplay::render_scene()
{
GLfloat color[] = {1.f, 1.f, 0.f, 0.f};
glsafe(::glColor4fv(color));
if (m_csgsettings.is_enabled()) {
OpenCSG::render(m_scene_cache.primitives_csg);
glDepthFunc(GL_EQUAL);
}
for (auto& p : m_scene_cache.primitives_csg) p->render();
if (m_csgsettings.is_enabled()) glDepthFunc(GL_LESS);
for (auto& p : m_scene_cache.primitives_free) p->render();
glFlush();
}
void Scene::set_print(uqptr<SLAPrint> &&print)
{
m_print = std::move(print);
// Notify displays
call(&Listener::on_scene_updated, m_listeners, *this);
}
BoundingBoxf3 Scene::get_bounding_box() const
{
return m_print->model().bounding_box();
}
void CSGDisplay::SceneCache::clear()
{
primitives_csg.clear();
primitives_free.clear();
primitives.clear();
}
shptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh)
{
auto p = std::make_shared<Primitive>();
p->load_mesh(mesh);
primitives.emplace_back(p);
primitives_free.emplace_back(p.get());
return p;
}
shptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh,
OpenCSG::Operation o,
unsigned c)
{
auto p = std::make_shared<Primitive>(o, c);
p->load_mesh(mesh);
primitives.emplace_back(p);
primitives_csg.emplace_back(p.get());
return p;
}
void IndexedVertexArray::push_geometry(float x, float y, float z, float nx, float ny, float nz)
{
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
if (this->vertices_and_normals_interleaved_VBO_id != 0)
return;
if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity())
this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6));
this->vertices_and_normals_interleaved.emplace_back(nx);
this->vertices_and_normals_interleaved.emplace_back(ny);
this->vertices_and_normals_interleaved.emplace_back(nz);
this->vertices_and_normals_interleaved.emplace_back(x);
this->vertices_and_normals_interleaved.emplace_back(y);
this->vertices_and_normals_interleaved.emplace_back(z);
this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size();
}
void IndexedVertexArray::push_triangle(int idx1, int idx2, int idx3) {
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
if (this->vertices_and_normals_interleaved_VBO_id != 0)
return;
if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity())
this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3));
this->triangle_indices.emplace_back(idx1);
this->triangle_indices.emplace_back(idx2);
this->triangle_indices.emplace_back(idx3);
this->triangle_indices_size = this->triangle_indices.size();
}
void IndexedVertexArray::load_mesh(const TriangleMesh &mesh)
{
assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0);
assert(quad_indices.empty() && triangle_indices_size == 0);
assert(vertices_and_normals_interleaved.size() % 6 == 0 && quad_indices_size == vertices_and_normals_interleaved.size());
this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count());
int vertices_count = 0;
for (size_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
const stl_facet &facet = mesh.stl.facet_start[i];
for (int j = 0; j < 3; ++j)
this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2));
this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2);
vertices_count += 3;
}
}
void IndexedVertexArray::finalize_geometry()
{
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
assert(this->triangle_indices_VBO_id == 0);
assert(this->quad_indices_VBO_id == 0);
if (!this->vertices_and_normals_interleaved.empty()) {
glsafe(
::glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER,
this->vertices_and_normals_interleaved_VBO_id));
glsafe(
::glBufferData(GL_ARRAY_BUFFER,
GLsizeiptr(
this->vertices_and_normals_interleaved.size() *
4),
this->vertices_and_normals_interleaved.data(),
GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
this->vertices_and_normals_interleaved.clear();
}
if (!this->triangle_indices.empty()) {
glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_id));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
this->triangle_indices_VBO_id));
glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER,
GLsizeiptr(this->triangle_indices.size() * 4),
this->triangle_indices.data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
this->triangle_indices.clear();
}
if (!this->quad_indices.empty()) {
glsafe(::glGenBuffers(1, &this->quad_indices_VBO_id));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
this->quad_indices_VBO_id));
glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER,
GLsizeiptr(this->quad_indices.size() * 4),
this->quad_indices.data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
this->quad_indices.clear();
}
}
void IndexedVertexArray::release_geometry()
{
if (this->vertices_and_normals_interleaved_VBO_id) {
glsafe(
::glDeleteBuffers(1,
&this->vertices_and_normals_interleaved_VBO_id));
this->vertices_and_normals_interleaved_VBO_id = 0;
}
if (this->triangle_indices_VBO_id) {
glsafe(::glDeleteBuffers(1, &this->triangle_indices_VBO_id));
this->triangle_indices_VBO_id = 0;
}
if (this->quad_indices_VBO_id) {
glsafe(::glDeleteBuffers(1, &this->quad_indices_VBO_id));
this->quad_indices_VBO_id = 0;
}
this->clear();
}
void IndexedVertexArray::render() const
{
assert(this->vertices_and_normals_interleaved_VBO_id != 0);
assert(this->triangle_indices_VBO_id != 0 ||
this->quad_indices_VBO_id != 0);
glsafe(::glBindBuffer(GL_ARRAY_BUFFER,
this->vertices_and_normals_interleaved_VBO_id));
glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float),
reinterpret_cast<const void *>(3 * sizeof(float))));
glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
// Render using the Vertex Buffer Objects.
if (this->triangle_indices_size > 0) {
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
this->triangle_indices_VBO_id));
glsafe(::glDrawElements(GL_TRIANGLES,
GLsizei(this->triangle_indices_size),
GL_UNSIGNED_INT, nullptr));
glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
}
if (this->quad_indices_size > 0) {
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
this->quad_indices_VBO_id));
glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size),
GL_UNSIGNED_INT, nullptr));
glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
}
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
void IndexedVertexArray::clear() {
this->vertices_and_normals_interleaved.clear();
this->triangle_indices.clear();
this->quad_indices.clear();
vertices_and_normals_interleaved_size = 0;
triangle_indices_size = 0;
quad_indices_size = 0;
}
void IndexedVertexArray::shrink_to_fit() {
this->vertices_and_normals_interleaved.shrink_to_fit();
this->triangle_indices.shrink_to_fit();
this->quad_indices.shrink_to_fit();
}
void Volume::render()
{
glsafe(::glPushMatrix());
glsafe(::glMultMatrixd(m_trafo.get_matrix().data()));
m_geom.render();
glsafe(::glPopMatrix());
}
void Display::clear_screen()
{
glViewport(0, 0, GLsizei(m_size.x()), GLsizei(m_size.y()));
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
Display::~Display()
{
OpenCSG::freeResources();
}
void Display::set_active(long width, long height)
{
if (!m_initialized) {
glewInit();
m_initialized = true;
}
// gray background
glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
// Enable two OpenGL lights
GLfloat light_diffuse[] = { 1.0f, 1.0f, 0.0f, 1.0f}; // White diffuse light
GLfloat light_position0[] = {-1.0f, -1.0f, -1.0f, 0.0f}; // Infinite light location
GLfloat light_position1[] = { 1.0f, 1.0f, 1.0f, 0.0f}; // Infinite light location
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_POSITION, light_position0);
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT1, GL_POSITION, light_position1);
glEnable(GL_LIGHT1);
glEnable(GL_LIGHTING);
glEnable(GL_NORMALIZE);
// Use depth buffering for hidden surface elimination
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
set_screen_size(width, height);
}
void Display::set_screen_size(long width, long height)
{
if (m_size.x() != width || m_size.y() != height)
m_camera->set_screen(width, height);
m_size = {width, height};
}
void Display::repaint()
{
clear_screen();
m_camera->view();
render_scene();
m_fps_counter.update();
swap_buffers();
}
void Controller::on_scene_updated(const Scene &scene)
{
const SLAPrint *print = scene.get_print();
if (!print) return;
auto bb = scene.get_bounding_box();
double d = std::max(std::max(bb.size().x(), bb.size().y()), bb.size().z());
m_wheel_pos = long(2 * d);
call_cameras(&Camera::set_zoom, m_wheel_pos);
call(&Display::on_scene_updated, m_displays, scene);
}
void Controller::on_scroll(long v, long d, MouseInput::WheelAxis /*wa*/)
{
m_wheel_pos += v / d;
call_cameras(&Camera::set_zoom, m_wheel_pos);
call(&Display::repaint, m_displays);
}
void Controller::on_moved_to(long x, long y)
{
if (m_left_btn) {
call_cameras(&Camera::rotate, (Vec2i{x, y} - m_mouse_pos).cast<float>());
call(&Display::repaint, m_displays);
}
m_mouse_pos = {x, y};
}
void CSGDisplay::apply_csgsettings(const CSGSettings &settings)
{
using namespace OpenCSG;
bool needupdate = m_csgsettings.get_convexity() != settings.get_convexity();
m_csgsettings = settings;
setOption(AlgorithmSetting, m_csgsettings.get_algo());
setOption(DepthComplexitySetting, m_csgsettings.get_depth_algo());
setOption(DepthBoundsOptimization, m_csgsettings.get_optimization());
if (needupdate) {
for (OpenCSG::Primitive * p : m_scene_cache.primitives_csg)
if (p->getConvexity() > 1)
p->setConvexity(m_csgsettings.get_convexity());
}
}
void CSGDisplay::on_scene_updated(const Scene &scene)
{
const SLAPrint *print = scene.get_print();
if (!print) return;
m_scene_cache.clear();
for (const SLAPrintObject *po : print->objects()) {
const ModelObject *mo = po->model_object();
TriangleMesh msh = mo->raw_mesh();
sla::DrainHoles holedata = mo->sla_drain_holes;
for (const ModelInstance *mi : mo->instances) {
TriangleMesh mshinst = msh;
auto interior = po->hollowed_interior_mesh();
interior.transform(po->trafo().inverse());
mshinst.merge(interior);
mshinst.require_shared_vertices();
mi->transform_mesh(&mshinst);
auto bb = mshinst.bounding_box();
auto center = bb.center().cast<float>();
mshinst.translate(-center);
mshinst.require_shared_vertices();
m_scene_cache.add_mesh(mshinst, OpenCSG::Intersection,
m_csgsettings.get_convexity());
}
for (const sla::DrainHole &holept : holedata) {
TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh());
holemesh.require_shared_vertices();
m_scene_cache.add_mesh(holemesh, OpenCSG::Subtraction, 1);
}
}
repaint();
}
void Camera::view()
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, m_zoom, 0.0, /* eye is at (0,zoom,0) */
m_referene.x(), m_referene.y(), m_referene.z(),
0.0, 0.0, 1.0); /* up is in positive Y direction */
// TODO Could have been set in prevoius gluLookAt in first argument
glRotatef(m_rot.y(), 1.0, 0.0, 0.0);
glRotatef(m_rot.x(), 0.0, 0.0, 1.0);
if (m_clip_z > 0.) {
GLdouble plane[] = {0., 0., 1., m_clip_z};
glClipPlane(GL_CLIP_PLANE0, plane);
glEnable(GL_CLIP_PLANE0);
} else {
glDisable(GL_CLIP_PLANE0);
}
}
void PerspectiveCamera::set_screen(long width, long height)
{
// Setup the view of the CSG shape
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, width / double(height), .1, 200.0);
glMatrixMode(GL_MODELVIEW);
}
bool enable_multisampling(bool e)
{
if (!e) { glDisable(GL_MULTISAMPLE); return false; }
GLint is_ms_context;
glGetIntegerv(GL_SAMPLE_BUFFERS, &is_ms_context);
if (is_ms_context) { glEnable(GL_MULTISAMPLE); return true; }
else return false;
}
MouseInput::Listener::~Listener() = default;
void FpsCounter::update()
{
++m_frames;
TimePoint msec = Clock::now();
double seconds_window = to_sec(msec - m_window);
m_fps = 0.5 * m_fps + 0.5 * (m_frames / seconds_window);
if (to_sec(msec - m_last) >= m_resolution) {
m_last = msec;
for (auto &l : m_listeners) l(m_fps);
}
if (seconds_window >= m_window_size) {
m_frames = 0;
m_window = msec;
}
}
}} // namespace Slic3r::GL

View file

@ -0,0 +1,493 @@
#ifndef SLIC3R_OCSG_EXMP_ENGINE_HPP
#define SLIC3R_OCSG_EXMP_ENGINE_HPP
#include <vector>
#include <memory>
#include <chrono>
#include <libslic3r/Geometry.hpp>
#include <libslic3r/Model.hpp>
#include <libslic3r/TriangleMesh.hpp>
#include <libslic3r/SLA/Hollowing.hpp>
#include <opencsg/opencsg.h>
namespace Slic3r {
class SLAPrint;
namespace GL {
// Simple shorthands for smart pointers
template<class T> using shptr = std::shared_ptr<T>;
template<class T> using uqptr = std::unique_ptr<T>;
template<class T> using wkptr = std::weak_ptr<T>;
template<class T, class A = std::allocator<T>> using vector = std::vector<T, A>;
// remove empty weak pointers from a vector
template<class L> inline void cleanup(vector<std::weak_ptr<L>> &listeners) {
auto it = std::remove_if(listeners.begin(), listeners.end(),
[](auto &l) { return !l.lock(); });
listeners.erase(it, listeners.end());
}
// Call a class method on each element of a vector of objects (weak pointers)
// of the same type.
template<class F, class L, class...Args>
inline void call(F &&f, vector<std::weak_ptr<L>> &listeners, Args&&... args) {
for (auto &l : listeners)
if (auto p = l.lock()) ((p.get())->*f)(std::forward<Args>(args)...);
}
// A representation of a mouse input for the engine.
class MouseInput
{
public:
enum WheelAxis { waVertical, waHorizontal };
// Interface to implement if an object wants to receive notifications
// about mouse events.
class Listener {
public:
virtual ~Listener();
virtual void on_left_click_down() {}
virtual void on_left_click_up() {}
virtual void on_right_click_down() {}
virtual void on_right_click_up() {}
virtual void on_double_click() {}
virtual void on_scroll(long /*v*/, long /*delta*/, WheelAxis ) {}
virtual void on_moved_to(long /*x*/, long /*y*/) {}
};
private:
vector<wkptr<Listener>> m_listeners;
public:
virtual ~MouseInput() = default;
virtual void left_click_down()
{
call(&Listener::on_left_click_down, m_listeners);
}
virtual void left_click_up()
{
call(&Listener::on_left_click_up, m_listeners);
}
virtual void right_click_down()
{
call(&Listener::on_right_click_down, m_listeners);
}
virtual void right_click_up()
{
call(&Listener::on_right_click_up, m_listeners);
}
virtual void double_click()
{
call(&Listener::on_double_click, m_listeners);
}
virtual void scroll(long v, long d, WheelAxis wa)
{
call(&Listener::on_scroll, m_listeners, v, d, wa);
}
virtual void move_to(long x, long y)
{
call(&Listener::on_moved_to, m_listeners, x, y);
}
void add_listener(shptr<Listener> listener)
{
m_listeners.emplace_back(listener);
cleanup(m_listeners);
}
};
// This is a stripped down version of Slic3r::IndexedVertexArray
class IndexedVertexArray {
public:
~IndexedVertexArray() { release_geometry(); }
// Vertices and their normals, interleaved to be used by void
// glInterleavedArrays(GL_N3F_V3F, 0, x)
vector<float> vertices_and_normals_interleaved;
vector<int> triangle_indices;
vector<int> quad_indices;
// When the geometry data is loaded into the graphics card as Vertex
// Buffer Objects, the above mentioned std::vectors are cleared and the
// following variables keep their original length.
size_t vertices_and_normals_interleaved_size{ 0 };
size_t triangle_indices_size{ 0 };
size_t quad_indices_size{ 0 };
// IDs of the Vertex Array Objects, into which the geometry has been loaded.
// Zero if the VBOs are not sent to GPU yet.
unsigned int vertices_and_normals_interleaved_VBO_id{ 0 };
unsigned int triangle_indices_VBO_id{ 0 };
unsigned int quad_indices_VBO_id{ 0 };
void push_geometry(float x, float y, float z, float nx, float ny, float nz);
inline void push_geometry(
double x, double y, double z, double nx, double ny, double nz)
{
push_geometry(float(x), float(y), float(z), float(nx), float(ny), float(nz));
}
inline void push_geometry(const Vec3d &p, const Vec3d &n)
{
push_geometry(p(0), p(1), p(2), n(0), n(1), n(2));
}
void push_triangle(int idx1, int idx2, int idx3);
void load_mesh(const TriangleMesh &mesh);
inline bool has_VBOs() const
{
return vertices_and_normals_interleaved_VBO_id != 0;
}
// Finalize the initialization of the geometry & indices,
// upload the geometry and indices to OpenGL VBO objects
// and shrink the allocated data, possibly relasing it if it has been
// loaded into the VBOs.
void finalize_geometry();
// Release the geometry data, release OpenGL VBOs.
void release_geometry();
void render() const;
// Is there any geometry data stored?
bool empty() const { return vertices_and_normals_interleaved_size == 0; }
void clear();
// Shrink the internal storage to tighly fit the data stored.
void shrink_to_fit();
};
// Try to enable or disable multisampling.
bool enable_multisampling(bool e = true);
class Volume {
IndexedVertexArray m_geom;
Geometry::Transformation m_trafo;
public:
void render();
void translation(const Vec3d &offset) { m_trafo.set_offset(offset); }
void rotation(const Vec3d &rot) { m_trafo.set_rotation(rot); }
void scale(const Vec3d &scaleing) { m_trafo.set_scaling_factor(scaleing); }
void scale(double s) { scale({s, s, s}); }
inline void load_mesh(const TriangleMesh &mesh)
{
m_geom.load_mesh(mesh);
m_geom.finalize_geometry();
}
};
// A primitive that can be used with OpenCSG rendering algorithms.
// Does a similar job to GLVolume.
class Primitive : public Volume, public OpenCSG::Primitive
{
public:
using OpenCSG::Primitive::Primitive;
Primitive() : OpenCSG::Primitive(OpenCSG::Intersection, 1) {}
void render() override { Volume::render(); }
};
// A simple representation of a camera in a 3D scene
class Camera {
protected:
Vec2f m_rot = {0., 0.};
Vec3d m_referene = {0., 0., 0.};
double m_zoom = 0.;
double m_clip_z = 0.;
public:
virtual ~Camera() = default;
virtual void view();
virtual void set_screen(long width, long height) = 0;
void set_rotation(const Vec2f &rotation) { m_rot = rotation; }
void rotate(const Vec2f &rotation) { m_rot += rotation; }
void set_zoom(double z) { m_zoom = z; }
void set_reference_point(const Vec3d &p) { m_referene = p; }
void set_clip_z(double z) { m_clip_z = z; }
};
// Reset a camera object
inline void reset(Camera &cam)
{
cam.set_rotation({0., 0.});
cam.set_zoom(0.);
cam.set_reference_point({0., 0., 0.});
cam.set_clip_z(0.);
}
// Specialization of a camera which shows in perspective projection
class PerspectiveCamera: public Camera {
public:
void set_screen(long width, long height) override;
};
// A simple counter of FPS. Subscribed objects will receive updates of the
// current fps.
class FpsCounter {
vector<std::function<void(double)>> m_listeners;
using Clock = std::chrono::high_resolution_clock;
using Duration = Clock::duration;
using TimePoint = Clock::time_point;
int m_frames = 0;
TimePoint m_last = Clock::now(), m_window = m_last;
double m_resolution = 0.1, m_window_size = 1.0;
double m_fps = 0.;
static double to_sec(Duration d)
{
return d.count() * double(Duration::period::num) / Duration::period::den;
}
public:
void update();
void add_listener(std::function<void(double)> lst)
{
m_listeners.emplace_back(lst);
}
void clear_listeners() { m_listeners = {}; }
void set_notification_interval(double seconds);
void set_measure_window_size(double seconds);
double get_notification_interval() const { return m_resolution; }
double get_mesure_window_size() const { return m_window_size; }
};
// Collection of the used OpenCSG library settings.
class CSGSettings {
public:
static const constexpr unsigned DEFAULT_CONVEXITY = 10;
private:
OpenCSG::Algorithm m_csgalg = OpenCSG::Algorithm::Automatic;
OpenCSG::DepthComplexityAlgorithm m_depth_algo = OpenCSG::NoDepthComplexitySampling;
OpenCSG::Optimization m_optim = OpenCSG::OptimizationDefault;
bool m_enable = true;
unsigned int m_convexity = DEFAULT_CONVEXITY;
public:
int get_algo() const { return int(m_csgalg); }
void set_algo(int alg)
{
if (alg < OpenCSG::Algorithm::AlgorithmUnused)
m_csgalg = OpenCSG::Algorithm(alg);
}
int get_depth_algo() const { return int(m_depth_algo); }
void set_depth_algo(int alg)
{
if (alg < OpenCSG::DepthComplexityAlgorithmUnused)
m_depth_algo = OpenCSG::DepthComplexityAlgorithm(alg);
}
int get_optimization() const { return int(m_optim); }
void set_optimization(int o)
{
if (o < OpenCSG::Optimization::OptimizationUnused)
m_optim = OpenCSG::Optimization(o);
}
void enable_csg(bool en = true) { m_enable = en; }
bool is_enabled() const { return m_enable; }
unsigned get_convexity() const { return m_convexity; }
void set_convexity(unsigned c) { m_convexity = c; }
};
// The scene is a wrapper around SLAPrint which holds the data to be visualized.
class Scene
{
uqptr<SLAPrint> m_print;
public:
// Subscribers will be notified if the model is changed. This might be a
// display which will have to load the meshes and repaint itself when
// the scene data changes.
// eg. We load a new 3mf through the UI, this will notify the controller
// associated with the scene and all the displays that the controller is
// connected with.
class Listener {
public:
virtual ~Listener() = default;
virtual void on_scene_updated(const Scene &scene) = 0;
};
Scene();
~Scene();
void set_print(uqptr<SLAPrint> &&print);
const SLAPrint * get_print() const { return m_print.get(); }
BoundingBoxf3 get_bounding_box() const;
void add_listener(shptr<Listener> listener)
{
m_listeners.emplace_back(listener);
cleanup(m_listeners);
}
private:
vector<wkptr<Listener>> m_listeners;
};
// The basic Display. This is almost just an interface but will do all the
// initialization and show the fps values. Overriding the render_scene is
// needed to show the scene content. The specific method of displaying the
// scene is up the the particular implementation (OpenCSG or other screen space
// boolean algorithms)
class Display : public Scene::Listener
{
protected:
Vec2i m_size;
bool m_initialized = false;
shptr<Camera> m_camera;
FpsCounter m_fps_counter;
public:
explicit Display(shptr<Camera> camera = nullptr)
: m_camera(camera ? camera : std::make_shared<PerspectiveCamera>())
{}
~Display() override;
shptr<const Camera> get_camera() const { return m_camera; }
shptr<Camera> get_camera() { return m_camera; }
void set_camera(shptr<Camera> cam) { m_camera = cam; }
virtual void swap_buffers() = 0;
virtual void set_active(long width, long height);
virtual void set_screen_size(long width, long height);
Vec2i get_screen_size() const { return m_size; }
virtual void repaint();
bool is_initialized() const { return m_initialized; }
virtual void clear_screen();
virtual void render_scene() {}
template<class _FpsCounter> void set_fps_counter(_FpsCounter &&fpsc)
{
m_fps_counter = std::forward<_FpsCounter>(fpsc);
}
const FpsCounter &get_fps_counter() const { return m_fps_counter; }
FpsCounter &get_fps_counter() { return m_fps_counter; }
};
// Special dispaly using OpenCSG for rendering the scene.
class CSGDisplay : public Display {
protected:
CSGSettings m_csgsettings;
// Cache the renderable primitives. These will be fetched when the scene
// is modified.
struct SceneCache {
vector<shptr<Primitive>> primitives;
vector<Primitive *> primitives_free;
vector<OpenCSG::Primitive *> primitives_csg;
void clear();
shptr<Primitive> add_mesh(const TriangleMesh &mesh);
shptr<Primitive> add_mesh(const TriangleMesh &mesh,
OpenCSG::Operation op,
unsigned covexity);
} m_scene_cache;
public:
// Receive or apply the new settings.
const CSGSettings & get_csgsettings() const { return m_csgsettings; }
void apply_csgsettings(const CSGSettings &settings);
void render_scene() override;
void on_scene_updated(const Scene &scene) override;
};
// The controller is a hub which dispatches mouse events to the connected
// displays. It keeps track of the mouse wheel position, the states whether
// the mouse is being held, dragged, etc... All the connected displays will
// mirror the camera movement (if there is more than one display).
class Controller : public std::enable_shared_from_this<Controller>,
public MouseInput::Listener,
public Scene::Listener
{
long m_wheel_pos = 0;
Vec2i m_mouse_pos, m_mouse_pos_rprev, m_mouse_pos_lprev;
bool m_left_btn = false, m_right_btn = false;
shptr<Scene> m_scene;
vector<wkptr<Display>> m_displays;
// Call a method of Camera on all the cameras of the attached displays
template<class F, class...Args>
void call_cameras(F &&f, Args&&... args) {
for (wkptr<Display> &l : m_displays)
if (auto disp = l.lock()) if (auto cam = disp->get_camera())
(cam.get()->*f)(std::forward<Args>(args)...);
}
public:
// Set the scene that will be controlled.
void set_scene(shptr<Scene> scene)
{
m_scene = scene;
m_scene->add_listener(shared_from_this());
}
const Scene * get_scene() const { return m_scene.get(); }
void add_display(shptr<Display> disp)
{
m_displays.emplace_back(disp);
cleanup(m_displays);
}
void remove_displays() { m_displays = {}; }
void on_scene_updated(const Scene &scene) override;
void on_left_click_down() override { m_left_btn = true; }
void on_left_click_up() override { m_left_btn = false; }
void on_right_click_down() override { m_right_btn = true; }
void on_right_click_up() override { m_right_btn = false; }
void on_scroll(long v, long d, MouseInput::WheelAxis wa) override;
void on_moved_to(long x, long y) override;
void move_clip_plane(double z) { call_cameras(&Camera::set_clip_z, z); }
};
}} // namespace Slic3r::GL
#endif // SLIC3R_OCSG_EXMP_ENGINE_HPP

View file

@ -0,0 +1,68 @@
#include "ShaderCSGDisplay.hpp"
#include "libslic3r/SLAPrint.hpp"
#include <GL/glew.h>
namespace Slic3r { namespace GL {
void ShaderCSGDisplay::add_mesh(const TriangleMesh &mesh)
{
auto v = std::make_shared<CSGVolume>();
v->load_mesh(mesh);
m_volumes.emplace_back(v);
}
void ShaderCSGDisplay::render_scene()
{
GLfloat color[] = {1.f, 1.f, 0.f, 0.f};
glColor4fv(color);
glDepthFunc(GL_LESS);
for (auto &v : m_volumes) v->render();
glFlush();
}
void ShaderCSGDisplay::on_scene_updated(const Scene &scene)
{
// TriangleMesh mesh = print->objects().front()->hollowed_interior_mesh();
// Look at CSGDisplay::on_scene_updated to see how its done there.
const SLAPrint *print = scene.get_print();
if (!print) return;
m_volumes.clear();
for (const SLAPrintObject *po : print->objects()) {
const ModelObject *mo = po->model_object();
TriangleMesh msh = mo->raw_mesh();
sla::DrainHoles holedata = mo->sla_drain_holes;
for (const ModelInstance *mi : mo->instances) {
TriangleMesh mshinst = msh;
auto interior = po->hollowed_interior_mesh();
interior.transform(po->trafo().inverse());
mshinst.merge(interior);
mshinst.require_shared_vertices();
mi->transform_mesh(&mshinst);
auto bb = mshinst.bounding_box();
auto center = bb.center().cast<float>();
mshinst.translate(-center);
mshinst.require_shared_vertices();
add_mesh(mshinst);
}
for (const sla::DrainHole &holept : holedata) {
TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh());
holemesh.require_shared_vertices();
add_mesh(holemesh);
}
}
repaint();
}
}} // namespace Slic3r::GL

View file

@ -0,0 +1,27 @@
#ifndef SHADERCSGDISPLAY_HPP
#define SHADERCSGDISPLAY_HPP
#include "Engine.hpp"
namespace Slic3r { namespace GL {
class CSGVolume: public Volume
{
// Extend...
};
class ShaderCSGDisplay: public Display {
protected:
vector<shptr<CSGVolume>> m_volumes;
void add_mesh(const TriangleMesh &mesh);
public:
void render_scene() override;
void on_scene_updated(const Scene &scene) override;
};
}}
#endif // SHADERCSGDISPLAY_HPP

734
sandboxes/opencsg/main.cpp Normal file
View file

@ -0,0 +1,734 @@
#include <iostream>
#include <utility>
#include <memory>
#include "Engine.hpp"
#include "ShaderCSGDisplay.hpp"
#include <GL/glew.h>
#include <opencsg/opencsg.h>
// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/slider.h>
#include <wx/tglbtn.h>
#include <wx/combobox.h>
#include <wx/spinctrl.h>
#include <wx/msgdlg.h>
#include <wx/glcanvas.h>
#include <wx/cmdline.h>
#include "libslic3r/Model.hpp"
#include "libslic3r/Format/3mf.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "slic3r/GUI/Job.hpp"
#include "slic3r/GUI/ProgressStatusBar.hpp"
using namespace Slic3r::GL;
class Renderer {
protected:
wxGLCanvas *m_canvas;
shptr<wxGLContext> m_context;
public:
Renderer(wxGLCanvas *c): m_canvas{c} {
auto ctx = new wxGLContext(m_canvas);
if (!ctx || !ctx->IsOK()) {
wxMessageBox("Could not create OpenGL context.", "Error",
wxOK | wxICON_ERROR);
return;
}
m_context.reset(ctx);
}
wxGLContext * context() { return m_context.get(); }
const wxGLContext * context() const { return m_context.get(); }
};
// Tell the CSGDisplay how to swap buffers and set the gl context.
class OCSGRenderer: public Renderer, public Slic3r::GL::CSGDisplay {
public:
OCSGRenderer(wxGLCanvas *c): Renderer{c} {}
void set_active(long w, long h) override
{
m_canvas->SetCurrent(*m_context);
Slic3r::GL::Display::set_active(w, h);
}
void swap_buffers() override { m_canvas->SwapBuffers(); }
};
// Tell the CSGDisplay how to swap buffers and set the gl context.
class ShaderCSGRenderer : public Renderer, public Slic3r::GL::ShaderCSGDisplay {
public:
ShaderCSGRenderer(wxGLCanvas *c): Renderer{c} {}
void set_active(long w, long h) override
{
m_canvas->SetCurrent(*m_context);
Slic3r::GL::Display::set_active(w, h);
}
void swap_buffers() override { m_canvas->SwapBuffers(); }
};
// The opengl rendering facility. Here we implement the rendering objects.
class Canvas: public wxGLCanvas
{
// One display is active at a time, the OCSGRenderer by default.
shptr<Slic3r::GL::Display> m_display;
public:
template<class...Args>
Canvas(Args &&...args): wxGLCanvas(std::forward<Args>(args)...) {}
shptr<Slic3r::GL::Display> get_display() const { return m_display; }
void set_display(shptr<Slic3r::GL::Display> d) { m_display = d; }
};
// Enumerate possible mouse events, we will record them.
enum EEvents { LCLK_U, RCLK_U, LCLK_D, RCLK_D, DDCLK, SCRL, MV };
struct Event
{
EEvents type;
long a, b;
Event(EEvents t, long x = 0, long y = 0) : type{t}, a{x}, b{y} {}
};
// Create a special mouse input adapter, which can store (record) the received
// mouse signals into a file and play back the stored events later.
class RecorderMouseInput: public MouseInput {
std::vector<Event> m_events;
bool m_recording = false, m_playing = false;
public:
void left_click_down() override
{
if (m_recording) m_events.emplace_back(LCLK_D);
if (!m_playing) MouseInput::left_click_down();
}
void left_click_up() override
{
if (m_recording) m_events.emplace_back(LCLK_U);
if (!m_playing) MouseInput::left_click_up();
}
void right_click_down() override
{
if (m_recording) m_events.emplace_back(RCLK_D);
if (!m_playing) MouseInput::right_click_down();
}
void right_click_up() override
{
if (m_recording) m_events.emplace_back(RCLK_U);
if (!m_playing) MouseInput::right_click_up();
}
void double_click() override
{
if (m_recording) m_events.emplace_back(DDCLK);
if (!m_playing) MouseInput::double_click();
}
void scroll(long v, long d, WheelAxis wa) override
{
if (m_recording) m_events.emplace_back(SCRL, v, d);
if (!m_playing) MouseInput::scroll(v, d, wa);
}
void move_to(long x, long y) override
{
if (m_recording) m_events.emplace_back(MV, x, y);
if (!m_playing) MouseInput::move_to(x, y);
}
void save(std::ostream &stream)
{
for (const Event &evt : m_events)
stream << evt.type << " " << evt.a << " " << evt.b << std::endl;
}
void load(std::istream &stream)
{
m_events.clear();
while (stream.good()) {
int type; long a, b;
stream >> type >> a >> b;
m_events.emplace_back(EEvents(type), a, b);
}
}
void record(bool r) { m_recording = r; if (r) m_events.clear(); }
void play()
{
m_playing = true;
for (const Event &evt : m_events) {
switch (evt.type) {
case LCLK_U: MouseInput::left_click_up(); break;
case LCLK_D: MouseInput::left_click_down(); break;
case RCLK_U: MouseInput::right_click_up(); break;
case RCLK_D: MouseInput::right_click_down(); break;
case DDCLK: MouseInput::double_click(); break;
case SCRL: MouseInput::scroll(evt.a, evt.b, WheelAxis::waVertical); break;
case MV: MouseInput::move_to(evt.a, evt.b); break;
}
wxTheApp->Yield();
if (!m_playing)
break;
}
m_playing = false;
}
void stop() { m_playing = false; }
bool is_playing() const { return m_playing; }
};
// The top level frame of the application.
class MyFrame: public wxFrame
{
// Instantiate the 3D engine.
shptr<Scene> m_scene; // Model
shptr<Canvas> m_canvas; // Views store
shptr<OCSGRenderer> m_ocsgdisplay; // View
shptr<ShaderCSGRenderer> m_shadercsg_display; // Another view
shptr<Controller> m_ctl; // Controller
// Add a status bar with progress indication.
shptr<Slic3r::GUI::ProgressStatusBar> m_stbar;
RecorderMouseInput m_mouse;
// When loading a Model from 3mf and preparing it, we use a separate thread.
class SLAJob: public Slic3r::GUI::Job {
MyFrame *m_parent;
std::unique_ptr<Slic3r::SLAPrint> m_print;
std::string m_fname;
public:
SLAJob(MyFrame *frame, const std::string &fname)
: Slic3r::GUI::Job{frame->m_stbar}
, m_parent{frame}
, m_fname{fname}
{}
// Runs in separate thread
void process() override;
const std::string & get_project_fname() const { return m_fname; }
protected:
// Runs in the UI thread.
void finalize() override
{
m_parent->m_scene->set_print(std::move(m_print));
m_parent->m_stbar->set_status_text(
wxString::Format("Model %s loaded.", m_fname));
}
};
uqptr<SLAJob> m_ui_job;
// To keep track of the running average of measured fps values.
double m_fps_avg = 0.;
// We need the record button across methods
wxToggleButton *m_record_btn;
wxComboBox * m_alg_select;
wxComboBox * m_depth_select;
wxComboBox * m_optimization_select;
wxSpinCtrl * m_convexity_spin;
wxToggleButton *m_csg_toggle;
wxToggleButton *m_ms_toggle;
wxStaticText *m_fpstext;
CSGSettings m_csg_settings;
void read_csg_settings(const wxCmdLineParser &parser);
void set_renderer_algorithm(const wxString &alg);
void activate_canvas_display();
public:
MyFrame(const wxString & title,
const wxPoint & pos,
const wxSize & size,
const wxCmdLineParser &parser);
// Grab a 3mf and load (hollow it out) within the UI job.
void load_model(const std::string &fname) {
m_ui_job = std::make_unique<SLAJob>(this, fname);
m_ui_job->start();
}
// Load a previously stored mouse event log and play it back.
void play_back_mouse(const std::string &events_fname)
{
std::fstream stream(events_fname, std::fstream::in);
if (stream.good()) {
std::string model_name;
std::getline(stream, model_name);
load_model(model_name);
while (!m_ui_job->is_finalized())
wxTheApp->Yield();;
int w, h;
stream >> w >> h;
SetSize(w, h);
m_mouse.load(stream);
if (m_record_btn) m_record_btn->Disable();
m_mouse.play();
}
}
Canvas * canvas() { return m_canvas.get(); }
const Canvas * canvas() const { return m_canvas.get(); }
// Bind the canvas mouse events to a class implementing MouseInput interface
void bind_canvas_events(MouseInput &msinput);
double get_fps_average() const { return m_fps_avg; }
};
// Possible OpenCSG configuration values. Will be used on the command line and
// on the UI widgets.
static const std::vector<wxString> CSG_ALGS = {"Auto", "Goldfeather", "SCS", "EnricoShader"};
static const std::vector<wxString> CSG_DEPTH = {"Off", "OcclusionQuery", "On"};
static const std::vector<wxString> CSG_OPT = { "Default", "ForceOn", "On", "Off" };
inline long get_idx(const wxString &a, const std::vector<wxString> &v)
{
auto it = std::find(v.begin(), v.end(), a.ToStdString());
return it - v.begin();
};
class App : public wxApp {
MyFrame *m_frame = nullptr;
wxString m_fname;
public:
bool OnInit() override {
wxCmdLineParser parser(argc, argv);
parser.AddOption("p", "play", "play back file", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
parser.AddOption("a", "algorithm", "OpenCSG algorithm [Auto|Goldfeather|SCS]", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
parser.AddOption("d", "depth", "OpenCSG depth strategy [Off|OcclusionQuery|On]", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
parser.AddOption("o", "optimization", "OpenCSG optimization strategy [Default|ForceOn|On|Off]", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
parser.AddOption("c", "convexity", "OpenCSG convexity parameter for generic meshes", wxCMD_LINE_VAL_NUMBER, wxCMD_LINE_PARAM_OPTIONAL);
parser.AddSwitch("", "disable-csg", "Disable csg rendering", wxCMD_LINE_PARAM_OPTIONAL);
parser.Parse();
bool is_play = parser.Found("play", &m_fname);
m_frame = new MyFrame("PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768), parser);
if (is_play) {
Bind(wxEVT_IDLE, &App::Play, this);
m_frame->Show( true );
} else m_frame->Show( true );
return true;
}
void Play(wxIdleEvent &) {
Unbind(wxEVT_IDLE, &App::Play, this);
m_frame->play_back_mouse(m_fname.ToStdString());
m_frame->Destroy();
}
};
wxIMPLEMENT_APP(App);
void MyFrame::read_csg_settings(const wxCmdLineParser &parser)
{
wxString alg;
parser.Found("algorithm", &alg);
wxString depth;
parser.Found("depth", &depth);
wxString opt;
parser.Found("optimization", &opt);
long convexity = 1;
parser.Found("convexity", &convexity);
bool csg_off = parser.Found("disable-csg");
if (auto a = get_idx(alg, CSG_ALGS) < OpenCSG::AlgorithmUnused)
m_csg_settings.set_algo(OpenCSG::Algorithm(a));
if (auto a = get_idx(depth, CSG_DEPTH) < OpenCSG::DepthComplexityAlgorithmUnused)
m_csg_settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(a));
if (auto a = get_idx(opt, CSG_OPT) < OpenCSG::OptimizationUnused)
m_csg_settings.set_optimization(OpenCSG::Optimization(a));
m_csg_settings.set_convexity(unsigned(convexity));
m_csg_settings.enable_csg(!csg_off);
if (m_ocsgdisplay) m_ocsgdisplay->apply_csgsettings(m_csg_settings);
}
void MyFrame::set_renderer_algorithm(const wxString &alg)
{
long alg_idx = get_idx(alg, CSG_ALGS);
if (alg_idx < 0 || alg_idx >= long(CSG_ALGS.size())) return;
// If there is a valid display in place, save its camera.
auto cam = m_canvas->get_display() ?
m_canvas->get_display()->get_camera() : nullptr;
if (alg == "EnricoShader") {
m_alg_select->SetSelection(int(alg_idx));
m_depth_select->Disable();
m_optimization_select->Disable();
m_csg_toggle->Disable();
m_ocsgdisplay.reset();
canvas()->set_display(nullptr);
m_shadercsg_display = std::make_shared<ShaderCSGRenderer>(canvas());
canvas()->set_display(m_shadercsg_display);
} else {
if (m_csg_settings.get_algo() > 0) m_depth_select->Enable(true);
m_alg_select->SetSelection(m_csg_settings.get_algo());
m_depth_select->SetSelection(m_csg_settings.get_depth_algo());
m_optimization_select->SetSelection(m_csg_settings.get_optimization());
m_convexity_spin->SetValue(int(m_csg_settings.get_convexity()));
m_csg_toggle->SetValue(m_csg_settings.is_enabled());
m_optimization_select->Enable();
m_csg_toggle->Enable();
m_shadercsg_display.reset();
canvas()->set_display(nullptr);
m_ocsgdisplay = std::make_shared<OCSGRenderer>(canvas());
m_ocsgdisplay->apply_csgsettings(m_csg_settings);
canvas()->set_display(m_ocsgdisplay);
}
if (cam)
m_canvas->get_display()->set_camera(cam);
m_ctl->remove_displays();
m_ctl->add_display(m_canvas->get_display());
m_canvas->get_display()->get_fps_counter().add_listener([this](double fps) {
m_fpstext->SetLabel(wxString::Format("fps: %.2f", fps));
m_fps_avg = 0.9 * m_fps_avg + 0.1 * fps;
});
if (IsShown()) {
activate_canvas_display();
m_canvas->get_display()->on_scene_updated(*m_scene);
}
}
void MyFrame::activate_canvas_display()
{
const wxSize ClientSize = m_canvas->GetClientSize();
m_canvas->get_display()->set_active(ClientSize.x, ClientSize.y);
enable_multisampling(m_ms_toggle->GetValue());
m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent &) {
// This is required even though dc is not used otherwise.
wxPaintDC dc(m_canvas.get());
const wxSize csize = m_canvas->GetClientSize();
m_canvas->get_display()->set_screen_size(csize.x, csize.y);
m_canvas->get_display()->repaint();
});
m_canvas->Bind(wxEVT_SIZE, [this](wxSizeEvent &) {
const wxSize csize = m_canvas->GetClientSize();
m_canvas->get_display()->set_screen_size(csize.x, csize.y);
m_canvas->get_display()->repaint();
});
// Do the repaint continuously
m_canvas->Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) {
m_canvas->get_display()->repaint();
evt.RequestMore();
});
bind_canvas_events(m_mouse);
}
MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size,
const wxCmdLineParser &parser):
wxFrame(nullptr, wxID_ANY, title, pos, size)
{
wxMenu *menuFile = new wxMenu;
menuFile->Append(wxID_OPEN);
menuFile->Append(wxID_EXIT);
wxMenuBar *menuBar = new wxMenuBar;
menuBar->Append( menuFile, "&File" );
SetMenuBar( menuBar );
m_stbar = std::make_shared<Slic3r::GUI::ProgressStatusBar>(this);
m_stbar->embed(this);
SetStatusText( "Welcome to wxWidgets!" );
int attribList[] =
{WX_GL_RGBA, WX_GL_DOUBLEBUFFER,
// RGB channels each should be allocated with 8 bit depth. One
// should almost certainly get these bit depths by default.
WX_GL_MIN_RED, 8, WX_GL_MIN_GREEN, 8, WX_GL_MIN_BLUE, 8,
// Requesting an 8 bit alpha channel. Interestingly, the NVIDIA
// drivers would most likely work with some alpha plane, but
// glReadPixels would not return the alpha channel on NVIDIA if
// not requested when the GL context is created.
WX_GL_MIN_ALPHA, 8, WX_GL_DEPTH_SIZE, 8, WX_GL_STENCIL_SIZE, 8,
WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0};
m_scene = std::make_shared<Scene>();
m_ctl = std::make_shared<Controller>();
m_ctl->set_scene(m_scene);
m_canvas = std::make_shared<Canvas>(this, wxID_ANY, attribList,
wxDefaultPosition, wxDefaultSize,
wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE);
read_csg_settings(parser);
wxPanel *control_panel = new wxPanel(this);
auto controlsizer = new wxBoxSizer(wxHORIZONTAL);
auto slider_sizer = new wxBoxSizer(wxVERTICAL);
auto console_sizer = new wxBoxSizer(wxVERTICAL);
auto slider = new wxSlider(control_panel, wxID_ANY, 0, 0, 100,
wxDefaultPosition, wxDefaultSize,
wxSL_VERTICAL);
slider_sizer->Add(slider, 1, wxEXPAND);
m_ms_toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling");
console_sizer->Add(m_ms_toggle, 0, wxALL | wxEXPAND, 5);
m_csg_toggle = new wxToggleButton(control_panel, wxID_ANY, "CSG");
m_csg_toggle->SetValue(true);
console_sizer->Add(m_csg_toggle, 0, wxALL | wxEXPAND, 5);
auto add_combobox = [control_panel, console_sizer]
(const wxString &label, const std::vector<wxString> &list)
{
auto widget = new wxComboBox(control_panel, wxID_ANY, list[0],
wxDefaultPosition, wxDefaultSize,
int(list.size()), list.data());
auto sz = new wxBoxSizer(wxHORIZONTAL);
sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0,
wxALL | wxALIGN_CENTER, 5);
sz->Add(widget, 1, wxALL | wxEXPAND, 5);
console_sizer->Add(sz, 0, wxEXPAND);
return widget;
};
auto add_spinctl = [control_panel, console_sizer]
(const wxString &label, int initial, int min, int max)
{
auto widget = new wxSpinCtrl(
control_panel, wxID_ANY,
wxString::Format("%d", initial),
wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max,
initial);
auto sz = new wxBoxSizer(wxHORIZONTAL);
sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0,
wxALL | wxALIGN_CENTER, 5);
sz->Add(widget, 1, wxALL | wxEXPAND, 5);
console_sizer->Add(sz, 0, wxEXPAND);
return widget;
};
m_convexity_spin = add_spinctl("Convexity", CSGSettings::DEFAULT_CONVEXITY, 0, 100);
m_alg_select = add_combobox("Algorithm", CSG_ALGS);
m_depth_select = add_combobox("Depth Complexity", CSG_DEPTH);
m_optimization_select = add_combobox("Optimization", CSG_OPT);
m_fpstext = new wxStaticText(control_panel, wxID_ANY, "");
console_sizer->Add(m_fpstext, 0, wxALL, 5);
m_record_btn = new wxToggleButton(control_panel, wxID_ANY, "Record");
console_sizer->Add(m_record_btn, 0, wxALL | wxEXPAND, 5);
controlsizer->Add(slider_sizer, 0, wxEXPAND);
controlsizer->Add(console_sizer, 1, wxEXPAND);
control_panel->SetSizer(controlsizer);
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_canvas.get(), 1, wxEXPAND);
sizer->Add(control_panel, 0, wxEXPAND);
SetSizer(sizer);
wxString alg;
if (!parser.Found("algorithm", &alg)) alg = "Auto";
set_renderer_algorithm(alg);
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt){
if (m_canvas) RemoveChild(m_canvas.get());
m_canvas.reset();
if (!m_mouse.is_playing()) evt.Skip();
else m_mouse.stop();
});
Bind(wxEVT_MENU, [this](wxCommandEvent &) {
wxFileDialog dlg(this, "Select project file", wxEmptyString,
wxEmptyString, "*.3mf", wxFD_OPEN|wxFD_FILE_MUST_EXIST);
if (dlg.ShowModal() == wxID_OK) load_model(dlg.GetPath().ToStdString());
}, wxID_OPEN);
Bind(wxEVT_MENU, [this](wxCommandEvent &) { Close(true); }, wxID_EXIT);
Bind(wxEVT_SHOW, [this](wxShowEvent &) {
activate_canvas_display();
});
Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) {
m_ctl->move_clip_plane(double(slider->GetValue()));
});
m_ms_toggle->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent &){
enable_multisampling(m_ms_toggle->GetValue());
m_canvas->get_display()->repaint();
});
m_csg_toggle->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent &){
CSGSettings stt = m_ocsgdisplay->get_csgsettings();
stt.enable_csg(m_csg_toggle->GetValue());
m_ocsgdisplay->apply_csgsettings(stt);
});
m_alg_select->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) {
wxString alg = m_alg_select->GetValue();
int sel = m_alg_select->GetSelection();
m_csg_settings.set_algo(sel);
set_renderer_algorithm(alg);
});
m_depth_select->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) {
int sel = m_depth_select->GetSelection();
m_csg_settings.set_depth_algo(sel);
if (m_ocsgdisplay) m_ocsgdisplay->apply_csgsettings(m_csg_settings);
});
m_optimization_select->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) {
int sel = m_optimization_select->GetSelection();
m_csg_settings.set_optimization(sel);
if (m_ocsgdisplay) m_ocsgdisplay->apply_csgsettings(m_csg_settings);
});
m_convexity_spin->Bind(wxEVT_SPINCTRL, [this](wxSpinEvent &) {
int c = m_convexity_spin->GetValue();
if (c > 0) {
m_csg_settings.set_convexity(unsigned(c));
if (m_ocsgdisplay) m_ocsgdisplay->apply_csgsettings(m_csg_settings);
}
});
m_record_btn->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent &) {
if (!m_ui_job) {
m_stbar->set_status_text("No project loaded!");
return;
}
if (m_record_btn->GetValue()) {
if (auto c = m_canvas->get_display()->get_camera()) reset(*c);
m_ctl->on_scene_updated(*m_scene);
m_mouse.record(true);
} else {
m_mouse.record(false);
wxFileDialog dlg(this, "Select output file",
wxEmptyString, wxEmptyString, "*.events",
wxFD_SAVE|wxFD_OVERWRITE_PROMPT);
if (dlg.ShowModal() == wxID_OK) {
std::fstream stream(dlg.GetPath().ToStdString(),
std::fstream::out);
if (stream.good()) {
stream << m_ui_job->get_project_fname() << "\n";
wxSize winsize = GetSize();
stream << winsize.x << " " << winsize.y << "\n";
m_mouse.save(stream);
}
}
}
});
}
void MyFrame::bind_canvas_events(MouseInput &ms)
{
m_canvas->Bind(wxEVT_MOUSEWHEEL, [&ms](wxMouseEvent &evt) {
ms.scroll(evt.GetWheelRotation(), evt.GetWheelDelta(),
evt.GetWheelAxis() == wxMOUSE_WHEEL_VERTICAL ?
Slic3r::GL::MouseInput::waVertical :
Slic3r::GL::MouseInput::waHorizontal);
});
m_canvas->Bind(wxEVT_MOTION, [&ms](wxMouseEvent &evt) {
ms.move_to(evt.GetPosition().x, evt.GetPosition().y);
});
m_canvas->Bind(wxEVT_RIGHT_DOWN, [&ms](wxMouseEvent & /*evt*/) {
ms.right_click_down();
});
m_canvas->Bind(wxEVT_RIGHT_UP, [&ms](wxMouseEvent & /*evt*/) {
ms.right_click_up();
});
m_canvas->Bind(wxEVT_LEFT_DOWN, [&ms](wxMouseEvent & /*evt*/) {
ms.left_click_down();
});
m_canvas->Bind(wxEVT_LEFT_UP, [&ms](wxMouseEvent & /*evt*/) {
ms.left_click_up();
});
ms.add_listener(m_ctl);
}
void MyFrame::SLAJob::process()
{
using SlStatus = Slic3r::PrintBase::SlicingStatus;
Slic3r::DynamicPrintConfig cfg;
auto model = Slic3r::Model::read_from_file(m_fname, &cfg);
m_print = std::make_unique<Slic3r::SLAPrint>();
m_print->apply(model, cfg);
Slic3r::PrintBase::TaskParams params;
params.to_object_step = Slic3r::slaposHollowing;
m_print->set_task(params);
m_print->set_status_callback([this](const SlStatus &status) {
update_status(status.percent, status.text);
});
try {
m_print->process();
} catch(std::exception &e) {
update_status(0, wxString("Exception during processing: ") + e.what());
}
}
//int main() {}

View file

@ -1,2 +1,7 @@
add_executable(openvdb_example openvdb_example.cpp) if(TARGET OpenVDB::openvdb)
target_link_libraries(openvdb_example libslic3r) add_executable(openvdb_example openvdb_example.cpp)
target_link_libraries(openvdb_example libslic3r)
target_link_libraries(openvdb_example OpenVDB::openvdb)
endif()

View file

@ -162,9 +162,25 @@ int CLI::run(int argc, char **argv)
// Initialize full print configs for both the FFF and SLA technologies. // Initialize full print configs for both the FFF and SLA technologies.
FullPrintConfig fff_print_config; FullPrintConfig fff_print_config;
// SLAFullPrintConfig sla_print_config; SLAFullPrintConfig sla_print_config;
// Synchronize the default parameters and the ones received on the command line.
if (printer_technology == ptFFF) {
fff_print_config.apply(m_print_config, true); fff_print_config.apply(m_print_config, true);
// sla_print_config.apply(m_print_config, true); m_print_config.apply(fff_print_config, true);
} else if (printer_technology == ptSLA) {
// The default value has to be different from the one in fff mode.
sla_print_config.output_filename_format.value = "[input_filename_base].sl1";
// The default bed shape should reflect the default display parameters
// and not the fff defaults.
double w = sla_print_config.display_width.getFloat();
double h = sla_print_config.display_height.getFloat();
sla_print_config.bed_shape.values = { Vec2d(0, 0), Vec2d(w, 0), Vec2d(w, h), Vec2d(0, h) };
sla_print_config.apply(m_print_config, true);
m_print_config.apply(sla_print_config, true);
}
// Loop through transform options. // Loop through transform options.
bool user_center_specified = false; bool user_center_specified = false;
@ -591,7 +607,7 @@ bool CLI::setup(int argc, char **argv)
// Initialize with defaults. // Initialize with defaults.
for (const t_optiondef_map *options : { &cli_actions_config_def.options, &cli_transform_config_def.options, &cli_misc_config_def.options }) for (const t_optiondef_map *options : { &cli_actions_config_def.options, &cli_transform_config_def.options, &cli_misc_config_def.options })
for (const std::pair<t_config_option_key, ConfigOptionDef> &optdef : *options) for (const std::pair<t_config_option_key, ConfigOptionDef> &optdef : *options)
m_config.optptr(optdef.first, true); m_config.option(optdef.first, true);
set_data_dir(m_config.opt_string("datadir")); set_data_dir(m_config.opt_string("datadir"));
@ -641,18 +657,10 @@ bool CLI::export_models(IO::ExportFormat format)
const std::string path = this->output_filepath(model, format); const std::string path = this->output_filepath(model, format);
bool success = false; bool success = false;
switch (format) { switch (format) {
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr, false); break; case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr, false); break;
#else
case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr); break;
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
case IO::OBJ: success = Slic3r::store_obj(path.c_str(), &model); break; case IO::OBJ: success = Slic3r::store_obj(path.c_str(), &model); break;
case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true); break; case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true); break;
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr, false); break; case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr, false); break;
#else
case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr); break;
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
default: assert(false); break; default: assert(false); break;
} }
if (success) if (success)

View file

@ -635,13 +635,30 @@ inline bool igl::copyleft::cgal::SelfIntersectMesh<
{ {
using namespace std; using namespace std;
// must be co-planar auto opposite_vertex = [](const Index a0, const Index a1) {
if( // get opposite index of A
A.supporting_plane() != B.supporting_plane() && int a2=-1;
A.supporting_plane() != B.supporting_plane().opposite()) for(int c=0;c<3;++c)
{ if(c!=a0 && c!=a1) {
return false; a2 = c;
break;
} }
assert(a2 != -1);
return a2;
};
// must be co-planar
Index a2 = opposite_vertex(shared[0].first, shared[1].first);
if (! B.supporting_plane().has_on(A.vertex(a2)))
return false;
Index b2 = opposite_vertex(shared[0].second, shared[1].second);
if (int(CGAL::coplanar_orientation(A.vertex(shared[0].first), A.vertex(shared[1].first), A.vertex(a2))) *
int(CGAL::coplanar_orientation(B.vertex(shared[0].second), B.vertex(shared[1].second), B.vertex(b2))) < 0)
// There is certainly no self intersection as the non-shared triangle vertices lie on opposite sides of the shared edge.
return false;
// Since A and B are non-degenerate the intersection must be a polygon // Since A and B are non-degenerate the intersection must be a polygon
// (triangle). Either // (triangle). Either
// - the vertex of A (B) opposite the shared edge of lies on B (A), or // - the vertex of A (B) opposite the shared edge of lies on B (A), or
@ -650,22 +667,10 @@ inline bool igl::copyleft::cgal::SelfIntersectMesh<
// Determine if the vertex opposite edge (a0,a1) in triangle A lies in // Determine if the vertex opposite edge (a0,a1) in triangle A lies in
// (intersects) triangle B // (intersects) triangle B
const auto & opposite_point_inside = []( const auto & opposite_point_inside = [](
const Triangle_3 & A, const Index a0, const Index a1, const Triangle_3 & B) const Triangle_3 & A, const Index a2, const Triangle_3 & B)
-> bool -> bool
{ {
// get opposite index return CGAL::do_intersect(A.vertex(a2),B);
Index a2 = -1;
for(int c = 0;c<3;c++)
{
if(c != a0 && c != a1)
{
a2 = c;
break;
}
}
assert(a2 != -1);
bool ret = CGAL::do_intersect(A.vertex(a2),B);
return ret;
}; };
// Determine if edge opposite vertex va in triangle A intersects edge // Determine if edge opposite vertex va in triangle A intersects edge
@ -681,8 +686,8 @@ inline bool igl::copyleft::cgal::SelfIntersectMesh<
}; };
if( if(
!opposite_point_inside(A,shared[0].first,shared[1].first,B) && !opposite_point_inside(A,a2,B) &&
!opposite_point_inside(B,shared[0].second,shared[1].second,A) && !opposite_point_inside(B,b2,A) &&
!opposite_edges_intersect(A,shared[0].first,B,shared[1].second) && !opposite_edges_intersect(A,shared[0].first,B,shared[1].second) &&
!opposite_edges_intersect(A,shared[1].first,B,shared[0].second)) !opposite_edges_intersect(A,shared[1].first,B,shared[0].second))
{ {

View file

@ -9,6 +9,11 @@ if (MINGW)
add_compile_options(-Wa,-mbig-obj) add_compile_options(-Wa,-mbig-obj)
endif () endif ()
set(OpenVDBUtils_SOURCES "")
if (TARGET OpenVDB::openvdb)
set(OpenVDBUtils_SOURCES OpenVDBUtils.cpp OpenVDBUtils.hpp)
endif()
add_library(libslic3r STATIC add_library(libslic3r STATIC
pchheader.cpp pchheader.cpp
pchheader.hpp pchheader.hpp
@ -116,6 +121,8 @@ add_library(libslic3r STATIC
Line.hpp Line.hpp
Model.cpp Model.cpp
Model.hpp Model.hpp
CustomGCode.cpp
CustomGCode.hpp
Arrange.hpp Arrange.hpp
Arrange.cpp Arrange.cpp
MotionPlanner.cpp MotionPlanner.cpp
@ -149,9 +156,9 @@ add_library(libslic3r STATIC
ShortestPath.cpp ShortestPath.cpp
ShortestPath.hpp ShortestPath.hpp
SLAPrint.cpp SLAPrint.cpp
SLAPrintSteps.cpp
SLAPrintSteps.hpp
SLAPrint.hpp SLAPrint.hpp
SLA/SLAAutoSupports.hpp
SLA/SLAAutoSupports.cpp
Slicing.cpp Slicing.cpp
Slicing.hpp Slicing.hpp
SlicingAdaptive.cpp SlicingAdaptive.cpp
@ -180,35 +187,80 @@ add_library(libslic3r STATIC
MinAreaBoundingBox.cpp MinAreaBoundingBox.cpp
miniz_extension.hpp miniz_extension.hpp
miniz_extension.cpp miniz_extension.cpp
SLA/SLACommon.hpp SimplifyMesh.hpp
SLA/SLABoilerPlate.hpp SimplifyMeshImpl.hpp
SLA/SLAPad.hpp SimplifyMesh.cpp
SLA/SLAPad.cpp ${OpenVDBUtils_SOURCES}
SLA/SLASupportTreeBuilder.hpp SLA/Common.hpp
SLA/SLASupportTreeBuildsteps.hpp SLA/Common.cpp
SLA/SLASupportTreeBuildsteps.cpp SLA/Pad.hpp
SLA/SLASupportTreeBuilder.cpp SLA/Pad.cpp
SLA/SLAConcurrency.hpp SLA/SupportTreeBuilder.hpp
SLA/SLASupportTree.hpp SLA/SupportTreeBuildsteps.hpp
SLA/SLASupportTree.cpp SLA/SupportTreeBuildsteps.cpp
SLA/SLASupportTreeIGL.cpp SLA/SupportTreeBuilder.cpp
SLA/SLARotfinder.hpp SLA/Concurrency.hpp
SLA/SLARotfinder.cpp SLA/SupportTree.hpp
SLA/SLABoostAdapter.hpp SLA/SupportTree.cpp
SLA/SLASpatIndex.hpp # SLA/SupportTreeIGL.cpp
SLA/SLARaster.hpp SLA/Rotfinder.hpp
SLA/SLARaster.cpp SLA/Rotfinder.cpp
SLA/SLARasterWriter.hpp SLA/BoostAdapter.hpp
SLA/SLARasterWriter.cpp SLA/SpatIndex.hpp
SLA/Raster.hpp
SLA/Raster.cpp
SLA/RasterWriter.hpp
SLA/RasterWriter.cpp
SLA/ConcaveHull.hpp SLA/ConcaveHull.hpp
SLA/ConcaveHull.cpp SLA/ConcaveHull.cpp
SLA/Hollowing.hpp
SLA/Hollowing.cpp
SLA/JobController.hpp
SLA/SupportPoint.hpp
SLA/SupportPointGenerator.hpp
SLA/SupportPointGenerator.cpp
SLA/Contour3D.hpp
SLA/Contour3D.cpp
SLA/EigenMesh3D.hpp
SLA/Clustering.hpp
) )
encoding_check(libslic3r) if (SLIC3R_STATIC)
set(CGAL_Boost_USE_STATIC_LIBS ON CACHE BOOL "" FORCE)
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE)
endif () endif ()
set(CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE ON CACHE BOOL "" FORCE)
cmake_policy(PUSH)
cmake_policy(SET CMP0011 NEW)
find_package(CGAL REQUIRED)
cmake_policy(POP)
add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp)
target_include_directories(libslic3r_cgal PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
# Reset compile options of libslic3r_cgal. Despite it being linked privately, CGAL options
# (-frounding-math) still propagate to dependent libs which is not desired.
get_target_property(_cgal_tgt CGAL::CGAL ALIASED_TARGET)
if (NOT TARGET ${_cgal_tgt})
set (_cgal_tgt CGAL::CGAL)
endif ()
get_target_property(_opts ${_cgal_tgt} INTERFACE_COMPILE_OPTIONS)
if (_opts)
set(_opts_bad "${_opts}")
set(_opts_good "${_opts}")
list(FILTER _opts_bad INCLUDE REGEX frounding-math)
list(FILTER _opts_good EXCLUDE REGEX frounding-math)
set_target_properties(${_cgal_tgt} PROPERTIES INTERFACE_COMPILE_OPTIONS "${_opts_good}")
target_compile_options(libslic3r_cgal PRIVATE "${_opts_bad}")
endif()
target_link_libraries(libslic3r_cgal PRIVATE ${_cgal_tgt} libigl)
if (MSVC AND "${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") # 32 bit MSVC workaround
target_compile_definitions(libslic3r_cgal PRIVATE CGAL_DO_NOT_USE_MPZF)
endif ()
encoding_check(libslic3r)
target_compile_definitions(libslic3r PUBLIC -DUSE_TBB -DTBB_USE_CAPTURED_EXCEPTION=0) target_compile_definitions(libslic3r PUBLIC -DUSE_TBB -DTBB_USE_CAPTURED_EXCEPTION=0)
target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNEST2D_INCLUDES} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNEST2D_INCLUDES} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
@ -228,10 +280,14 @@ target_link_libraries(libslic3r
qhull qhull
semver semver
TBB::tbb TBB::tbb
# OpenVDB::openvdb libslic3r_cgal
${CMAKE_DL_LIBS} ${CMAKE_DL_LIBS}
) )
if (TARGET OpenVDB::openvdb)
target_link_libraries(libslic3r OpenVDB::openvdb)
endif()
if(WIN32) if(WIN32)
target_link_libraries(libslic3r Psapi.lib) target_link_libraries(libslic3r Psapi.lib)
endif() endif()
@ -239,3 +295,7 @@ endif()
if(SLIC3R_PROFILE) if(SLIC3R_PROFILE)
target_link_libraries(slic3r Shiny) target_link_libraries(slic3r Shiny)
endif() endif()
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE)
endif ()

View file

@ -757,6 +757,12 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
return opt; return opt;
} }
const ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key) const
{
auto it = options.find(opt_key);
return (it == options.end()) ? nullptr : it->second.get();
}
void DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra, t_config_option_keys* keys) void DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra, t_config_option_keys* keys)
{ {
std::vector<const char*> args; std::vector<const char*> args;

View file

@ -1494,8 +1494,49 @@ protected:
ConfigOptionDef* add_nullable(const t_config_option_key &opt_key, ConfigOptionType type); ConfigOptionDef* add_nullable(const t_config_option_key &opt_key, ConfigOptionType type);
}; };
// A pure interface to resolving ConfigOptions.
// This pure interface is useful as a base of ConfigBase, also it may be overriden to combine
// various config sources.
class ConfigOptionResolver
{
public:
ConfigOptionResolver() {}
virtual ~ConfigOptionResolver() {}
// Find a ConfigOption instance for a given name.
virtual const ConfigOption* optptr(const t_config_option_key &opt_key) const = 0;
bool has(const t_config_option_key &opt_key) const { return this->optptr(opt_key) != nullptr; }
const ConfigOption* option(const t_config_option_key &opt_key) const { return this->optptr(opt_key); }
template<typename TYPE>
const TYPE* option(const t_config_option_key& opt_key) const
{
const ConfigOption* opt = this->optptr(opt_key);
return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast<const TYPE*>(opt);
}
const ConfigOption* option_throw(const t_config_option_key& opt_key) const
{
const ConfigOption* opt = this->optptr(opt_key);
if (opt == nullptr)
throw UnknownOptionException(opt_key);
return opt;
}
template<typename TYPE>
const TYPE* option_throw(const t_config_option_key& opt_key) const
{
const ConfigOption* opt = this->option_throw(opt_key);
if (opt->type() != TYPE::static_type())
throw BadOptionTypeException("Conversion to a wrong type");
return static_cast<TYPE*>(opt);
}
};
// An abstract configuration store. // An abstract configuration store.
class ConfigBase class ConfigBase : public ConfigOptionResolver
{ {
public: public:
// Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling. // Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling.
@ -1503,7 +1544,7 @@ public:
// but it carries the defaults of the configuration values. // but it carries the defaults of the configuration values.
ConfigBase() {} ConfigBase() {}
virtual ~ConfigBase() {} ~ConfigBase() override {}
// Virtual overridables: // Virtual overridables:
public: public:
@ -1513,6 +1554,7 @@ public:
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0; virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0;
// Collect names of all configuration values maintained by this configuration store. // Collect names of all configuration values maintained by this configuration store.
virtual t_config_option_keys keys() const = 0; virtual t_config_option_keys keys() const = 0;
protected: protected:
// Verify whether the opt_key has not been obsoleted or renamed. // Verify whether the opt_key has not been obsoleted or renamed.
// Both opt_key and value may be modified by handle_legacy(). // Both opt_key and value may be modified by handle_legacy().
@ -1521,12 +1563,10 @@ protected:
virtual void handle_legacy(t_config_option_key &/*opt_key*/, std::string &/*value*/) const {} virtual void handle_legacy(t_config_option_key &/*opt_key*/, std::string &/*value*/) const {}
public: public:
using ConfigOptionResolver::option;
using ConfigOptionResolver::option_throw;
// Non-virtual methods: // Non-virtual methods:
bool has(const t_config_option_key &opt_key) const { return this->option(opt_key) != nullptr; }
const ConfigOption* option(const t_config_option_key &opt_key) const
{ return const_cast<ConfigBase*>(this)->option(opt_key, false); }
ConfigOption* option(const t_config_option_key &opt_key, bool create = false) ConfigOption* option(const t_config_option_key &opt_key, bool create = false)
{ return this->optptr(opt_key, create); } { return this->optptr(opt_key, create); }
@ -1537,10 +1577,6 @@ public:
return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast<TYPE*>(opt); return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast<TYPE*>(opt);
} }
template<typename TYPE>
const TYPE* option(const t_config_option_key &opt_key) const
{ return const_cast<ConfigBase*>(this)->option<TYPE>(opt_key, false); }
ConfigOption* option_throw(const t_config_option_key &opt_key, bool create = false) ConfigOption* option_throw(const t_config_option_key &opt_key, bool create = false)
{ {
ConfigOption *opt = this->optptr(opt_key, create); ConfigOption *opt = this->optptr(opt_key, create);
@ -1549,9 +1585,6 @@ public:
return opt; return opt;
} }
const ConfigOption* option_throw(const t_config_option_key &opt_key) const
{ return const_cast<ConfigBase*>(this)->option_throw(opt_key, false); }
template<typename TYPE> template<typename TYPE>
TYPE* option_throw(const t_config_option_key &opt_key, bool create = false) TYPE* option_throw(const t_config_option_key &opt_key, bool create = false)
{ {
@ -1561,10 +1594,6 @@ public:
return static_cast<TYPE*>(opt); return static_cast<TYPE*>(opt);
} }
template<typename TYPE>
const TYPE* option_throw(const t_config_option_key &opt_key) const
{ return const_cast<ConfigBase*>(this)->option_throw<TYPE>(opt_key, false); }
// Apply all keys of other ConfigBase defined by this->def() to this ConfigBase. // Apply all keys of other ConfigBase defined by this->def() to this ConfigBase.
// An UnknownOptionException is thrown in case some option keys of other are not defined by this->def(), // An UnknownOptionException is thrown in case some option keys of other are not defined by this->def(),
// or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set. // or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set.
@ -1735,6 +1764,8 @@ public:
{ return dynamic_cast<T*>(this->option(opt_key, create)); } { return dynamic_cast<T*>(this->option(opt_key, create)); }
template<class T> const T* opt(const t_config_option_key &opt_key) const template<class T> const T* opt(const t_config_option_key &opt_key) const
{ return dynamic_cast<const T*>(this->option(opt_key)); } { return dynamic_cast<const T*>(this->option(opt_key)); }
// Overrides ConfigResolver::optptr().
const ConfigOption* optptr(const t_config_option_key &opt_key) const override;
// Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. // Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name.
ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override; ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override;
// Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. // Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store.

View file

@ -0,0 +1,72 @@
#include "CustomGCode.hpp"
#include "Config.hpp"
#include "GCode/PreviewData.hpp"
#include "GCodeWriter.hpp"
namespace Slic3r {
namespace CustomGCode {
// If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer),
// and if CustomGCode::Info.gcodes is empty (there is no color print data available in a new format
// then CustomGCode::Info.gcodes should be updated considering this option.
extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrintConfig* config)
{
auto *colorprint_heights = config->option<ConfigOptionFloats>("colorprint_heights");
if (colorprint_heights == nullptr)
return;
if (info.gcodes.empty() && ! colorprint_heights->values.empty()) {
// Convert the old colorprint_heighs only if there is no equivalent data in a new format.
const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors();
const auto& colorprint_values = colorprint_heights->values;
info.gcodes.clear();
info.gcodes.reserve(colorprint_values.size());
int i = 0;
for (auto val : colorprint_values)
info.gcodes.emplace_back(Item{ val, ColorChangeCode, 1, colors[(++i)%7] });
info.mode = SingleExtruder;
}
// The "colorprint_heights" config value has been deprecated. At this point of time it has been converted
// to a new format and therefore it shall be erased.
config->erase("colorprint_heights");
}
// If information for custom Gcode per print Z was imported from older Slicer, mode will be undefined.
// So, we should set CustomGCode::Info.mode should be updated considering code values from items.
extern void check_mode_for_custom_gcode_per_print_z(Info& info)
{
if (info.mode != Undef)
return;
bool is_single_extruder = true;
for (auto item : info.gcodes)
{
if (item.gcode == ToolChangeCode) {
info.mode = MultiAsSingle;
return;
}
if (item.gcode == ColorChangeCode && item.extruder > 1)
is_single_extruder = false;
}
info.mode = is_single_extruder ? SingleExtruder : MultiExtruder;
}
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
// print_z corresponds to the first layer printed with the new extruder.
std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders)
{
std::vector<std::pair<double, unsigned int>> custom_tool_changes;
for (const Item& custom_gcode : custom_gcode_per_print_z.gcodes)
if (custom_gcode.gcode == ToolChangeCode) {
// If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast<unsigned int>(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder));
}
return custom_tool_changes;
}
} // namespace CustomGCode
} // namespace Slic3r

View file

@ -0,0 +1,86 @@
#ifndef slic3r_CustomGCode_hpp_
#define slic3r_CustomGCode_hpp_
#include <string>
#include <vector>
namespace Slic3r {
class DynamicPrintConfig;
// Additional Codes which can be set by user using DoubleSlider
static constexpr char ColorChangeCode[] = "M600";
static constexpr char PausePrintCode[] = "M601";
static constexpr char ToolChangeCode[] = "tool_change";
namespace CustomGCode {
struct Item
{
bool operator<(const Item& rhs) const { return this->print_z < rhs.print_z; }
bool operator==(const Item& rhs) const
{
return (rhs.print_z == this->print_z ) &&
(rhs.gcode == this->gcode ) &&
(rhs.extruder == this->extruder ) &&
(rhs.color == this->color );
}
bool operator!=(const Item& rhs) const { return ! (*this == rhs); }
double print_z;
std::string gcode;
int extruder; // Informative value for ColorChangeCode and ToolChangeCode
// "gcode" == ColorChangeCode => M600 will be applied for "extruder" extruder
// "gcode" == ToolChangeCode => for whole print tool will be switched to "extruder" extruder
std::string color; // if gcode is equal to PausePrintCode,
// this field is used for save a short message shown on Printer display
};
enum Mode
{
Undef,
SingleExtruder, // Single extruder printer preset is selected
MultiAsSingle, // Multiple extruder printer preset is selected, but
// this mode works just for Single extruder print
// (The same extruder is assigned to all ModelObjects and ModelVolumes).
MultiExtruder // Multiple extruder printer preset is selected
};
// string anlogue of custom_code_per_height mode
static constexpr char SingleExtruderMode[] = "SingleExtruder";
static constexpr char MultiAsSingleMode [] = "MultiAsSingle";
static constexpr char MultiExtruderMode [] = "MultiExtruder";
struct Info
{
Mode mode = Undef;
std::vector<Item> gcodes;
bool operator==(const Info& rhs) const
{
return (rhs.mode == this->mode ) &&
(rhs.gcodes == this->gcodes );
}
bool operator!=(const Info& rhs) const { return !(*this == rhs); }
};
// If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer),
// and if CustomGCode::Info.gcodes is empty (there is no color print data available in a new format
// then CustomGCode::Info.gcodes should be updated considering this option.
extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrintConfig* config);
// If information for custom Gcode per print Z was imported from older Slicer, mode will be undefined.
// So, we should set CustomGCode::Info.mode should be updated considering code values from items.
extern void check_mode_for_custom_gcode_per_print_z(Info& info);
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
// print_z corresponds to the first layer printed with the new extruder.
std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders);
} // namespace CustomGCode
} // namespace Slic3r
#endif /* slic3r_CustomGCode_hpp_ */

View file

@ -657,4 +657,23 @@ bool remove_sticks(ExPolygon &poly)
return remove_sticks(poly.contour) || remove_sticks(poly.holes); return remove_sticks(poly.contour) || remove_sticks(poly.holes);
} }
void keep_largest_contour_only(ExPolygons &polygons)
{
if (polygons.size() > 1) {
double max_area = 0.;
ExPolygon* max_area_polygon = nullptr;
for (ExPolygon& p : polygons) {
double a = p.contour.area();
if (a > max_area) {
max_area = a;
max_area_polygon = &p;
}
}
assert(max_area_polygon != nullptr);
ExPolygon p(std::move(*max_area_polygon));
polygons.clear();
polygons.emplace_back(std::move(p));
}
}
} // namespace Slic3r } // namespace Slic3r

View file

@ -327,6 +327,7 @@ extern BoundingBox get_extents_rotated(const ExPolygons &polygons, double angle)
extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons); extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons);
extern bool remove_sticks(ExPolygon &poly); extern bool remove_sticks(ExPolygon &poly);
extern void keep_largest_contour_only(ExPolygons &polygons);
extern std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygons &expp); extern std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygons &expp);
extern std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygon &ex); extern std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygon &ex);

View file

@ -1,12 +1,18 @@
#include "Flow.hpp" #include "Flow.hpp"
#include "I18N.hpp"
#include "Print.hpp" #include "Print.hpp"
#include <cmath> #include <cmath>
#include <assert.h> #include <assert.h>
#include <boost/algorithm/string/predicate.hpp>
// Mark string for localization and translate.
#define L(s) Slic3r::I18N::translate(s)
namespace Slic3r { namespace Slic3r {
// This static method returns a sane extrusion width default. // This static method returns a sane extrusion width default.
static inline float auto_extrusion_width(FlowRole role, float nozzle_diameter, float height) float Flow::auto_extrusion_width(FlowRole role, float nozzle_diameter)
{ {
switch (role) { switch (role) {
case frSupportMaterial: case frSupportMaterial:
@ -22,6 +28,92 @@ static inline float auto_extrusion_width(FlowRole role, float nozzle_diameter, f
} }
} }
// Used by the Flow::extrusion_width() funtion to provide hints to the user on default extrusion width values,
// and to provide reasonable values to the PlaceholderParser.
static inline FlowRole opt_key_to_flow_role(const std::string &opt_key)
{
if (opt_key == "perimeter_extrusion_width" ||
// or all the defaults:
opt_key == "extrusion_width" || opt_key == "first_layer_extrusion_width")
return frPerimeter;
else if (opt_key == "external_perimeter_extrusion_width")
return frExternalPerimeter;
else if (opt_key == "infill_extrusion_width")
return frInfill;
else if (opt_key == "solid_infill_extrusion_width")
return frSolidInfill;
else if (opt_key == "top_infill_extrusion_width")
return frTopSolidInfill;
else if (opt_key == "support_material_extrusion_width")
return frSupportMaterial;
else
throw std::runtime_error("opt_key_to_flow_role: invalid argument");
};
static inline void throw_on_missing_variable(const std::string &opt_key, const char *dependent_opt_key)
{
throw std::runtime_error((boost::format(L("Cannot calculate extrusion width for %1%: Variable \"%2%\" not accessible.")) % opt_key % dependent_opt_key).str());
}
// Used to provide hints to the user on default extrusion width values, and to provide reasonable values to the PlaceholderParser.
double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloatOrPercent* opt, const ConfigOptionResolver& config, const unsigned int first_printing_extruder)
{
assert(opt != nullptr);
bool first_layer = boost::starts_with(opt_key, "first_layer_");
#if 0
// This is the logic used for skit / brim, but not for the rest of the 1st layer.
if (opt->value == 0. && first_layer) {
// The "first_layer_extrusion_width" was set to zero, try a substitute.
opt = config.option<ConfigOptionFloatOrPercent>("perimeter_extrusion_width");
if (opt == nullptr)
throw_on_missing_variable(opt_key, "perimeter_extrusion_width");
}
#endif
if (opt->value == 0.) {
// The role specific extrusion width value was set to zero, try the role non-specific extrusion width.
opt = config.option<ConfigOptionFloatOrPercent>("extrusion_width");
if (opt == nullptr)
throw_on_missing_variable(opt_key, "extrusion_width");
// Use the "layer_height" instead of "first_layer_height".
first_layer = false;
}
if (opt->percent) {
auto opt_key_layer_height = first_layer ? "first_layer_height" : "layer_height";
auto opt_layer_height = config.option(opt_key_layer_height);
if (opt_layer_height == nullptr)
throw_on_missing_variable(opt_key, opt_key_layer_height);
double layer_height = opt_layer_height->getFloat();
if (first_layer && static_cast<const ConfigOptionFloatOrPercent*>(opt_layer_height)->percent) {
// first_layer_height depends on layer_height.
opt_layer_height = config.option("layer_height");
if (opt_layer_height == nullptr)
throw_on_missing_variable(opt_key, "layer_height");
layer_height *= 0.01 * opt_layer_height->getFloat();
}
return opt->get_abs_value(layer_height);
}
if (opt->value == 0.) {
// If user left option to 0, calculate a sane default width.
auto opt_nozzle_diameters = config.option<ConfigOptionFloats>("nozzle_diameter");
if (opt_nozzle_diameters == nullptr)
throw_on_missing_variable(opt_key, "nozzle_diameter");
return auto_extrusion_width(opt_key_to_flow_role(opt_key), float(opt_nozzle_diameters->get_at(first_printing_extruder)));
}
return opt->value;
}
// Used to provide hints to the user on default extrusion width values, and to provide reasonable values to the PlaceholderParser.
double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionResolver &config, const unsigned int first_printing_extruder)
{
return extrusion_width(opt_key, config.option<ConfigOptionFloatOrPercent>(opt_key), config, first_printing_extruder);
}
// This constructor builds a Flow object from an extrusion width config setting // This constructor builds a Flow object from an extrusion width config setting
// and other context properties. // and other context properties.
Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio) Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio)
@ -39,7 +131,7 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent
sqrt(bridge_flow_ratio) * nozzle_diameter; sqrt(bridge_flow_ratio) * nozzle_diameter;
} else if (! width.percent && width.value == 0.) { } else if (! width.percent && width.value == 0.) {
// If user left option to 0, calculate a sane default width. // If user left option to 0, calculate a sane default width.
w = auto_extrusion_width(role, nozzle_diameter, height); w = auto_extrusion_width(role, nozzle_diameter);
} else { } else {
// If user set a manual value, use it. // If user set a manual value, use it.
w = float(width.get_abs_value(height)); w = float(width.get_abs_value(height));

View file

@ -64,6 +64,16 @@ public:
// This method is used exclusively to calculate new flow of 100% infill, where the extrusion width was allowed to scale // This method is used exclusively to calculate new flow of 100% infill, where the extrusion width was allowed to scale
// to fit a region with integer number of lines. // to fit a region with integer number of lines.
static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge);
// Sane extrusion width defautl based on nozzle diameter.
// The defaults were derived from manual Prusa MK3 profiles.
static float auto_extrusion_width(FlowRole role, float nozzle_diameter);
// Extrusion width from full config, taking into account the defaults (when set to zero) and ratios (percentages).
// Precise value depends on layer index (1st layer vs. other layers vs. variable layer height),
// on active extruder etc. Therefore the value calculated by this function shall be used as a hint only.
static double extrusion_width(const std::string &opt_key, const ConfigOptionFloatOrPercent *opt, const ConfigOptionResolver &config, const unsigned int first_printing_extruder = 0);
static double extrusion_width(const std::string &opt_key, const ConfigOptionResolver &config, const unsigned int first_printing_extruder = 0);
}; };
extern Flow support_material_flow(const PrintObject *object, float layer_height = 0.f); extern Flow support_material_flow(const PrintObject *object, float layer_height = 0.f);

View file

@ -55,6 +55,7 @@ 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_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 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_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"; const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml";
const char* MODEL_TAG = "model"; const char* MODEL_TAG = "model";
@ -385,6 +386,7 @@ namespace Slic3r {
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap; typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap; typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap;
typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap; typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
typedef std::map<int, std::vector<sla::DrainHole>> IdToSlaDrainHolesMap;
// Version of the 3mf file // Version of the 3mf file
unsigned int m_version; unsigned int m_version;
@ -403,6 +405,7 @@ namespace Slic3r {
IdToLayerHeightsProfileMap m_layer_heights_profiles; IdToLayerHeightsProfileMap m_layer_heights_profiles;
IdToLayerConfigRangesMap m_layer_config_ranges; IdToLayerConfigRangesMap m_layer_config_ranges;
IdToSlaSupportPointsMap m_sla_support_points; IdToSlaSupportPointsMap m_sla_support_points;
IdToSlaDrainHolesMap m_sla_drain_holes;
std::string m_curr_metadata_name; std::string m_curr_metadata_name;
std::string m_curr_characters; std::string m_curr_characters;
std::string m_name; std::string m_name;
@ -422,6 +425,7 @@ namespace Slic3r {
void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_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_custom_gcode_per_print_z_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
@ -629,6 +633,11 @@ namespace Slic3r {
// extract sla support points file // extract sla support points file
_extract_sla_support_points_from_archive(archive, stat); _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)) else if (boost::algorithm::iequals(name, PRINT_CONFIG_FILE))
{ {
// extract slic3r print config file // extract slic3r print config file
@ -684,6 +693,11 @@ namespace Slic3r {
model_object->sla_points_status = sla::PointsStatus::UserModified; 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 = obj_drain_holes->second;
}
IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first); IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first);
if (obj_metadata != m_objects_metadata.end()) if (obj_metadata != m_objects_metadata.end())
{ {
@ -955,8 +969,9 @@ namespace Slic3r {
// Info on format versioning - see 3mf.hpp // Info on format versioning - see 3mf.hpp
int version = 0; int version = 0;
if (!objects.empty() && objects[0].find("support_points_format_version=") != std::string::npos) { std::string key("support_points_format_version=");
objects[0].erase(objects[0].begin(), objects[0].begin() + 30); // removes the string 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]); version = std::stoi(objects[0]);
objects.erase(objects.begin()); // pop the header objects.erase(objects.begin()); // pop the header
} }
@ -1023,6 +1038,90 @@ namespace Slic3r {
} }
} }
void _3MF_Importer::_extract_sla_drain_holes_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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<object_data_points.size(); i+=8)
sla_drain_holes.emplace_back(Vec3f{float(std::atof(object_data_points[i+0].c_str())),
float(std::atof(object_data_points[i+1].c_str())),
float(std::atof(object_data_points[i+2].c_str()))},
Vec3f{float(std::atof(object_data_points[i+3].c_str())),
float(std::atof(object_data_points[i+4].c_str())),
float(std::atof(object_data_points[i+5].c_str()))},
float(std::atof(object_data_points[i+6].c_str())),
float(std::atof(object_data_points[i+7].c_str())));
}
if (!sla_drain_holes.empty())
m_sla_drain_holes.insert(IdToSlaDrainHolesMap::value_type(object_id, sla_drain_holes));
}
}
}
bool _3MF_Importer::_extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model) bool _3MF_Importer::_extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model)
{ {
@ -1092,6 +1191,14 @@ namespace Slic3r {
for (const auto& code : code_tree) for (const auto& code : code_tree)
{ {
if (code.first == "mode")
{
pt::ptree tree = code.second;
std::string mode = tree.get<std::string>("<xmlattr>.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") if (code.first != "code")
continue; continue;
pt::ptree tree = code.second; pt::ptree tree = code.second;
@ -1100,7 +1207,7 @@ namespace Slic3r {
int extruder = tree.get<int> ("<xmlattr>.extruder" ); int extruder = tree.get<int> ("<xmlattr>.extruder" );
std::string color = tree.get<std::string> ("<xmlattr>.color" ); std::string color = tree.get<std::string> ("<xmlattr>.color" );
m_model->custom_gcode_per_print_z.gcodes.push_back(Model::CustomGCode{print_z, gcode, extruder, color}) ; m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, gcode, extruder, color}) ;
} }
} }
} }
@ -1859,24 +1966,14 @@ namespace Slic3r {
typedef std::vector<BuildItem> BuildItemsList; typedef std::vector<BuildItem> BuildItemsList;
typedef std::map<int, ObjectData> IdToObjectDataMap; typedef std::map<int, ObjectData> IdToObjectDataMap;
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
bool m_fullpath_sources{ true }; bool m_fullpath_sources{ true };
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
public: public:
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr); bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr);
#else #else
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources); bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources);
#endif // ENABLE_THUMBNAIL_GENERATOR #endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data = nullptr);
#else
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config);
#endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
private: private:
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
@ -1896,12 +1993,12 @@ namespace Slic3r {
bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model); 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_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_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_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_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); bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model);
}; };
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data) bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data)
{ {
@ -1916,21 +2013,6 @@ namespace Slic3r {
return _save_model_to_file(filename, model, config); return _save_model_to_file(filename, model, config);
} }
#endif // ENABLE_THUMBNAIL_GENERATOR #endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data)
{
clear_errors();
return _save_model_to_file(filename, model, config, thumbnail_data);
}
#else
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config)
{
clear_errors();
return _save_model_to_file(filename, model, config);
}
#endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data) bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data)
@ -2018,6 +2100,14 @@ namespace Slic3r {
return false; 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"). // 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 // All custom gcode per height of whole Model are stored here
if (!_add_custom_gcode_per_print_z_file_to_archive(archive, model)) if (!_add_custom_gcode_per_print_z_file_to_archive(archive, model))
@ -2475,6 +2565,50 @@ namespace Slic3r {
return true; return true;
} }
bool _3MF_Exporter::_add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model)
{
const char *const fmt = "object_id=%d|";
std::string out;
unsigned int count = 0;
for (const ModelObject* object : model.objects)
{
++count;
auto& drain_holes = object->sla_drain_holes;
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<const void*>(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) bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config)
{ {
char buffer[1024]; char buffer[1024];
@ -2565,12 +2699,8 @@ namespace Slic3r {
// stores volume's source data // stores volume's source data
if (!volume->source.input_file.empty()) if (!volume->source.input_file.empty())
{ {
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
std::string input_file = xml_escape(m_fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string()); std::string input_file = xml_escape(m_fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string());
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n"; stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n";
#else
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->source.input_file) << "\"/>\n";
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n"; stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n"; stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n"; stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
@ -2615,7 +2745,7 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
pt::ptree tree; pt::ptree tree;
pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", ""); pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", "");
for (const Model::CustomGCode& code : model.custom_gcode_per_print_z.gcodes) for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes)
{ {
pt::ptree& code_tree = main_tree.add("code", ""); pt::ptree& code_tree = main_tree.add("code", "");
// store minX and maxZ // store minX and maxZ
@ -2625,6 +2755,12 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
code_tree.put("<xmlattr>.color" , code.color ); code_tree.put("<xmlattr>.color" , code.color );
} }
pt::ptree& mode_tree = main_tree.add("mode", "");
// store mode of a custom_gcode_per_print_z
mode_tree.put("<xmlattr>.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()) if (!tree.empty())
{ {
std::ostringstream oss; std::ostringstream oss;
@ -2659,37 +2795,21 @@ bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool c
return res; return res;
} }
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data) bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data)
#else #else
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources) bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources)
#endif // ENABLE_THUMBNAIL_GENERATOR #endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data)
#else
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config)
#endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
{ {
if ((path == nullptr) || (model == nullptr)) if ((path == nullptr) || (model == nullptr))
return false; return false;
_3MF_Exporter exporter; _3MF_Exporter exporter;
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources, thumbnail_data); bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources, thumbnail_data);
#else #else
bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources); bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources);
#endif // ENABLE_THUMBNAIL_GENERATOR #endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR
bool res = exporter.save_model_to_file(path, *model, config, thumbnail_data);
#else
bool res = exporter.save_model_to_file(path, *model, config);
#endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
if (!res) if (!res)
exporter.log_errors(); exporter.log_errors();

View file

@ -20,6 +20,10 @@ namespace Slic3r {
support_points_format_version = 1 support_points_format_version = 1
}; };
enum {
drain_holes_format_version = 1
};
class Model; class Model;
class DynamicPrintConfig; class DynamicPrintConfig;
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
@ -31,19 +35,11 @@ namespace Slic3r {
// Save the given model and the config data contained in the given Print into a 3mf file. // Save the given model and the config data contained in the given Print into a 3mf file.
// The model could be modified during the export process if meshes are not repaired or have no shared vertices // The model could be modified during the export process if meshes are not repaired or have no shared vertices
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr); extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr);
#else #else
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources); extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources);
#endif // ENABLE_THUMBNAIL_GENERATOR #endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data = nullptr);
#else
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config);
#endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
}; // namespace Slic3r }; // namespace Slic3r

View file

@ -13,6 +13,7 @@
#include "../Utils.hpp" #include "../Utils.hpp"
#include "../I18N.hpp" #include "../I18N.hpp"
#include "../Geometry.hpp" #include "../Geometry.hpp"
#include "../CustomGCode.hpp"
#include "AMF.hpp" #include "AMF.hpp"
@ -156,6 +157,7 @@ struct AMFParserContext
NODE_TYPE_PRINTABLE, // amf/constellation/instance/mirrorz NODE_TYPE_PRINTABLE, // amf/constellation/instance/mirrorz
NODE_TYPE_CUSTOM_GCODE, // amf/custom_code_per_height NODE_TYPE_CUSTOM_GCODE, // amf/custom_code_per_height
NODE_TYPE_GCODE_PER_HEIGHT, // amf/custom_code_per_height/code NODE_TYPE_GCODE_PER_HEIGHT, // amf/custom_code_per_height/code
NODE_TYPE_CUSTOM_GCODE_MODE, // amf/custom_code_per_height/mode
NODE_TYPE_METADATA, // anywhere under amf/*/metadata NODE_TYPE_METADATA, // anywhere under amf/*/metadata
}; };
@ -308,13 +310,19 @@ void AMFParserContext::startElement(const char *name, const char **atts)
else else
this->stop(); this->stop();
} }
else if (strcmp(name, "code") == 0 && m_path[1] == NODE_TYPE_CUSTOM_GCODE) { else if (m_path[1] == NODE_TYPE_CUSTOM_GCODE) {
if (strcmp(name, "code") == 0) {
node_type_new = NODE_TYPE_GCODE_PER_HEIGHT; node_type_new = NODE_TYPE_GCODE_PER_HEIGHT;
m_value[0] = get_attribute(atts, "height"); m_value[0] = get_attribute(atts, "print_z");
m_value[1] = get_attribute(atts, "gcode"); m_value[1] = get_attribute(atts, "gcode");
m_value[2] = get_attribute(atts, "extruder"); m_value[2] = get_attribute(atts, "extruder");
m_value[3] = get_attribute(atts, "color"); m_value[3] = get_attribute(atts, "color");
} }
else if (strcmp(name, "mode") == 0) {
node_type_new = NODE_TYPE_CUSTOM_GCODE_MODE;
m_value[0] = get_attribute(atts, "value");
}
}
break; break;
case 3: case 3:
if (m_path[2] == NODE_TYPE_MESH) { if (m_path[2] == NODE_TYPE_MESH) {
@ -632,18 +640,29 @@ void AMFParserContext::endElement(const char * /* name */)
break; break;
case NODE_TYPE_GCODE_PER_HEIGHT: { case NODE_TYPE_GCODE_PER_HEIGHT: {
double height = double(atof(m_value[0].c_str())); double print_z = double(atof(m_value[0].c_str()));
const std::string& gcode = m_value[1]; const std::string& gcode = m_value[1];
int extruder = atoi(m_value[2].c_str()); int extruder = atoi(m_value[2].c_str());
const std::string& color = m_value[3]; const std::string& color = m_value[3];
m_model.custom_gcode_per_print_z.gcodes.push_back(Model::CustomGCode{height, gcode, extruder, color}); m_model.custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, gcode, extruder, color});
for (std::string& val: m_value) for (std::string& val: m_value)
val.clear(); val.clear();
break; break;
} }
case NODE_TYPE_CUSTOM_GCODE_MODE: {
const std::string& mode = m_value[0];
m_model.custom_gcode_per_print_z.mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
CustomGCode::Mode::MultiExtruder;
for (std::string& val: m_value)
val.clear();
break;
}
case NODE_TYPE_METADATA: case NODE_TYPE_METADATA:
if ((m_config != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0) if ((m_config != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0)
m_config->load_from_gcode_string(m_value[1].c_str()); m_config->load_from_gcode_string(m_value[1].c_str());
@ -1003,11 +1022,7 @@ bool load_amf(const char* path, DynamicPrintConfig* config, Model* model, bool c
return false; return false;
} }
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources) bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources)
#else
bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
{ {
if ((path == nullptr) || (model == nullptr)) if ((path == nullptr) || (model == nullptr))
return false; return false;
@ -1161,12 +1176,8 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << "</metadata>\n"; stream << "</metadata>\n";
if (!volume->source.input_file.empty()) if (!volume->source.input_file.empty())
{ {
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
std::string input_file = xml_escape(fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string()); std::string input_file = xml_escape(fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string());
stream << " <metadata type=\"slic3r.source_file\">" << input_file << "</metadata>\n"; stream << " <metadata type=\"slic3r.source_file\">" << input_file << "</metadata>\n";
#else
stream << " <metadata type=\"slic3r.source_file\">" << xml_escape(volume->source.input_file) << "</metadata>\n";
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
stream << " <metadata type=\"slic3r.source_object_id\">" << volume->source.object_idx << "</metadata>\n"; stream << " <metadata type=\"slic3r.source_object_id\">" << volume->source.object_idx << "</metadata>\n";
stream << " <metadata type=\"slic3r.source_volume_id\">" << volume->source.volume_idx << "</metadata>\n"; stream << " <metadata type=\"slic3r.source_volume_id\">" << volume->source.volume_idx << "</metadata>\n";
stream << " <metadata type=\"slic3r.source_offset_x\">" << volume->source.mesh_offset(0) << "</metadata>\n"; stream << " <metadata type=\"slic3r.source_offset_x\">" << volume->source.mesh_offset(0) << "</metadata>\n";
@ -1237,16 +1248,23 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
pt::ptree& main_tree = tree.add("custom_gcodes_per_height", ""); pt::ptree& main_tree = tree.add("custom_gcodes_per_height", "");
for (const Model::CustomGCode& code : model->custom_gcode_per_print_z.gcodes) for (const CustomGCode::Item& code : model->custom_gcode_per_print_z.gcodes)
{ {
pt::ptree& code_tree = main_tree.add("code", ""); pt::ptree& code_tree = main_tree.add("code", "");
// store minX and maxZ // store custom_gcode_per_print_z gcodes information
code_tree.put("<xmlattr>.print_z" , code.print_z ); code_tree.put("<xmlattr>.print_z" , code.print_z );
code_tree.put("<xmlattr>.gcode" , code.gcode ); code_tree.put("<xmlattr>.gcode" , code.gcode );
code_tree.put("<xmlattr>.extruder" , code.extruder ); code_tree.put("<xmlattr>.extruder" , code.extruder );
code_tree.put("<xmlattr>.color" , code.color ); code_tree.put("<xmlattr>.color" , code.color );
} }
pt::ptree& mode_tree = main_tree.add("mode", "");
// store mode of a custom_gcode_per_print_z
mode_tree.put("<xmlattr>.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()) if (!tree.empty())
{ {
std::ostringstream oss; std::ostringstream oss;
@ -1259,6 +1277,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
// Post processing("beautification") of the output string // Post processing("beautification") of the output string
boost::replace_all(out, "><code", ">\n <code"); boost::replace_all(out, "><code", ">\n <code");
boost::replace_all(out, "><mode", ">\n <mode");
boost::replace_all(out, "><", ">\n<"); boost::replace_all(out, "><", ">\n<");
stream << out << "\n"; stream << out << "\n";

View file

@ -11,11 +11,7 @@ extern bool load_amf(const char* path, DynamicPrintConfig* config, Model* model,
// Save the given model and the config data into an amf file. // Save the given model and the config data into an amf file.
// The model could be modified during the export process if meshes are not repaired or have no shared vertices // The model could be modified during the export process if meshes are not repaired or have no shared vertices
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
extern bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources); extern bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources);
#else
extern bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config);
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
}; // namespace Slic3r }; // namespace Slic3r

View file

@ -355,6 +355,35 @@ bool objparse(const char *path, ObjData &data)
return true; return true;
} }
bool objparse(std::istream &stream, ObjData &data)
{
try {
char buf[65536 * 2];
size_t len = 0;
size_t lenPrev = 0;
while ((len = size_t(stream.read(buf + lenPrev, 65536).gcount())) != 0) {
len += lenPrev;
size_t lastLine = 0;
for (size_t i = 0; i < len; ++ i)
if (buf[i] == '\r' || buf[i] == '\n') {
buf[i] = 0;
char *c = buf + lastLine;
while (*c == ' ' || *c == '\t')
++ c;
obj_parseline(c, data);
lastLine = i + 1;
}
lenPrev = len - lastLine;
memmove(buf, buf + lastLine, lenPrev);
}
}
catch (std::bad_alloc&) {
printf("Out of memory\r\n");
}
return true;
}
template<typename T> template<typename T>
bool savevector(FILE *pFile, const std::vector<T> &v) bool savevector(FILE *pFile, const std::vector<T> &v)
{ {

View file

@ -3,6 +3,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <istream>
namespace ObjParser { namespace ObjParser {
@ -97,6 +98,7 @@ struct ObjData {
}; };
extern bool objparse(const char *path, ObjData &data); extern bool objparse(const char *path, ObjData &data);
extern bool objparse(std::istream &stream, ObjData &data);
extern bool objbinsave(const char *path, const ObjData &data); extern bool objbinsave(const char *path, const ObjData &data);

View file

@ -8,6 +8,7 @@
#include "GCode/WipeTower.hpp" #include "GCode/WipeTower.hpp"
#include "ShortestPath.hpp" #include "ShortestPath.hpp"
#include "Utils.hpp" #include "Utils.hpp"
#include "libslic3r.h"
#include <algorithm> #include <algorithm>
#include <cstdlib> #include <cstdlib>
@ -164,12 +165,12 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP
cnt = (cnt + 1) / 2; cnt = (cnt + 1) / 2;
} }
// And collect copies of the objects. // And collect copies of the objects.
for (const Point &copy : object->copies()) { for (const PrintInstance &instance : object->instances()) {
// All the layers were reduced to the 1st item of polygons_per_layer. // All the layers were reduced to the 1st item of polygons_per_layer.
size_t i = islands.size(); size_t i = islands.size();
polygons_append(islands, polygons_per_layer.front()); polygons_append(islands, polygons_per_layer.front());
for (; i < islands.size(); ++ i) for (; i < islands.size(); ++ i)
islands[i].translate(copy); islands[i].translate(instance.shift);
} }
} }
return islands; return islands;
@ -965,7 +966,7 @@ namespace DoExport {
skirts.emplace_back(std::move(s)); skirts.emplace_back(std::move(s));
} }
ooze_prevention.enable = true; ooze_prevention.enable = true;
ooze_prevention.standby_points = offset(Slic3r::Geometry::convex_hull(skirts), scale_(3.f)).front().equally_spaced_points(scale_(10.)); ooze_prevention.standby_points = offset(Slic3r::Geometry::convex_hull(skirts), float(scale_(3.))).front().equally_spaced_points(float(scale_(10.)));
#if 0 #if 0
require "Slic3r/SVG.pm"; require "Slic3r/SVG.pm";
Slic3r::SVG::output( Slic3r::SVG::output(
@ -1038,8 +1039,8 @@ namespace DoExport {
std::string filament_stats_string_out; std::string filament_stats_string_out;
print_statistics.clear(); print_statistics.clear();
print_statistics.estimated_normal_print_time = normal_time_estimator.get_time_dhms(); print_statistics.estimated_normal_print_time = normal_time_estimator.get_time_dhm/*s*/();
print_statistics.estimated_silent_print_time = silent_time_estimator_enabled ? silent_time_estimator.get_time_dhms() : "N/A"; print_statistics.estimated_silent_print_time = silent_time_estimator_enabled ? silent_time_estimator.get_time_dhm/*s*/() : "N/A";
print_statistics.estimated_normal_color_print_times = normal_time_estimator.get_color_times_dhms(true); print_statistics.estimated_normal_color_print_times = normal_time_estimator.get_color_times_dhms(true);
if (silent_time_estimator_enabled) if (silent_time_estimator_enabled)
print_statistics.estimated_silent_color_print_times = silent_time_estimator.get_color_times_dhms(true); print_statistics.estimated_silent_color_print_times = silent_time_estimator.get_color_times_dhms(true);
@ -1094,6 +1095,41 @@ namespace DoExport {
} }
} }
// Sort the PrintObjects by their increasing Z, likely useful for avoiding colisions on Deltas during sequential prints.
static inline std::vector<const PrintInstance*> sort_object_instances_by_max_z(const Print &print)
{
std::vector<const PrintObject*> objects(print.objects().begin(), print.objects().end());
std::sort(objects.begin(), objects.end(), [](const PrintObject *po1, const PrintObject *po2) { return po1->height() < po2->height(); });
std::vector<const PrintInstance*> instances;
instances.reserve(objects.size());
for (const PrintObject *object : objects)
for (size_t i = 0; i < object->instances().size(); ++ i)
instances.emplace_back(&object->instances()[i]);
return instances;
}
// Produce a vector of PrintObjects in the order of their respective ModelObjects in print.model().
std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Print& print)
{
// Build up map from ModelInstance* to PrintInstance*
std::vector<std::pair<const ModelInstance*, const PrintInstance*>> model_instance_to_print_instance;
model_instance_to_print_instance.reserve(print.num_object_instances());
for (const PrintObject *print_object : print.objects())
for (const PrintInstance &print_instance : print_object->instances())
model_instance_to_print_instance.emplace_back(print_instance.model_instance, &print_instance);
std::sort(model_instance_to_print_instance.begin(), model_instance_to_print_instance.end(), [](auto &l, auto &r) { return l.first < r.first; });
std::vector<const PrintInstance*> instances;
instances.reserve(model_instance_to_print_instance.size());
for (const ModelObject *model_object : print.model().objects)
for (const ModelInstance *model_instance : model_object->instances) {
auto it = std::lower_bound(model_instance_to_print_instance.begin(), model_instance_to_print_instance.end(), std::make_pair(model_instance, nullptr), [](auto &l, auto &r) { return l.first < r.first; });
if (it != model_instance_to_print_instance.end() && it->first == model_instance)
instances.emplace_back(it->second);
}
return instances;
}
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb) void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb)
#else #else
@ -1125,7 +1161,7 @@ void GCode::_do_export(Print& print, FILE* file)
for (auto layer : object->support_layers()) for (auto layer : object->support_layers())
zs.push_back(layer->print_z); zs.push_back(layer->print_z);
std::sort(zs.begin(), zs.end()); std::sort(zs.begin(), zs.end());
m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin())); m_layer_count += (unsigned int)(object->instances().size() * (std::unique(zs.begin(), zs.end()) - zs.begin()));
} }
} else { } else {
// Print all objects with the same print_z together. // Print all objects with the same print_z together.
@ -1218,13 +1254,18 @@ void GCode::_do_export(Print& print, FILE* file)
ToolOrdering tool_ordering; ToolOrdering tool_ordering;
unsigned int initial_extruder_id = (unsigned int)-1; unsigned int initial_extruder_id = (unsigned int)-1;
unsigned int final_extruder_id = (unsigned int)-1; unsigned int final_extruder_id = (unsigned int)-1;
size_t initial_print_object_id = 0;
bool has_wipe_tower = false; bool has_wipe_tower = false;
std::vector<const PrintInstance*> print_object_instances_ordering;
std::vector<const PrintInstance*>::const_iterator print_object_instance_sequential_active;
if (print.config().complete_objects.value) { if (print.config().complete_objects.value) {
// Order object instances for sequential print.
print_object_instances_ordering = sort_object_instances_by_model_order(print);
// print_object_instances_ordering = sort_object_instances_by_max_z(print);
// Find the 1st printing object, find its tool ordering and the initial extruder ID. // Find the 1st printing object, find its tool ordering and the initial extruder ID.
for (; initial_print_object_id < print.objects().size(); ++initial_print_object_id) { print_object_instance_sequential_active = print_object_instances_ordering.begin();
tool_ordering = ToolOrdering(*print.objects()[initial_print_object_id], initial_extruder_id); for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1) tool_ordering = ToolOrdering(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id);
if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast<unsigned int>(-1))
break; break;
} }
// We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode. // We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode.
@ -1244,6 +1285,8 @@ void GCode::_do_export(Print& print, FILE* file)
// In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z. // In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z.
// Therefore initialize the printing extruders from there. // Therefore initialize the printing extruders from there.
this->set_extruders(tool_ordering.all_extruders()); this->set_extruders(tool_ordering.all_extruders());
// Order object instances using a nearest neighbor search.
print_object_instances_ordering = chain_print_object_instances(print);
} }
if (initial_extruder_id == (unsigned int)-1) { if (initial_extruder_id == (unsigned int)-1) {
// Nothing to print! // Nothing to print!
@ -1324,17 +1367,11 @@ void GCode::_do_export(Print& print, FILE* file)
// Do all objects for each layer. // Do all objects for each layer.
if (print.config().complete_objects.value) { if (print.config().complete_objects.value) {
// Print objects from the smallest to the tallest to avoid collisions
// when moving onto next object starting point.
std::vector<PrintObject*> objects(print.objects());
std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size(2) < po2->size(2); });
size_t finished_objects = 0; size_t finished_objects = 0;
for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) { const PrintObject *prev_object = (*print_object_instance_sequential_active)->print_object;
const PrintObject &object = *objects[object_id]; for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
for (const Point &copy : object.copies()) { const PrintObject &object = *(*print_object_instance_sequential_active)->print_object;
// Get optimal tool ordering to minimize tool switches of a multi-exruder print. if (&object != prev_object || tool_ordering.first_extruder() != final_extruder_id) {
if (object_id != initial_print_object_id || &copy != object.copies().data()) {
// Don't initialize for the first object and first copy.
tool_ordering = ToolOrdering(object, final_extruder_id); tool_ordering = ToolOrdering(object, final_extruder_id);
unsigned int new_extruder_id = tool_ordering.first_extruder(); unsigned int new_extruder_id = tool_ordering.first_extruder();
if (new_extruder_id == (unsigned int)-1) if (new_extruder_id == (unsigned int)-1)
@ -1345,7 +1382,7 @@ void GCode::_do_export(Print& print, FILE* file)
assert(final_extruder_id != (unsigned int)-1); assert(final_extruder_id != (unsigned int)-1);
} }
print.throw_if_canceled(); print.throw_if_canceled();
this->set_origin(unscale(copy)); this->set_origin(unscale((*print_object_instance_sequential_active)->shift));
if (finished_objects > 0) { if (finished_objects > 0) {
// Move to the origin position for the copy we're going to print. // Move to the origin position for the copy we're going to print.
// This happens before Z goes down to layer 0 again, so that no collision happens hopefully. // This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
@ -1374,7 +1411,7 @@ void GCode::_do_export(Print& print, FILE* file)
for (const LayerToPrint &ltp : layers_to_print) { for (const LayerToPrint &ltp : layers_to_print) {
std::vector<LayerToPrint> lrs; std::vector<LayerToPrint> lrs;
lrs.emplace_back(std::move(ltp)); lrs.emplace_back(std::move(ltp));
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), nullptr, &copy - object.copies().data()); this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), nullptr, *print_object_instance_sequential_active - object.instances().data());
print.throw_if_canceled(); print.throw_if_canceled();
} }
#ifdef HAS_PRESSURE_EQUALIZER #ifdef HAS_PRESSURE_EQUALIZER
@ -1385,11 +1422,9 @@ void GCode::_do_export(Print& print, FILE* file)
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
// Reset it when starting another object from 1st layer. // Reset it when starting another object from 1st layer.
m_second_layer_things_done = false; m_second_layer_things_done = false;
} prev_object = &object;
} }
} else { } else {
// Order object instances using a nearest neighbor search.
std::vector<std::pair<size_t, size_t>> print_object_instances_ordering = chain_print_object_instances(print);
// Sort layers by Z. // Sort layers by Z.
// All extrusion moves with the same top layer height are extruded uninterrupted. // All extrusion moves with the same top layer height are extruded uninterrupted.
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print); std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
@ -1694,7 +1729,7 @@ std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
std::vector<GCode::ObjectByExtruder> &objects_by_extruder, std::vector<GCode::ObjectByExtruder> &objects_by_extruder,
const std::vector<LayerToPrint> &layers, const std::vector<LayerToPrint> &layers,
// Ordering must be defined for normal (non-sequential print). // Ordering must be defined for normal (non-sequential print).
const std::vector<std::pair<size_t, size_t>> *ordering, const std::vector<const PrintInstance*> *ordering,
// For sequential print, the instance of the object to be printing has to be defined. // For sequential print, the instance of the object to be printing has to be defined.
const size_t single_object_instance_idx) const size_t single_object_instance_idx)
{ {
@ -1723,13 +1758,13 @@ std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
if (! sorted.empty()) { if (! sorted.empty()) {
const Print &print = *sorted.front().first->print(); const Print &print = *sorted.front().first->print();
out.reserve(sorted.size()); out.reserve(sorted.size());
for (const std::pair<size_t, size_t> &instance_id : *ordering) { for (const PrintInstance *instance : *ordering) {
const PrintObject &print_object = *print.objects()[instance_id.first]; const PrintObject &print_object = *instance->print_object;
std::pair<const PrintObject*, ObjectByExtruder*> key(&print_object, nullptr); std::pair<const PrintObject*, ObjectByExtruder*> key(&print_object, nullptr);
auto it = std::lower_bound(sorted.begin(), sorted.end(), key); auto it = std::lower_bound(sorted.begin(), sorted.end(), key);
if (it != sorted.end() && it->first == &print_object) if (it != sorted.end() && it->first == &print_object)
// ObjectByExtruder for this PrintObject was found. // ObjectByExtruder for this PrintObject was found.
out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance_id.second); out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance - print_object.instances().data());
} }
} }
} }
@ -1740,10 +1775,10 @@ namespace ProcessLayer
{ {
static std::string emit_custom_gcode_per_print_z( static std::string emit_custom_gcode_per_print_z(
const Model::CustomGCode *custom_gcode, const CustomGCode::Item *custom_gcode,
// ID of the first extruder printing this layer. // ID of the first extruder printing this layer.
unsigned int first_extruder_id, unsigned int first_extruder_id,
bool single_material_print) bool single_extruder_printer)
{ {
std::string gcode; std::string gcode;
@ -1752,30 +1787,38 @@ namespace ProcessLayer
assert(custom_gcode->gcode != ToolChangeCode); assert(custom_gcode->gcode != ToolChangeCode);
const std::string &custom_code = custom_gcode->gcode; const std::string &custom_code = custom_gcode->gcode;
bool color_change = custom_code == ColorChangeCode;
bool tool_change = custom_code == ToolChangeCode;
// Tool Change is applied as Color Change for a single extruder printer only.
assert(! tool_change || single_extruder_printer);
std::string pause_print_msg; std::string pause_print_msg;
int m600_extruder_before_layer = -1; int m600_extruder_before_layer = -1;
if (custom_code == ColorChangeCode && custom_gcode->extruder > 0) if (color_change && custom_gcode->extruder > 0)
m600_extruder_before_layer = custom_gcode->extruder - 1; m600_extruder_before_layer = custom_gcode->extruder - 1;
else if (custom_code == PausePrintCode) else if (custom_code == PausePrintCode)
pause_print_msg = custom_gcode->color; pause_print_msg = custom_gcode->color;
// we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count
if (custom_code == ColorChangeCode) // color change if (color_change || tool_change)
{ {
// Color Change or Tool Change as Color Change.
// add tag for analyzer // add tag for analyzer
gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n"; gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n";
// add tag for time estimator // add tag for time estimator
gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n"; gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n";
if (!single_material_print && m600_extruder_before_layer >= 0 && first_extruder_id != m600_extruder_before_layer if (!single_extruder_printer && m600_extruder_before_layer >= 0 && first_extruder_id != m600_extruder_before_layer
// && !MMU1 // && !MMU1
) { ) {
//! FIXME_in_fw show message during print pause //! FIXME_in_fw show message during print pause
gcode += "M601\n"; // pause print gcode += "M601\n"; // pause print
gcode += "M117 Change filament for Extruder " + std::to_string(m600_extruder_before_layer) + "\n"; gcode += "M117 Change filament for Extruder " + std::to_string(m600_extruder_before_layer) + "\n";
} }
else else {
gcode += custom_code + "\n"; gcode += ColorChangeCode;
gcode += "\n";
}
} }
else else
{ {
@ -1836,6 +1879,8 @@ namespace Skirt {
const Print &print, const Print &print,
const std::vector<GCode::LayerToPrint> &layers, const std::vector<GCode::LayerToPrint> &layers,
const LayerTools &layer_tools, const LayerTools &layer_tools,
// First non-empty support layer.
const SupportLayer *support_layer,
// Heights (print_z) at which the skirt has already been extruded. // Heights (print_z) at which the skirt has already been extruded.
std::vector<coordf_t> &skirt_done) std::vector<coordf_t> &skirt_done)
{ {
@ -1849,7 +1894,7 @@ namespace Skirt {
// This print_z has not been extruded yet (sequential print) // This print_z has not been extruded yet (sequential print)
skirt_done.back() < layer_tools.print_z - EPSILON && skirt_done.back() < layer_tools.print_z - EPSILON &&
// and this layer is an object layer, or it is a raft layer. // and this layer is an object layer, or it is a raft layer.
(layer_tools.has_object || layers.front().support_layer->id() < (size_t)layers.front().support_layer->object()->config().raft_layers.value)) { (layer_tools.has_object || support_layer->id() < (size_t)support_layer->object()->config().raft_layers.value)) {
#if 0 #if 0
// Prime just the first printing extruder. This is original Slic3r's implementation. // Prime just the first printing extruder. This is original Slic3r's implementation.
skirt_loops_per_extruder_out[layer_tools.extruders.front()] = std::pair<size_t, size_t>(0, print.config().skirts.value); skirt_loops_per_extruder_out[layer_tools.extruders.front()] = std::pair<size_t, size_t>(0, print.config().skirts.value);
@ -1879,7 +1924,7 @@ void GCode::process_layer(
const std::vector<LayerToPrint> &layers, const std::vector<LayerToPrint> &layers,
const LayerTools &layer_tools, const LayerTools &layer_tools,
// Pairs of PrintObject index and its instance index. // Pairs of PrintObject index and its instance index.
const std::vector<std::pair<size_t, size_t>> *ordering, const std::vector<const PrintInstance*> *ordering,
// If set to size_t(-1), then print all copies of all objects. // If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object. // Otherwise print a single copy of a single object.
const size_t single_object_instance_idx) const size_t single_object_instance_idx)
@ -1977,7 +2022,7 @@ void GCode::process_layer(
// not at the print_z of the interlaced support material layers. // not at the print_z of the interlaced support material layers.
skirt_loops_per_extruder = first_layer ? skirt_loops_per_extruder = first_layer ?
Skirt::make_skirt_loops_per_extruder_1st_layer(print, layers, layer_tools, m_skirt_done) : Skirt::make_skirt_loops_per_extruder_1st_layer(print, layers, layer_tools, m_skirt_done) :
Skirt::make_skirt_loops_per_extruder_other_layers(print, layers, layer_tools, m_skirt_done); Skirt::make_skirt_loops_per_extruder_other_layers(print, layers, layer_tools, support_layer, m_skirt_done);
// Group extrusions by an extruder, then by an object, an island and a region. // Group extrusions by an extruder, then by an object, an island and a region.
std::map<unsigned int, std::vector<ObjectByExtruder>> by_extruder; std::map<unsigned int, std::vector<ObjectByExtruder>> by_extruder;
@ -2084,14 +2129,14 @@ void GCode::process_layer(
// Let's recover vector of extruder overrides: // Let's recover vector of extruder overrides:
const WipingExtrusions::ExtruderPerCopy *entity_overrides = nullptr; const WipingExtrusions::ExtruderPerCopy *entity_overrides = nullptr;
if (is_anything_overridden) {
printing_extruders.clear();
if (! layer_tools.has_extruder(correct_extruder_id)) { if (! layer_tools.has_extruder(correct_extruder_id)) {
// this entity is not overridden, but its extruder is not in layer_tools - we'll print it // this entity is not overridden, but its extruder is not in layer_tools - we'll print it
// by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools) // by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools)
correct_extruder_id = layer_tools.extruders.back(); correct_extruder_id = layer_tools.extruders.back();
} }
entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->copies().size()); printing_extruders.clear();
if (is_anything_overridden) {
entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->instances().size());
if (entity_overrides == nullptr) { if (entity_overrides == nullptr) {
printing_extruders.emplace_back(correct_extruder_id); printing_extruders.emplace_back(correct_extruder_id);
} else { } else {
@ -2102,10 +2147,10 @@ void GCode::process_layer(
extruder : extruder :
// at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation) // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation)
static_cast<unsigned int>(- extruder - 1)); static_cast<unsigned int>(- extruder - 1));
}
Slic3r::sort_remove_duplicates(printing_extruders); Slic3r::sort_remove_duplicates(printing_extruders);
}
} else } else
printing_extruders = { (unsigned int)correct_extruder_id }; printing_extruders.emplace_back(correct_extruder_id);
// Now we must add this extrusion into the by_extruder map, once for each extruder that will print it: // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
for (unsigned int extruder : printing_extruders) for (unsigned int extruder : printing_extruders)
@ -2205,7 +2250,7 @@ void GCode::process_layer(
if (this->config().gcode_label_objects) if (this->config().gcode_label_objects)
gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n"; gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n";
// When starting a new object, use the external motion planner for the first travel move. // When starting a new object, use the external motion planner for the first travel move.
const Point &offset = instance_to_print.print_object.copies()[instance_to_print.instance_id]; const Point &offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset); std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
if (m_last_obj_copy != this_object_copy) if (m_last_obj_copy != this_object_copy)
m_avoid_crossing_perimeters.use_external_mp_once = true; m_avoid_crossing_perimeters.use_external_mp_once = true;
@ -2577,8 +2622,9 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
} }
} }
else if (seam_position == spRear) { else if (seam_position == spRear) {
last_pos = m_layer->object()->bounding_box().center(); // Object is centered around (0,0) in its current coordinate system.
last_pos(1) += coord_t(3. * m_layer->object()->bounding_box().radius()); last_pos.x() = 0;
last_pos.y() += coord_t(3. * m_layer->object()->bounding_box().radius());
last_pos_weight = 5.f; last_pos_weight = 5.f;
} }

View file

@ -231,7 +231,7 @@ private:
const std::vector<LayerToPrint> &layers, const std::vector<LayerToPrint> &layers,
const LayerTools &layer_tools, const LayerTools &layer_tools,
// Pairs of PrintObject index and its instance index. // Pairs of PrintObject index and its instance index.
const std::vector<std::pair<size_t, size_t>> *ordering, const std::vector<const PrintInstance*> *ordering,
// If set to size_t(-1), then print all copies of all objects. // If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object. // Otherwise print a single copy of a single object.
const size_t single_object_idx = size_t(-1)); const size_t single_object_idx = size_t(-1));
@ -305,7 +305,7 @@ private:
// Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances. // Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances.
const std::vector<LayerToPrint> &layers, const std::vector<LayerToPrint> &layers,
// Ordering must be defined for normal (non-sequential print). // Ordering must be defined for normal (non-sequential print).
const std::vector<std::pair<size_t, size_t>> *ordering, const std::vector<const PrintInstance*> *ordering,
// For sequential print, the instance of the object to be printing has to be defined. // For sequential print, the instance of the object to be printing has to be defined.
const size_t single_object_instance_idx); const size_t single_object_instance_idx);
@ -430,6 +430,8 @@ private:
friend class WipeTowerIntegration; friend class WipeTowerIntegration;
}; };
std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Print& print);
} }
#endif #endif

View file

@ -121,9 +121,9 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
if (support_layer) if (support_layer)
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
bbox_this.merge(extrusionentity_extents(extrusion_entity)); bbox_this.merge(extrusionentity_extents(extrusion_entity));
for (const Point &offset : print_object.copies()) { for (const PrintInstance &instance : print_object.instances()) {
BoundingBoxf bbox_translated(bbox_this); BoundingBoxf bbox_translated(bbox_this);
bbox_translated.translate(unscale(offset)); bbox_translated.translate(unscale(instance.shift));
bbox.merge(bbox_translated); bbox.merge(bbox_translated);
} }
} }

View file

@ -16,8 +16,8 @@ std::string SpiralVase::process_layer(const std::string &gcode)
// If we're not going to modify G-code, just feed it to the reader // If we're not going to modify G-code, just feed it to the reader
// in order to update positions. // in order to update positions.
if (!this->enable) { if (! this->enable) {
this->_reader.parse_buffer(gcode); m_reader.parse_buffer(gcode);
return gcode; return gcode;
} }
@ -29,7 +29,7 @@ std::string SpiralVase::process_layer(const std::string &gcode)
{ {
//FIXME Performance warning: This copies the GCodeConfig of the reader. //FIXME Performance warning: This copies the GCodeConfig of the reader.
GCodeReader r = this->_reader; // clone GCodeReader r = m_reader; // clone
r.parse_buffer(gcode, [&total_layer_length, &layer_height, &z, &set_z] r.parse_buffer(gcode, [&total_layer_length, &layer_height, &z, &set_z]
(GCodeReader &reader, const GCodeReader::GCodeLine &line) { (GCodeReader &reader, const GCodeReader::GCodeLine &line) {
if (line.cmd_is("G1")) { if (line.cmd_is("G1")) {
@ -50,7 +50,7 @@ std::string SpiralVase::process_layer(const std::string &gcode)
z -= layer_height; z -= layer_height;
std::string new_gcode; std::string new_gcode;
this->_reader.parse_buffer(gcode, [&new_gcode, &z, &layer_height, &total_layer_length] m_reader.parse_buffer(gcode, [&new_gcode, &z, &layer_height, &total_layer_length]
(GCodeReader &reader, GCodeReader::GCodeLine line) { (GCodeReader &reader, GCodeReader::GCodeLine line) {
if (line.cmd_is("G1")) { if (line.cmd_is("G1")) {
if (line.has_z()) { if (line.has_z()) {

View file

@ -7,20 +7,19 @@
namespace Slic3r { namespace Slic3r {
class SpiralVase { class SpiralVase {
public: public:
bool enable; bool enable = false;
SpiralVase(const PrintConfig &config) SpiralVase(const PrintConfig &config) : m_config(&config)
: enable(false), _config(&config)
{ {
this->_reader.z() = (float)this->_config->z_offset; m_reader.z() = (float)m_config->z_offset;
this->_reader.apply_config(*this->_config); m_reader.apply_config(*m_config);
}; };
std::string process_layer(const std::string &gcode); std::string process_layer(const std::string &gcode);
private: private:
const PrintConfig* _config; const PrintConfig *m_config;
GCodeReader _reader; GCodeReader m_reader;
}; };
} }

View file

@ -130,10 +130,11 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
// Do it only if all the objects were configured to be printed with a single extruder. // Do it only if all the objects were configured to be printed with a single extruder.
std::vector<std::pair<double, unsigned int>> per_layer_extruder_switches; std::vector<std::pair<double, unsigned int>> per_layer_extruder_switches;
if (auto num_extruders = unsigned(print.config().nozzle_diameter.size()); if (auto num_extruders = unsigned(print.config().nozzle_diameter.size());
num_extruders > 1 && print.object_extruders().size() == 1) { num_extruders > 1 && print.object_extruders().size() == 1 && // the current Print's configuration is CustomGCode::MultiAsSingle
print.model().custom_gcode_per_print_z.mode == CustomGCode::MultiAsSingle) {
// Printing a single extruder platter on a printer with more than 1 extruder (or single-extruder multi-material). // Printing a single extruder platter on a printer with more than 1 extruder (or single-extruder multi-material).
// There may be custom per-layer tool changes available at the model. // There may be custom per-layer tool changes available at the model.
per_layer_extruder_switches = custom_tool_changes(print.model(), num_extruders); per_layer_extruder_switches = custom_tool_changes(print.model().custom_gcode_per_print_z, num_extruders);
} }
// Collect extruders reuqired to print the layers. // Collect extruders reuqired to print the layers.
@ -462,15 +463,23 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
// Only valid for non-sequential print. // Only valid for non-sequential print.
assert(! print.config().complete_objects.value); assert(! print.config().complete_objects.value);
const Model::CustomGCodeInfo &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z; const CustomGCode::Info &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z;
if (custom_gcode_per_print_z.gcodes.empty()) if (custom_gcode_per_print_z.gcodes.empty())
return; return;
unsigned int num_extruders = *std::max_element(m_all_printing_extruders.begin(), m_all_printing_extruders.end()) + 1; auto num_extruders = unsigned(print.config().nozzle_diameter.size());
CustomGCode::Mode mode =
(num_extruders == 1) ? CustomGCode::SingleExtruder :
print.object_extruders().size() == 1 ? CustomGCode::MultiAsSingle : CustomGCode::MultiExtruder;
CustomGCode::Mode model_mode = print.model().custom_gcode_per_print_z.mode;
std::vector<unsigned char> extruder_printing_above(num_extruders, false); std::vector<unsigned char> extruder_printing_above(num_extruders, false);
auto custom_gcode_it = custom_gcode_per_print_z.gcodes.rbegin(); auto custom_gcode_it = custom_gcode_per_print_z.gcodes.rbegin();
// Tool changes and color changes will be ignored, if the model's tool/color changes were entered in mm mode and the print is in non mm mode
// or vice versa.
bool ignore_tool_and_color_changes = (mode == CustomGCode::MultiExtruder) != (model_mode == CustomGCode::MultiExtruder);
// If printing on a single extruder machine, make the tool changes trigger color change (M600) events. // If printing on a single extruder machine, make the tool changes trigger color change (M600) events.
bool tool_changes_as_color_changes = num_extruders == 1; bool tool_changes_as_color_changes = mode == CustomGCode::SingleExtruder && model_mode == CustomGCode::MultiAsSingle;
// From the last layer to the first one: // From the last layer to the first one:
for (auto it_lt = m_layer_tools.rbegin(); it_lt != m_layer_tools.rend(); ++ it_lt) { for (auto it_lt = m_layer_tools.rbegin(); it_lt != m_layer_tools.rend(); ++ it_lt) {
LayerTools &lt = *it_lt; LayerTools &lt = *it_lt;
@ -483,16 +492,23 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
// Custom G-codes were processed. // Custom G-codes were processed.
break; break;
// Some custom G-code is configured for this layer or a layer below. // Some custom G-code is configured for this layer or a layer below.
const Model::CustomGCode &custom_gcode = *custom_gcode_it; const CustomGCode::Item &custom_gcode = *custom_gcode_it;
// print_z of the layer below the current layer. // print_z of the layer below the current layer.
coordf_t print_z_below = 0.; coordf_t print_z_below = 0.;
if (auto it_lt_below = it_lt; ++ it_lt_below != m_layer_tools.rend()) if (auto it_lt_below = it_lt; ++ it_lt_below != m_layer_tools.rend())
print_z_below = it_lt_below->print_z; print_z_below = it_lt_below->print_z;
if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) { if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) {
// The custom G-code applies to the current layer. // The custom G-code applies to the current layer.
if ( tool_changes_as_color_changes || custom_gcode.gcode != ColorChangeCode || bool color_change = custom_gcode.gcode == ColorChangeCode;
(custom_gcode.extruder <= num_extruders && extruder_printing_above[unsigned(custom_gcode.extruder - 1)])) bool tool_change = custom_gcode.gcode == ToolChangeCode;
bool pause_or_custom_gcode = ! color_change && ! tool_change;
bool apply_color_change = ! ignore_tool_and_color_changes &&
// If it is color change, it will actually be useful as the exturder above will print. // If it is color change, it will actually be useful as the exturder above will print.
(color_change ?
mode == CustomGCode::SingleExtruder ||
(custom_gcode.extruder <= int(num_extruders) && extruder_printing_above[unsigned(custom_gcode.extruder - 1)]) :
tool_change && tool_changes_as_color_changes);
if (pause_or_custom_gcode || apply_color_change)
lt.custom_gcode = &custom_gcode; lt.custom_gcode = &custom_gcode;
// Consume that custom G-code event. // Consume that custom G-code event.
++ custom_gcode_it; ++ custom_gcode_it;
@ -602,7 +618,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON); const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON);
if (this_layer == nullptr) if (this_layer == nullptr)
continue; continue;
size_t num_of_copies = object->copies().size(); size_t num_of_copies = object->instances().size();
// iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves // iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { for (unsigned int copy = 0; copy < num_of_copies; ++copy) {
@ -677,7 +693,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON); const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON);
if (this_layer == nullptr) if (this_layer == nullptr)
continue; continue;
size_t num_of_copies = object->copies().size(); size_t num_of_copies = object->instances().size();
for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {

View file

@ -114,7 +114,7 @@ public:
size_t wipe_tower_partitions = 0; size_t wipe_tower_partitions = 0;
coordf_t wipe_tower_layer_height = 0.; coordf_t wipe_tower_layer_height = 0.;
// Custom G-code (color change, extruder switch, pause) to be performed before this layer starts to print. // Custom G-code (color change, extruder switch, pause) to be performed before this layer starts to print.
const Model::CustomGCode *custom_gcode = nullptr; const CustomGCode::Item *custom_gcode = nullptr;
WipingExtrusions& wiping_extrusions() { WipingExtrusions& wiping_extrusions() {
m_wiping_extrusions.set_layer_tools_ptr(this); m_wiping_extrusions.set_layer_tools_ptr(this);

View file

@ -707,6 +707,11 @@ namespace Slic3r {
return _get_time_dhms(get_time()); return _get_time_dhms(get_time());
} }
std::string GCodeTimeEstimator::get_time_dhm() const
{
return _get_time_dhm(get_time());
}
std::string GCodeTimeEstimator::get_time_minutes() const std::string GCodeTimeEstimator::get_time_minutes() const
{ {
return _get_time_minutes(get_time()); return _get_time_minutes(get_time());
@ -1616,6 +1621,29 @@ namespace Slic3r {
return buffer; return buffer;
} }
std::string GCodeTimeEstimator::_get_time_dhm(float time_in_secs)
{
char buffer[64];
int minutes = std::round(time_in_secs / 60.);
if (minutes <= 0) {
::sprintf(buffer, "%ds", (int)time_in_secs);
} else {
int days = minutes / 1440;
minutes -= days * 1440;
int hours = minutes / 60;
minutes -= hours * 60;
if (days > 0)
::sprintf(buffer, "%dd %dh %dm", days, hours, minutes);
else if (hours > 0)
::sprintf(buffer, "%dh %dm", hours, minutes);
else
::sprintf(buffer, "%dm", minutes);
}
return buffer;
}
std::string GCodeTimeEstimator::_get_time_minutes(float time_in_secs) std::string GCodeTimeEstimator::_get_time_minutes(float time_in_secs)
{ {
return std::to_string((int)(::roundf(time_in_secs / 60.0f))); return std::to_string((int)(::roundf(time_in_secs / 60.0f)));

View file

@ -363,6 +363,9 @@ namespace Slic3r {
// Returns the estimated time, in format DDd HHh MMm SSs // Returns the estimated time, in format DDd HHh MMm SSs
std::string get_time_dhms() const; std::string get_time_dhms() const;
// Returns the estimated time, in format DDd HHh MMm
std::string get_time_dhm() const;
// Returns the estimated time, in minutes (integer) // Returns the estimated time, in minutes (integer)
std::string get_time_minutes() const; std::string get_time_minutes() const;
@ -473,6 +476,8 @@ namespace Slic3r {
// Returns the given time is seconds in format DDd HHh MMm SSs // Returns the given time is seconds in format DDd HHh MMm SSs
static std::string _get_time_dhms(float time_in_secs); static std::string _get_time_dhms(float time_in_secs);
// Returns the given time is minutes in format DDd HHh MMm
static std::string _get_time_dhm(float time_in_secs);
// Returns the given, in minutes (integer) // Returns the given, in minutes (integer)
static std::string _get_time_minutes(float time_in_secs); static std::string _get_time_minutes(float time_in_secs);

View file

@ -1,4 +1,5 @@
#include "GCodeWriter.hpp" #include "GCodeWriter.hpp"
#include "CustomGCode.hpp"
#include <algorithm> #include <algorithm>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>

View file

@ -10,11 +10,6 @@
namespace Slic3r { namespace Slic3r {
// Additional Codes which can be set by user using DoubleSlider
static constexpr char ColorChangeCode[] = "M600";
static constexpr char PausePrintCode[] = "M601";
static constexpr char ToolChangeCode[] = "tool_change";
class GCodeWriter { class GCodeWriter {
public: public:
GCodeConfig config; GCodeConfig config;

View file

@ -105,6 +105,7 @@ public:
coordf_t slice_z; // Z used for slicing in unscaled coordinates coordf_t slice_z; // Z used for slicing in unscaled coordinates
coordf_t print_z; // Z used for printing in unscaled coordinates coordf_t print_z; // Z used for printing in unscaled coordinates
coordf_t height; // layer height in unscaled coordinates coordf_t height; // layer height in unscaled coordinates
coordf_t bottom_z() const { return this->print_z - this->height; }
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
// (with possibly differing extruder ID and slicing parameters) and merged. // (with possibly differing extruder ID and slicing parameters) and merged.

View file

@ -362,8 +362,10 @@ void LayerRegion::prepare_fill_surfaces()
alter fill_surfaces boundaries on which our idempotency relies since that's alter fill_surfaces boundaries on which our idempotency relies since that's
the only meaningful information returned by psPerimeters. */ the only meaningful information returned by psPerimeters. */
bool spiral_vase = this->layer()->object()->print()->config().spiral_vase;
// if no solid layers are requested, turn top/bottom surfaces to internal // if no solid layers are requested, turn top/bottom surfaces to internal
if (this->region()->config().top_solid_layers == 0) { if (! spiral_vase && this->region()->config().top_solid_layers == 0) {
for (Surface &surface : this->fill_surfaces.surfaces) for (Surface &surface : this->fill_surfaces.surfaces)
if (surface.is_top()) if (surface.is_top())
surface.surface_type = this->layer()->object()->config().infill_only_where_needed ? stInternalVoid : stInternal; surface.surface_type = this->layer()->object()->config().infill_only_where_needed ? stInternalVoid : stInternal;
@ -375,7 +377,7 @@ void LayerRegion::prepare_fill_surfaces()
} }
// turn too small internal regions into solid regions according to the user setting // turn too small internal regions into solid regions according to the user setting
if (this->region()->config().fill_density.value > 0) { if (! spiral_vase && this->region()->config().fill_density.value > 0) {
// scaling an area requires two calls! // scaling an area requires two calls!
double min_area = scale_(scale_(this->region()->config().solid_infill_below_area.value)); double min_area = scale_(scale_(this->region()->config().solid_infill_below_area.value));
for (Surface &surface : this->fill_surfaces.surfaces) for (Surface &surface : this->fill_surfaces.surfaces)

View file

@ -252,46 +252,6 @@ template<class T> struct remove_cvref
template<class T> using remove_cvref_t = typename remove_cvref<T>::type; template<class T> using remove_cvref_t = typename remove_cvref<T>::type;
template<class T> using DefaultContainer = std::vector<T>;
/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
template<class T, class I, template<class> class Container = DefaultContainer>
inline Container<remove_cvref_t<T>> linspace(const T &start,
const T &stop,
const I &n)
{
Container<remove_cvref_t<T>> vals(n, T());
T stride = (stop - start) / n;
size_t i = 0;
std::generate(vals.begin(), vals.end(), [&i, start, stride] {
return start + i++ * stride;
});
return vals;
}
/// A set of equidistant values starting from 'start' (inclusive), ending
/// in the closest multiple of 'stride' less than or equal to 'end' and
/// leaving 'stride' space between each value.
/// Very similar to Matlab [start:stride:end] notation.
template<class T, template<class> class Container = DefaultContainer>
inline Container<remove_cvref_t<T>> grid(const T &start,
const T &stop,
const T &stride)
{
Container<remove_cvref_t<T>>
vals(size_t(std::ceil((stop - start) / stride)), T());
int i = 0;
std::generate(vals.begin(), vals.end(), [&i, start, stride] {
return start + i++ * stride;
});
return vals;
}
// A shorter C++14 style form of the enable_if metafunction // A shorter C++14 style form of the enable_if metafunction
template<bool B, class T> template<bool B, class T>
using enable_if_t = typename std::enable_if<B, T>::type; using enable_if_t = typename std::enable_if<B, T>::type;
@ -392,6 +352,56 @@ inline IntegerOnly<I, std::vector<T, Args...>> reserve_vector(I capacity)
return ret; return ret;
} }
/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
template<class T, class I>
inline std::vector<T> linspace_vector(const ArithmeticOnly<T> &start,
const T &stop,
const IntegerOnly<I> &n)
{
std::vector<T> vals(n, T());
T stride = (stop - start) / n;
size_t i = 0;
std::generate(vals.begin(), vals.end(), [&i, start, stride] {
return start + i++ * stride;
});
return vals;
}
template<size_t N, class T>
inline std::array<ArithmeticOnly<T>, N> linspace_array(const T &start, const T &stop)
{
std::array<T, N> vals = {T()};
T stride = (stop - start) / N;
size_t i = 0;
std::generate(vals.begin(), vals.end(), [&i, start, stride] {
return start + i++ * stride;
});
return vals;
}
/// A set of equidistant values starting from 'start' (inclusive), ending
/// in the closest multiple of 'stride' less than or equal to 'end' and
/// leaving 'stride' space between each value.
/// Very similar to Matlab [start:stride:end] notation.
template<class T>
inline std::vector<ArithmeticOnly<T>> grid(const T &start,
const T &stop,
const T &stride)
{
std::vector<T> vals(size_t(std::ceil((stop - start) / stride)), T());
int i = 0;
std::generate(vals.begin(), vals.end(), [&i, start, stride] {
return start + i++ * stride;
});
return vals;
}
} // namespace Slic3r } // namespace Slic3r
#endif // MTUTILS_HPP #endif // MTUTILS_HPP

View file

@ -0,0 +1,276 @@
#include "MeshBoolean.hpp"
#include "libslic3r/TriangleMesh.hpp"
#undef PI
// Include igl first. It defines "L" macro which then clashes with our localization
#include <igl/copyleft/cgal/mesh_boolean.h>
#undef L
// CGAL headers
#include <CGAL/Polygon_mesh_processing/corefinement.h>
#include <CGAL/Exact_integer.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/orient_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/repair_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/repair.h>
#include <CGAL/Polygon_mesh_processing/remesh.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <CGAL/Polygon_mesh_processing/orientation.h>
#include <CGAL/Cartesian_converter.h>
namespace Slic3r {
namespace MeshBoolean {
using MapMatrixXfUnaligned = Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
using MapMatrixXiUnaligned = Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
TriangleMesh eigen_to_triangle_mesh(const EigenMesh &emesh)
{
auto &VC = emesh.first; auto &FC = emesh.second;
Pointf3s points(size_t(VC.rows()));
std::vector<Vec3crd> facets(size_t(FC.rows()));
for (Eigen::Index i = 0; i < VC.rows(); ++i)
points[size_t(i)] = VC.row(i);
for (Eigen::Index i = 0; i < FC.rows(); ++i)
facets[size_t(i)] = FC.row(i);
TriangleMesh out{points, facets};
out.require_shared_vertices();
return out;
}
EigenMesh triangle_mesh_to_eigen(const TriangleMesh &mesh)
{
EigenMesh emesh;
emesh.first = MapMatrixXfUnaligned(mesh.its.vertices.front().data(),
Eigen::Index(mesh.its.vertices.size()),
3).cast<double>();
emesh.second = MapMatrixXiUnaligned(mesh.its.indices.front().data(),
Eigen::Index(mesh.its.indices.size()),
3);
return emesh;
}
void minus(EigenMesh &A, const EigenMesh &B)
{
auto &[VA, FA] = A;
auto &[VB, FB] = B;
Eigen::MatrixXd VC;
Eigen::MatrixXi FC;
igl::MeshBooleanType boolean_type(igl::MESH_BOOLEAN_TYPE_MINUS);
igl::copyleft::cgal::mesh_boolean(VA, FA, VB, FB, boolean_type, VC, FC);
VA = std::move(VC); FA = std::move(FC);
}
void minus(TriangleMesh& A, const TriangleMesh& B)
{
EigenMesh eA = triangle_mesh_to_eigen(A);
minus(eA, triangle_mesh_to_eigen(B));
A = eigen_to_triangle_mesh(eA);
}
void self_union(EigenMesh &A)
{
EigenMesh result;
auto &[V, F] = A;
auto &[VC, FC] = result;
igl::MeshBooleanType boolean_type(igl::MESH_BOOLEAN_TYPE_UNION);
igl::copyleft::cgal::mesh_boolean(V, F, Eigen::MatrixXd(), Eigen::MatrixXi(), boolean_type, VC, FC);
A = std::move(result);
}
void self_union(TriangleMesh& mesh)
{
auto eM = triangle_mesh_to_eigen(mesh);
self_union(eM);
mesh = eigen_to_triangle_mesh(eM);
}
namespace cgal {
namespace CGALProc = CGAL::Polygon_mesh_processing;
namespace CGALParams = CGAL::Polygon_mesh_processing::parameters;
using EpecKernel = CGAL::Exact_predicates_exact_constructions_kernel;
using EpicKernel = CGAL::Exact_predicates_inexact_constructions_kernel;
using _EpicMesh = CGAL::Surface_mesh<EpicKernel::Point_3>;
using _EpecMesh = CGAL::Surface_mesh<EpecKernel::Point_3>;
struct CGALMesh { _EpicMesh m; };
// /////////////////////////////////////////////////////////////////////////////
// Converions from and to CGAL mesh
// /////////////////////////////////////////////////////////////////////////////
template<class _Mesh> void triangle_mesh_to_cgal(const TriangleMesh &M, _Mesh &out)
{
using Index3 = std::array<size_t, 3>;
if (M.empty()) return;
std::vector<typename _Mesh::Point> points;
std::vector<Index3> indices;
points.reserve(M.its.vertices.size());
indices.reserve(M.its.indices.size());
for (auto &v : M.its.vertices) points.emplace_back(v.x(), v.y(), v.z());
for (auto &_f : M.its.indices) {
auto f = _f.cast<size_t>();
indices.emplace_back(Index3{f(0), f(1), f(2)});
}
CGALProc::orient_polygon_soup(points, indices);
CGALProc::polygon_soup_to_polygon_mesh(points, indices, out);
// Number the faces because 'orient_to_bound_a_volume' needs a face <--> index map
unsigned index = 0;
for (auto face : out.faces()) face = CGAL::SM_Face_index(index++);
if(CGAL::is_closed(out))
CGALProc::orient_to_bound_a_volume(out);
else
std::runtime_error("Mesh not watertight");
}
inline Vec3d to_vec3d(const _EpicMesh::Point &v)
{
return {v.x(), v.y(), v.z()};
}
inline Vec3d to_vec3d(const _EpecMesh::Point &v)
{
CGAL::Cartesian_converter<EpecKernel, EpicKernel> cvt;
auto iv = cvt(v);
return {iv.x(), iv.y(), iv.z()};
}
template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
{
Pointf3s points;
std::vector<Vec3crd> facets;
points.reserve(cgalmesh.num_vertices());
facets.reserve(cgalmesh.num_faces());
for (auto &vi : cgalmesh.vertices()) {
auto &v = cgalmesh.point(vi); // Don't ask...
points.emplace_back(to_vec3d(v));
}
for (auto &face : cgalmesh.faces()) {
auto vtc = cgalmesh.vertices_around_face(cgalmesh.halfedge(face));
int i = 0;
Vec3crd trface;
for (auto v : vtc) trface(i++) = static_cast<int>(v);
facets.emplace_back(trface);
}
TriangleMesh out{points, facets};
out.require_shared_vertices();
return out;
}
std::unique_ptr<CGALMesh, CGALMeshDeleter> triangle_mesh_to_cgal(const TriangleMesh &M)
{
std::unique_ptr<CGALMesh, CGALMeshDeleter> out(new CGALMesh{});
triangle_mesh_to_cgal(M, out->m);
return out;
}
TriangleMesh cgal_to_triangle_mesh(const CGALMesh &cgalmesh)
{
return cgal_to_triangle_mesh(cgalmesh.m);
}
// /////////////////////////////////////////////////////////////////////////////
// Boolean operations for CGAL meshes
// /////////////////////////////////////////////////////////////////////////////
static bool _cgal_diff(CGALMesh &A, CGALMesh &B, CGALMesh &R)
{
const auto &p = CGALParams::throw_on_self_intersection(true);
return CGALProc::corefine_and_compute_difference(A.m, B.m, R.m, p, p);
}
static bool _cgal_union(CGALMesh &A, CGALMesh &B, CGALMesh &R)
{
const auto &p = CGALParams::throw_on_self_intersection(true);
return CGALProc::corefine_and_compute_union(A.m, B.m, R.m, p, p);
}
static bool _cgal_intersection(CGALMesh &A, CGALMesh &B, CGALMesh &R)
{
const auto &p = CGALParams::throw_on_self_intersection(true);
return CGALProc::corefine_and_compute_intersection(A.m, B.m, R.m, p, p);
}
template<class Op> void _cgal_do(Op &&op, CGALMesh &A, CGALMesh &B)
{
bool success = false;
try {
CGALMesh result;
success = op(A, B, result);
A = std::move(result); // In-place operation does not work
} catch (...) {
success = false;
}
if (! success)
throw std::runtime_error("CGAL mesh boolean operation failed.");
}
void minus(CGALMesh &A, CGALMesh &B) { _cgal_do(_cgal_diff, A, B); }
void plus(CGALMesh &A, CGALMesh &B) { _cgal_do(_cgal_union, A, B); }
void intersect(CGALMesh &A, CGALMesh &B) { _cgal_do(_cgal_intersection, A, B); }
bool does_self_intersect(const CGALMesh &mesh) { return CGALProc::does_self_intersect(mesh.m); }
// /////////////////////////////////////////////////////////////////////////////
// Now the public functions for TriangleMesh input:
// /////////////////////////////////////////////////////////////////////////////
template<class Op> void _mesh_boolean_do(Op &&op, TriangleMesh &A, const TriangleMesh &B)
{
CGALMesh meshA;
CGALMesh meshB;
triangle_mesh_to_cgal(A, meshA.m);
triangle_mesh_to_cgal(B, meshB.m);
_cgal_do(op, meshA, meshB);
A = cgal_to_triangle_mesh(meshA.m);
}
void minus(TriangleMesh &A, const TriangleMesh &B)
{
_mesh_boolean_do(_cgal_diff, A, B);
}
void plus(TriangleMesh &A, const TriangleMesh &B)
{
_mesh_boolean_do(_cgal_union, A, B);
}
void intersect(TriangleMesh &A, const TriangleMesh &B)
{
_mesh_boolean_do(_cgal_intersection, A, B);
}
bool does_self_intersect(const TriangleMesh &mesh)
{
CGALMesh cgalm;
triangle_mesh_to_cgal(mesh, cgalm.m);
return CGALProc::does_self_intersect(cgalm.m);
}
void CGALMeshDeleter::operator()(CGALMesh *ptr) { delete ptr; }
} // namespace cgal
} // namespace MeshBoolean
} // namespace Slic3r

View file

@ -0,0 +1,49 @@
#ifndef libslic3r_MeshBoolean_hpp_
#define libslic3r_MeshBoolean_hpp_
#include <memory>
#include <exception>
#include <libslic3r/TriangleMesh.hpp>
#include <Eigen/Geometry>
namespace Slic3r {
namespace MeshBoolean {
using EigenMesh = std::pair<Eigen::MatrixXd, Eigen::MatrixXi>;
TriangleMesh eigen_to_triangle_mesh(const EigenMesh &emesh);
EigenMesh triangle_mesh_to_eigen(const TriangleMesh &mesh);
void minus(EigenMesh &A, const EigenMesh &B);
void self_union(EigenMesh &A);
void minus(TriangleMesh& A, const TriangleMesh& B);
void self_union(TriangleMesh& mesh);
namespace cgal {
struct CGALMesh;
struct CGALMeshDeleter { void operator()(CGALMesh *ptr); };
std::unique_ptr<CGALMesh, CGALMeshDeleter> triangle_mesh_to_cgal(const TriangleMesh &M);
TriangleMesh cgal_to_triangle_mesh(const CGALMesh &cgalmesh);
// Do boolean mesh difference with CGAL bypassing igl.
void minus(TriangleMesh &A, const TriangleMesh &B);
void plus(TriangleMesh &A, const TriangleMesh &B);
void intersect(TriangleMesh &A, const TriangleMesh &B);
void minus(CGALMesh &A, CGALMesh &B);
void plus(CGALMesh &A, CGALMesh &B);
void intersect(CGALMesh &A, CGALMesh &B);
bool does_self_intersect(const TriangleMesh &mesh);
bool does_self_intersect(const CGALMesh &mesh);
}
} // namespace MeshBoolean
} // namespace Slic3r
#endif // libslic3r_MeshBoolean_hpp_

View file

@ -126,7 +126,8 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
if (add_default_instances) if (add_default_instances)
model.add_default_instances(); model.add_default_instances();
update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z.gcodes, config); CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
return model; return model;
} }
@ -163,7 +164,8 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
if (add_default_instances) if (add_default_instances)
model.add_default_instances(); model.add_default_instances();
update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z.gcodes, config); CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
return model; return model;
} }
@ -618,6 +620,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
assert(this->config.id() == rhs.config.id()); assert(this->config.id() == rhs.config.id());
this->sla_support_points = rhs.sla_support_points; this->sla_support_points = rhs.sla_support_points;
this->sla_points_status = rhs.sla_points_status; this->sla_points_status = rhs.sla_points_status;
this->sla_drain_holes = rhs.sla_drain_holes;
this->layer_config_ranges = rhs.layer_config_ranges; // #ys_FIXME_experiment this->layer_config_ranges = rhs.layer_config_ranges; // #ys_FIXME_experiment
this->layer_height_profile = rhs.layer_height_profile; this->layer_height_profile = rhs.layer_height_profile;
this->printable = rhs.printable; this->printable = rhs.printable;
@ -658,6 +661,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
assert(this->config.id() == rhs.config.id()); assert(this->config.id() == rhs.config.id());
this->sla_support_points = std::move(rhs.sla_support_points); this->sla_support_points = std::move(rhs.sla_support_points);
this->sla_points_status = std::move(rhs.sla_points_status); this->sla_points_status = std::move(rhs.sla_points_status);
this->sla_drain_holes = std::move(rhs.sla_drain_holes);
this->layer_config_ranges = std::move(rhs.layer_config_ranges); // #ys_FIXME_experiment this->layer_config_ranges = std::move(rhs.layer_config_ranges); // #ys_FIXME_experiment
this->layer_height_profile = std::move(rhs.layer_height_profile); this->layer_height_profile = std::move(rhs.layer_height_profile);
this->origin_translation = std::move(rhs.origin_translation); this->origin_translation = std::move(rhs.origin_translation);
@ -903,11 +907,9 @@ const BoundingBoxf3& ModelObject::raw_bounding_box() const
const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true); const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true);
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
{
if (v->is_model_part()) if (v->is_model_part())
m_raw_bounding_box.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix())); m_raw_bounding_box.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix()));
} }
}
return m_raw_bounding_box; return m_raw_bounding_box;
} }
@ -1111,17 +1113,19 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
if (keep_upper) { if (keep_upper) {
upper->set_model(nullptr); upper->set_model(nullptr);
upper->sla_support_points.clear(); upper->sla_support_points.clear();
upper->sla_drain_holes.clear();
upper->sla_points_status = sla::PointsStatus::NoPoints; upper->sla_points_status = sla::PointsStatus::NoPoints;
upper->clear_volumes(); upper->clear_volumes();
upper->input_file = ""; upper->input_file.clear();
} }
if (keep_lower) { if (keep_lower) {
lower->set_model(nullptr); lower->set_model(nullptr);
lower->sla_support_points.clear(); lower->sla_support_points.clear();
lower->sla_drain_holes.clear();
lower->sla_points_status = sla::PointsStatus::NoPoints; lower->sla_points_status = sla::PointsStatus::NoPoints;
lower->clear_volumes(); lower->clear_volumes();
lower->input_file = ""; lower->input_file.clear();
} }
// Because transformations are going to be applied to meshes directly, // Because transformations are going to be applied to meshes directly,
@ -1154,7 +1158,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
if (keep_upper) { upper->add_volume(*volume); } if (keep_upper) { upper->add_volume(*volume); }
if (keep_lower) { lower->add_volume(*volume); } if (keep_lower) { lower->add_volume(*volume); }
} }
else { else if (! volume->mesh().empty()) {
TriangleMesh upper_mesh, lower_mesh; TriangleMesh upper_mesh, lower_mesh;
// Transform the mesh by the combined transformation matrix. // Transform the mesh by the combined transformation matrix.
@ -1163,6 +1168,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
mesh.transform(instance_matrix * volume_matrix, true); mesh.transform(instance_matrix * volume_matrix, true);
volume->reset_mesh(); volume->reset_mesh();
mesh.require_shared_vertices();
// Perform cut // Perform cut
TriangleMeshSlicer tms(&mesh); TriangleMeshSlicer tms(&mesh);
tms.cut(float(z), &upper_mesh, &lower_mesh); tms.cut(float(z), &upper_mesh, &lower_mesh);
@ -1841,19 +1848,6 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
return ret; return ret;
} }
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
// print_z corresponds to the first layer printed with the new extruder.
std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Model &model, size_t num_extruders)
{
std::vector<std::pair<double, unsigned int>> custom_tool_changes;
for (const Model::CustomGCode &custom_gcode : model.custom_gcode_per_print_z.gcodes)
if (custom_gcode.gcode == ToolChangeCode) {
// If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast<unsigned int>(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder));
}
return custom_tool_changes;
}
// Test whether the two models contain the same number of ModelObjects with the same set of IDs // Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing. // ordered in the same order. In that case it is not necessary to kill the background processing.
bool model_object_list_equal(const Model &model_old, const Model &model_new) bool model_object_list_equal(const Model &model_old, const Model &model_new)
@ -1942,28 +1936,6 @@ extern bool model_has_advanced_features(const Model &model)
return false; return false;
} }
extern void update_custom_gcode_per_print_z_from_config(std::vector<Model::CustomGCode>& custom_gcode_per_print_z, DynamicPrintConfig* config)
{
auto *colorprint_heights = config->option<ConfigOptionFloats>("colorprint_heights");
if (colorprint_heights == nullptr)
return;
if (custom_gcode_per_print_z.empty() && ! colorprint_heights->values.empty()) {
// Convert the old colorprint_heighs only if there is no equivalent data in a new format.
const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors();
const auto& colorprint_values = colorprint_heights->values;
custom_gcode_per_print_z.clear();
custom_gcode_per_print_z.reserve(colorprint_values.size());
int i = 0;
for (auto val : colorprint_values)
custom_gcode_per_print_z.emplace_back(Model::CustomGCode{ val, ColorChangeCode, 1, colors[(++i)%7] });
}
// The "colorprint_heights" config value has been deprecated. At this point of time it has been converted
// to a new format and therefore it shall be erased.
config->erase("colorprint_heights");
}
#ifndef NDEBUG #ifndef NDEBUG
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
void check_model_ids_validity(const Model &model) void check_model_ids_validity(const Model &model)

View file

@ -8,9 +8,11 @@
#include "Point.hpp" #include "Point.hpp"
#include "PrintConfig.hpp" #include "PrintConfig.hpp"
#include "Slicing.hpp" #include "Slicing.hpp"
#include "SLA/SLACommon.hpp" #include "SLA/SupportPoint.hpp"
#include "SLA/Hollowing.hpp"
#include "TriangleMesh.hpp" #include "TriangleMesh.hpp"
#include "Arrange.hpp" #include "Arrange.hpp"
#include "CustomGCode.hpp"
#include <map> #include <map>
#include <memory> #include <memory>
@ -198,11 +200,14 @@ public:
// This vector holds position of selected support points for SLA. The data are // This vector holds position of selected support points for SLA. The data are
// saved in mesh coordinates to allow using them for several instances. // saved in mesh coordinates to allow using them for several instances.
// The format is (x, y, z, point_size, supports_island) // The format is (x, y, z, point_size, supports_island)
std::vector<sla::SupportPoint> sla_support_points; sla::SupportPoints sla_support_points;
// To keep track of where the points came from (used for synchronization between // To keep track of where the points came from (used for synchronization between
// the SLA gizmo and the backend). // the SLA gizmo and the backend).
sla::PointsStatus sla_points_status = sla::PointsStatus::NoPoints; sla::PointsStatus sla_points_status = sla::PointsStatus::NoPoints;
// Holes to be drilled into the object so resin can flow out
sla::DrainHoles sla_drain_holes;
/* This vector accumulates the total translation applied to the object by the /* This vector accumulates the total translation applied to the object by the
center_around_origin() method. Callers might want to apply the same translation center_around_origin() method. Callers might want to apply the same translation
to new volumes before adding them to this object in order to preserve alignment to new volumes before adding them to this object in order to preserve alignment
@ -372,7 +377,7 @@ private:
template<class Archive> void serialize(Archive &ar) { template<class Archive> void serialize(Archive &ar) {
ar(cereal::base_class<ObjectBase>(this)); ar(cereal::base_class<ObjectBase>(this));
Internal::StaticSerializationWrapper<ModelConfig> config_wrapper(config); Internal::StaticSerializationWrapper<ModelConfig> config_wrapper(config);
ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, printable, origin_translation, ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation,
m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid); m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid);
} }
}; };
@ -669,6 +674,7 @@ public:
set_rotation(Z, rotation); set_rotation(Z, rotation);
set_offset(X, unscale<double>(offs(X))); set_offset(X, unscale<double>(offs(X)));
set_offset(Y, unscale<double>(offs(Y))); set_offset(Y, unscale<double>(offs(Y)));
this->object->invalidate_bounding_box();
} }
protected: protected:
@ -749,48 +755,7 @@ public:
ModelWipeTower wipe_tower; ModelWipeTower wipe_tower;
// Extensions for color print // Extensions for color print
struct CustomGCode CustomGCode::Info custom_gcode_per_print_z;
{
bool operator<(const CustomGCode& rhs) const { return this->print_z < rhs.print_z; }
bool operator==(const CustomGCode& rhs) const
{
return (rhs.print_z == this->print_z ) &&
(rhs.gcode == this->gcode ) &&
(rhs.extruder == this->extruder ) &&
(rhs.color == this->color );
}
bool operator!=(const CustomGCode& rhs) const { return ! (*this == rhs); }
double print_z;
std::string gcode;
int extruder; // Informative value for ColorChangeCode and ToolChangeCode
// "gcode" == ColorChangeCode => M600 will be applied for "extruder" extruder
// "gcode" == ToolChangeCode => for whole print tool will be switched to "extruder" extruder
std::string color; // if gcode is equal to PausePrintCode,
// this field is used for save a short message shown on Printer display
};
struct CustomGCodeInfo
{
enum MODE
{
SingleExtruder, // single extruder printer preset is selected
MultiAsSingle, // multiple extruder printer preset is selected, but
// this mode works just for Single extruder print
// (For all print from objects settings is used just one extruder)
MultiExtruder // multiple extruder printer preset is selected
} mode;
std::vector<CustomGCode> gcodes;
bool operator==(const CustomGCodeInfo& rhs) const
{
return (rhs.mode == this->mode ) &&
(rhs.gcodes == this->gcodes );
}
bool operator!=(const CustomGCodeInfo& rhs) const { return !(*this == rhs); }
}
custom_gcode_per_print_z;
// Default constructor assigns a new ID to the model. // Default constructor assigns a new ID to the model.
Model() { assert(this->id().valid()); } Model() { assert(this->id().valid()); }
@ -872,10 +837,6 @@ private:
#undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE #undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE
#undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE #undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
// print_z corresponds to the first layer printed with the new extruder.
extern std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Model &model, size_t num_extruders);
// Test whether the two models contain the same number of ModelObjects with the same set of IDs // Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing. // ordered in the same order. In that case it is not necessary to kill the background processing.
extern bool model_object_list_equal(const Model &model_old, const Model &model_new); extern bool model_object_list_equal(const Model &model_old, const Model &model_new);
@ -893,10 +854,6 @@ extern bool model_volume_list_changed(const ModelObject &model_object_old, const
extern bool model_has_multi_part_objects(const Model &model); extern bool model_has_multi_part_objects(const Model &model);
// If the model has advanced features, then it cannot be processed in simple mode. // If the model has advanced features, then it cannot be processed in simple mode.
extern bool model_has_advanced_features(const Model &model); extern bool model_has_advanced_features(const Model &model);
// If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer),
// and if model.custom_gcode_per_print_z is empty (there is no color print data available in a new format
// then model.custom_gcode_per_print_z should be updated considering this option.
extern void update_custom_gcode_per_print_z_from_config(std::vector<Model::CustomGCode>& custom_gcode_per_print_z, DynamicPrintConfig* config);
#ifndef NDEBUG #ifndef NDEBUG
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.

View file

@ -0,0 +1,135 @@
#define NOMINMAX
#include "OpenVDBUtils.hpp"
#include <openvdb/tools/MeshToVolume.h>
#include <openvdb/tools/VolumeToMesh.h>
#include <openvdb/tools/LevelSetRebuild.h>
//#include "MTUtils.hpp"
namespace Slic3r {
class TriangleMeshDataAdapter {
public:
const TriangleMesh &mesh;
size_t polygonCount() const { return mesh.its.indices.size(); }
size_t pointCount() const { return mesh.its.vertices.size(); }
size_t vertexCount(size_t) const { return 3; }
// Return position pos in local grid index space for polygon n and vertex v
void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const;
};
class Contour3DDataAdapter {
public:
const sla::Contour3D &mesh;
size_t polygonCount() const { return mesh.faces3.size() + mesh.faces4.size(); }
size_t pointCount() const { return mesh.points.size(); }
size_t vertexCount(size_t n) const { return n < mesh.faces3.size() ? 3 : 4; }
// Return position pos in local grid index space for polygon n and vertex v
void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const;
};
void TriangleMeshDataAdapter::getIndexSpacePoint(size_t n,
size_t v,
openvdb::Vec3d &pos) const
{
auto vidx = size_t(mesh.its.indices[n](Eigen::Index(v)));
Slic3r::Vec3d p = mesh.its.vertices[vidx].cast<double>();
pos = {p.x(), p.y(), p.z()};
}
void Contour3DDataAdapter::getIndexSpacePoint(size_t n,
size_t v,
openvdb::Vec3d &pos) const
{
size_t vidx = 0;
if (n < mesh.faces3.size()) vidx = size_t(mesh.faces3[n](Eigen::Index(v)));
else vidx = size_t(mesh.faces4[n - mesh.faces3.size()](Eigen::Index(v)));
Slic3r::Vec3d p = mesh.points[vidx];
pos = {p.x(), p.y(), p.z()};
}
// TODO: Do I need to call initialize? Seems to work without it as well but the
// docs say it should be called ones. It does a mutex lock-unlock sequence all
// even if was called previously.
openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh &mesh,
const openvdb::math::Transform &tr,
float exteriorBandWidth,
float interiorBandWidth,
int flags)
{
openvdb::initialize();
return openvdb::tools::meshToVolume<openvdb::FloatGrid>(
TriangleMeshDataAdapter{mesh}, tr, exteriorBandWidth,
interiorBandWidth, flags);
}
openvdb::FloatGrid::Ptr mesh_to_grid(const sla::Contour3D &mesh,
const openvdb::math::Transform &tr,
float exteriorBandWidth,
float interiorBandWidth,
int flags)
{
openvdb::initialize();
return openvdb::tools::meshToVolume<openvdb::FloatGrid>(
Contour3DDataAdapter{mesh}, tr, exteriorBandWidth, interiorBandWidth,
flags);
}
template<class Grid>
sla::Contour3D _volumeToMesh(const Grid &grid,
double isovalue,
double adaptivity,
bool relaxDisorientedTriangles)
{
openvdb::initialize();
std::vector<openvdb::Vec3s> points;
std::vector<openvdb::Vec3I> triangles;
std::vector<openvdb::Vec4I> quads;
openvdb::tools::volumeToMesh(grid, points, triangles, quads, isovalue,
adaptivity, relaxDisorientedTriangles);
sla::Contour3D ret;
ret.points.reserve(points.size());
ret.faces3.reserve(triangles.size());
ret.faces4.reserve(quads.size());
for (auto &v : points) ret.points.emplace_back(to_vec3d(v));
for (auto &v : triangles) ret.faces3.emplace_back(to_vec3i(v));
for (auto &v : quads) ret.faces4.emplace_back(to_vec4i(v));
return ret;
}
TriangleMesh grid_to_mesh(const openvdb::FloatGrid &grid,
double isovalue,
double adaptivity,
bool relaxDisorientedTriangles)
{
return to_triangle_mesh(
_volumeToMesh(grid, isovalue, adaptivity, relaxDisorientedTriangles));
}
sla::Contour3D grid_to_contour3d(const openvdb::FloatGrid &grid,
double isovalue,
double adaptivity,
bool relaxDisorientedTriangles)
{
return _volumeToMesh(grid, isovalue, adaptivity,
relaxDisorientedTriangles);
}
openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid, double iso, double er, double ir)
{
return openvdb::tools::levelSetRebuild(grid, float(iso), float(er), float(ir));
}
} // namespace Slic3r

View file

@ -0,0 +1,45 @@
#ifndef OPENVDBUTILS_HPP
#define OPENVDBUTILS_HPP
#include <libslic3r/TriangleMesh.hpp>
#include <libslic3r/SLA/Common.hpp>
#include <libslic3r/SLA/Contour3D.hpp>
#include <openvdb/openvdb.h>
namespace Slic3r {
inline Vec3f to_vec3f(const openvdb::Vec3s &v) { return Vec3f{v.x(), v.y(), v.z()}; }
inline Vec3d to_vec3d(const openvdb::Vec3s &v) { return to_vec3f(v).cast<double>(); }
inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1]), int(v[2])}; }
inline Vec4i to_vec4i(const openvdb::Vec4I &v) { return Vec4i{int(v[0]), int(v[1]), int(v[2]), int(v[3])}; }
openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh & mesh,
const openvdb::math::Transform &tr = {},
float exteriorBandWidth = 3.0f,
float interiorBandWidth = 3.0f,
int flags = 0);
openvdb::FloatGrid::Ptr mesh_to_grid(const sla::Contour3D & mesh,
const openvdb::math::Transform &tr = {},
float exteriorBandWidth = 3.0f,
float interiorBandWidth = 3.0f,
int flags = 0);
sla::Contour3D grid_to_contour3d(const openvdb::FloatGrid &grid,
double isovalue,
double adaptivity,
bool relaxDisorientedTriangles = true);
TriangleMesh grid_to_mesh(const openvdb::FloatGrid &grid,
double isovalue = 0.0,
double adaptivity = 0.0,
bool relaxDisorientedTriangles = true);
openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid,
double iso,
double ext_range = 3.,
double int_range = 3.);
} // namespace Slic3r
#endif // OPENVDBUTILS_HPP

View file

@ -312,6 +312,10 @@ void PerimeterGenerator::process()
for (ExPolygon &ex : expp) for (ExPolygon &ex : expp)
ex.medial_axis(ext_perimeter_width + ext_perimeter_spacing2, min_width, &thin_walls); ex.medial_axis(ext_perimeter_width + ext_perimeter_spacing2, min_width, &thin_walls);
} }
if (print_config->spiral_vase && offsets.size() > 1) {
// Remove all but the largest area polygon.
keep_largest_contour_only(offsets);
}
} else { } else {
//FIXME Is this offset correct if the line width of the inner perimeters differs //FIXME Is this offset correct if the line width of the inner perimeters differs
// from the line width of the infill? // from the line width of the infill?

View file

@ -1,4 +1,5 @@
#include "PlaceholderParser.hpp" #include "PlaceholderParser.hpp"
#include "Flow.hpp"
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
#include <iomanip> #include <iomanip>
@ -99,11 +100,7 @@ static inline bool opts_equal(const DynamicConfig &config_old, const DynamicConf
const ConfigOption *opt_old = config_old.option(opt_key); const ConfigOption *opt_old = config_old.option(opt_key);
const ConfigOption *opt_new = config_new.option(opt_key); const ConfigOption *opt_new = config_new.option(opt_key);
assert(opt_new != nullptr); assert(opt_new != nullptr);
if (opt_old == nullptr) return opt_old != nullptr && *opt_new == *opt_old;
return false;
return (opt_new->type() == coFloatOrPercent) ?
dynamic_cast<const ConfigOptionFloat*>(opt_old)->value == config_new.get_abs_value(opt_key) :
*opt_new == *opt_old;
} }
std::vector<std::string> PlaceholderParser::config_diff(const DynamicPrintConfig &rhs) std::vector<std::string> PlaceholderParser::config_diff(const DynamicPrintConfig &rhs)
@ -126,14 +123,7 @@ bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs)
bool modified = false; bool modified = false;
for (const t_config_option_key &opt_key : rhs.keys()) { for (const t_config_option_key &opt_key : rhs.keys()) {
if (! opts_equal(m_config, rhs, opt_key)) { if (! opts_equal(m_config, rhs, opt_key)) {
// Store a copy of the config option. this->set(opt_key, rhs.option(opt_key)->clone());
// Convert FloatOrPercent values to floats first.
//FIXME there are some ratio_over chains, which end with empty ratio_with.
// For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly.
const ConfigOption *opt_rhs = rhs.option(opt_key);
this->set(opt_key, (opt_rhs->type() == coFloatOrPercent) ?
new ConfigOptionFloat(rhs.get_abs_value(opt_key)) :
opt_rhs->clone());
modified = true; modified = true;
} }
} }
@ -142,16 +132,8 @@ bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs)
void PlaceholderParser::apply_only(const DynamicPrintConfig &rhs, const std::vector<std::string> &keys) void PlaceholderParser::apply_only(const DynamicPrintConfig &rhs, const std::vector<std::string> &keys)
{ {
for (const t_config_option_key &opt_key : keys) { for (const t_config_option_key &opt_key : keys)
// Store a copy of the config option. this->set(opt_key, rhs.option(opt_key)->clone());
// Convert FloatOrPercent values to floats first.
//FIXME there are some ratio_over chains, which end with empty ratio_with.
// For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly.
const ConfigOption *opt_rhs = rhs.option(opt_key);
this->set(opt_key, (opt_rhs->type() == coFloatOrPercent) ?
new ConfigOptionFloat(rhs.get_abs_value(opt_key)) :
opt_rhs->clone());
}
} }
void PlaceholderParser::apply_config(DynamicPrintConfig &&rhs) void PlaceholderParser::apply_config(DynamicPrintConfig &&rhs)
@ -635,7 +617,7 @@ namespace client
return os; return os;
} }
struct MyContext { struct MyContext : public ConfigOptionResolver {
const DynamicConfig *external_config = nullptr; const DynamicConfig *external_config = nullptr;
const DynamicConfig *config = nullptr; const DynamicConfig *config = nullptr;
const DynamicConfig *config_override = nullptr; const DynamicConfig *config_override = nullptr;
@ -650,7 +632,7 @@ namespace client
static void evaluate_full_macro(const MyContext *ctx, bool &result) { result = ! ctx->just_boolean_expression; } static void evaluate_full_macro(const MyContext *ctx, bool &result) { result = ! ctx->just_boolean_expression; }
const ConfigOption* resolve_symbol(const std::string &opt_key) const const ConfigOption* optptr(const t_config_option_key &opt_key) const override
{ {
const ConfigOption *opt = nullptr; const ConfigOption *opt = nullptr;
if (config_override != nullptr) if (config_override != nullptr)
@ -662,6 +644,8 @@ namespace client
return opt; return opt;
} }
const ConfigOption* resolve_symbol(const std::string &opt_key) const { return this->optptr(opt_key); }
template <typename Iterator> template <typename Iterator>
static void legacy_variable_expansion( static void legacy_variable_expansion(
const MyContext *ctx, const MyContext *ctx,
@ -758,7 +742,43 @@ namespace client
case coPoint: output.set_s(opt.opt->serialize()); break; case coPoint: output.set_s(opt.opt->serialize()); break;
case coBool: output.set_b(opt.opt->getBool()); break; case coBool: output.set_b(opt.opt->getBool()); break;
case coFloatOrPercent: case coFloatOrPercent:
ctx->throw_exception("FloatOrPercent variables are not supported", opt.it_range); {
std::string opt_key(opt.it_range.begin(), opt.it_range.end());
if (boost::ends_with(opt_key, "extrusion_width")) {
// Extrusion width supports defaults and a complex graph of dependencies.
output.set_d(Flow::extrusion_width(opt_key, *ctx, static_cast<unsigned int>(ctx->current_extruder_id)));
} else if (! static_cast<const ConfigOptionFloatOrPercent*>(opt.opt)->percent) {
// Not a percent, just return the value.
output.set_d(opt.opt->getFloat());
} else {
// Resolve dependencies using the "ratio_over" link to a parent value.
const ConfigOptionDef *opt_def = print_config_def.get(opt_key);
assert(opt_def != nullptr);
double v = opt.opt->getFloat() * 0.01; // percent to ratio
for (;;) {
const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over);
if (opt_parent == nullptr)
ctx->throw_exception("FloatOrPercent variable failed to resolve the \"ratio_over\" dependencies", opt.it_range);
if (boost::ends_with(opt_def->ratio_over, "extrusion_width")) {
// Extrusion width supports defaults and a complex graph of dependencies.
assert(opt_parent->type() == coFloatOrPercent);
v *= Flow::extrusion_width(opt_def->ratio_over, static_cast<const ConfigOptionFloatOrPercent*>(opt_parent), *ctx, static_cast<unsigned int>(ctx->current_extruder_id));
break;
}
if (opt_parent->type() == coFloat || opt_parent->type() == coFloatOrPercent) {
v *= opt_parent->getFloat();
if (opt_parent->type() == coFloat || ! static_cast<const ConfigOptionFloatOrPercent*>(opt_parent)->percent)
break;
v *= 0.01; // percent to ratio
}
// Continue one level up in the "ratio_over" hierarchy.
opt_def = print_config_def.get(opt_def->ratio_over);
assert(opt_def != nullptr);
}
output.set_d(v);
}
break;
}
default: default:
ctx->throw_exception("Unknown scalar variable type", opt.it_range); ctx->throw_exception("Unknown scalar variable type", opt.it_range);
} }

View file

@ -24,8 +24,7 @@
#include <boost/format.hpp> #include <boost/format.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
//! macro used to mark string used at localization, // Mark string for localization and translate.
//! return same string
#define L(s) Slic3r::I18N::translate(s) #define L(s) Slic3r::I18N::translate(s)
namespace Slic3r { namespace Slic3r {
@ -174,7 +173,11 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
steps.emplace_back(psSkirt); steps.emplace_back(psSkirt);
} else if ( } else if (
opt_key == "nozzle_diameter" opt_key == "nozzle_diameter"
|| opt_key == "resolution") { || opt_key == "resolution"
// Spiral Vase forces different kind of slicing than the normal model:
// In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer.
// Therefore toggling the Spiral Vase on / off requires complete reslicing.
|| opt_key == "spiral_vase") {
osteps.emplace_back(posSlice); osteps.emplace_back(posSlice);
} else if ( } else if (
opt_key == "complete_objects" opt_key == "complete_objects"
@ -196,7 +199,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|| opt_key == "high_current_on_filament_swap" || opt_key == "high_current_on_filament_swap"
|| opt_key == "infill_first" || opt_key == "infill_first"
|| opt_key == "single_extruder_multi_material" || opt_key == "single_extruder_multi_material"
|| opt_key == "spiral_vase"
|| opt_key == "temperature" || opt_key == "temperature"
|| opt_key == "wipe_tower" || opt_key == "wipe_tower"
|| opt_key == "wipe_tower_width" || opt_key == "wipe_tower_width"
@ -326,7 +328,7 @@ unsigned int Print::num_object_instances() const
{ {
unsigned int instances = 0; unsigned int instances = 0;
for (const PrintObject *print_object : m_objects) for (const PrintObject *print_object : m_objects)
instances += (unsigned int)print_object->copies().size(); instances += (unsigned int)print_object->instances().size();
return instances; return instances;
} }
@ -447,33 +449,30 @@ static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &
return true; return true;
} }
struct PrintInstances struct PrintObjectTrafoAndInstances
{ {
Transform3d trafo; Transform3d trafo;
Points copies; PrintInstances instances;
bool operator<(const PrintInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); } bool operator<(const PrintObjectTrafoAndInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); }
}; };
// Generate a list of trafos and XY offsets for instances of a ModelObject // Generate a list of trafos and XY offsets for instances of a ModelObject
static std::vector<PrintInstances> print_objects_from_model_object(const ModelObject &model_object) static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object(const ModelObject &model_object)
{ {
std::set<PrintInstances> trafos; std::set<PrintObjectTrafoAndInstances> trafos;
PrintInstances trafo; PrintObjectTrafoAndInstances trafo;
trafo.copies.assign(1, Point());
for (ModelInstance *model_instance : model_object.instances) for (ModelInstance *model_instance : model_object.instances)
if (model_instance->is_printable()) { if (model_instance->is_printable()) {
trafo.trafo = model_instance->get_matrix(); trafo.trafo = model_instance->get_matrix();
// Set the Z axis of the transformation. auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
trafo.copies.front() = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]); // Reset the XY axes of the transformation.
trafo.trafo.data()[12] = 0; trafo.trafo.data()[12] = 0;
trafo.trafo.data()[13] = 0; trafo.trafo.data()[13] = 0;
auto it = trafos.find(trafo); // Search or insert a trafo.
if (it == trafos.end()) auto it = trafos.emplace(trafo).first;
trafos.emplace(trafo); const_cast<PrintObjectTrafoAndInstances&>(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift });
else
const_cast<PrintInstances&>(*it).copies.emplace_back(trafo.copies.front());
} }
return std::vector<PrintInstances>(trafos.begin(), trafos.end()); return std::vector<PrintObjectTrafoAndInstances>(trafos.begin(), trafos.end());
} }
// Compare just the layer ranges and their layer heights, not the associated configs. // Compare just the layer ranges and their layer heights, not the associated configs.
@ -494,11 +493,11 @@ static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_
} }
// Returns true if va == vb when all CustomGCode items that are not ToolChangeCode are ignored. // Returns true if va == vb when all CustomGCode items that are not ToolChangeCode are ignored.
static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<Model::CustomGCode> &va, const std::vector<Model::CustomGCode> &vb) static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<CustomGCode::Item> &va, const std::vector<CustomGCode::Item> &vb)
{ {
auto it_a = va.begin(); auto it_a = va.begin();
auto it_b = vb.begin(); auto it_b = vb.begin();
while (it_a != va.end() && it_b != vb.end()) { while (it_a != va.end() || it_b != vb.end()) {
if (it_a != va.end() && it_a->gcode != ToolChangeCode) { if (it_a != va.end() && it_a->gcode != ToolChangeCode) {
// Skip any CustomGCode items, which are not tool changes. // Skip any CustomGCode items, which are not tool changes.
++ it_a; ++ it_a;
@ -530,7 +529,6 @@ void Print::config_diffs(
const DynamicPrintConfig &new_full_config, const DynamicPrintConfig &new_full_config,
t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys &region_diff, t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys &region_diff,
t_config_option_keys &full_config_diff, t_config_option_keys &full_config_diff,
DynamicPrintConfig &placeholder_parser_overrides,
DynamicPrintConfig &filament_overrides) const DynamicPrintConfig &filament_overrides) const
{ {
// Collect changes to print config, account for overrides of extruder retract values by filament presets. // Collect changes to print config, account for overrides of extruder retract values by filament presets.
@ -566,19 +564,11 @@ void Print::config_diffs(
object_diff = m_default_object_config.diff(new_full_config); object_diff = m_default_object_config.diff(new_full_config);
region_diff = m_default_region_config.diff(new_full_config); region_diff = m_default_region_config.diff(new_full_config);
// Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser. // Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser.
// As the PlaceholderParser does not interpret the FloatOrPercent values itself, these values are stored into the PlaceholderParser converted to floats.
for (const t_config_option_key &opt_key : new_full_config.keys()) { for (const t_config_option_key &opt_key : new_full_config.keys()) {
const ConfigOption *opt_old = m_full_print_config.option(opt_key); const ConfigOption *opt_old = m_full_print_config.option(opt_key);
const ConfigOption *opt_new = new_full_config.option(opt_key); const ConfigOption *opt_new = new_full_config.option(opt_key);
if (opt_old == nullptr || *opt_new != *opt_old) if (opt_old == nullptr || *opt_new != *opt_old)
full_config_diff.emplace_back(opt_key); full_config_diff.emplace_back(opt_key);
if (opt_new->type() == coFloatOrPercent) {
// The m_placeholder_parser is never modified by the background processing, GCode.cpp/hpp makes a copy.
const ConfigOption *opt_old_pp = this->placeholder_parser().config().option(opt_key);
double new_value = new_full_config.get_abs_value(opt_key);
if (opt_old_pp == nullptr || static_cast<const ConfigOptionFloat*>(opt_old_pp)->value != new_value)
placeholder_parser_overrides.set_key_value(opt_key, new ConfigOptionFloat(new_value));
}
} }
} }
@ -596,8 +586,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles. // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
t_config_option_keys print_diff, object_diff, region_diff, full_config_diff; t_config_option_keys print_diff, object_diff, region_diff, full_config_diff;
DynamicPrintConfig placeholder_parser_overrides, filament_overrides; DynamicPrintConfig filament_overrides;
this->config_diffs(new_full_config, print_diff, object_diff, region_diff, full_config_diff, placeholder_parser_overrides, filament_overrides); this->config_diffs(new_full_config, print_diff, object_diff, region_diff, full_config_diff, filament_overrides);
// Do not use the ApplyStatus as we will use the max function when updating apply_status. // Do not use the ApplyStatus as we will use the max function when updating apply_status.
unsigned int apply_status = APPLY_STATUS_UNCHANGED; unsigned int apply_status = APPLY_STATUS_UNCHANGED;
@ -617,9 +607,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// which should be stopped if print_diff is not empty. // which should be stopped if print_diff is not empty.
size_t num_extruders = m_config.nozzle_diameter.size(); size_t num_extruders = m_config.nozzle_diameter.size();
bool num_extruders_changed = false; bool num_extruders_changed = false;
if (! full_config_diff.empty() || ! placeholder_parser_overrides.empty()) { if (! full_config_diff.empty()) {
update_apply_status(this->invalidate_step(psGCodeExport)); update_apply_status(this->invalidate_step(psGCodeExport));
m_placeholder_parser.apply_config(std::move(placeholder_parser_overrides));
// Set the profile aliases for the PrintBase::output_filename() // Set the profile aliases for the PrintBase::output_filename()
m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone()); m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone()); m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
@ -723,7 +712,11 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
model_object_status.emplace(model_object->id(), ModelObjectStatus::New); model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
} else { } else {
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) { if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
update_apply_status(custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes) ? update_apply_status(num_extruders_changed ||
// Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering.
//FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable
// to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same.
(num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes)) ?
// The Tool Ordering and the Wipe Tower are no more valid. // The Tool Ordering and the Wipe Tower are no more valid.
this->invalidate_steps({ psWipeTower, psGCodeExport }) : this->invalidate_steps({ psWipeTower, psGCodeExport }) :
// There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering. // There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
@ -839,7 +832,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// Update the ModelObject instance, possibly invalidate the linked PrintObjects. // Update the ModelObject instance, possibly invalidate the linked PrintObjects.
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved); assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
// Check whether a model part volume was added or removed, their transformations or order changed. // Check whether a model part volume was added or removed, their transformations or order changed.
// Only volume IDs, volume types and their order are checked, configuration and other parameters are NOT checked. // Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked.
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART); bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER); bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER); bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER);
@ -891,12 +884,31 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step. // Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step.
model_object.name = model_object_new.name; model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file; model_object.input_file = model_object_new.input_file;
// Only refresh ModelInstances if there is any change.
if (model_object.instances.size() != model_object_new.instances.size() ||
! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) {
// G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list.
update_apply_status(this->invalidate_step(psGCodeExport));
model_object.clear_instances(); model_object.clear_instances();
model_object.instances.reserve(model_object_new.instances.size()); model_object.instances.reserve(model_object_new.instances.size());
for (const ModelInstance *model_instance : model_object_new.instances) { for (const ModelInstance *model_instance : model_object_new.instances) {
model_object.instances.emplace_back(new ModelInstance(*model_instance)); model_object.instances.emplace_back(new ModelInstance(*model_instance));
model_object.instances.back()->set_model_object(&model_object); model_object.instances.back()->set_model_object(&model_object);
} }
} else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(),
[](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable &&
l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) {
// If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid.
// This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only.
model_object.invalidate_bounding_box();
// Synchronize the content of instances.
auto new_instance = model_object_new.instances.begin();
for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) {
(*old_instance)->set_transformation((*new_instance)->get_transformation());
(*old_instance)->print_volume_state = (*new_instance)->print_volume_state;
(*old_instance)->printable = (*new_instance)->printable;
}
}
} }
} }
@ -917,13 +929,11 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
} }
// Generate a list of trafos and XY offsets for instances of a ModelObject // Generate a list of trafos and XY offsets for instances of a ModelObject
PrintObjectConfig config = PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders); PrintObjectConfig config = PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders);
std::vector<PrintInstances> new_print_instances = print_objects_from_model_object(*model_object); std::vector<PrintObjectTrafoAndInstances> new_print_instances = print_objects_from_model_object(*model_object);
if (old.empty()) { if (old.empty()) {
// Simple case, just generate new instances. // Simple case, just generate new instances.
for (const PrintInstances &print_instances : new_print_instances) { for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) {
PrintObject *print_object = new PrintObject(this, model_object, false); PrintObject *print_object = new PrintObject(this, model_object, print_instances.trafo, std::move(print_instances.instances));
print_object->set_trafo(print_instances.trafo);
print_object->set_copies(print_instances.copies);
print_object->config_apply(config); print_object->config_apply(config);
print_objects_new.emplace_back(print_object); print_objects_new.emplace_back(print_object);
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
@ -936,13 +946,11 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); }); std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); });
// Merge the old / new lists. // Merge the old / new lists.
auto it_old = old.begin(); auto it_old = old.begin();
for (const PrintInstances &new_instances : new_print_instances) { for (PrintObjectTrafoAndInstances &new_instances : new_print_instances) {
for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old); for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old);
if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) { if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) {
// This is a new instance (or a set of instances with the same trafo). Just add it. // This is a new instance (or a set of instances with the same trafo). Just add it.
PrintObject *print_object = new PrintObject(this, model_object, false); PrintObject *print_object = new PrintObject(this, model_object, new_instances.trafo, std::move(new_instances.instances));
print_object->set_trafo(new_instances.trafo);
print_object->set_copies(new_instances.copies);
print_object->config_apply(config); print_object->config_apply(config);
print_objects_new.emplace_back(print_object); print_objects_new.emplace_back(print_object);
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
@ -951,7 +959,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Deleted; const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Deleted;
} else { } else {
// The PrintObject already exists and the copies differ. // The PrintObject already exists and the copies differ.
PrintBase::ApplyStatus status = (*it_old)->print_object->set_copies(new_instances.copies); PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances));
if (status != PrintBase::APPLY_STATUS_UNCHANGED) if (status != PrintBase::APPLY_STATUS_UNCHANGED)
update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED); update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED);
print_objects_new.emplace_back((*it_old)->print_object); print_objects_new.emplace_back((*it_old)->print_object);
@ -1144,6 +1152,62 @@ bool Print::has_skirt() const
|| this->has_infinite_skirt(); || this->has_infinite_skirt();
} }
static inline bool sequential_print_horizontal_clearance_valid(const Print &print)
{
Polygons convex_hulls_other;
std::map<ObjectID, Polygon> map_model_object_to_convex_hull;
for (const PrintObject *print_object : print.objects()) {
assert(! print_object->model_object()->instances.empty());
assert(! print_object->instances().empty());
ObjectID model_object_id = print_object->model_object()->id();
auto it_convex_hull = map_model_object_to_convex_hull.find(model_object_id);
// Get convex hull of all printable volumes assigned to this print object.
ModelInstance *model_instance0 = print_object->model_object()->instances.front();
if (it_convex_hull == map_model_object_to_convex_hull.end()) {
// Calculate the convex hull of a printable object.
// Grow convex hull with the clearance margin.
// FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
// which causes that the warning will be showed after arrangement with the
// appropriate object distance. Even if I set this to jtMiter the warning still shows up.
it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id,
offset(print_object->model_object()->convex_hull_2d(
Geometry::assemble_transform(Vec3d::Zero(), model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
float(scale_(0.5 * print.config().extruder_clearance_radius.value - EPSILON)),
jtRound, float(scale_(0.1))).front());
}
// Make a copy, so it may be rotated for instances.
Polygon convex_hull0 = it_convex_hull->second;
double z_diff = Geometry::rotation_diff_z(model_instance0->get_rotation(), print_object->instances().front().model_instance->get_rotation());
if (std::abs(z_diff) > EPSILON)
convex_hull0.rotate(z_diff);
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
for (const PrintInstance &instance : print_object->instances()) {
Polygon convex_hull = convex_hull0;
// instance.shift is a position of a centered object, while model object may not be centered.
// Conver the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
convex_hull.translate(instance.shift - print_object->center_offset());
if (! intersection(convex_hulls_other, convex_hull).empty())
return false;
polygons_append(convex_hulls_other, convex_hull);
}
}
return true;
}
static inline bool sequential_print_vertical_clearance_valid(const Print &print)
{
std::vector<const PrintInstance*> print_instances_ordered = sort_object_instances_by_model_order(print);
// Ignore the last instance printed.
print_instances_ordered.pop_back();
// Find the other highest instance.
auto it = std::max_element(print_instances_ordered.begin(), print_instances_ordered.end(), [](auto l, auto r) {
return l->print_object->height() < r->print_object->height();
});
return it == print_instances_ordered.end() || (*it)->print_object->height() <= scale_(print.config().extruder_clearance_height.value);
}
// Precondition: Print::validate() requires the Print::apply() to be called its invocation. // Precondition: Print::validate() requires the Print::apply() to be called its invocation.
std::string Print::validate() const std::string Print::validate() const
{ {
@ -1154,53 +1218,16 @@ std::string Print::validate() const
return L("The supplied settings will cause an empty print."); return L("The supplied settings will cause an empty print.");
if (m_config.complete_objects) { if (m_config.complete_objects) {
// Check horizontal clearance. if (! sequential_print_horizontal_clearance_valid(*this))
{
Polygons convex_hulls_other;
for (const PrintObject *print_object : m_objects) {
assert(! print_object->model_object()->instances.empty());
assert(! print_object->copies().empty());
// Get convex hull of all meshes assigned to this print object.
ModelInstance *model_instance0 = print_object->model_object()->instances.front();
Vec3d rotation = model_instance0->get_rotation();
rotation.z() = 0.;
// Calculate the convex hull of a printable object centered around X=0,Y=0.
// Grow convex hull with the clearance margin.
// FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
// which causes that the warning will be showed after arrangement with the
// appropriate object distance. Even if I set this to jtMiter the warning still shows up.
Polygon convex_hull0 = offset(
print_object->model_object()->convex_hull_2d(
Geometry::assemble_transform(Vec3d::Zero(), rotation, model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
float(scale_(0.5 * m_config.extruder_clearance_radius.value)), jtRound, float(scale_(0.1))).front();
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
for (const Point &copy : print_object->copies()) {
Polygon convex_hull = convex_hull0;
convex_hull.translate(copy);
if (! intersection(convex_hulls_other, convex_hull).empty())
return L("Some objects are too close; your extruder will collide with them."); return L("Some objects are too close; your extruder will collide with them.");
polygons_append(convex_hulls_other, convex_hull); if (! sequential_print_vertical_clearance_valid(*this))
}
}
}
// Check vertical clearance.
{
std::vector<coord_t> object_height;
for (const PrintObject *object : m_objects)
object_height.insert(object_height.end(), object->copies().size(), object->size(2));
std::sort(object_height.begin(), object_height.end());
// Ignore the tallest *copy* (this is why we repeat height for all of them):
// it will be printed as last one so its height doesn't matter.
object_height.pop_back();
if (! object_height.empty() && object_height.back() > scale_(m_config.extruder_clearance_height.value))
return L("Some objects are too tall and cannot be printed without extruder collisions."); return L("Some objects are too tall and cannot be printed without extruder collisions.");
} }
} // end if (m_config.complete_objects)
if (m_config.spiral_vase) { if (m_config.spiral_vase) {
size_t total_copies_count = 0; size_t total_copies_count = 0;
for (const PrintObject *object : m_objects) for (const PrintObject *object : m_objects)
total_copies_count += object->copies().size(); total_copies_count += object->instances().size();
// #4043 // #4043
if (total_copies_count > 1 && ! m_config.complete_objects.value) if (total_copies_count > 1 && ! m_config.complete_objects.value)
return L("The Spiral Vase option can only be used when printing a single object."); return L("The Spiral Vase option can only be used when printing a single object.");
@ -1411,16 +1438,17 @@ std::string Print::validate() const
return std::string(); return std::string();
} }
#if 0
// the bounding box of objects placed in copies position // the bounding box of objects placed in copies position
// (without taking skirt/brim/support material into account) // (without taking skirt/brim/support material into account)
BoundingBox Print::bounding_box() const BoundingBox Print::bounding_box() const
{ {
BoundingBox bb; BoundingBox bb;
for (const PrintObject *object : m_objects) for (const PrintObject *object : m_objects)
for (Point copy : object->m_copies) { for (const PrintInstance &instance : object->instances()) {
bb.merge(copy); BoundingBox bb2(object->bounding_box());
copy += to_2d(object->size); bb.merge(bb2.min + instance.shift);
bb.merge(copy); bb.merge(bb2.max + instance.shift);
} }
return bb; return bb;
} }
@ -1465,6 +1493,7 @@ BoundingBox Print::total_bounding_box() const
return bb; return bb;
} }
#endif
double Print::skirt_first_layer_height() const double Print::skirt_first_layer_height() const
{ {
@ -1562,6 +1591,8 @@ void Print::process()
} else if (! this->config().complete_objects.value) { } else if (! this->config().complete_objects.value) {
// Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches. // Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches.
m_tool_ordering = ToolOrdering(*this, -1, false); m_tool_ordering = ToolOrdering(*this, -1, false);
if (m_tool_ordering.empty() || m_tool_ordering.last_extruder() == unsigned(-1))
throw std::runtime_error("The print is empty. The model is not printable with current print settings.");
} }
this->set_done(psWipeTower); this->set_done(psWipeTower);
} }
@ -1657,10 +1688,10 @@ void Print::_make_skirt()
append(object_points, extrusion_entity->as_polyline().points); append(object_points, extrusion_entity->as_polyline().points);
} }
// Repeat points for each object copy. // Repeat points for each object copy.
for (const Point &shift : object->m_copies) { for (const PrintInstance &instance : object->instances()) {
Points copy_points = object_points; Points copy_points = object_points;
for (Point &pt : copy_points) for (Point &pt : copy_points)
pt += shift; pt += instance.shift;
append(points, copy_points); append(points, copy_points);
} }
} }
@ -1778,11 +1809,11 @@ void Print::_make_brim()
object_islands.push_back(expoly.contour); object_islands.push_back(expoly.contour);
if (! object->support_layers().empty()) if (! object->support_layers().empty())
object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));
islands.reserve(islands.size() + object_islands.size() * object->m_copies.size()); islands.reserve(islands.size() + object_islands.size() * object->instances().size());
for (const Point &pt : object->m_copies) for (const PrintInstance &instance : object->instances())
for (Polygon &poly : object_islands) { for (Polygon &poly : object_islands) {
islands.push_back(poly); islands.push_back(poly);
islands.back().translate(pt); islands.back().translate(instance.shift);
} }
} }
Polygons loops; Polygons loops;

View file

@ -24,6 +24,7 @@ class PrintObject;
class ModelObject; class ModelObject;
class GCode; class GCode;
class GCodePreviewData; class GCodePreviewData;
enum class SlicingMode : uint32_t;
// Print step IDs for keeping track of the print state. // Print step IDs for keeping track of the print state.
enum PrintStep { enum PrintStep {
@ -84,7 +85,7 @@ private:
PrintRegion(Print* print) : m_refcnt(0), m_print(print) {} PrintRegion(Print* print) : m_refcnt(0), m_print(print) {}
PrintRegion(Print* print, const PrintRegionConfig &config) : m_refcnt(0), m_print(print), m_config(config) {} PrintRegion(Print* print, const PrintRegionConfig &config) : m_refcnt(0), m_print(print), m_config(config) {}
~PrintRegion() {} ~PrintRegion() = default;
}; };
@ -92,6 +93,21 @@ typedef std::vector<Layer*> LayerPtrs;
typedef std::vector<SupportLayer*> SupportLayerPtrs; typedef std::vector<SupportLayer*> SupportLayerPtrs;
class BoundingBoxf3; // TODO: for temporary constructor parameter class BoundingBoxf3; // TODO: for temporary constructor parameter
// Single instance of a PrintObject.
// As multiple PrintObjects may be generated for a single ModelObject (their instances differ in rotation around Z),
// ModelObject's instancess will be distributed among these multiple PrintObjects.
struct PrintInstance
{
// Parent PrintObject
PrintObject *print_object;
// Source ModelInstance of a ModelObject, for which this print_object was created.
const ModelInstance *model_instance;
// Shift of this instance's center into the world coordinates.
Point shift;
};
typedef std::vector<PrintInstance> PrintInstances;
class PrintObject : public PrintObjectBaseWithState<Print, PrintObjectStep, posCount> class PrintObject : public PrintObjectBaseWithState<Print, PrintObjectStep, posCount>
{ {
private: // Prevents erroneous use by other classes. private: // Prevents erroneous use by other classes.
@ -101,21 +117,22 @@ public:
// vector of (layer height ranges and vectors of volume ids), indexed by region_id // vector of (layer height ranges and vectors of volume ids), indexed by region_id
std::vector<std::vector<std::pair<t_layer_height_range, int>>> region_volumes; std::vector<std::vector<std::pair<t_layer_height_range, int>>> region_volumes;
// this is set to true when LayerRegion->slices is split in top/internal/bottom // Size of an object: XYZ in scaled coordinates. The size might not be quite snug in XY plane.
// so that next call to make_perimeters() performs a union() before computing loops const Vec3crd& size() const { return m_size; }
bool typed_slices;
Vec3crd size; // XYZ in scaled coordinates
const PrintObjectConfig& config() const { return m_config; } const PrintObjectConfig& config() const { return m_config; }
const LayerPtrs& layers() const { return m_layers; } const LayerPtrs& layers() const { return m_layers; }
const SupportLayerPtrs& support_layers() const { return m_support_layers; } const SupportLayerPtrs& support_layers() const { return m_support_layers; }
const Transform3d& trafo() const { return m_trafo; } const Transform3d& trafo() const { return m_trafo; }
const Points& copies() const { return m_copies; } const PrintInstances& instances() const { return m_instances; }
const Point copy_center(size_t idx) const { return m_copies[idx] + m_copies_shift + Point(this->size.x() / 2, this->size.y() / 2); }
// since the object is aligned to origin, bounding box coincides with size // Bounding box is used to align the object infill patterns, and to calculate attractor for the rear seam.
BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); } // The bounding box may not be quite snug.
BoundingBox bounding_box() const { return BoundingBox(Point(- m_size.x() / 2, - m_size.y() / 2), Point(m_size.x() / 2, m_size.y() / 2)); }
// Height is used for slicing, for sorting the objects by height for sequential printing and for checking vertical clearence in sequential print mode.
// The height is snug.
coord_t height() const { return m_size.z(); }
// Centering offset of the sliced mesh from the scaled and rotated mesh of the model.
const Point& center_offset() const { return m_center_offset; }
// adds region_id, too, if necessary // adds region_id, too, if necessary
void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) { void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) {
@ -177,17 +194,16 @@ public:
std::vector<ExPolygons> slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); } std::vector<ExPolygons> slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); }
std::vector<ExPolygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); } std::vector<ExPolygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
protected: private:
// to be called from Print only. // to be called from Print only.
friend class Print; friend class Print;
PrintObject(Print* print, ModelObject* model_object, bool add_instances = true); PrintObject(Print* print, ModelObject* model_object, const Transform3d& trafo, PrintInstances&& instances);
~PrintObject() {} ~PrintObject() = default;
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); } void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); }
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); } void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); }
void set_trafo(const Transform3d& trafo) { m_trafo = trafo; } PrintBase::ApplyStatus set_instances(PrintInstances &&instances);
PrintBase::ApplyStatus set_copies(const Points &points);
// Invalidates the step, and its depending steps in PrintObject and Print. // Invalidates the step, and its depending steps in PrintObject and Print.
bool invalidate_step(PrintObjectStep step); bool invalidate_step(PrintObjectStep step);
// Invalidates all PrintObject and Print steps. // Invalidates all PrintObject and Print steps.
@ -219,25 +235,30 @@ private:
void combine_infill(); void combine_infill();
void _generate_support_material(); void _generate_support_material();
// XYZ in scaled coordinates
Vec3crd m_size;
PrintObjectConfig m_config; PrintObjectConfig m_config;
// Translation in Z + Rotation + Scaling / Mirroring. // Translation in Z + Rotation + Scaling / Mirroring.
Transform3d m_trafo = Transform3d::Identity(); Transform3d m_trafo = Transform3d::Identity();
// Slic3r::Point objects in scaled G-code coordinates // Slic3r::Point objects in scaled G-code coordinates
Points m_copies; std::vector<PrintInstance> m_instances;
// scaled coordinates to add to copies (to compensate for the alignment // The mesh is being centered before thrown to Clipper, so that the Clipper's fixed coordinates require less bits.
// operated when creating the object but still preserving a coherent API // This is the adjustment of the the Object's coordinate system towards PrintObject's coordinate system.
// for external callers) Point m_center_offset;
Point m_copies_shift;
SlicingParameters m_slicing_params; SlicingParameters m_slicing_params;
LayerPtrs m_layers; LayerPtrs m_layers;
SupportLayerPtrs m_support_layers; SupportLayerPtrs m_support_layers;
std::vector<ExPolygons> slice_region(size_t region_id, const std::vector<float> &z) const; // this is set to true when LayerRegion->slices is split in top/internal/bottom
// so that next call to make_perimeters() performs a union() before computing loops
bool m_typed_slices = false;
std::vector<ExPolygons> slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode) const;
std::vector<ExPolygons> slice_modifiers(size_t region_id, const std::vector<float> &z) const; std::vector<ExPolygons> slice_modifiers(size_t region_id, const std::vector<float> &z) const;
std::vector<ExPolygons> slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const; std::vector<ExPolygons> slice_volumes(const std::vector<float> &z, SlicingMode mode, const std::vector<const ModelVolume*> &volumes) const;
std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const ModelVolume &volume) const; std::vector<ExPolygons> slice_volume(const std::vector<float> &z, SlicingMode mode, const ModelVolume &volume) const;
std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, const ModelVolume &volume) const; std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, SlicingMode mode, const ModelVolume &volume) const;
}; };
struct WipeTowerData struct WipeTowerData
@ -325,7 +346,7 @@ private: // Prevents erroneous use by other classes.
typedef PrintBaseWithState<PrintStep, psCount> Inherited; typedef PrintBaseWithState<PrintStep, psCount> Inherited;
public: public:
Print() {} Print() = default;
virtual ~Print() { this->clear(); } virtual ~Print() { this->clear(); }
PrinterTechnology technology() const noexcept { return ptFFF; } PrinterTechnology technology() const noexcept { return ptFFF; }
@ -361,8 +382,6 @@ public:
// Returns an empty string if valid, otherwise returns an error message. // Returns an empty string if valid, otherwise returns an error message.
std::string validate() const override; std::string validate() const override;
BoundingBox bounding_box() const;
BoundingBox total_bounding_box() const;
double skirt_first_layer_height() const; double skirt_first_layer_height() const;
Flow brim_flow() const; Flow brim_flow() const;
Flow skirt_flow() const; Flow skirt_flow() const;
@ -417,7 +436,6 @@ private:
const DynamicPrintConfig &new_full_config, const DynamicPrintConfig &new_full_config,
t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys &region_diff, t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys &region_diff,
t_config_option_keys &full_config_diff, t_config_option_keys &full_config_diff,
DynamicPrintConfig &placeholder_parser_overrides,
DynamicPrintConfig &filament_overrides) const; DynamicPrintConfig &filament_overrides) const;
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys); bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);

View file

@ -168,6 +168,17 @@ void PrintConfigDef::init_fff_params()
def->min = 0; def->min = 0;
def->set_default_value(new ConfigOptionInt(3)); def->set_default_value(new ConfigOptionInt(3));
def = this->add("bottom_solid_min_thickness", coFloat);
//TRN To be shown in Print Settings "Top solid layers"
def->label = L("Bottom");
def->category = L("Layers and Perimeters");
def->tooltip = L("The number of bottom solid layers is increased above bottom_solid_layers if necessary to satisfy "
"minimum thickness of bottom shell.");
def->full_label = L("Minimum bottom shell thickness");
def->sidetext = L("mm");
def->min = 0;
def->set_default_value(new ConfigOptionFloat(0.));
def = this->add("bridge_acceleration", coFloat); def = this->add("bridge_acceleration", coFloat);
def->label = L("Bridge"); def->label = L("Bridge");
def->tooltip = L("This is the acceleration your printer will use for bridges. " def->tooltip = L("This is the acceleration your printer will use for bridges. "
@ -562,7 +573,6 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("If layer print time is estimated below this number of seconds, fan will be enabled " def->tooltip = L("If layer print time is estimated below this number of seconds, fan will be enabled "
"and its speed will be calculated by interpolating the minimum and maximum speeds."); "and its speed will be calculated by interpolating the minimum and maximum speeds.");
def->sidetext = L("approximate seconds"); def->sidetext = L("approximate seconds");
def->width = 6;
def->min = 0; def->min = 0;
def->max = 1000; def->max = 1000;
def->mode = comExpert; def->mode = comExpert;
@ -718,8 +728,9 @@ void PrintConfigDef::init_fff_params()
def->gui_type = "f_enum_open"; def->gui_type = "f_enum_open";
def->gui_flags = "show_value"; def->gui_flags = "show_value";
def->enum_values.push_back("PLA"); def->enum_values.push_back("PLA");
def->enum_values.push_back("ABS");
def->enum_values.push_back("PET"); def->enum_values.push_back("PET");
def->enum_values.push_back("ABS");
def->enum_values.push_back("ASA");
def->enum_values.push_back("FLEX"); def->enum_values.push_back("FLEX");
def->enum_values.push_back("HIPS"); def->enum_values.push_back("HIPS");
def->enum_values.push_back("EDGE"); def->enum_values.push_back("EDGE");
@ -1092,7 +1103,6 @@ void PrintConfigDef::init_fff_params()
def->mode = comExpert; def->mode = comExpert;
def->set_default_value(new ConfigOptionBool(true)); def->set_default_value(new ConfigOptionBool(true));
const int machine_limits_opt_width = 7;
{ {
struct AxisDefault { struct AxisDefault {
std::string name; std::string name;
@ -1124,7 +1134,6 @@ void PrintConfigDef::init_fff_params()
(void)L("Maximum feedrate of the E axis"); (void)L("Maximum feedrate of the E axis");
def->sidetext = L("mm/s"); def->sidetext = L("mm/s");
def->min = 0; def->min = 0;
def->width = machine_limits_opt_width;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats(axis.max_feedrate)); def->set_default_value(new ConfigOptionFloats(axis.max_feedrate));
// Add the machine acceleration limits for XYZE axes (M201) // Add the machine acceleration limits for XYZE axes (M201)
@ -1142,7 +1151,6 @@ void PrintConfigDef::init_fff_params()
(void)L("Maximum acceleration of the E axis"); (void)L("Maximum acceleration of the E axis");
def->sidetext = L("mm/s²"); def->sidetext = L("mm/s²");
def->min = 0; def->min = 0;
def->width = machine_limits_opt_width;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats(axis.max_acceleration)); def->set_default_value(new ConfigOptionFloats(axis.max_acceleration));
// Add the machine jerk limits for XYZE axes (M205) // Add the machine jerk limits for XYZE axes (M205)
@ -1160,7 +1168,6 @@ void PrintConfigDef::init_fff_params()
(void)L("Maximum jerk of the E axis"); (void)L("Maximum jerk of the E axis");
def->sidetext = L("mm/s"); def->sidetext = L("mm/s");
def->min = 0; def->min = 0;
def->width = machine_limits_opt_width;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats(axis.max_jerk)); def->set_default_value(new ConfigOptionFloats(axis.max_jerk));
} }
@ -1173,7 +1180,6 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Minimum feedrate when extruding (M205 S)"); def->tooltip = L("Minimum feedrate when extruding (M205 S)");
def->sidetext = L("mm/s"); def->sidetext = L("mm/s");
def->min = 0; def->min = 0;
def->width = machine_limits_opt_width;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats{ 0., 0. }); def->set_default_value(new ConfigOptionFloats{ 0., 0. });
@ -1184,7 +1190,6 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Minimum travel feedrate (M205 T)"); def->tooltip = L("Minimum travel feedrate (M205 T)");
def->sidetext = L("mm/s"); def->sidetext = L("mm/s");
def->min = 0; def->min = 0;
def->width = machine_limits_opt_width;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats{ 0., 0. }); def->set_default_value(new ConfigOptionFloats{ 0., 0. });
@ -1195,7 +1200,6 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Maximum acceleration when extruding (M204 S)"); def->tooltip = L("Maximum acceleration when extruding (M204 S)");
def->sidetext = L("mm/s²"); def->sidetext = L("mm/s²");
def->min = 0; def->min = 0;
def->width = machine_limits_opt_width;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats{ 1500., 1250. }); def->set_default_value(new ConfigOptionFloats{ 1500., 1250. });
@ -1206,7 +1210,6 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Maximum acceleration when retracting (M204 T)"); def->tooltip = L("Maximum acceleration when retracting (M204 T)");
def->sidetext = L("mm/s²"); def->sidetext = L("mm/s²");
def->min = 0; def->min = 0;
def->width = machine_limits_opt_width;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats{ 1500., 1250. }); def->set_default_value(new ConfigOptionFloats{ 1500., 1250. });
@ -1703,7 +1706,6 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("If layer print time is estimated below this number of seconds, print moves " def->tooltip = L("If layer print time is estimated below this number of seconds, print moves "
"speed will be scaled down to extend duration to this value."); "speed will be scaled down to extend duration to this value.");
def->sidetext = L("approximate seconds"); def->sidetext = L("approximate seconds");
def->width = 6;
def->min = 0; def->min = 0;
def->max = 1000; def->max = 1000;
def->mode = comExpert; def->mode = comExpert;
@ -1781,6 +1783,13 @@ void PrintConfigDef::init_fff_params()
def->shortcut.push_back("bottom_solid_layers"); def->shortcut.push_back("bottom_solid_layers");
def->min = 0; def->min = 0;
def = this->add("solid_min_thickness", coFloat);
def->label = L("Minimum thickness of a top / bottom shell");
def->tooltip = L("Minimum thickness of a top / bottom shell");
def->shortcut.push_back("top_solid_min_thickness");
def->shortcut.push_back("bottom_solid_min_thickness");
def->min = 0;
def = this->add("spiral_vase", coBool); def = this->add("spiral_vase", coBool);
def->label = L("Spiral vase"); def->label = L("Spiral vase");
def->tooltip = L("This feature will raise Z gradually while printing a single-walled object " def->tooltip = L("This feature will raise Z gradually while printing a single-walled object "
@ -2127,6 +2136,18 @@ void PrintConfigDef::init_fff_params()
def->min = 0; def->min = 0;
def->set_default_value(new ConfigOptionInt(3)); def->set_default_value(new ConfigOptionInt(3));
def = this->add("top_solid_min_thickness", coFloat);
//TRN To be shown in Print Settings "Top solid layers"
def->label = L("Top");
def->category = L("Layers and Perimeters");
def->tooltip = L("The number of top solid layers is increased above top_solid_layers if necessary to satisfy "
"minimum thickness of top shell."
" This is useful to prevent pillowing effect when printing with variable layer height.");
def->full_label = L("Minimum top shell thickness");
def->sidetext = L("mm");
def->min = 0;
def->set_default_value(new ConfigOptionFloat(0.));
def = this->add("travel_speed", coFloat); def = this->add("travel_speed", coFloat);
def->label = L("Travel"); def->label = L("Travel");
def->tooltip = L("Speed for travel moves (jumps between distant extrusion points)."); def->tooltip = L("Speed for travel moves (jumps between distant extrusion points).");
@ -2873,6 +2894,47 @@ void PrintConfigDef::init_sla_params()
def->min = 0; def->min = 0;
def->mode = comExpert; def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(0.3)); def->set_default_value(new ConfigOptionFloat(0.3));
def = this->add("hollowing_enable", coBool);
def->label = L("Enable hollowing");
def->category = L("Hollowing");
def->tooltip = L("Hollow out a model to have an empty interior");
def->mode = comSimple;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("hollowing_min_thickness", coFloat);
def->label = L("Wall thickness");
def->category = L("Hollowing");
def->tooltip = L("Minimum wall thickness of a hollowed model.");
def->sidetext = L("mm");
def->min = 1;
def->max = 10;
def->mode = comSimple;
def->set_default_value(new ConfigOptionFloat(3.));
def = this->add("hollowing_quality", coFloat);
def->label = L("Accuracy");
def->category = L("Hollowing");
def->tooltip = L("Performance vs accuracy of calculation. Lower values may produce unwanted artifacts.");
def->min = 0;
def->max = 1;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(0.5));
def = this->add("hollowing_closing_distance", coFloat);
def->label = L("Closing distance");
def->category = L("Hollowing");
def->tooltip = L(
"Hollowing is done in two steps: first, an imaginary interior is "
"calculated deeper (offset plus the closing distance) in the object and "
"then it's inflated back to the specified offset. A greater closing "
"distance makes the interior more rounded. At zero, the interior will "
"resemble the exterior the most.");
def->sidetext = L("mm");
def->min = 0;
def->max = 10;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(2.0));
} }
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)

View file

@ -46,12 +46,6 @@ enum SeamPosition {
spRandom, spNearest, spAligned, spRear spRandom, spNearest, spAligned, spRear
}; };
/*
enum FilamentType {
ftPLA, ftABS, ftPET, ftHIPS, ftFLEX, ftSCAFF, ftEDGE, ftNGEN, ftPVA
};
*/
enum SLAMaterial { enum SLAMaterial {
slamTough, slamTough,
slamFlex, slamFlex,
@ -149,24 +143,6 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<SeamPosition>::ge
return keys_map; return keys_map;
} }
/*
template<> inline const t_config_enum_values& ConfigOptionEnum<FilamentType>::get_enum_values() {
static t_config_enum_values keys_map;
if (keys_map.empty()) {
keys_map["PLA"] = ftPLA;
keys_map["ABS"] = ftABS;
keys_map["PET"] = ftPET;
keys_map["HIPS"] = ftHIPS;
keys_map["FLEX"] = ftFLEX;
keys_map["SCAFF"] = ftSCAFF;
keys_map["EDGE"] = ftEDGE;
keys_map["NGEN"] = ftNGEN;
keys_map["PVA"] = ftPVA;
}
return keys_map;
}
*/
template<> inline const t_config_enum_values& ConfigOptionEnum<SLADisplayOrientation>::get_enum_values() { template<> inline const t_config_enum_values& ConfigOptionEnum<SLADisplayOrientation>::get_enum_values() {
static const t_config_enum_values keys_map = { static const t_config_enum_values keys_map = {
{ "landscape", sladoLandscape}, { "landscape", sladoLandscape},
@ -354,6 +330,9 @@ protected:
#define STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \ #define STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \
public: \ public: \
/* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \ /* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \
const ConfigOption* optptr(const t_config_option_key &opt_key) const override \
{ return s_cache_##CLASS_NAME.optptr(opt_key, this); } \
/* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \
ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override \ ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override \
{ return s_cache_##CLASS_NAME.optptr(opt_key, this); } \ { return s_cache_##CLASS_NAME.optptr(opt_key, this); } \
/* Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. */ \ /* Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. */ \
@ -487,6 +466,7 @@ class PrintRegionConfig : public StaticPrintConfig
public: public:
ConfigOptionFloat bridge_angle; ConfigOptionFloat bridge_angle;
ConfigOptionInt bottom_solid_layers; ConfigOptionInt bottom_solid_layers;
ConfigOptionFloat bottom_solid_min_thickness;
ConfigOptionFloat bridge_flow_ratio; ConfigOptionFloat bridge_flow_ratio;
ConfigOptionFloat bridge_speed; ConfigOptionFloat bridge_speed;
ConfigOptionBool ensure_vertical_shell_thickness; ConfigOptionBool ensure_vertical_shell_thickness;
@ -522,6 +502,7 @@ public:
ConfigOptionBool thin_walls; ConfigOptionBool thin_walls;
ConfigOptionFloatOrPercent top_infill_extrusion_width; ConfigOptionFloatOrPercent top_infill_extrusion_width;
ConfigOptionInt top_solid_layers; ConfigOptionInt top_solid_layers;
ConfigOptionFloat top_solid_min_thickness;
ConfigOptionFloatOrPercent top_solid_infill_speed; ConfigOptionFloatOrPercent top_solid_infill_speed;
ConfigOptionBool wipe_into_infill; ConfigOptionBool wipe_into_infill;
@ -530,6 +511,7 @@ protected:
{ {
OPT_PTR(bridge_angle); OPT_PTR(bridge_angle);
OPT_PTR(bottom_solid_layers); OPT_PTR(bottom_solid_layers);
OPT_PTR(bottom_solid_min_thickness);
OPT_PTR(bridge_flow_ratio); OPT_PTR(bridge_flow_ratio);
OPT_PTR(bridge_speed); OPT_PTR(bridge_speed);
OPT_PTR(ensure_vertical_shell_thickness); OPT_PTR(ensure_vertical_shell_thickness);
@ -563,6 +545,7 @@ protected:
OPT_PTR(top_infill_extrusion_width); OPT_PTR(top_infill_extrusion_width);
OPT_PTR(top_solid_infill_speed); OPT_PTR(top_solid_infill_speed);
OPT_PTR(top_solid_layers); OPT_PTR(top_solid_layers);
OPT_PTR(top_solid_min_thickness);
OPT_PTR(wipe_into_infill); OPT_PTR(wipe_into_infill);
} }
}; };
@ -1017,7 +1000,7 @@ public:
ConfigOptionFloat support_base_height /*= 1.0*/; ConfigOptionFloat support_base_height /*= 1.0*/;
// The minimum distance of the pillar base from the model in mm. // The minimum distance of the pillar base from the model in mm.
ConfigOptionFloat support_base_safety_distance; /*= 1.0*/; ConfigOptionFloat support_base_safety_distance; /*= 1.0*/
// The default angle for connecting support sticks and junctions. // The default angle for connecting support sticks and junctions.
ConfigOptionFloat support_critical_angle /*= 45*/; ConfigOptionFloat support_critical_angle /*= 45*/;
@ -1062,7 +1045,7 @@ public:
// ///////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////
// Zero elevation mode parameters: // Zero elevation mode parameters:
// - The object pad will be derived from the the model geometry. // - The object pad will be derived from the model geometry.
// - There will be a gap between the object pad and the generated pad // - There will be a gap between the object pad and the generated pad
// according to the support_base_safety_distance parameter. // according to the support_base_safety_distance parameter.
// - The two pads will be connected with tiny connector sticks // - The two pads will be connected with tiny connector sticks
@ -1085,6 +1068,28 @@ public:
// How much should the tiny connectors penetrate into the model body // How much should the tiny connectors penetrate into the model body
ConfigOptionFloat pad_object_connector_penetration; ConfigOptionFloat pad_object_connector_penetration;
// /////////////////////////////////////////////////////////////////////////
// Model hollowing parameters:
// - Models can be hollowed out as part of the SLA print process
// - Thickness of the hollowed model walls can be adjusted
// -
// - Additional holes will be drilled into the hollow model to allow for
// - resin removal.
// /////////////////////////////////////////////////////////////////////////
ConfigOptionBool hollowing_enable;
// The minimum thickness of the model walls to maintain. Note that the
// resulting walls may be thicker due to smoothing out fine cavities where
// resin could stuck.
ConfigOptionFloat hollowing_min_thickness;
// Indirectly controls the voxel size (resolution) used by openvdb
ConfigOptionFloat hollowing_quality;
// Indirectly controls the minimum size of created cavities.
ConfigOptionFloat hollowing_closing_distance;
protected: protected:
void initialize(StaticCacheBase &cache, const char *base_ptr) void initialize(StaticCacheBase &cache, const char *base_ptr)
{ {
@ -1121,6 +1126,10 @@ protected:
OPT_PTR(pad_object_connector_stride); OPT_PTR(pad_object_connector_stride);
OPT_PTR(pad_object_connector_width); OPT_PTR(pad_object_connector_width);
OPT_PTR(pad_object_connector_penetration); OPT_PTR(pad_object_connector_penetration);
OPT_PTR(hollowing_enable);
OPT_PTR(hollowing_min_thickness);
OPT_PTR(hollowing_quality);
OPT_PTR(hollowing_closing_distance);
} }
}; };

View file

@ -40,52 +40,54 @@
namespace Slic3r { namespace Slic3r {
PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_instances) : // Constructor is called from the main thread, therefore all Model / ModelObject / ModelIntance data are valid.
PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transform3d& trafo, PrintInstances&& instances) :
PrintObjectBaseWithState(print, model_object), PrintObjectBaseWithState(print, model_object),
typed_slices(false), m_trafo(trafo)
size(Vec3crd::Zero())
{ {
// Compute the translation to be applied to our meshes so that we work with smaller coordinates // Compute centering offet to be applied to our meshes so that we work with smaller coordinates
{ // requiring less bits to represent Clipper coordinates.
// Translate meshes so that our toolpath generation algorithms work with smaller
// XY coordinates; this translation is an optimization and not strictly required. // Snug bounding box of a rotated and scaled object by the 1st instantion, without the instance translation applied.
// A cloned mesh will be aligned to 0 before slicing in slice_region() since we // All the instances share the transformation matrix with the exception of translation in XY and rotation by Z,
// don't assume it's already aligned and we don't alter the original position in model. // therefore a bounding box from 1st instance of a ModelObject is good enough for calculating the object center,
// We store the XY translation so that we can place copies correctly in the output G-code // snug height and an approximate bounding box in XY.
// (copies are expressed in G-code coordinates and this translation is not publicly exposed). BoundingBoxf3 bbox = model_object->raw_bounding_box();
const BoundingBoxf3 modobj_bbox = model_object->raw_bounding_box(); Vec3d bbox_center = bbox.center();
m_copies_shift = Point::new_scale(modobj_bbox.min(0), modobj_bbox.min(1)); // We may need to rotate the bbox / bbox_center from the original instance to the current instance.
// Scale the object size and store it double z_diff = Geometry::rotation_diff_z(model_object->instances.front()->get_rotation(), instances.front().model_instance->get_rotation());
this->size = (modobj_bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>(); if (std::abs(z_diff) > EPSILON) {
auto z_rot = Eigen::AngleAxisd(z_diff, Vec3d::UnitZ());
bbox = bbox.transformed(Transform3d(z_rot));
bbox_center = (z_rot * bbox_center).eval();
} }
if (add_instances) { // Center of the transformed mesh (without translation).
Points copies; m_center_offset = Point::new_scale(bbox_center.x(), bbox_center.y());
copies.reserve(m_model_object->instances.size()); // Size of the transformed mesh. This bounding may not be snug in XY plane, but it is snug in Z.
for (const ModelInstance *mi : m_model_object->instances) { m_size = (bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
assert(mi->is_printable());
const Vec3d& offset = mi->get_offset(); this->set_instances(std::move(instances));
copies.emplace_back(Point::new_scale(offset(0), offset(1)));
}
this->set_copies(copies);
}
} }
PrintBase::ApplyStatus PrintObject::set_copies(const Points &points) PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances)
{ {
// Order copies with a nearest-neighbor search. for (PrintInstance &i : instances)
std::vector<Point> copies; // Add the center offset, which will be subtracted from the mesh when slicing.
copies.reserve(points.size()); i.shift += m_center_offset;
for (const Point &pt : points)
copies.emplace_back(pt + m_copies_shift);
// Invalidate and set copies. // Invalidate and set copies.
PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED; PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED;
if (copies != m_copies) { bool equal_length = instances.size() == m_instances.size();
bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(),
[](const PrintInstance& lhs, const PrintInstance& rhs) { return lhs.model_instance == rhs.model_instance && lhs.shift == rhs.shift; });
if (! equal) {
status = PrintBase::APPLY_STATUS_CHANGED; status = PrintBase::APPLY_STATUS_CHANGED;
if (m_print->invalidate_steps({ psSkirt, psBrim, psGCodeExport }) || if (m_print->invalidate_steps({ psSkirt, psBrim, psGCodeExport }) ||
(copies.size() != m_copies.size() && m_print->invalidate_step(psWipeTower))) (! equal_length && m_print->invalidate_step(psWipeTower)))
status = PrintBase::APPLY_STATUS_INVALIDATED; status = PrintBase::APPLY_STATUS_INVALIDATED;
m_copies = copies; m_instances = std::move(instances);
for (PrintInstance &i : m_instances)
i.print_object = this;
} }
return status; return status;
} }
@ -151,12 +153,12 @@ void PrintObject::make_perimeters()
BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info();
// merge slices if they were split into types // merge slices if they were split into types
if (this->typed_slices) { if (m_typed_slices) {
for (Layer *layer : m_layers) { for (Layer *layer : m_layers) {
layer->merge_slices(); layer->merge_slices();
m_print->throw_if_canceled(); m_print->throw_if_canceled();
} }
this->typed_slices = false; m_typed_slices = false;
} }
// compare each layer to the one below, and mark those slices needing // compare each layer to the one below, and mark those slices needing
@ -463,8 +465,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|| opt_key == "raft_layers" || opt_key == "raft_layers"
|| opt_key == "slice_closing_radius") { || opt_key == "slice_closing_radius") {
steps.emplace_back(posSlice); steps.emplace_back(posSlice);
} } else if (
else if (
opt_key == "clip_multipart_objects" opt_key == "clip_multipart_objects"
|| opt_key == "elefant_foot_compensation" || opt_key == "elefant_foot_compensation"
|| opt_key == "support_material_contact_distance" || opt_key == "support_material_contact_distance"
@ -505,7 +506,9 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|| opt_key == "infill_every_layers" || opt_key == "infill_every_layers"
|| opt_key == "solid_infill_every_layers" || opt_key == "solid_infill_every_layers"
|| opt_key == "bottom_solid_layers" || opt_key == "bottom_solid_layers"
|| opt_key == "bottom_solid_min_thickness"
|| opt_key == "top_solid_layers" || opt_key == "top_solid_layers"
|| opt_key == "top_solid_min_thickness"
|| opt_key == "solid_infill_below_area" || opt_key == "solid_infill_below_area"
|| opt_key == "infill_extruder" || opt_key == "infill_extruder"
|| opt_key == "solid_infill_extruder" || opt_key == "solid_infill_extruder"
@ -620,6 +623,14 @@ bool PrintObject::has_support_material() const
|| m_config.support_material_enforce_layers > 0; || m_config.support_material_enforce_layers > 0;
} }
static const PrintRegion* first_printing_region(const PrintObject &print_object)
{
for (size_t idx_region = 0; idx_region < print_object.region_volumes.size(); ++ idx_region)
if (!print_object.region_volumes.empty())
return print_object.print()->regions()[idx_region];
return nullptr;
}
// This function analyzes slices of a region (SurfaceCollection slices). // This function analyzes slices of a region (SurfaceCollection slices).
// Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface. // Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface.
// Initially all slices are of type stInternal. // Initially all slices are of type stInternal.
@ -638,7 +649,9 @@ void PrintObject::detect_surfaces_type()
// are completely hidden inside a collective body of intersecting parts. // are completely hidden inside a collective body of intersecting parts.
// This is useful if one of the parts is to be dissolved, or if it is transparent and the internal shells // This is useful if one of the parts is to be dissolved, or if it is transparent and the internal shells
// should be visible. // should be visible.
bool interface_shells = m_config.interface_shells.value; bool spiral_vase = this->print()->config().spiral_vase.value;
bool interface_shells = ! spiral_vase && m_config.interface_shells.value;
size_t num_layers = spiral_vase ? first_printing_region(*this)->config().bottom_solid_layers : m_layers.size();
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start"; BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start";
@ -651,10 +664,15 @@ void PrintObject::detect_surfaces_type()
// Cache the result of the following parallel_loop. // Cache the result of the following parallel_loop.
std::vector<Surfaces> surfaces_new; std::vector<Surfaces> surfaces_new;
if (interface_shells) if (interface_shells)
surfaces_new.assign(m_layers.size(), Surfaces()); surfaces_new.assign(num_layers, Surfaces());
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0,
spiral_vase ?
// In spiral vase mode, reserve the last layer for the top surface if more than 1 layer is planned for the vase bottom.
((num_layers > 1) ? num_layers - 1 : num_layers) :
// In non-spiral vase mode, go over all layers.
m_layers.size()),
[this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) { [this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) {
// If we have raft layers, consider bottom layer as a bridge just like any other bottom surface lying on the void. // If we have raft layers, consider bottom layer as a bridge just like any other bottom surface lying on the void.
SurfaceType surface_type_bottom_1st = SurfaceType surface_type_bottom_1st =
@ -669,7 +687,7 @@ void PrintObject::detect_surfaces_type()
m_print->throw_if_canceled(); m_print->throw_if_canceled();
// BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z; // BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z;
Layer *layer = m_layers[idx_layer]; Layer *layer = m_layers[idx_layer];
LayerRegion *layerm = layer->get_region(idx_region); LayerRegion *layerm = layer->m_regions[idx_region];
// comparison happens against the *full* slices (considering all regions) // comparison happens against the *full* slices (considering all regions)
// unless internal shells are requested // unless internal shells are requested
Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr; Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr;
@ -684,7 +702,7 @@ void PrintObject::detect_surfaces_type()
Surfaces top; Surfaces top;
if (upper_layer) { if (upper_layer) {
Polygons upper_slices = interface_shells ? Polygons upper_slices = interface_shells ?
to_polygons(upper_layer->get_region(idx_region)->slices.surfaces) : to_polygons(upper_layer->m_regions[idx_region]->slices.surfaces) :
to_polygons(upper_layer->lslices); to_polygons(upper_layer->lslices);
surfaces_append(top, surfaces_append(top,
//FIXME implement offset2_ex working over ExPolygons, that should be a bit more efficient than calling offset_ex twice. //FIXME implement offset2_ex working over ExPolygons, that should be a bit more efficient than calling offset_ex twice.
@ -727,7 +745,7 @@ void PrintObject::detect_surfaces_type()
offset2_ex( offset2_ex(
diff( diff(
intersection(layerm_slices_surfaces, to_polygons(lower_layer->lslices)), // supported intersection(layerm_slices_surfaces, to_polygons(lower_layer->lslices)), // supported
to_polygons(lower_layer->get_region(idx_region)->slices.surfaces), to_polygons(lower_layer->m_regions[idx_region]->slices.surfaces),
true), true),
-offset, offset), -offset, offset),
stBottom); stBottom);
@ -795,18 +813,25 @@ void PrintObject::detect_surfaces_type()
if (interface_shells) { if (interface_shells) {
// Move surfaces_new to layerm->slices.surfaces // Move surfaces_new to layerm->slices.surfaces
for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer) for (size_t idx_layer = 0; idx_layer < num_layers; ++ idx_layer)
m_layers[idx_layer]->get_region(idx_region)->slices.surfaces = std::move(surfaces_new[idx_layer]); m_layers[idx_layer]->m_regions[idx_region]->slices.surfaces = std::move(surfaces_new[idx_layer]);
}
if (spiral_vase && num_layers > 1) {
// Turn the last bottom layer infill to a top infill, so it will be extruded with a proper pattern.
Surfaces &surfaces = m_layers[num_layers - 1]->m_regions[idx_region]->slices.surfaces;
for (Surface &surface : surfaces)
surface.surface_type = stTop;
} }
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - start"; BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - start";
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) { [this, idx_region, interface_shells](const tbb::blocked_range<size_t>& range) {
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
LayerRegion *layerm = m_layers[idx_layer]->get_region(idx_region); LayerRegion *layerm = m_layers[idx_layer]->m_regions[idx_region];
layerm->slices_to_fill_surfaces_clipped(); layerm->slices_to_fill_surfaces_clipped();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final"); layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final");
@ -818,7 +843,7 @@ void PrintObject::detect_surfaces_type()
} // for each this->print->region_count } // for each this->print->region_count
// Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.) // Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.)
this->typed_slices = true; m_typed_slices = true;
} }
void PrintObject::process_external_surfaces() void PrintObject::process_external_surfaces()
@ -912,18 +937,33 @@ void PrintObject::discover_vertical_shells()
Polygons bottom_surfaces; Polygons bottom_surfaces;
Polygons holes; Polygons holes;
}; };
std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(m_layers.size(), DiscoverVerticalShellsCacheEntry()); bool spiral_vase = this->print()->config().spiral_vase.value;
size_t num_layers = spiral_vase ? first_printing_region(*this)->config().bottom_solid_layers : m_layers.size();
coordf_t min_layer_height = this->slicing_parameters().min_layer_height;
// Does this region possibly produce more than 1 top or bottom layer?
auto has_extra_layers_fn = [min_layer_height](const PrintRegionConfig &config) {
auto num_extra_layers = [min_layer_height](int num_solid_layers, coordf_t min_shell_thickness) {
if (num_solid_layers == 0)
return 0;
int n = num_solid_layers - 1;
int n2 = int(ceil(min_shell_thickness / min_layer_height));
return std::max(n, n2 - 1);
};
return num_extra_layers(config.top_solid_layers, config.top_solid_min_thickness) +
num_extra_layers(config.bottom_solid_layers, config.bottom_solid_min_thickness) > 0;
};
std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(num_layers, DiscoverVerticalShellsCacheEntry());
bool top_bottom_surfaces_all_regions = this->region_volumes.size() > 1 && ! m_config.interface_shells.value; bool top_bottom_surfaces_all_regions = this->region_volumes.size() > 1 && ! m_config.interface_shells.value;
if (top_bottom_surfaces_all_regions) { if (top_bottom_surfaces_all_regions) {
// This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness // This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
// is calculated over all materials. // is calculated over all materials.
// Is the "ensure vertical wall thickness" applicable to any region? // Is the "ensure vertical wall thickness" applicable to any region?
bool has_extra_layers = false; bool has_extra_layers = false;
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++idx_region) {
const PrintRegion &region = *m_print->get_region(idx_region); const PrintRegionConfig &config = m_print->get_region(idx_region)->config();
if (region.config().ensure_vertical_shell_thickness.value && if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) {
(region.config().top_solid_layers.value > 1 || region.config().bottom_solid_layers.value > 1)) {
has_extra_layers = true; has_extra_layers = true;
break;
} }
} }
if (! has_extra_layers) if (! has_extra_layers)
@ -931,9 +971,9 @@ void PrintObject::discover_vertical_shells()
return; return;
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - start : cache top / bottom"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - start : cache top / bottom";
//FIXME Improve the heuristics for a grain size. //FIXME Improve the heuristics for a grain size.
size_t grain_size = std::max(m_layers.size() / 16, size_t(1)); size_t grain_size = std::max(num_layers / 16, size_t(1));
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size(), grain_size), tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) { [this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
const size_t num_regions = this->region_volumes.size(); const size_t num_regions = this->region_volumes.size();
@ -982,8 +1022,8 @@ void PrintObject::discover_vertical_shells()
polygons_append(cache.holes, offset(offset_ex(layer.lslices, 0.3f * perimeter_min_spacing), - perimeter_offset - 0.3f * perimeter_min_spacing)); polygons_append(cache.holes, offset(offset_ex(layer.lslices, 0.3f * perimeter_min_spacing), - perimeter_offset - 0.3f * perimeter_min_spacing));
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{ {
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices)); Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.lslices));
svg.draw(layer.slices, "blue"); svg.draw(layer.lslices, "blue");
svg.draw(union_ex(cache.holes), "red"); svg.draw(union_ex(cache.holes), "red");
svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05)); svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05));
svg.Close(); svg.Close();
@ -1004,21 +1044,19 @@ void PrintObject::discover_vertical_shells()
if (! region.config().ensure_vertical_shell_thickness.value) if (! region.config().ensure_vertical_shell_thickness.value)
// This region will be handled by discover_horizontal_shells(). // This region will be handled by discover_horizontal_shells().
continue; continue;
int n_extra_top_layers = std::max(0, region.config().top_solid_layers.value - 1); if (! has_extra_layers_fn(region.config()))
int n_extra_bottom_layers = std::max(0, region.config().bottom_solid_layers.value - 1);
if (n_extra_top_layers + n_extra_bottom_layers == 0)
// Zero or 1 layer, there is no additional vertical wall thickness enforced. // Zero or 1 layer, there is no additional vertical wall thickness enforced.
continue; continue;
//FIXME Improve the heuristics for a grain size. //FIXME Improve the heuristics for a grain size.
size_t grain_size = std::max(m_layers.size() / 16, size_t(1)); size_t grain_size = std::max(num_layers / 16, size_t(1));
if (! top_bottom_surfaces_all_regions) { if (! top_bottom_surfaces_all_regions) {
// This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness // This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness
// is calculated over a single material. // is calculated over a single material.
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : cache top / bottom"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : cache top / bottom";
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size(), grain_size), tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, idx_region, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) { [this, idx_region, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
@ -1046,8 +1084,8 @@ void PrintObject::discover_vertical_shells()
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : ensure vertical wall thickness"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : ensure vertical wall thickness";
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size(), grain_size), tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, idx_region, n_extra_top_layers, n_extra_bottom_layers, &cache_top_botom_regions] [this, idx_region, &cache_top_botom_regions]
(const tbb::blocked_range<size_t>& range) { (const tbb::blocked_range<size_t>& range) {
// printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end());
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
@ -1060,6 +1098,7 @@ void PrintObject::discover_vertical_shells()
Layer *layer = m_layers[idx_layer]; Layer *layer = m_layers[idx_layer];
LayerRegion *layerm = layer->m_regions[idx_region]; LayerRegion *layerm = layer->m_regions[idx_region];
const PrintRegionConfig &region_config = layerm->region()->config();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial"); layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial");
@ -1099,30 +1138,47 @@ void PrintObject::discover_vertical_shells()
} }
} }
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Reset the top / bottom inflated regions caches of entries, which are out of the moving window. polygons_append(holes, cache_top_botom_regions[idx_layer].holes);
bool hole_first = true; {
for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) // Gather top regions projected to this layer.
if (n >= 0 && n < (int)m_layers.size()) { coordf_t print_z = layer->print_z;
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[n]; int n_top_layers = region_config.top_solid_layers.value;
if (hole_first) { for (int i = int(idx_layer) + 1;
hole_first = false; i < int(cache_top_botom_regions.size()) &&
polygons_append(holes, cache.holes); (i < int(idx_layer) + n_top_layers ||
} m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON);
else if (! holes.empty()) { ++ i) {
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
if (! holes.empty())
holes = intersection(holes, cache.holes); holes = intersection(holes, cache.holes);
} if (! cache.top_surfaces.empty()) {
size_t n_shell_old = shell.size();
if (n > int(idx_layer))
// Collect top surfaces.
polygons_append(shell, cache.top_surfaces); polygons_append(shell, cache.top_surfaces);
else if (n < int(idx_layer)) // Running the union_ using the Clipper library piece by piece is cheaper
// Collect bottom and bottom bridge surfaces. // than running the union_ all at once.
shell = union_(shell, false);
}
}
}
{
// Gather bottom regions projected to this layer.
coordf_t bottom_z = layer->bottom_z();
int n_bottom_layers = region_config.bottom_solid_layers.value;
for (int i = int(idx_layer) - 1;
i >= 0 &&
(i > int(idx_layer) - n_bottom_layers ||
bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON);
-- i) {
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
if (! holes.empty())
holes = intersection(holes, cache.holes);
if (! cache.bottom_surfaces.empty()) {
polygons_append(shell, cache.bottom_surfaces); polygons_append(shell, cache.bottom_surfaces);
// Running the union_ using the Clipper library piece by piece is cheaper // Running the union_ using the Clipper library piece by piece is cheaper
// than running the union_ all at once. // than running the union_ all at once.
if (n_shell_old < shell.size())
shell = union_(shell, false); shell = union_(shell, false);
} }
}
}
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{ {
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell));
@ -1448,7 +1504,7 @@ void PrintObject::update_slicing_parameters()
{ {
if (! m_slicing_params.valid) if (! m_slicing_params.valid)
m_slicing_params = SlicingParameters::create_from_config( m_slicing_params = SlicingParameters::create_from_config(
this->print()->config(), m_config, unscale<double>(this->size(2)), this->object_extruders()); this->print()->config(), m_config, unscale<double>(this->height()), this->object_extruders());
} }
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z) SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
@ -1535,7 +1591,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
{ {
BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info(); BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info();
this->typed_slices = false; m_typed_slices = false;
#ifdef SLIC3R_PROFILE #ifdef SLIC3R_PROFILE
// Disable parallelization so the Shiny profiler works // Disable parallelization so the Shiny profiler works
@ -1602,13 +1658,14 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
// Slice all non-modifier volumes. // Slice all non-modifier volumes.
bool clipped = false; bool clipped = false;
bool upscaled = false; bool upscaled = false;
auto slicing_mode = this->print()->config().spiral_vase ? SlicingMode::PositiveLargestContour : SlicingMode::Regular;
if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) { if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) {
// Cheap path: Slice regions without mutual clipping. // Cheap path: Slice regions without mutual clipping.
// The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region. // The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
// slicing in parallel // slicing in parallel
std::vector<ExPolygons> expolygons_by_layer = this->slice_region(region_id, slice_zs); std::vector<ExPolygons> expolygons_by_layer = this->slice_region(region_id, slice_zs, slicing_mode);
m_print->throw_if_canceled(); m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start"; BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start";
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
@ -1646,7 +1703,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
else else
ranges.emplace_back(volumes_and_ranges[j].first); ranges.emplace_back(volumes_and_ranges[j].first);
// slicing in parallel // slicing in parallel
sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, *model_volume)); sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, slicing_mode, *model_volume));
i = j; i = j;
} else } else
++ i; ++ i;
@ -1855,7 +1912,7 @@ end:
} }
// To be used only if there are no layer span specific configurations applied, which would lead to z ranges being generated for this region. // To be used only if there are no layer span specific configurations applied, which would lead to z ranges being generated for this region.
std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z) const std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode) const
{ {
std::vector<const ModelVolume*> volumes; std::vector<const ModelVolume*> volumes;
if (region_id < this->region_volumes.size()) { if (region_id < this->region_volumes.size()) {
@ -1865,7 +1922,7 @@ std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::v
volumes.emplace_back(volume); volumes.emplace_back(volume);
} }
} }
return this->slice_volumes(z, volumes); return this->slice_volumes(z, mode, volumes);
} }
// Z ranges are not applicable to modifier meshes, therefore a sinle volume will be found in volume_and_range at most once. // Z ranges are not applicable to modifier meshes, therefore a sinle volume will be found in volume_and_range at most once.
@ -1915,7 +1972,7 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std
if (volume->is_modifier()) if (volume->is_modifier())
volumes.emplace_back(volume); volumes.emplace_back(volume);
} }
out = this->slice_volumes(slice_zs, volumes); out = this->slice_volumes(slice_zs, SlicingMode::Regular, volumes);
} else { } else {
// Some modifier in this region was split to layer spans. // Some modifier in this region was split to layer spans.
std::vector<char> merge; std::vector<char> merge;
@ -1933,7 +1990,7 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j) for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
ranges.emplace_back(volumes_and_ranges[j].first); ranges.emplace_back(volumes_and_ranges[j].first);
// slicing in parallel // slicing in parallel
std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, *model_volume); std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, SlicingMode::Regular, *model_volume);
if (out.empty()) { if (out.empty()) {
out = std::move(this_slices); out = std::move(this_slices);
merge.assign(out.size(), false); merge.assign(out.size(), false);
@ -1972,10 +2029,10 @@ std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType
zs.reserve(this->layers().size()); zs.reserve(this->layers().size());
for (const Layer *l : this->layers()) for (const Layer *l : this->layers())
zs.emplace_back((float)l->slice_z); zs.emplace_back((float)l->slice_z);
return this->slice_volumes(zs, volumes); return this->slice_volumes(zs, SlicingMode::Regular, volumes);
} }
std::vector<ExPolygons> PrintObject::slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const std::vector<ExPolygons> PrintObject::slice_volumes(const std::vector<float> &z, SlicingMode mode, const std::vector<const ModelVolume*> &volumes) const
{ {
std::vector<ExPolygons> layers; std::vector<ExPolygons> layers;
if (! volumes.empty()) { if (! volumes.empty()) {
@ -1997,7 +2054,7 @@ std::vector<ExPolygons> PrintObject::slice_volumes(const std::vector<float> &z,
if (mesh.stl.stats.number_of_facets > 0) { if (mesh.stl.stats.number_of_facets > 0) {
mesh.transform(m_trafo, true); mesh.transform(m_trafo, true);
// apply XY shift // apply XY shift
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0); mesh.translate(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0);
// perform actual slicing // perform actual slicing
const Print *print = this->print(); const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
@ -2005,14 +2062,14 @@ std::vector<ExPolygons> PrintObject::slice_volumes(const std::vector<float> &z,
mesh.require_shared_vertices(); mesh.require_shared_vertices();
TriangleMeshSlicer mslicer; TriangleMeshSlicer mslicer;
mslicer.init(&mesh, callback); mslicer.init(&mesh, callback);
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback); mslicer.slice(z, mode, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled(); m_print->throw_if_canceled();
} }
} }
return layers; return layers;
} }
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const ModelVolume &volume) const std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, SlicingMode mode, const ModelVolume &volume) const
{ {
std::vector<ExPolygons> layers; std::vector<ExPolygons> layers;
if (! z.empty()) { if (! z.empty()) {
@ -2027,7 +2084,7 @@ std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, c
if (mesh.stl.stats.number_of_facets > 0) { if (mesh.stl.stats.number_of_facets > 0) {
mesh.transform(m_trafo, true); mesh.transform(m_trafo, true);
// apply XY shift // apply XY shift
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0); mesh.translate(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0);
// perform actual slicing // perform actual slicing
TriangleMeshSlicer mslicer; TriangleMeshSlicer mslicer;
const Print *print = this->print(); const Print *print = this->print();
@ -2035,7 +2092,7 @@ std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, c
// TriangleMeshSlicer needs the shared vertices. // TriangleMeshSlicer needs the shared vertices.
mesh.require_shared_vertices(); mesh.require_shared_vertices();
mslicer.init(&mesh, callback); mslicer.init(&mesh, callback);
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback); mslicer.slice(z, mode, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled(); m_print->throw_if_canceled();
} }
} }
@ -2043,13 +2100,13 @@ std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, c
} }
// Filter the zs not inside the ranges. The ranges are closed at the botton and open at the top, they are sorted lexicographically and non overlapping. // Filter the zs not inside the ranges. The ranges are closed at the botton and open at the top, they are sorted lexicographically and non overlapping.
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, const ModelVolume &volume) const std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, SlicingMode mode, const ModelVolume &volume) const
{ {
std::vector<ExPolygons> out; std::vector<ExPolygons> out;
if (! z.empty() && ! ranges.empty()) { if (! z.empty() && ! ranges.empty()) {
if (ranges.size() == 1 && z.front() >= ranges.front().first && z.back() < ranges.front().second) { if (ranges.size() == 1 && z.front() >= ranges.front().first && z.back() < ranges.front().second) {
// All layers fit into a single range. // All layers fit into a single range.
out = this->slice_volume(z, volume); out = this->slice_volume(z, mode, volume);
} else { } else {
std::vector<float> z_filtered; std::vector<float> z_filtered;
std::vector<std::pair<size_t, size_t>> n_filtered; std::vector<std::pair<size_t, size_t>> n_filtered;
@ -2065,7 +2122,7 @@ std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, c
n_filtered.emplace_back(std::make_pair(first, i)); n_filtered.emplace_back(std::make_pair(first, i));
} }
if (! n_filtered.empty()) { if (! n_filtered.empty()) {
std::vector<ExPolygons> layers = this->slice_volume(z_filtered, volume); std::vector<ExPolygons> layers = this->slice_volume(z_filtered, mode, volume);
out.assign(z.size(), ExPolygons()); out.assign(z.size(), ExPolygons());
i = 0; i = 0;
for (const std::pair<size_t, size_t> &span : n_filtered) for (const std::pair<size_t, size_t> &span : n_filtered)
@ -2278,7 +2335,8 @@ void PrintObject::discover_horizontal_shells()
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (size_t i = 0; i < m_layers.size(); ++ i) { for (size_t i = 0; i < m_layers.size(); ++ i) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
LayerRegion *layerm = m_layers[i]->regions()[region_id]; Layer *layer = m_layers[i];
LayerRegion *layerm = layer->regions()[region_id];
const PrintRegionConfig &region_config = layerm->region()->config(); const PrintRegionConfig &region_config = layerm->region()->config();
if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 && if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 &&
(i % region_config.solid_infill_every_layers) == 0) { (i % region_config.solid_infill_every_layers) == 0) {
@ -2293,6 +2351,8 @@ void PrintObject::discover_horizontal_shells()
if (region_config.ensure_vertical_shell_thickness.value) if (region_config.ensure_vertical_shell_thickness.value)
continue; continue;
coordf_t print_z = layer->print_z;
coordf_t bottom_z = layer->bottom_z();
for (size_t idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) { for (size_t idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge; SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge;
@ -2321,10 +2381,15 @@ void PrintObject::discover_horizontal_shells()
continue; continue;
// Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == stTop) ? 'top' : 'bottom'; // Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == stTop) ? 'top' : 'bottom';
size_t solid_layers = (type == stTop) ? region_config.top_solid_layers.value : region_config.bottom_solid_layers.value; // Scatter top / bottom regions to other layers. Scattering process is inherently serial, it is difficult to parallelize without locking.
for (int n = (type == stTop) ? i-1 : i+1; std::abs(n - (int)i) < solid_layers; (type == stTop) ? -- n : ++ n) { for (int n = (type == stTop) ? int(i) - 1 : int(i) + 1;
if (n < 0 || n >= int(m_layers.size())) (type == stTop) ?
continue; (n >= 0 && (int(i) - n < region_config.top_solid_layers.value ||
print_z - m_layers[n]->print_z < region_config.top_solid_min_thickness.value - EPSILON)) :
(n < int(m_layers.size()) && (n - int(i) < region_config.bottom_solid_layers.value ||
m_layers[n]->bottom_z() - bottom_z < region_config.bottom_solid_min_thickness.value - EPSILON));
(type == stTop) ? -- n : ++ n)
{
// Slic3r::debugf " looking for neighbors on layer %d...\n", $n; // Slic3r::debugf " looking for neighbors on layer %d...\n", $n;
// Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface. // Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface.
LayerRegion *neighbor_layerm = m_layers[n]->regions()[region_id]; LayerRegion *neighbor_layerm = m_layers[n]->regions()[region_id];
@ -2412,7 +2477,8 @@ void PrintObject::discover_horizontal_shells()
// is grown, and that little space is an internal solid shell so // is grown, and that little space is an internal solid shell so
// it triggers this too_narrow logic.) // it triggers this too_narrow logic.)
internal)); internal));
solid = new_internal_solid; // see https://github.com/prusa3d/PrusaSlicer/pull/3426
// solid = new_internal_solid;
} }
} }

View file

@ -1,7 +1,7 @@
#ifndef SLABOOSTADAPTER_HPP #ifndef SLA_BOOSTADAPTER_HPP
#define SLABOOSTADAPTER_HPP #define SLA_BOOSTADAPTER_HPP
#include "SLA/SLABoilerPlate.hpp" #include <libslic3r/SLA/Common.hpp>
#include <boost/geometry.hpp> #include <boost/geometry.hpp>
namespace boost { namespace boost {

View file

@ -0,0 +1,30 @@
#ifndef SLA_CLUSTERING_HPP
#define SLA_CLUSTERING_HPP
#include <vector>
#include <libslic3r/SLA/Common.hpp>
#include <libslic3r/SLA/SpatIndex.hpp>
namespace Slic3r { namespace sla {
using ClusterEl = std::vector<unsigned>;
using ClusteredPoints = std::vector<ClusterEl>;
// Clustering a set of points by the given distance.
ClusteredPoints cluster(const std::vector<unsigned>& indices,
std::function<Vec3d(unsigned)> pointfn,
double dist,
unsigned max_points);
ClusteredPoints cluster(const PointSet& points,
double dist,
unsigned max_points);
ClusteredPoints cluster(
const std::vector<unsigned>& indices,
std::function<Vec3d(unsigned)> pointfn,
std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
unsigned max_points);
}}
#endif // CLUSTERING_HPP

View file

@ -0,0 +1,769 @@
#include <cmath>
#include <libslic3r/SLA/Common.hpp>
#include <libslic3r/SLA/Concurrency.hpp>
#include <libslic3r/SLA/SupportTree.hpp>
#include <libslic3r/SLA/SpatIndex.hpp>
#include <libslic3r/SLA/EigenMesh3D.hpp>
#include <libslic3r/SLA/Contour3D.hpp>
#include <libslic3r/SLA/Clustering.hpp>
#include <libslic3r/SLA/Hollowing.hpp>
// Workaround: IGL signed_distance.h will define PI in the igl namespace.
#undef PI
// HEAVY headers... takes eternity to compile
// for concave hull merging decisions
#include <libslic3r/SLA/BoostAdapter.hpp>
#include "boost/geometry/index/rtree.hpp"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4244)
#pragma warning(disable: 4267)
#endif
#include <igl/ray_mesh_intersect.h>
#include <igl/point_mesh_squared_distance.h>
#include <igl/remove_duplicate_vertices.h>
#include <igl/collapse_small_triangles.h>
#include <igl/signed_distance.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include <tbb/parallel_for.h>
#include "ClipperUtils.hpp"
namespace Slic3r {
namespace sla {
// Bring back PI from the igl namespace
using igl::PI;
/* **************************************************************************
* PointIndex implementation
* ************************************************************************** */
class PointIndex::Impl {
public:
using BoostIndex = boost::geometry::index::rtree< PointIndexEl,
boost::geometry::index::rstar<16, 4> /* ? */ >;
BoostIndex m_store;
};
PointIndex::PointIndex(): m_impl(new Impl()) {}
PointIndex::~PointIndex() {}
PointIndex::PointIndex(const PointIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
PointIndex::PointIndex(PointIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
PointIndex& PointIndex::operator=(const PointIndex &cpy)
{
m_impl.reset(new Impl(*cpy.m_impl));
return *this;
}
PointIndex& PointIndex::operator=(PointIndex &&cpy)
{
m_impl.swap(cpy.m_impl);
return *this;
}
void PointIndex::insert(const PointIndexEl &el)
{
m_impl->m_store.insert(el);
}
bool PointIndex::remove(const PointIndexEl& el)
{
return m_impl->m_store.remove(el) == 1;
}
std::vector<PointIndexEl>
PointIndex::query(std::function<bool(const PointIndexEl &)> fn) const
{
namespace bgi = boost::geometry::index;
std::vector<PointIndexEl> ret;
m_impl->m_store.query(bgi::satisfies(fn), std::back_inserter(ret));
return ret;
}
std::vector<PointIndexEl> PointIndex::nearest(const Vec3d &el, unsigned k = 1) const
{
namespace bgi = boost::geometry::index;
std::vector<PointIndexEl> ret; ret.reserve(k);
m_impl->m_store.query(bgi::nearest(el, k), std::back_inserter(ret));
return ret;
}
size_t PointIndex::size() const
{
return m_impl->m_store.size();
}
void PointIndex::foreach(std::function<void (const PointIndexEl &)> fn)
{
for(auto& el : m_impl->m_store) fn(el);
}
void PointIndex::foreach(std::function<void (const PointIndexEl &)> fn) const
{
for(const auto &el : m_impl->m_store) fn(el);
}
/* **************************************************************************
* BoxIndex implementation
* ************************************************************************** */
class BoxIndex::Impl {
public:
using BoostIndex = boost::geometry::index::
rtree<BoxIndexEl, boost::geometry::index::rstar<16, 4> /* ? */>;
BoostIndex m_store;
};
BoxIndex::BoxIndex(): m_impl(new Impl()) {}
BoxIndex::~BoxIndex() {}
BoxIndex::BoxIndex(const BoxIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
BoxIndex::BoxIndex(BoxIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
BoxIndex& BoxIndex::operator=(const BoxIndex &cpy)
{
m_impl.reset(new Impl(*cpy.m_impl));
return *this;
}
BoxIndex& BoxIndex::operator=(BoxIndex &&cpy)
{
m_impl.swap(cpy.m_impl);
return *this;
}
void BoxIndex::insert(const BoxIndexEl &el)
{
m_impl->m_store.insert(el);
}
bool BoxIndex::remove(const BoxIndexEl& el)
{
return m_impl->m_store.remove(el) == 1;
}
std::vector<BoxIndexEl> BoxIndex::query(const BoundingBox &qrbb,
BoxIndex::QueryType qt)
{
namespace bgi = boost::geometry::index;
std::vector<BoxIndexEl> ret; ret.reserve(m_impl->m_store.size());
switch (qt) {
case qtIntersects:
m_impl->m_store.query(bgi::intersects(qrbb), std::back_inserter(ret));
break;
case qtWithin:
m_impl->m_store.query(bgi::within(qrbb), std::back_inserter(ret));
}
return ret;
}
size_t BoxIndex::size() const
{
return m_impl->m_store.size();
}
void BoxIndex::foreach(std::function<void (const BoxIndexEl &)> fn)
{
for(auto& el : m_impl->m_store) fn(el);
}
/* ****************************************************************************
* EigenMesh3D implementation
* ****************************************************************************/
class EigenMesh3D::AABBImpl: public igl::AABB<Eigen::MatrixXd, 3> {
public:
#ifdef SLIC3R_SLA_NEEDS_WINDTREE
igl::WindingNumberAABB<Vec3d, Eigen::MatrixXd, Eigen::MatrixXi> windtree;
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
};
static const constexpr double MESH_EPS = 1e-6;
void to_eigen_mesh(const TriangleMesh &tmesh, Eigen::MatrixXd &V, Eigen::MatrixXi &F)
{
const stl_file& stl = tmesh.stl;
V.resize(3*stl.stats.number_of_facets, 3);
F.resize(stl.stats.number_of_facets, 3);
for (unsigned int i = 0; i < stl.stats.number_of_facets; ++i) {
const stl_facet &facet = stl.facet_start[i];
V.block<1, 3>(3 * i + 0, 0) = facet.vertex[0].cast<double>();
V.block<1, 3>(3 * i + 1, 0) = facet.vertex[1].cast<double>();
V.block<1, 3>(3 * i + 2, 0) = facet.vertex[2].cast<double>();
F(i, 0) = int(3*i+0);
F(i, 1) = int(3*i+1);
F(i, 2) = int(3*i+2);
}
if (!tmesh.has_shared_vertices())
{
Eigen::MatrixXd rV;
Eigen::MatrixXi rF;
// We will convert this to a proper 3d mesh with no duplicate points.
Eigen::VectorXi SVI, SVJ;
igl::remove_duplicate_vertices(V, F, MESH_EPS, rV, SVI, SVJ, rF);
V = std::move(rV);
F = std::move(rF);
}
}
void to_triangle_mesh(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, TriangleMesh &out)
{
Pointf3s points(size_t(V.rows()));
std::vector<Vec3crd> facets(size_t(F.rows()));
for (Eigen::Index i = 0; i < V.rows(); ++i)
points[size_t(i)] = V.row(i);
for (Eigen::Index i = 0; i < F.rows(); ++i)
facets[size_t(i)] = F.row(i);
out = {points, facets};
}
EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) {
auto&& bb = tmesh.bounding_box();
m_ground_level += bb.min(Z);
to_eigen_mesh(tmesh, m_V, m_F);
// Build the AABB accelaration tree
m_aabb->init(m_V, m_F);
#ifdef SLIC3R_SLA_NEEDS_WINDTREE
m_aabb->windtree.set_mesh(m_V, m_F);
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
}
EigenMesh3D::~EigenMesh3D() {}
EigenMesh3D::EigenMesh3D(const EigenMesh3D &other):
m_V(other.m_V), m_F(other.m_F), m_ground_level(other.m_ground_level),
m_aabb( new AABBImpl(*other.m_aabb) ) {}
EigenMesh3D::EigenMesh3D(const Contour3D &other)
{
m_V.resize(Eigen::Index(other.points.size()), 3);
m_F.resize(Eigen::Index(other.faces3.size() + 2 * other.faces4.size()), 3);
for (Eigen::Index i = 0; i < Eigen::Index(other.points.size()); ++i)
m_V.row(i) = other.points[size_t(i)];
for (Eigen::Index i = 0; i < Eigen::Index(other.faces3.size()); ++i)
m_F.row(i) = other.faces3[size_t(i)];
size_t N = other.faces3.size() + 2 * other.faces4.size();
for (size_t i = other.faces3.size(); i < N; i += 2) {
size_t quad_idx = (i - other.faces3.size()) / 2;
auto & quad = other.faces4[quad_idx];
m_F.row(Eigen::Index(i)) = Vec3i{quad(0), quad(1), quad(2)};
m_F.row(Eigen::Index(i + 1)) = Vec3i{quad(2), quad(3), quad(0)};
}
}
EigenMesh3D &EigenMesh3D::operator=(const EigenMesh3D &other)
{
m_V = other.m_V;
m_F = other.m_F;
m_ground_level = other.m_ground_level;
m_aabb.reset(new AABBImpl(*other.m_aabb)); return *this;
}
EigenMesh3D &EigenMesh3D::operator=(EigenMesh3D &&other) = default;
EigenMesh3D::EigenMesh3D(EigenMesh3D &&other) = default;
EigenMesh3D::hit_result
EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const
{
assert(is_approx(dir.norm(), 1.));
igl::Hit hit;
hit.t = std::numeric_limits<float>::infinity();
if (m_holes.empty()) {
m_aabb->intersect_ray(m_V, m_F, s, dir, hit);
hit_result ret(*this);
ret.m_t = double(hit.t);
ret.m_dir = dir;
ret.m_source = s;
if(!std::isinf(hit.t) && !std::isnan(hit.t))
ret.m_normal = this->normal_by_face_id(hit.id);
return ret;
}
else {
// If there are holes, the hit_results will be made by
// query_ray_hits (object) and filter_hits (holes):
return filter_hits(query_ray_hits(s, dir));
}
}
std::vector<EigenMesh3D::hit_result>
EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const
{
std::vector<EigenMesh3D::hit_result> outs;
std::vector<igl::Hit> hits;
m_aabb->intersect_ray(m_V, m_F, s, dir, hits);
// The sort is necessary, the hits are not always sorted.
std::sort(hits.begin(), hits.end(),
[](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; });
// Remove duplicates. They sometimes appear, for example when the ray is cast
// along an axis of a cube due to floating-point approximations in igl (?)
hits.erase(std::unique(hits.begin(), hits.end(),
[](const igl::Hit& a, const igl::Hit& b)
{ return a.t == b.t; }),
hits.end());
// Convert the igl::Hit into hit_result
outs.reserve(hits.size());
for (const igl::Hit& hit : hits) {
outs.emplace_back(EigenMesh3D::hit_result(*this));
outs.back().m_t = double(hit.t);
outs.back().m_dir = dir;
outs.back().m_source = s;
if(!std::isinf(hit.t) && !std::isnan(hit.t))
outs.back().m_normal = this->normal_by_face_id(hit.id);
}
return outs;
}
EigenMesh3D::hit_result EigenMesh3D::filter_hits(
const std::vector<EigenMesh3D::hit_result>& object_hits) const
{
assert(! m_holes.empty());
hit_result out(*this);
if (object_hits.empty())
return out;
const Vec3d& s = object_hits.front().source();
const Vec3d& dir = object_hits.front().direction();
// A helper struct to save an intersetion with a hole
struct HoleHit {
HoleHit(float t_p, const Vec3d& normal_p, bool entry_p) :
t(t_p), normal(normal_p), entry(entry_p) {}
float t;
Vec3d normal;
bool entry;
};
std::vector<HoleHit> hole_isects;
hole_isects.reserve(m_holes.size());
auto sf = s.cast<float>();
auto dirf = dir.cast<float>();
// Collect hits on all holes, preserve information about entry/exit
for (const sla::DrainHole& hole : m_holes) {
std::array<std::pair<float, Vec3d>, 2> isects;
if (hole.get_intersections(sf, dirf, isects)) {
// Ignore hole hits behind the source
if (isects[0].first > 0.f) hole_isects.emplace_back(isects[0].first, isects[0].second, true);
if (isects[1].first > 0.f) hole_isects.emplace_back(isects[1].first, isects[1].second, false);
}
}
// Holes can intersect each other, sort the hits by t
std::sort(hole_isects.begin(), hole_isects.end(),
[](const HoleHit& a, const HoleHit& b) { return a.t < b.t; });
// Now inspect the intersections with object and holes, in the order of
// increasing distance. Keep track how deep are we nested in mesh/holes and
// pick the correct intersection.
// This needs to be done twice - first to find out how deep in the structure
// the source is, then to pick the correct intersection.
int hole_nested = 0;
int object_nested = 0;
for (int dry_run=1; dry_run>=0; --dry_run) {
hole_nested = -hole_nested;
object_nested = -object_nested;
bool is_hole = false;
bool is_entry = false;
const HoleHit* next_hole_hit = hole_isects.empty() ? nullptr : &hole_isects.front();
const hit_result* next_mesh_hit = &object_hits.front();
while (next_hole_hit || next_mesh_hit) {
if (next_hole_hit && next_mesh_hit) // still have hole and obj hits
is_hole = (next_hole_hit->t < next_mesh_hit->m_t);
else
is_hole = next_hole_hit; // one or the other ran out
// Is this entry or exit hit?
is_entry = is_hole ? next_hole_hit->entry : ! next_mesh_hit->is_inside();
if (! dry_run) {
if (! is_hole && hole_nested == 0) {
// This is a valid object hit
return *next_mesh_hit;
}
if (is_hole && ! is_entry && object_nested != 0) {
// This holehit is the one we seek
out.m_t = next_hole_hit->t;
out.m_normal = next_hole_hit->normal;
out.m_source = s;
out.m_dir = dir;
return out;
}
}
// Increase/decrease the counter
(is_hole ? hole_nested : object_nested) += (is_entry ? 1 : -1);
// Advance the respective pointer
if (is_hole && next_hole_hit++ == &hole_isects.back())
next_hole_hit = nullptr;
if (! is_hole && next_mesh_hit++ == &object_hits.back())
next_mesh_hit = nullptr;
}
}
// if we got here, the ray ended up in infinity
return out;
}
#ifdef SLIC3R_SLA_NEEDS_WINDTREE
EigenMesh3D::si_result EigenMesh3D::signed_distance(const Vec3d &p) const {
double sign = 0; double sqdst = 0; int i = 0; Vec3d c;
igl::signed_distance_winding_number(*m_aabb, m_V, m_F, m_aabb->windtree,
p, sign, sqdst, i, c);
return si_result(sign * std::sqrt(sqdst), i, c);
}
bool EigenMesh3D::inside(const Vec3d &p) const {
return m_aabb->windtree.inside(p);
}
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
double EigenMesh3D::squared_distance(const Vec3d &p, int& i, Vec3d& c) const {
double sqdst = 0;
Eigen::Matrix<double, 1, 3> pp = p;
Eigen::Matrix<double, 1, 3> cc;
sqdst = m_aabb->squared_distance(m_V, m_F, pp, i, cc);
c = cc;
return sqdst;
}
/* ****************************************************************************
* Misc functions
* ****************************************************************************/
namespace {
bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2,
double eps = 0.05)
{
using Line3D = Eigen::ParametrizedLine<double, 3>;
auto line = Line3D::Through(e1, e2);
double d = line.distance(p);
return std::abs(d) < eps;
}
template<class Vec> double distance(const Vec& pp1, const Vec& pp2) {
auto p = pp2 - pp1;
return std::sqrt(p.transpose() * p);
}
}
PointSet normals(const PointSet& points,
const EigenMesh3D& mesh,
double eps,
std::function<void()> thr, // throw on cancel
const std::vector<unsigned>& pt_indices)
{
if (points.rows() == 0 || mesh.V().rows() == 0 || mesh.F().rows() == 0)
return {};
std::vector<unsigned> range = pt_indices;
if (range.empty()) {
range.resize(size_t(points.rows()), 0);
std::iota(range.begin(), range.end(), 0);
}
PointSet ret(range.size(), 3);
// for (size_t ridx = 0; ridx < range.size(); ++ridx)
ccr::enumerate(
range.begin(), range.end(),
[&ret, &mesh, &points, thr, eps](unsigned el, size_t ridx) {
thr();
auto eidx = Eigen::Index(el);
int faceid = 0;
Vec3d p;
mesh.squared_distance(points.row(eidx), faceid, p);
auto trindex = mesh.F().row(faceid);
const Vec3d &p1 = mesh.V().row(trindex(0));
const Vec3d &p2 = mesh.V().row(trindex(1));
const Vec3d &p3 = mesh.V().row(trindex(2));
// We should check if the point lies on an edge of the hosting
// triangle. If it does then all the other triangles using the
// same two points have to be searched and the final normal should
// be some kind of aggregation of the participating triangle
// normals. We should also consider the cases where the support
// point lies right on a vertex of its triangle. The procedure is
// the same, get the neighbor triangles and calculate an average
// normal.
// mark the vertex indices of the edge. ia and ib marks and edge
// ic will mark a single vertex.
int ia = -1, ib = -1, ic = -1;
if (std::abs(distance(p, p1)) < eps) {
ic = trindex(0);
} else if (std::abs(distance(p, p2)) < eps) {
ic = trindex(1);
} else if (std::abs(distance(p, p3)) < eps) {
ic = trindex(2);
} else if (point_on_edge(p, p1, p2, eps)) {
ia = trindex(0);
ib = trindex(1);
} else if (point_on_edge(p, p2, p3, eps)) {
ia = trindex(1);
ib = trindex(2);
} else if (point_on_edge(p, p1, p3, eps)) {
ia = trindex(0);
ib = trindex(2);
}
// vector for the neigboring triangles including the detected one.
std::vector<Vec3i> neigh;
if (ic >= 0) { // The point is right on a vertex of the triangle
for (int n = 0; n < mesh.F().rows(); ++n) {
thr();
Vec3i ni = mesh.F().row(n);
if ((ni(X) == ic || ni(Y) == ic || ni(Z) == ic))
neigh.emplace_back(ni);
}
} else if (ia >= 0 && ib >= 0) { // the point is on and edge
// now get all the neigboring triangles
for (int n = 0; n < mesh.F().rows(); ++n) {
thr();
Vec3i ni = mesh.F().row(n);
if ((ni(X) == ia || ni(Y) == ia || ni(Z) == ia) &&
(ni(X) == ib || ni(Y) == ib || ni(Z) == ib))
neigh.emplace_back(ni);
}
}
// Calculate the normals for the neighboring triangles
std::vector<Vec3d> neighnorms;
neighnorms.reserve(neigh.size());
for (const Vec3i &tri : neigh) {
const Vec3d & pt1 = mesh.V().row(tri(0));
const Vec3d & pt2 = mesh.V().row(tri(1));
const Vec3d & pt3 = mesh.V().row(tri(2));
Eigen::Vector3d U = pt2 - pt1;
Eigen::Vector3d V = pt3 - pt1;
neighnorms.emplace_back(U.cross(V).normalized());
}
// Throw out duplicates. They would cause trouble with summing. We
// will use std::unique which works on sorted ranges. We will sort
// by the coefficient-wise sum of the normals. It should force the
// same elements to be consecutive.
std::sort(neighnorms.begin(), neighnorms.end(),
[](const Vec3d &v1, const Vec3d &v2) {
return v1.sum() < v2.sum();
});
auto lend = std::unique(neighnorms.begin(), neighnorms.end(),
[](const Vec3d &n1, const Vec3d &n2) {
// Compare normals for equivalence.
// This is controvers stuff.
auto deq = [](double a, double b) {
return std::abs(a - b) < 1e-3;
};
return deq(n1(X), n2(X)) &&
deq(n1(Y), n2(Y)) &&
deq(n1(Z), n2(Z));
});
if (!neighnorms.empty()) { // there were neighbors to count with
// sum up the normals and then normalize the result again.
// This unification seems to be enough.
Vec3d sumnorm(0, 0, 0);
sumnorm = std::accumulate(neighnorms.begin(), lend, sumnorm);
sumnorm.normalize();
ret.row(long(ridx)) = sumnorm;
} else { // point lies safely within its triangle
Eigen::Vector3d U = p2 - p1;
Eigen::Vector3d V = p3 - p1;
ret.row(long(ridx)) = U.cross(V).normalized();
}
});
return ret;
}
namespace bgi = boost::geometry::index;
using Index3D = bgi::rtree< PointIndexEl, bgi::rstar<16, 4> /* ? */ >;
namespace {
bool cmp_ptidx_elements(const PointIndexEl& e1, const PointIndexEl& e2)
{
return e1.second < e2.second;
};
ClusteredPoints cluster(Index3D &sindex,
unsigned max_points,
std::function<std::vector<PointIndexEl>(
const Index3D &, const PointIndexEl &)> qfn)
{
using Elems = std::vector<PointIndexEl>;
// Recursive function for visiting all the points in a given distance to
// each other
std::function<void(Elems&, Elems&)> group =
[&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster)
{
for(auto& p : pts) {
std::vector<PointIndexEl> tmp = qfn(sindex, p);
std::sort(tmp.begin(), tmp.end(), cmp_ptidx_elements);
Elems newpts;
std::set_difference(tmp.begin(), tmp.end(),
cluster.begin(), cluster.end(),
std::back_inserter(newpts), cmp_ptidx_elements);
int c = max_points && newpts.size() + cluster.size() > max_points?
int(max_points - cluster.size()) : int(newpts.size());
cluster.insert(cluster.end(), newpts.begin(), newpts.begin() + c);
std::sort(cluster.begin(), cluster.end(), cmp_ptidx_elements);
if(!newpts.empty() && (!max_points || cluster.size() < max_points))
group(newpts, cluster);
}
};
std::vector<Elems> clusters;
for(auto it = sindex.begin(); it != sindex.end();) {
Elems cluster = {};
Elems pts = {*it};
group(pts, cluster);
for(auto& c : cluster) sindex.remove(c);
it = sindex.begin();
clusters.emplace_back(cluster);
}
ClusteredPoints result;
for(auto& cluster : clusters) {
result.emplace_back();
for(auto c : cluster) result.back().emplace_back(c.second);
}
return result;
}
std::vector<PointIndexEl> distance_queryfn(const Index3D& sindex,
const PointIndexEl& p,
double dist,
unsigned max_points)
{
std::vector<PointIndexEl> tmp; tmp.reserve(max_points);
sindex.query(
bgi::nearest(p.first, max_points),
std::back_inserter(tmp)
);
for(auto it = tmp.begin(); it < tmp.end(); ++it)
if(distance(p.first, it->first) > dist) it = tmp.erase(it);
return tmp;
}
} // namespace
// Clustering a set of points by the given criteria
ClusteredPoints cluster(
const std::vector<unsigned>& indices,
std::function<Vec3d(unsigned)> pointfn,
double dist,
unsigned max_points)
{
// A spatial index for querying the nearest points
Index3D sindex;
// Build the index
for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
return cluster(sindex, max_points,
[dist, max_points](const Index3D& sidx, const PointIndexEl& p)
{
return distance_queryfn(sidx, p, dist, max_points);
});
}
// Clustering a set of points by the given criteria
ClusteredPoints cluster(
const std::vector<unsigned>& indices,
std::function<Vec3d(unsigned)> pointfn,
std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
unsigned max_points)
{
// A spatial index for querying the nearest points
Index3D sindex;
// Build the index
for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
return cluster(sindex, max_points,
[max_points, predicate](const Index3D& sidx, const PointIndexEl& p)
{
std::vector<PointIndexEl> tmp; tmp.reserve(max_points);
sidx.query(bgi::satisfies([p, predicate](const PointIndexEl& e){
return predicate(p, e);
}), std::back_inserter(tmp));
return tmp;
});
}
ClusteredPoints cluster(const PointSet& pts, double dist, unsigned max_points)
{
// A spatial index for querying the nearest points
Index3D sindex;
// Build the index
for(Eigen::Index i = 0; i < pts.rows(); i++)
sindex.insert(std::make_pair(Vec3d(pts.row(i)), unsigned(i)));
return cluster(sindex, max_points,
[dist, max_points](const Index3D& sidx, const PointIndexEl& p)
{
return distance_queryfn(sidx, p, dist, max_points);
});
}
} // namespace sla
} // namespace Slic3r

View file

@ -0,0 +1,32 @@
#ifndef SLA_COMMON_HPP
#define SLA_COMMON_HPP
#include <memory>
#include <vector>
#include <numeric>
#include <functional>
#include <Eigen/Geometry>
//#include "SLASpatIndex.hpp"
//#include <libslic3r/ExPolygon.hpp>
//#include <libslic3r/TriangleMesh.hpp>
// #define SLIC3R_SLA_NEEDS_WINDTREE
namespace Slic3r {
// Typedefs from Point.hpp
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> Vec3f;
typedef Eigen::Matrix<double, 3, 1, Eigen::DontAlign> Vec3d;
typedef Eigen::Matrix<int, 4, 1, Eigen::DontAlign> Vec4i;
namespace sla {
using PointSet = Eigen::MatrixXd;
} // namespace sla
} // namespace Slic3r
#endif // SLASUPPORTTREE_HPP

View file

@ -1,7 +1,9 @@
#include "ConcaveHull.hpp" #include <libslic3r/SLA/ConcaveHull.hpp>
#include <libslic3r/SLA/SpatIndex.hpp>
#include <libslic3r/MTUtils.hpp> #include <libslic3r/MTUtils.hpp>
#include <libslic3r/ClipperUtils.hpp> #include <libslic3r/ClipperUtils.hpp>
#include "SLASpatIndex.hpp"
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
namespace Slic3r { namespace Slic3r {
@ -40,7 +42,7 @@ Point ConcaveHull::centroid(const Points &pp)
// As it shows, the current offset_ex in ClipperUtils hangs if used in jtRound // As it shows, the current offset_ex in ClipperUtils hangs if used in jtRound
// mode // mode
ClipperLib::Paths fast_offset(const ClipperLib::Paths &paths, static ClipperLib::Paths fast_offset(const ClipperLib::Paths &paths,
coord_t delta, coord_t delta,
ClipperLib::JoinType jointype) ClipperLib::JoinType jointype)
{ {
@ -73,7 +75,7 @@ Points ConcaveHull::calculate_centroids() const
Points centroids = reserve_vector<Point>(m_polys.size()); Points centroids = reserve_vector<Point>(m_polys.size());
std::transform(m_polys.begin(), m_polys.end(), std::transform(m_polys.begin(), m_polys.end(),
std::back_inserter(centroids), std::back_inserter(centroids),
[this](const Polygon &poly) { return centroid(poly); }); [](const Polygon &poly) { return centroid(poly); });
return centroids; return centroids;
} }

View file

@ -1,5 +1,5 @@
#ifndef CONCAVEHULL_HPP #ifndef SLA_CONCAVEHULL_HPP
#define CONCAVEHULL_HPP #define SLA_CONCAVEHULL_HPP
#include <libslic3r/ExPolygon.hpp> #include <libslic3r/ExPolygon.hpp>

View file

@ -1,5 +1,5 @@
#ifndef SLACONCURRENCY_H #ifndef SLA_CONCURRENCY_H
#define SLACONCURRENCY_H #define SLA_CONCURRENCY_H
#include <tbb/spin_mutex.h> #include <tbb/spin_mutex.h>
#include <tbb/mutex.h> #include <tbb/mutex.h>

View file

@ -0,0 +1,149 @@
#include <libslic3r/SLA/Contour3D.hpp>
#include <libslic3r/SLA/EigenMesh3D.hpp>
#include <libslic3r/Format/objparser.hpp>
namespace Slic3r { namespace sla {
Contour3D::Contour3D(const TriangleMesh &trmesh)
{
points.reserve(trmesh.its.vertices.size());
faces3.reserve(trmesh.its.indices.size());
for (auto &v : trmesh.its.vertices)
points.emplace_back(v.cast<double>());
std::copy(trmesh.its.indices.begin(), trmesh.its.indices.end(),
std::back_inserter(faces3));
}
Contour3D::Contour3D(TriangleMesh &&trmesh)
{
points.reserve(trmesh.its.vertices.size());
for (auto &v : trmesh.its.vertices)
points.emplace_back(v.cast<double>());
faces3.swap(trmesh.its.indices);
}
Contour3D::Contour3D(const EigenMesh3D &emesh) {
points.reserve(size_t(emesh.V().rows()));
faces3.reserve(size_t(emesh.F().rows()));
for (int r = 0; r < emesh.V().rows(); r++)
points.emplace_back(emesh.V().row(r).cast<double>());
for (int i = 0; i < emesh.F().rows(); i++)
faces3.emplace_back(emesh.F().row(i));
}
Contour3D &Contour3D::merge(const Contour3D &ctr)
{
auto N = coord_t(points.size());
auto N_f3 = faces3.size();
auto N_f4 = faces4.size();
points.insert(points.end(), ctr.points.begin(), ctr.points.end());
faces3.insert(faces3.end(), ctr.faces3.begin(), ctr.faces3.end());
faces4.insert(faces4.end(), ctr.faces4.begin(), ctr.faces4.end());
for(size_t n = N_f3; n < faces3.size(); n++) {
auto& idx = faces3[n]; idx.x() += N; idx.y() += N; idx.z() += N;
}
for(size_t n = N_f4; n < faces4.size(); n++) {
auto& idx = faces4[n]; for (int k = 0; k < 4; k++) idx(k) += N;
}
return *this;
}
Contour3D &Contour3D::merge(const Pointf3s &triangles)
{
const size_t offs = points.size();
points.insert(points.end(), triangles.begin(), triangles.end());
faces3.reserve(faces3.size() + points.size() / 3);
for(int i = int(offs); i < int(points.size()); i += 3)
faces3.emplace_back(i, i + 1, i + 2);
return *this;
}
void Contour3D::to_obj(std::ostream &stream)
{
for(auto& p : points)
stream << "v " << p.transpose() << "\n";
for(auto& f : faces3)
stream << "f " << (f + Vec3i(1, 1, 1)).transpose() << "\n";
for(auto& f : faces4)
stream << "f " << (f + Vec4i(1, 1, 1, 1)).transpose() << "\n";
}
void Contour3D::from_obj(std::istream &stream)
{
ObjParser::ObjData data;
ObjParser::objparse(stream, data);
points.reserve(data.coordinates.size() / 4 + 1);
auto &coords = data.coordinates;
for (size_t i = 0; i < coords.size(); i += 4)
points.emplace_back(coords[i], coords[i + 1], coords[i + 2]);
Vec3i triangle;
Vec4i quad;
size_t v = 0;
while(v < data.vertices.size()) {
size_t N = 0;
size_t i = v;
while (data.vertices[v++].coordIdx != -1) ++N;
std::function<void(int, int)> setfn;
if (N < 3 || N > 4) continue;
else if (N == 3) setfn = [&triangle](int k, int f) { triangle(k) = f; };
else setfn = [&quad](int k, int f) { quad(k) = f; };
for (size_t j = 0; j < N; ++j)
setfn(int(j), data.vertices[i + j].coordIdx);
}
}
TriangleMesh to_triangle_mesh(const Contour3D &ctour) {
if (ctour.faces4.empty()) return {ctour.points, ctour.faces3};
std::vector<Vec3i> triangles;
triangles.reserve(ctour.faces3.size() + 2 * ctour.faces4.size());
std::copy(ctour.faces3.begin(), ctour.faces3.end(),
std::back_inserter(triangles));
for (auto &quad : ctour.faces4) {
triangles.emplace_back(quad(0), quad(1), quad(2));
triangles.emplace_back(quad(2), quad(3), quad(0));
}
return {ctour.points, std::move(triangles)};
}
TriangleMesh to_triangle_mesh(Contour3D &&ctour) {
if (ctour.faces4.empty())
return {std::move(ctour.points), std::move(ctour.faces3)};
std::vector<Vec3i> triangles;
triangles.reserve(ctour.faces3.size() + 2 * ctour.faces4.size());
std::copy(ctour.faces3.begin(), ctour.faces3.end(),
std::back_inserter(triangles));
for (auto &quad : ctour.faces4) {
triangles.emplace_back(quad(0), quad(1), quad(2));
triangles.emplace_back(quad(2), quad(3), quad(0));
}
return {std::move(ctour.points), std::move(triangles)};
}
}} // namespace Slic3r::sla

View file

@ -0,0 +1,45 @@
#ifndef SLA_CONTOUR3D_HPP
#define SLA_CONTOUR3D_HPP
#include <libslic3r/SLA/Common.hpp>
#include <libslic3r/TriangleMesh.hpp>
namespace Slic3r { namespace sla {
class EigenMesh3D;
/// Dumb vertex mesh consisting of triangles (or) quads. Capable of merging with
/// other meshes of this type and converting to and from other mesh formats.
struct Contour3D {
std::vector<Vec3d> points;
std::vector<Vec3i> faces3;
std::vector<Vec4i> faces4;
Contour3D() = default;
Contour3D(const TriangleMesh &trmesh);
Contour3D(TriangleMesh &&trmesh);
Contour3D(const EigenMesh3D &emesh);
Contour3D& merge(const Contour3D& ctr);
Contour3D& merge(const Pointf3s& triangles);
// Write the index triangle structure to OBJ file for debugging purposes.
void to_obj(std::ostream& stream);
void from_obj(std::istream &stream);
inline bool empty() const
{
return points.empty() || (faces4.empty() && faces3.empty());
}
};
/// Mesh from an existing contour.
TriangleMesh to_triangle_mesh(const Contour3D& ctour);
/// Mesh from an evaporating 3D contour
TriangleMesh to_triangle_mesh(Contour3D&& ctour);
}} // namespace Slic3r::sla
#endif // CONTOUR3D_HPP

View file

@ -0,0 +1,160 @@
#ifndef SLA_EIGENMESH3D_H
#define SLA_EIGENMESH3D_H
#include <libslic3r/SLA/Common.hpp>
#include "libslic3r/SLA/Hollowing.hpp"
namespace Slic3r {
class TriangleMesh;
namespace sla {
struct Contour3D;
void to_eigen_mesh(const TriangleMesh &mesh, Eigen::MatrixXd &V, Eigen::MatrixXi &F);
void to_triangle_mesh(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, TriangleMesh &);
/// An index-triangle structure for libIGL functions. Also serves as an
/// alternative (raw) input format for the SLASupportTree.
// Implemented in libslic3r/SLA/Common.cpp
class EigenMesh3D {
class AABBImpl;
Eigen::MatrixXd m_V;
Eigen::MatrixXi m_F;
double m_ground_level = 0, m_gnd_offset = 0;
std::unique_ptr<AABBImpl> m_aabb;
// This holds a copy of holes in the mesh. Initialized externally
// by load_mesh setter.
std::vector<DrainHole> m_holes;
public:
explicit EigenMesh3D(const TriangleMesh&);
explicit EigenMesh3D(const Contour3D &other);
EigenMesh3D(const EigenMesh3D& other);
EigenMesh3D& operator=(const EigenMesh3D&);
EigenMesh3D(EigenMesh3D &&other);
EigenMesh3D& operator=(EigenMesh3D &&other);
~EigenMesh3D();
inline double ground_level() const { return m_ground_level + m_gnd_offset; }
inline void ground_level_offset(double o) { m_gnd_offset = o; }
inline double ground_level_offset() const { return m_gnd_offset; }
inline const Eigen::MatrixXd& V() const { return m_V; }
inline const Eigen::MatrixXi& F() const { return m_F; }
// Result of a raycast
class hit_result {
// m_t holds a distance from m_source to the intersection.
double m_t = infty();
const EigenMesh3D *m_mesh = nullptr;
Vec3d m_dir;
Vec3d m_source;
Vec3d m_normal;
friend class EigenMesh3D;
// A valid object of this class can only be obtained from
// EigenMesh3D::query_ray_hit method.
explicit inline hit_result(const EigenMesh3D& em): m_mesh(&em) {}
public:
// This denotes no hit on the mesh.
static inline constexpr double infty() { return std::numeric_limits<double>::infinity(); }
explicit inline hit_result(double val = infty()) : m_t(val) {}
inline double distance() const { return m_t; }
inline const Vec3d& direction() const { return m_dir; }
inline const Vec3d& source() const { return m_source; }
inline Vec3d position() const { return m_source + m_dir * m_t; }
inline bool is_valid() const { return m_mesh != nullptr; }
inline bool is_hit() const { return !std::isinf(m_t); }
inline const Vec3d& normal() const {
assert(is_valid());
return m_normal;
}
inline bool is_inside() const {
return is_hit() && normal().dot(m_dir) > 0;
}
};
// Inform the object about location of holes
// creates internal copy of the vector
void load_holes(const std::vector<DrainHole>& holes) {
m_holes = holes;
}
// Casting a ray on the mesh, returns the distance where the hit occures.
hit_result query_ray_hit(const Vec3d &s, const Vec3d &dir) const;
// Casts a ray on the mesh and returns all hits
std::vector<hit_result> query_ray_hits(const Vec3d &s, const Vec3d &dir) const;
// Iterates over hits and holes and returns the true hit, possibly
// on the inside of a hole.
hit_result filter_hits(const std::vector<EigenMesh3D::hit_result>& obj_hits) const;
class si_result {
double m_value;
int m_fidx;
Vec3d m_p;
si_result(double val, int i, const Vec3d& c):
m_value(val), m_fidx(i), m_p(c) {}
friend class EigenMesh3D;
public:
si_result() = delete;
double value() const { return m_value; }
operator double() const { return m_value; }
const Vec3d& point_on_mesh() const { return m_p; }
int F_idx() const { return m_fidx; }
};
#ifdef SLIC3R_SLA_NEEDS_WINDTREE
// The signed distance from a point to the mesh. Outputs the distance,
// the index of the triangle and the closest point in mesh coordinate space.
si_result signed_distance(const Vec3d& p) const;
bool inside(const Vec3d& p) const;
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
double squared_distance(const Vec3d& p, int& i, Vec3d& c) const;
inline double squared_distance(const Vec3d &p) const
{
int i;
Vec3d c;
return squared_distance(p, i, c);
}
Vec3d normal_by_face_id(int face_id) const {
auto trindex = F().row(face_id);
const Vec3d& p1 = V().row(trindex(0));
const Vec3d& p2 = V().row(trindex(1));
const Vec3d& p3 = V().row(trindex(2));
Eigen::Vector3d U = p2 - p1;
Eigen::Vector3d V = p3 - p1;
return U.cross(V).normalized();
}
};
// Calculate the normals for the selected points (from 'points' set) on the
// mesh. This will call squared distance for each point.
PointSet normals(const PointSet& points,
const EigenMesh3D& convert_mesh,
double eps = 0.05, // min distance from edges
std::function<void()> throw_on_cancel = [](){},
const std::vector<unsigned>& selected_points = {});
}} // namespace Slic3r::sla
#endif // EIGENMESH3D_H

View file

@ -0,0 +1,277 @@
#include <functional>
#include <libslic3r/OpenVDBUtils.hpp>
#include <libslic3r/TriangleMesh.hpp>
#include <libslic3r/SLA/Hollowing.hpp>
#include <libslic3r/SLA/Contour3D.hpp>
#include <libslic3r/SLA/EigenMesh3D.hpp>
#include <libslic3r/SLA/SupportTreeBuilder.hpp>
#include <libslic3r/ClipperUtils.hpp>
#include <libslic3r/SimplifyMesh.hpp>
#include <boost/log/trivial.hpp>
#include <libslic3r/MTUtils.hpp>
#include <libslic3r/I18N.hpp>
//! macro used to mark string used at localization,
//! return same string
#define L(s) Slic3r::I18N::translate(s)
namespace Slic3r {
namespace sla {
template<class S, class = FloatingOnly<S>>
inline void _scale(S s, TriangleMesh &m) { m.scale(float(s)); }
template<class S, class = FloatingOnly<S>>
inline void _scale(S s, Contour3D &m) { for (auto &p : m.points) p *= s; }
static TriangleMesh _generate_interior(const TriangleMesh &mesh,
const JobController &ctl,
double min_thickness,
double voxel_scale,
double closing_dist)
{
TriangleMesh imesh{mesh};
_scale(voxel_scale, imesh);
double offset = voxel_scale * min_thickness;
double D = voxel_scale * closing_dist;
float out_range = 0.1f * float(offset);
float in_range = 1.1f * float(offset + D);
if (ctl.stopcondition()) return {};
else ctl.statuscb(0, L("Hollowing"));
auto gridptr = mesh_to_grid(imesh, {}, out_range, in_range);
assert(gridptr);
if (!gridptr) {
BOOST_LOG_TRIVIAL(error) << "Returned OpenVDB grid is NULL";
return {};
}
if (ctl.stopcondition()) return {};
else ctl.statuscb(30, L("Hollowing"));
if (closing_dist > .0) {
gridptr = redistance_grid(*gridptr, -(offset + D), double(in_range));
} else {
D = -offset;
}
if (ctl.stopcondition()) return {};
else ctl.statuscb(70, L("Hollowing"));
double iso_surface = D;
double adaptivity = 0.;
auto omesh = grid_to_mesh(*gridptr, iso_surface, adaptivity);
_scale(1. / voxel_scale, omesh);
if (ctl.stopcondition()) return {};
else ctl.statuscb(100, L("Hollowing"));
return omesh;
}
std::unique_ptr<TriangleMesh> generate_interior(const TriangleMesh & mesh,
const HollowingConfig &hc,
const JobController & ctl)
{
static const double MIN_OVERSAMPL = 3.;
static const double MAX_OVERSAMPL = 8.;
// I can't figure out how to increase the grid resolution through openvdb
// API so the model will be scaled up before conversion and the result
// scaled down. Voxels have a unit size. If I set voxelSize smaller, it
// scales the whole geometry down, and doesn't increase the number of
// voxels.
//
// max 8x upscale, min is native voxel size
auto voxel_scale = MIN_OVERSAMPL + (MAX_OVERSAMPL - MIN_OVERSAMPL) * hc.quality;
auto meshptr = std::make_unique<TriangleMesh>(
_generate_interior(mesh, ctl, hc.min_thickness, voxel_scale,
hc.closing_distance));
if (meshptr) {
// This flips the normals to be outward facing...
meshptr->require_shared_vertices();
indexed_triangle_set its = std::move(meshptr->its);
Slic3r::simplify_mesh(its);
// flip normals back...
for (stl_triangle_vertex_indices &ind : its.indices)
std::swap(ind(0), ind(2));
*meshptr = Slic3r::TriangleMesh{its};
}
return meshptr;
}
Contour3D DrainHole::to_mesh() const
{
auto r = double(radius);
auto h = double(height);
sla::Contour3D hole = sla::cylinder(r, h, steps);
Eigen::Quaterniond q;
q.setFromTwoVectors(Vec3d{0., 0., 1.}, normal.cast<double>());
for(auto& p : hole.points) p = q * p + pos.cast<double>();
return hole;
}
bool DrainHole::operator==(const DrainHole &sp) const
{
return (pos == sp.pos) && (normal == sp.normal) &&
is_approx(radius, sp.radius) &&
is_approx(height, sp.height);
}
bool DrainHole::is_inside(const Vec3f& pt) const
{
Eigen::Hyperplane<float, 3> plane(normal, pos);
float dist = plane.signedDistance(pt);
if (dist < float(EPSILON) || dist > height)
return false;
Eigen::ParametrizedLine<float, 3> axis(pos, normal);
if ( axis.squaredDistance(pt) < pow(radius, 2.f))
return true;
return false;
}
// Given a line s+dir*t, find parameter t of intersections with the hole
// and the normal (points inside the hole). Outputs through out reference,
// returns true if two intersections were found.
bool DrainHole::get_intersections(const Vec3f& s, const Vec3f& dir,
std::array<std::pair<float, Vec3d>, 2>& out)
const
{
assert(is_approx(normal.norm(), 1.f));
const Eigen::ParametrizedLine<float, 3> ray(s, dir.normalized());
for (size_t i=0; i<2; ++i)
out[i] = std::make_pair(sla::EigenMesh3D::hit_result::infty(), Vec3d::Zero());
const float sqr_radius = pow(radius, 2.f);
// first check a bounding sphere of the hole:
Vec3f center = pos+normal*height/2.f;
float sqr_dist_limit = pow(height/2.f, 2.f) + sqr_radius ;
if (ray.squaredDistance(center) > sqr_dist_limit)
return false;
// The line intersects the bounding sphere, look for intersections with
// bases of the cylinder.
size_t found = 0; // counts how many intersections were found
Eigen::Hyperplane<float, 3> base;
if (! is_approx(ray.direction().dot(normal), 0.f)) {
for (size_t i=1; i<=1; --i) {
Vec3f cylinder_center = pos+i*height*normal;
if (i == 0) {
// The hole base can be identical to mesh surface if it is flat
// let's better move the base outward a bit
cylinder_center -= EPSILON*normal;
}
base = Eigen::Hyperplane<float, 3>(normal, cylinder_center);
Vec3f intersection = ray.intersectionPoint(base);
// Only accept the point if it is inside the cylinder base.
if ((cylinder_center-intersection).squaredNorm() < sqr_radius) {
out[found].first = ray.intersectionParameter(base);
out[found].second = (i==0 ? 1. : -1.) * normal.cast<double>();
++found;
}
}
}
else
{
// In case the line was perpendicular to the cylinder axis, previous
// block was skipped, but base will later be assumed to be valid.
base = Eigen::Hyperplane<float, 3>(normal, pos-EPSILON*normal);
}
// In case there is still an intersection to be found, check the wall
if (found != 2 && ! is_approx(std::abs(ray.direction().dot(normal)), 1.f)) {
// Project the ray onto the base plane
Vec3f proj_origin = base.projection(ray.origin());
Vec3f proj_dir = base.projection(ray.origin()+ray.direction())-proj_origin;
// save how the parameter scales and normalize the projected direction
float par_scale = proj_dir.norm();
proj_dir = proj_dir/par_scale;
Eigen::ParametrizedLine<float, 3> projected_ray(proj_origin, proj_dir);
// Calculate point on the secant that's closest to the center
// and its distance to the circle along the projected line
Vec3f closest = projected_ray.projection(pos);
float dist = sqrt((sqr_radius - (closest-pos).squaredNorm()));
// Unproject both intersections on the original line and check
// they are on the cylinder and not past it:
for (int i=-1; i<=1 && found !=2; i+=2) {
Vec3f isect = closest + i*dist * projected_ray.direction();
Vec3f to_isect = isect-proj_origin;
float par = to_isect.norm() / par_scale;
if (to_isect.normalized().dot(proj_dir.normalized()) < 0.f)
par *= -1.f;
Vec3d hit_normal = (pos-isect).normalized().cast<double>();
isect = ray.pointAt(par);
// check that the intersection is between the base planes:
float vert_dist = base.signedDistance(isect);
if (vert_dist > 0.f && vert_dist < height) {
out[found].first = par;
out[found].second = hit_normal;
++found;
}
}
}
// If only one intersection was found, it is some corner case,
// no intersection will be returned:
if (found != 2)
return false;
// Sort the intersections:
if (out[0].first > out[1].first)
std::swap(out[0], out[1]);
return true;
}
void cut_drainholes(std::vector<ExPolygons> & obj_slices,
const std::vector<float> &slicegrid,
float closing_radius,
const sla::DrainHoles & holes,
std::function<void(void)> thr)
{
TriangleMesh mesh;
for (const sla::DrainHole &holept : holes)
mesh.merge(sla::to_triangle_mesh(holept.to_mesh()));
if (mesh.empty()) return;
mesh.require_shared_vertices();
TriangleMeshSlicer slicer(&mesh);
std::vector<ExPolygons> hole_slices;
slicer.slice(slicegrid, SlicingMode::Regular, closing_radius, &hole_slices, thr);
if (obj_slices.size() != hole_slices.size())
BOOST_LOG_TRIVIAL(warning)
<< "Sliced object and drain-holes layer count does not match!";
size_t until = std::min(obj_slices.size(), hole_slices.size());
for (size_t i = 0; i < until; ++i)
obj_slices[i] = diff_ex(obj_slices[i], hole_slices[i]);
}
}} // namespace Slic3r::sla

View file

@ -0,0 +1,75 @@
#ifndef SLA_HOLLOWING_HPP
#define SLA_HOLLOWING_HPP
#include <memory>
#include <libslic3r/SLA/Common.hpp>
#include <libslic3r/SLA/Contour3D.hpp>
#include <libslic3r/SLA/JobController.hpp>
namespace Slic3r {
class TriangleMesh;
namespace sla {
struct HollowingConfig
{
double min_thickness = 2.;
double quality = 0.5;
double closing_distance = 0.5;
bool enabled = true;
};
struct DrainHole
{
Vec3f pos;
Vec3f normal;
float radius;
float height;
DrainHole()
: pos(Vec3f::Zero()), normal(Vec3f::UnitZ()), radius(5.f), height(10.f)
{}
DrainHole(Vec3f p, Vec3f n, float r, float h)
: pos(p), normal(n), radius(r), height(h)
{}
DrainHole(const DrainHole& rhs) :
DrainHole(rhs.pos, rhs.normal, rhs.radius, rhs.height) {}
bool operator==(const DrainHole &sp) const;
bool operator!=(const DrainHole &sp) const { return !(sp == (*this)); }
bool is_inside(const Vec3f& pt) const;
bool get_intersections(const Vec3f& s, const Vec3f& dir,
std::array<std::pair<float, Vec3d>, 2>& out) const;
Contour3D to_mesh() const;
template<class Archive> inline void serialize(Archive &ar)
{
ar(pos, normal, radius, height);
}
static constexpr size_t steps = 32;
};
using DrainHoles = std::vector<DrainHole>;
std::unique_ptr<TriangleMesh> generate_interior(const TriangleMesh &mesh,
const HollowingConfig & = {},
const JobController &ctl = {});
void cut_drainholes(std::vector<ExPolygons> & obj_slices,
const std::vector<float> &slicegrid,
float closing_radius,
const sla::DrainHoles & holes,
std::function<void(void)> thr);
}
}
#endif // HOLLOWINGFILTER_H

Some files were not shown because too many files have changed in this diff Show more