Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_gcode_viewer
|
@ -104,7 +104,7 @@ list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/)
|
|||
|
||||
enable_testing ()
|
||||
|
||||
# Enable C++11 language standard.
|
||||
# Enable C++17 language standard.
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
|
@ -392,8 +392,7 @@ if (NOT GLEW_FOUND)
|
|||
endif ()
|
||||
|
||||
# Find the Cereal serialization library
|
||||
add_library(cereal INTERFACE)
|
||||
target_include_directories(cereal INTERFACE include)
|
||||
find_package(cereal REQUIRED)
|
||||
|
||||
# l10n
|
||||
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization")
|
||||
|
@ -426,6 +425,12 @@ if(SLIC3R_STATIC)
|
|||
set(USE_BLOSC TRUE)
|
||||
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})
|
||||
function(prusaslicer_copy_dlls target)
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
|
||||
|
@ -434,23 +439,30 @@ function(prusaslicer_copy_dlls target)
|
|||
set(_bits 32)
|
||||
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
|
||||
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"
|
||||
VERBATIM)
|
||||
|
||||
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"
|
||||
VERBATIM)
|
||||
|
||||
endfunction()
|
||||
|
||||
#find_package(OpenVDB 5.0 COMPONENTS openvdb)
|
||||
#slic3r_remap_configs(IlmBase::Half RelWithDebInfo Release)
|
||||
|
||||
# libslic3r, PrusaSlicer GUI and the PrusaSlicer executable.
|
||||
add_subdirectory(src)
|
||||
|
|
106
cmake/modules/CheckAtomic.cmake
Normal 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()
|
|
@ -108,6 +108,18 @@ if(POLICY CMP0074)
|
|||
cmake_policy(SET CMP0074 NEW)
|
||||
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(${CMAKE_CURRENT_LIST_DIR}/OpenVDBUtils.cmake)
|
||||
|
||||
|
@ -146,7 +158,7 @@ set(_OPENVDB_ROOT_SEARCH_DIR "")
|
|||
|
||||
# Additionally try and use pkconfig to find OpenVDB
|
||||
|
||||
find_package(PkgConfig)
|
||||
find_package(PkgConfig ${_quiet} )
|
||||
pkg_check_modules(PC_OpenVDB QUIET OpenVDB)
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
|
@ -221,21 +233,26 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS})
|
|||
)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
else()
|
||||
set(OpenVDB_${COMPONENT}_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE})
|
||||
else ()
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" _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})
|
||||
endif ()
|
||||
|
||||
|
@ -247,6 +264,7 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS})
|
|||
set(OpenVDB_${COMPONENT}_FOUND FALSE)
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
endforeach()
|
||||
|
||||
if(UNIX AND OPENVDB_USE_STATIC_LIBS)
|
||||
|
@ -280,7 +298,7 @@ OPENVDB_ABI_VERSION_FROM_PRINT(
|
|||
ABI OpenVDB_ABI
|
||||
)
|
||||
|
||||
if(NOT OpenVDB_FIND_QUIET)
|
||||
if(NOT OpenVDB_FIND_QUIETLY)
|
||||
if(NOT OpenVDB_ABI)
|
||||
message(WARNING "Unable to determine OpenVDB ABI version from OpenVDB "
|
||||
"installation. The library major version \"${OpenVDB_MAJOR_VERSION}\" "
|
||||
|
@ -298,7 +316,17 @@ endif()
|
|||
|
||||
# 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)
|
||||
pkg_check_modules(IlmBase QUIET IlmBase)
|
||||
endif()
|
||||
|
@ -306,20 +334,20 @@ if (IlmBase_FOUND AND NOT TARGET IlmBase::Half)
|
|||
message(STATUS "Falling back to IlmBase found by pkg-config...")
|
||||
|
||||
find_library(IlmHalf_LIBRARY NAMES Half)
|
||||
if(IlmHalf_LIBRARY-NOTFOUND)
|
||||
message(FATAL_ERROR "IlmBase::Half can not be found!")
|
||||
if(IlmHalf_LIBRARY-NOTFOUND OR NOT IlmBase_INCLUDE_DIRS)
|
||||
just_fail("IlmBase::Half can not be found!")
|
||||
endif()
|
||||
|
||||
add_library(IlmBase::Half UNKNOWN IMPORTED)
|
||||
set_target_properties(IlmBase::Half PROPERTIES
|
||||
IMPORTED_LOCATION "${IlmHalf_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${IlmBase_INCLUDE_DIRS})
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${IlmBase_INCLUDE_DIRS}")
|
||||
elseif(NOT IlmBase_FOUND)
|
||||
message(FATAL_ERROR "IlmBase::Half can not be found!")
|
||||
just_fail("IlmBase::Half can not be found!")
|
||||
endif()
|
||||
find_package(TBB REQUIRED COMPONENTS tbb)
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(Boost REQUIRED COMPONENTS iostreams system)
|
||||
find_package(TBB ${_quiet} ${_required} COMPONENTS tbb)
|
||||
find_package(ZLIB ${_quiet} ${_required})
|
||||
find_package(Boost ${_quiet} ${_required} COMPONENTS iostreams system )
|
||||
|
||||
# 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
|
||||
|
@ -380,7 +408,7 @@ unset(_OPENVDB_PREREQUISITE_LIST)
|
|||
unset(_HAS_DEP)
|
||||
|
||||
if(OpenVDB_USES_BLOSC)
|
||||
find_package(Blosc )
|
||||
find_package(Blosc QUIET)
|
||||
if(NOT Blosc_FOUND OR NOT TARGET Blosc::blosc)
|
||||
message(STATUS "find_package could not find Blosc. Using fallback blosc search...")
|
||||
find_path(Blosc_INCLUDE_DIR blosc.h)
|
||||
|
@ -392,25 +420,25 @@ if(OpenVDB_USES_BLOSC)
|
|||
IMPORTED_LOCATION "${Blosc_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${Blosc_INCLUDE_DIR})
|
||||
elseif()
|
||||
message(FATAL_ERROR "Blosc library can not be found!")
|
||||
just_fail("Blosc library can not be found!")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(OpenVDB_USES_LOG4CPLUS)
|
||||
find_package(Log4cplus REQUIRED)
|
||||
find_package(Log4cplus ${_quiet} ${_required})
|
||||
endif()
|
||||
|
||||
if(OpenVDB_USES_ILM)
|
||||
find_package(IlmBase REQUIRED)
|
||||
find_package(IlmBase ${_quiet} ${_required})
|
||||
endif()
|
||||
|
||||
if(OpenVDB_USES_EXR)
|
||||
find_package(OpenEXR REQUIRED)
|
||||
find_package(OpenEXR ${_quiet} ${_required})
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(Threads ${_quiet} ${_required})
|
||||
endif()
|
||||
|
||||
# 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})
|
||||
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)
|
||||
set_target_properties(OpenVDB::${COMPONENT} PROPERTIES
|
||||
INTERFACE_COMPILE_OPTIONS "${OpenVDB_DEFINITIONS}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${OpenVDB_INCLUDE_DIR}"
|
||||
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
|
||||
IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}"
|
||||
)
|
||||
|
||||
if (_is_multi)
|
||||
set_target_properties(OpenVDB::${COMPONENT} PROPERTIES
|
||||
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}"
|
||||
)
|
||||
else ()
|
||||
set_target_properties(OpenVDB::${COMPONENT} PROPERTIES
|
||||
IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}"
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (OPENVDB_USE_STATIC_LIBS)
|
||||
|
@ -521,7 +558,7 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS})
|
|||
endif()
|
||||
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}")
|
||||
endif()
|
||||
|
||||
|
|
26
cmake/modules/Findcereal.cmake
Normal 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()
|
|
@ -125,7 +125,9 @@ function(OPENVDB_ABI_VERSION_FROM_PRINT OPENVDB_PRINT)
|
|||
cmake_parse_arguments(_VDB "QUIET" "ABI" "" ${ARGN})
|
||||
|
||||
if(NOT EXISTS ${OPENVDB_PRINT})
|
||||
if(NOT OpenVDB_FIND_QUIETLY)
|
||||
message(WARNING "vdb_print not found! ${OPENVDB_PRINT}")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
@ -148,7 +150,9 @@ function(OPENVDB_ABI_VERSION_FROM_PRINT OPENVDB_PRINT)
|
|||
endif()
|
||||
|
||||
if(${_VDB_PRINT_RETURN_STATUS})
|
||||
if(NOT OpenVDB_FIND_QUIETLY)
|
||||
message(WARNING "vdb_print returned with status ${_VDB_PRINT_RETURN_STATUS}")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
|
3
deps/GMP/GMP.cmake
vendored
|
@ -18,7 +18,8 @@ if (MSVC)
|
|||
|
||||
else ()
|
||||
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
|
||||
CONFIGURE_COMMAND ./configure --enable-shared=no --enable-cxx=yes --enable-static=yes "--prefix=${DESTDIR}/usr/local" --with-pic
|
||||
BUILD_COMMAND make -j
|
||||
|
|
635
deps/openvdb-mods.patch
vendored
|
@ -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>
|
||||
Date: Wed, 16 Oct 2019 17:42:50 +0200
|
||||
Subject: [PATCH] Build fixes for PrusaSlicer integration
|
||||
Date: Thu, 23 Jan 2020 17:17:36 +0100
|
||||
Subject: [PATCH] openvdb-mods
|
||||
|
||||
Signed-off-by: tamasmeszaros <meszaros.q@gmail.com>
|
||||
---
|
||||
CMakeLists.txt | 3 -
|
||||
cmake/FindBlosc.cmake | 218 ---------------
|
||||
cmake/CheckAtomic.cmake | 106 ++++++
|
||||
cmake/FindBlosc.cmake | 218 ------------
|
||||
cmake/FindCppUnit.cmake | 4 +-
|
||||
cmake/FindIlmBase.cmake | 337 ----------------------
|
||||
cmake/FindOpenEXR.cmake | 329 ----------------------
|
||||
cmake/FindIlmBase.cmake | 337 ------------------
|
||||
cmake/FindOpenEXR.cmake | 329 ------------------
|
||||
cmake/FindOpenVDB.cmake | 19 +-
|
||||
cmake/FindTBB.cmake | 605 ++++++++++++++++++++--------------------
|
||||
openvdb/CMakeLists.txt | 13 +-
|
||||
cmake/FindTBB.cmake | 599 ++++++++++++++++----------------
|
||||
openvdb/CMakeLists.txt | 16 +-
|
||||
openvdb/Grid.cc | 3 +
|
||||
openvdb/PlatformConfig.h | 9 +-
|
||||
openvdb/cmd/CMakeLists.txt | 4 +-
|
||||
openvdb/unittest/CMakeLists.txt | 3 +-
|
||||
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/FindIlmBase.cmake
|
||||
delete mode 100644 cmake/FindOpenEXR.cmake
|
||||
|
@ -40,6 +41,119 @@ index 580b353..6d364c1 100644
|
|||
cmake/FindOpenVDB.cmake
|
||||
cmake/FindTBB.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
|
||||
deleted file mode 100644
|
||||
index 5aacfdd..0000000
|
||||
|
@ -965,7 +1079,7 @@ index 339c1a2..0000000
|
|||
- message(FATAL_ERROR "Unable to find OpenEXR")
|
||||
-endif()
|
||||
diff --git a/cmake/FindOpenVDB.cmake b/cmake/FindOpenVDB.cmake
|
||||
index 63a2eda..6211071 100644
|
||||
index 63a2eda..d9f6d07 100644
|
||||
--- a/cmake/FindOpenVDB.cmake
|
||||
+++ b/cmake/FindOpenVDB.cmake
|
||||
@@ -244,7 +244,7 @@ set(OpenVDB_LIB_COMPONENTS "")
|
||||
|
@ -1012,7 +1126,7 @@ index 63a2eda..6211071 100644
|
|||
endforeach()
|
||||
|
||||
diff --git a/cmake/FindTBB.cmake b/cmake/FindTBB.cmake
|
||||
index bdf9c81..c6bdec9 100644
|
||||
index bdf9c81..06093a4 100644
|
||||
--- a/cmake/FindTBB.cmake
|
||||
+++ b/cmake/FindTBB.cmake
|
||||
@@ -1,333 +1,332 @@
|
||||
|
@ -1022,35 +1136,21 @@ index bdf9c81..c6bdec9 100644
|
|||
-# All rights reserved. This software is distributed under the
|
||||
-# Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
|
||||
+# 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
|
||||
+# of this software and associated documentation files (the "Software"), to deal
|
||||
+# in the Software without restriction, including without limitation the rights
|
||||
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
+# copies of the Software, and to permit persons to whom the Software is
|
||||
+# 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
|
||||
-# its contributors may be used to endorse or promote products derived
|
||||
-# 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
|
||||
-# "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.
|
||||
-# IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
|
||||
-# 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:
|
||||
-
|
||||
|
@ -1142,6 +1249,13 @@ index bdf9c81..c6bdec9 100644
|
|||
-if(POLICY CMP0057)
|
||||
- cmake_policy(SET CMP0057 NEW)
|
||||
-endif()
|
||||
+# FindTBB
|
||||
+# -------
|
||||
+#
|
||||
+# Find TBB include directories and libraries.
|
||||
+#
|
||||
+# Usage:
|
||||
+#
|
||||
+# find_package(TBB [major[.minor]] [EXACT]
|
||||
+# [QUIET] [REQUIRED]
|
||||
+# [[COMPONENTS] [components...]]
|
||||
|
@ -1244,12 +1358,10 @@ index bdf9c81..c6bdec9 100644
|
|||
- set(TBB_FIND_COMPONENTS ${_TBB_COMPONENT_LIST})
|
||||
-endif()
|
||||
+include(FindPackageHandleStandardArgs)
|
||||
+
|
||||
+find_package(Threads QUIET REQUIRED)
|
||||
|
||||
-# Append TBB_ROOT or $ENV{TBB_ROOT} if set (prioritize the direct cmake var)
|
||||
-set(_TBB_ROOT_SEARCH_DIR "")
|
||||
+if(NOT TBB_FOUND)
|
||||
+find_package(Threads QUIET REQUIRED)
|
||||
|
||||
-if(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})
|
||||
- if(_ENV_TBB_ROOT)
|
||||
- list(APPEND _TBB_ROOT_SEARCH_DIR ${_ENV_TBB_ROOT})
|
||||
+ ##################################
|
||||
+ # 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()
|
||||
+
|
||||
+ ##################################
|
||||
+ # 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()
|
||||
+if(NOT TBB_FOUND)
|
||||
|
||||
-# 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})
|
||||
-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
|
||||
+ if(WINDOWS_STORE)
|
||||
+ 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")
|
||||
+ endif()
|
||||
|
||||
-# ------------------------------------------------------------------------
|
||||
-# Search for TBB lib DIR
|
||||
-# ------------------------------------------------------------------------
|
||||
-# platform branching
|
||||
+ # Add the library path search suffix for the VC independent version of TBB
|
||||
+ 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)
|
||||
- list(INSERT TBB_PATH_SUFFIXES 0 lib/x86_64-linux-gnu)
|
||||
-endif()
|
||||
+ # Search for the libraries
|
||||
+ find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX}
|
||||
+ HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
|
||||
+ PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
|
||||
+ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
|
||||
+ elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
+ # OS X
|
||||
+ set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
|
||||
|
||||
-if(APPLE)
|
||||
- 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 1 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)
|
||||
- endif()
|
||||
- endif()
|
||||
+ 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()
|
||||
-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)
|
||||
- set(_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
- set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
|
||||
-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 "")
|
||||
-
|
||||
|
@ -1516,39 +1563,44 @@ index bdf9c81..c6bdec9 100644
|
|||
- # Extract the directory and apply the matched text (in brackets)
|
||||
- get_filename_component(Tbb_${COMPONENT}_DIR "${Tbb_${COMPONENT}_LIBRARY}" DIRECTORY)
|
||||
- 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()
|
||||
+ 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})
|
||||
+ ##################################
|
||||
+ # Set compile flags and libraries
|
||||
+ # Find TBB components
|
||||
+ ##################################
|
||||
|
||||
- if(Tbb_${COMPONENT}_LIBRARY)
|
||||
- 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_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})
|
||||
+ set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb)
|
||||
endif()
|
||||
-endforeach()
|
||||
|
||||
|
@ -1556,61 +1608,51 @@ index bdf9c81..c6bdec9 100644
|
|||
- set(CMAKE_FIND_LIBRARY_SUFFIXES ${_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
- unset(_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES)
|
||||
-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()
|
||||
+ if(TBB_STATIC)
|
||||
+ set(TBB_STATIC_SUFFIX "_static")
|
||||
+ endif()
|
||||
|
||||
-# ------------------------------------------------------------------------
|
||||
-# 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_BUILD_TYPE)
|
||||
+ unset(TBB_LIB_PATH_SUFFIX)
|
||||
+ unset(TBB_DEFAULT_SEARCH_DIR)
|
||||
+ unset(TBB_${_comp}_LIBRARY_DEBUG CACHE)
|
||||
+ unset(TBB_${_comp}_LIBRARY_RELEASE CACHE)
|
||||
+
|
||||
+ 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}")
|
||||
+ # Search for the libraries
|
||||
+ find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX}
|
||||
+ HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
|
||||
+ PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
|
||||
+ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
|
||||
+
|
||||
+ 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(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()
|
||||
+
|
||||
+ 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)
|
||||
-find_package_handle_standard_args(TBB
|
||||
|
@ -1646,13 +1688,82 @@ index bdf9c81..c6bdec9 100644
|
|||
- INTERFACE_COMPILE_OPTIONS "${Tbb_DEFINITIONS}"
|
||||
- INTERFACE_INCLUDE_DIRECTORIES "${Tbb_INCLUDE_DIR}"
|
||||
- )
|
||||
- endif()
|
||||
- endforeach()
|
||||
endif()
|
||||
endforeach()
|
||||
-elseif(TBB_FIND_REQUIRED)
|
||||
- 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()
|
||||
diff --git a/openvdb/CMakeLists.txt b/openvdb/CMakeLists.txt
|
||||
index 89301bd..df27aae 100644
|
||||
index 89301bd..6a3c90c 100644
|
||||
--- a/openvdb/CMakeLists.txt
|
||||
+++ b/openvdb/CMakeLists.txt
|
||||
@@ -78,7 +78,7 @@ else()
|
||||
|
@ -1664,7 +1775,21 @@ index 89301bd..df27aae 100644
|
|||
message(DEPRECATION "Support for TBB versions < ${FUTURE_MINIMUM_TBB_VERSION} "
|
||||
"is deprecated and will be removed.")
|
||||
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()
|
||||
|
||||
|
@ -1676,7 +1801,7 @@ index 89301bd..df27aae 100644
|
|||
##### Core library configuration
|
||||
|
||||
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)
|
||||
add_library(openvdb_shared SHARED ${OPENVDB_LIBRARY_SOURCE_FILES})
|
||||
|
@ -1779,5 +1904,5 @@ index df51830..0ab0c12 100644
|
|||
/// @todo This changes the compressor setting globally.
|
||||
if (blosc_set_compressor(compname) < 0) continue;
|
||||
--
|
||||
2.16.2.windows.1
|
||||
2.17.1
|
||||
|
||||
|
|
15
resources/icons/edit_gcode_f.svg
Normal 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 |
12
resources/icons/error_tick_f.svg
Normal 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 |
|
@ -7,9 +7,9 @@
|
|||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg8"
|
||||
version="1.1"
|
||||
viewBox="0 0 210 297"
|
||||
height="297mm"
|
||||
width="210mm">
|
||||
viewBox="0 0 3.7041666 3.7041667"
|
||||
height="3.7041667mm"
|
||||
width="3.7041667mm">
|
||||
<defs
|
||||
id="defs2">
|
||||
<clipPath
|
||||
|
@ -19,6 +19,20 @@
|
|||
d="M 0,800 H 800 V 0 H 0 Z"
|
||||
id="path3731" />
|
||||
</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>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
|
@ -33,111 +47,120 @@
|
|||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
transform="translate(294.04601,-97.736694)"
|
||||
id="layer1">
|
||||
<g
|
||||
id="g3721"
|
||||
transform="matrix(0.35277777,0,0,-0.35277777,-27.74952,290.88146)">
|
||||
transform="translate(-265.33929,12.851203)"
|
||||
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
|
||||
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"
|
||||
id="path3723" />
|
||||
id="path4687" />
|
||||
<path
|
||||
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"
|
||||
id="path3725" />
|
||||
id="path4689" />
|
||||
<g
|
||||
id="g3727">
|
||||
id="g4691">
|
||||
<g
|
||||
id="g3729"
|
||||
clip-path="url(#clipPath3733)">
|
||||
id="g4693"
|
||||
clip-path="url(#clipPath4697-9)">
|
||||
<g
|
||||
id="g3735"
|
||||
id="g4699"
|
||||
transform="translate(380.7793,225.1963)">
|
||||
<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"
|
||||
style="fill:#e96700;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path3737" />
|
||||
id="path4701" />
|
||||
</g>
|
||||
<g
|
||||
id="g3739"
|
||||
id="g4703"
|
||||
transform="translate(377.0244,168.7666)">
|
||||
<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"
|
||||
style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path3741" />
|
||||
id="path4705" />
|
||||
</g>
|
||||
<g
|
||||
id="g3743"
|
||||
id="g4707"
|
||||
transform="translate(421.8594,225.1963)">
|
||||
<path
|
||||
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"
|
||||
id="path3745" />
|
||||
id="path4709" />
|
||||
</g>
|
||||
<g
|
||||
id="g3747"
|
||||
id="g4711"
|
||||
transform="translate(421.8594,225.1963)">
|
||||
<path
|
||||
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"
|
||||
id="path3749" />
|
||||
id="path4713" />
|
||||
</g>
|
||||
<g
|
||||
id="g3751"
|
||||
id="g4715"
|
||||
transform="translate(333.957,198.1406)">
|
||||
<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"
|
||||
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path3753" />
|
||||
id="path4717" />
|
||||
</g>
|
||||
<g
|
||||
id="g3755"
|
||||
id="g4719"
|
||||
transform="translate(297.957,198.1406)">
|
||||
<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"
|
||||
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path3757" />
|
||||
id="path4721" />
|
||||
</g>
|
||||
<g
|
||||
id="g3759"
|
||||
id="g4723"
|
||||
transform="translate(261.957,198.1406)">
|
||||
<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"
|
||||
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path3761" />
|
||||
id="path4725" />
|
||||
</g>
|
||||
<g
|
||||
id="g3763"
|
||||
id="g4727"
|
||||
transform="translate(225.957,198.1406)">
|
||||
<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"
|
||||
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path3765" />
|
||||
id="path4729" />
|
||||
</g>
|
||||
<g
|
||||
id="g3767"
|
||||
id="g4731"
|
||||
transform="translate(189.957,198.1406)">
|
||||
<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"
|
||||
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path3769" />
|
||||
id="path4733" />
|
||||
</g>
|
||||
<g
|
||||
id="g3771"
|
||||
id="g4735"
|
||||
transform="translate(369.2207,225.1963)">
|
||||
<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"
|
||||
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path3773" />
|
||||
id="path4737" />
|
||||
</g>
|
||||
<path
|
||||
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"
|
||||
id="path3775" />
|
||||
id="path4739" />
|
||||
<path
|
||||
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"
|
||||
id="path3777" />
|
||||
id="path4741" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
|
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 6.8 KiB |
80
resources/icons/hollow.svg
Normal 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 |
25
resources/icons/hollowing.svg
Normal 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 |
13
resources/icons/mode_advanced.svg
Normal 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 |
13
resources/icons/mode_expert.svg
Normal 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 |
13
resources/icons/mode_simple.svg
Normal 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 |
18
resources/icons/pause_print_f.svg
Normal 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 |
14
resources/icons/sla_printer.svg
Normal 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 |
25
resources/icons/white/hollowing.svg
Normal 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 |
14
resources/icons/white/sla_printer.svg
Normal 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 |
|
@ -8,12 +8,15 @@ src/slic3r/GUI/ButtonsDescription.cpp
|
|||
src/slic3r/GUI/ConfigManipulation.cpp
|
||||
src/slic3r/GUI/ConfigSnapshotDialog.cpp
|
||||
src/slic3r/GUI/ConfigWizard.cpp
|
||||
src/slic3r/GUI/DoubleSlider.cpp
|
||||
src/slic3r/GUI/ExtruderSequenceDialog.cpp
|
||||
src/slic3r/GUI/Field.cpp
|
||||
src/slic3r/GUI/FirmwareDialog.cpp
|
||||
src/slic3r/GUI/GLCanvas3D.cpp
|
||||
src/slic3r/GUI/GLCanvas3DManager.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoRotate.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_ObjectSettings.cpp
|
||||
src/slic3r/GUI/GUI_Preview.cpp
|
||||
src/slic3r/GUI/Job.hpp
|
||||
src/slic3r/GUI/KBShortcutsDialog.cpp
|
||||
src/slic3r/GUI/MainFrame.cpp
|
||||
src/slic3r/GUI/Mouse3DController.cpp
|
||||
src/slic3r/GUI/MsgDialog.cpp
|
||||
src/slic3r/GUI/ObjectDataViewModel.cpp
|
||||
src/slic3r/GUI/OptionsGroup.cpp
|
||||
src/slic3r/GUI/Plater.cpp
|
||||
src/slic3r/GUI/Preferences.cpp
|
||||
|
@ -46,21 +51,23 @@ src/slic3r/GUI/Tab.hpp
|
|||
src/slic3r/GUI/UpdateDialogs.cpp
|
||||
src/slic3r/GUI/WipeTowerDialog.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/PresetUpdater.cpp
|
||||
src/slic3r/Utils/Duet.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/GCode.cpp
|
||||
src/libslic3r/ExtrusionEntity.cpp
|
||||
src/libslic3r/Flow.cpp
|
||||
src/libslic3r/Format/3mf.cpp
|
||||
src/libslic3r/Format/AMF.cpp
|
||||
src/libslic3r/Print.cpp
|
||||
src/libslic3r/SLAPrint.cpp
|
||||
src/libslic3r/SLAPrintSteps.cpp
|
||||
src/libslic3r/PrintBase.cpp
|
||||
src/libslic3r/PrintConfig.cpp
|
||||
src/libslic3r/PrintObject.cpp
|
||||
|
|
BIN
resources/localization/nl/PrusaSlicer.mo
Normal file
9960
resources/localization/nl/PrusaSlicer_nl.po
Normal file
8
resources/profiles/Creality.idx
Normal 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
|
|
@ -5,7 +5,7 @@
|
|||
name = Creality
|
||||
# Configuration version of this file. Config file will only be installed, if the config_version differs.
|
||||
# This means, the server may force the PrusaSlicer configuration to be downgraded.
|
||||
config_version = 0.0.1
|
||||
config_version = 0.0.2-alpha1
|
||||
# Where to get the updates from?
|
||||
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%
|
||||
|
@ -20,7 +20,7 @@ variants = 0.4
|
|||
technology = FFF
|
||||
bed_model = ender3_bed.stl
|
||||
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
|
||||
# not make it into the user interface.
|
||||
|
|
|
@ -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
|
||||
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
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
name = Prusa Research
|
||||
# Configuration version of this file. Config file will only be installed, if the config_version differs.
|
||||
# This means, the server may force the PrusaSlicer configuration to be downgraded.
|
||||
config_version = 1.1.1-alpha2
|
||||
config_version = 1.1.1-alpha4
|
||||
# Where to get the updates from?
|
||||
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%
|
||||
|
@ -24,7 +24,7 @@ technology = FFF
|
|||
family = MINI
|
||||
bed_model = mini_bed.stl
|
||||
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]
|
||||
name = Original Prusa i3 MK3S
|
||||
|
@ -33,7 +33,7 @@ technology = FFF
|
|||
family = MK3
|
||||
bed_model = mk3_bed.stl
|
||||
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]
|
||||
name = Original Prusa i3 MK3
|
||||
|
@ -42,7 +42,7 @@ technology = FFF
|
|||
family = MK3
|
||||
bed_model = mk3_bed.stl
|
||||
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]
|
||||
name = Original Prusa i3 MK3S MMU2S
|
||||
|
@ -51,7 +51,7 @@ technology = FFF
|
|||
family = MK3
|
||||
bed_model = mk3_bed.stl
|
||||
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]
|
||||
name = Original Prusa i3 MK3 MMU2
|
||||
|
@ -60,7 +60,7 @@ technology = FFF
|
|||
family = MK3
|
||||
bed_model = mk3_bed.stl
|
||||
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]
|
||||
name = Original Prusa i3 MK2.5S
|
||||
|
@ -69,7 +69,7 @@ technology = FFF
|
|||
family = MK2.5
|
||||
bed_model = mk3_bed.stl
|
||||
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]
|
||||
name = Original Prusa i3 MK2.5
|
||||
|
@ -78,7 +78,7 @@ technology = FFF
|
|||
family = MK2.5
|
||||
bed_model = mk3_bed.stl
|
||||
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]
|
||||
name = Original Prusa i3 MK2.5S MMU2S
|
||||
|
@ -87,7 +87,7 @@ technology = FFF
|
|||
family = MK2.5
|
||||
bed_model = mk3_bed.stl
|
||||
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]
|
||||
name = Original Prusa i3 MK2.5 MMU2
|
||||
|
@ -96,7 +96,7 @@ technology = FFF
|
|||
family = MK2.5
|
||||
bed_model = mk3_bed.stl
|
||||
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]
|
||||
name = Original Prusa i3 MK2S
|
||||
|
@ -105,7 +105,7 @@ technology = FFF
|
|||
family = MK2
|
||||
bed_model = mk2_bed.stl
|
||||
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]
|
||||
name = Original Prusa i3 MK2S MMU1
|
||||
|
@ -114,7 +114,7 @@ technology = FFF
|
|||
family = MK2
|
||||
bed_model = mk2_bed.stl
|
||||
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]
|
||||
name = Original Prusa SL1
|
||||
|
@ -123,7 +123,7 @@ technology = SLA
|
|||
family = SL1
|
||||
bed_model = sl1_bed.stl
|
||||
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
|
||||
# not make it into the user interface.
|
||||
|
@ -226,6 +226,8 @@ wipe_tower_width = 60
|
|||
wipe_tower_x = 170
|
||||
wipe_tower_y = 140
|
||||
xy_size_compensation = 0
|
||||
top_solid_min_thickness = 0.6
|
||||
bottom_solid_min_thickness = 0.5
|
||||
|
||||
[print:*MK3*]
|
||||
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
|
||||
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
|
||||
solid_infill_extrusion_width = 0.7
|
||||
solid_infill_extrusion_width = 0.65
|
||||
top_infill_extrusion_width = 0.6
|
||||
support_material_extrusion_width = 0.55
|
||||
bridge_flow_ratio = 0.95
|
||||
|
@ -555,7 +557,7 @@ inherits = *0.10mm*; *MK3*
|
|||
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
|
||||
external_perimeter_speed = 25
|
||||
infill_acceleration = 1250
|
||||
infill_acceleration = 1000
|
||||
infill_speed = 80
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
|
@ -655,7 +657,7 @@ inherits = *0.15mm*; *MK3*
|
|||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
|
||||
external_perimeter_speed = 25
|
||||
infill_acceleration = 1250
|
||||
infill_acceleration = 1000
|
||||
infill_speed = 80
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
|
@ -670,7 +672,7 @@ inherits = *0.15mm*; *MK3*
|
|||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
|
||||
external_perimeter_speed = 35
|
||||
infill_acceleration = 1250
|
||||
infill_acceleration = 1000
|
||||
infill_speed = 200
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 60
|
||||
|
@ -735,7 +737,7 @@ inherits = *0.15mm*; *0.6nozzleMK3*; *MK306*
|
|||
# alias = 0.15mm DETAIL
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
|
||||
external_perimeter_speed = 35
|
||||
infill_acceleration = 1250
|
||||
infill_acceleration = 1000
|
||||
infill_speed = 70
|
||||
max_print_speed = 100
|
||||
perimeter_speed = 45
|
||||
|
@ -780,7 +782,7 @@ inherits = *0.20mm*; *MK3*
|
|||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
|
||||
external_perimeter_speed = 25
|
||||
infill_acceleration = 1250
|
||||
infill_acceleration = 1000
|
||||
infill_speed = 80
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
|
@ -795,7 +797,7 @@ inherits = *0.20mm*; *MK3*
|
|||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
|
||||
external_perimeter_speed = 35
|
||||
infill_acceleration = 1250
|
||||
infill_acceleration = 1000
|
||||
infill_speed = 200
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 60
|
||||
|
@ -860,7 +862,7 @@ inherits = *0.20mm*; *0.6nozzleMK3*; *MK306*
|
|||
# alias = 0.20mm DETAIL
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
|
||||
external_perimeter_speed = 35
|
||||
infill_acceleration = 1250
|
||||
infill_acceleration = 1000
|
||||
infill_speed = 70
|
||||
max_print_speed = 100
|
||||
perimeter_speed = 45
|
||||
|
@ -905,7 +907,7 @@ inherits = *0.30mm*; *0.6nozzleMK3*; *MK306*
|
|||
# alias = 0.30mm QUALITY
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
|
||||
external_perimeter_speed = 35
|
||||
infill_acceleration = 1250
|
||||
infill_acceleration = 1000
|
||||
infill_speed = 70
|
||||
max_print_speed = 100
|
||||
perimeter_speed = 45
|
||||
|
@ -942,7 +944,7 @@ bottom_solid_layers = 3
|
|||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
|
||||
external_perimeter_speed = 35
|
||||
infill_acceleration = 1250
|
||||
infill_acceleration = 1000
|
||||
infill_speed = 85
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 50
|
||||
|
@ -1027,7 +1029,7 @@ inherits = *0.35mm*; *0.6nozzleMK3*; *MK306*
|
|||
# alias = 0.35mm SPEED
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
|
||||
external_perimeter_speed = 35
|
||||
infill_acceleration = 1250
|
||||
infill_acceleration = 1000
|
||||
infill_speed = 70
|
||||
max_print_speed = 100
|
||||
perimeter_speed = 45
|
||||
|
@ -1035,6 +1037,8 @@ solid_infill_speed = 70
|
|||
top_solid_infill_speed = 45
|
||||
external_perimeter_extrusion_width = 0.68
|
||||
perimeter_extrusion_width = 0.68
|
||||
infill_extrusion_width = 0.68
|
||||
solid_infill_extrusion_width = 0.68
|
||||
|
||||
# XXXXXXXXXXXXXXXXXXXX
|
||||
# XXX--- 0.40mm ---XXX
|
||||
|
@ -1063,7 +1067,7 @@ inherits = *0.40mm*; *0.6nozzleMK3*; *MK306*
|
|||
# alias = 0.40mm DRAFT
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
|
||||
external_perimeter_speed = 35
|
||||
infill_acceleration = 1250
|
||||
infill_acceleration = 1000
|
||||
infill_speed = 70
|
||||
max_print_speed = 100
|
||||
perimeter_speed = 45
|
||||
|
@ -1185,6 +1189,7 @@ top_infill_extrusion_width = 0.4
|
|||
small_perimeter_speed = 15
|
||||
perimeter_extrusion_width = 0.4
|
||||
external_perimeter_extrusion_width = 0.4
|
||||
support_material_xy_spacing = 60%
|
||||
|
||||
[print:0.07mm ULTRADETAIL @MINI]
|
||||
inherits = *0.07mm*; *MINI*
|
||||
|
@ -1196,6 +1201,7 @@ top_infill_extrusion_width = 0.4
|
|||
small_perimeter_speed = 15
|
||||
perimeter_extrusion_width = 0.4
|
||||
external_perimeter_extrusion_width = 0.4
|
||||
support_material_xy_spacing = 60%
|
||||
|
||||
[print:0.10mm DETAIL @MINI]
|
||||
inherits = *0.10mm*; *MINI*
|
||||
|
@ -1212,6 +1218,7 @@ fill_pattern = gyroid
|
|||
fill_density = 15%
|
||||
perimeters = 3
|
||||
bridge_acceleration = 1000
|
||||
support_material_xy_spacing = 60%
|
||||
|
||||
[print:0.15mm QUALITY @MINI]
|
||||
inherits = *0.15mm*; *MINI*
|
||||
|
@ -1226,6 +1233,7 @@ top_solid_infill_speed = 40
|
|||
fill_pattern = gyroid
|
||||
fill_density = 15%
|
||||
bridge_flow_ratio = 0.85
|
||||
support_material_xy_spacing = 60%
|
||||
|
||||
[print:0.15mm SPEED @MINI]
|
||||
inherits = *0.15mm*; *MINI*
|
||||
|
@ -1238,6 +1246,7 @@ infill_speed = 140
|
|||
solid_infill_speed = 140
|
||||
top_solid_infill_speed = 40
|
||||
bridge_flow_ratio = 0.85
|
||||
support_material_xy_spacing = 60%
|
||||
|
||||
[print:0.20mm QUALITY @MINI]
|
||||
inherits = *0.20mm*; *MINI*
|
||||
|
@ -1251,6 +1260,7 @@ solid_infill_speed = 80
|
|||
top_solid_infill_speed = 40
|
||||
fill_pattern = gyroid
|
||||
fill_density = 15%
|
||||
support_material_xy_spacing = 60%
|
||||
|
||||
[print:0.20mm SPEED @MINI]
|
||||
inherits = *0.20mm*; *MINI*
|
||||
|
@ -1263,6 +1273,7 @@ infill_speed = 140
|
|||
max_print_speed = 150
|
||||
solid_infill_speed = 140
|
||||
top_solid_infill_speed = 40
|
||||
support_material_xy_spacing = 60%
|
||||
|
||||
[print:0.25mm DRAFT @MINI]
|
||||
inherits = *0.25mm*; *MINI*
|
||||
|
@ -1279,6 +1290,7 @@ first_layer_extrusion_width = 0.42
|
|||
infill_extrusion_width = 0.45
|
||||
solid_infill_extrusion_width = 0.45
|
||||
top_infill_extrusion_width = 0.4
|
||||
support_material_xy_spacing = 60%
|
||||
|
||||
# 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)
|
||||
cooling = 0
|
||||
disable_fan_first_layers = 3
|
||||
extrusion_multiplier = 1.2
|
||||
extrusion_multiplier = 1.15
|
||||
fan_always_on = 0
|
||||
fan_below_layer_time = 100
|
||||
filament_colour = #008000
|
||||
|
@ -1626,7 +1638,7 @@ filament_density = 1.24
|
|||
inherits = *PLA*
|
||||
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)
|
||||
extrusion_multiplier = 1.2
|
||||
extrusion_multiplier = 1.1
|
||||
filament_cost = 58.30
|
||||
filament_density = 1.15
|
||||
filament_colour = #dfc287
|
||||
|
@ -1634,12 +1646,13 @@ filament_max_volumetric_speed = 9
|
|||
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"
|
||||
temperature = 200
|
||||
filament_retract_lift = 0.2
|
||||
|
||||
[filament:ColorFabb corkFill]
|
||||
inherits = *PLA*
|
||||
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)
|
||||
extrusion_multiplier = 1.2
|
||||
extrusion_multiplier = 1.1
|
||||
filament_cost = 58.30
|
||||
filament_density = 1.18
|
||||
filament_colour = #634d33
|
||||
|
@ -1647,6 +1660,7 @@ filament_max_volumetric_speed = 6
|
|||
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:ColorFabb XT]
|
||||
inherits = *PET*
|
||||
|
@ -1788,7 +1802,7 @@ temperature = 275
|
|||
inherits = *PLA*
|
||||
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)
|
||||
extrusion_multiplier = 1.2
|
||||
extrusion_multiplier = 1.1
|
||||
filament_cost = 68
|
||||
filament_density = 1.15
|
||||
filament_colour = #804040
|
||||
|
@ -1796,6 +1810,21 @@ filament_max_volumetric_speed = 10
|
|||
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"
|
||||
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]
|
||||
inherits = *ABS*
|
||||
|
@ -1855,7 +1884,7 @@ inherits = *FLEX*
|
|||
filament_vendor = SainSmart
|
||||
fan_always_on = 1
|
||||
filament_max_volumetric_speed = 2.5
|
||||
extrusion_multiplier = 1.15
|
||||
extrusion_multiplier = 1.1
|
||||
first_layer_temperature = 230
|
||||
first_layer_bed_temperature = 50
|
||||
temperature = 230
|
||||
|
@ -1881,7 +1910,7 @@ inherits = *FLEX*
|
|||
filament_vendor = Filatech
|
||||
fan_always_on = 1
|
||||
filament_max_volumetric_speed = 2.5
|
||||
extrusion_multiplier = 1.15
|
||||
extrusion_multiplier = 1.1
|
||||
first_layer_temperature = 230
|
||||
first_layer_bed_temperature = 50
|
||||
temperature = 230
|
||||
|
@ -1914,6 +1943,7 @@ first_layer_bed_temperature = 100
|
|||
first_layer_temperature = 270
|
||||
temperature = 270
|
||||
bridge_fan_speed = 0
|
||||
filament_max_volumetric_speed = 8
|
||||
|
||||
[filament:PrimaSelect PVA+]
|
||||
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"
|
||||
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]
|
||||
inherits = *PET*
|
||||
filament_vendor = Made for Prusa
|
||||
|
@ -2753,6 +2802,7 @@ filament_density = 1.07
|
|||
inherits = Polymaker PC-Max; *ABSMINI*
|
||||
# alias = Polymaker PC-Max
|
||||
filament_type = PC
|
||||
filament_max_volumetric_speed = 7
|
||||
bed_temperature = 100
|
||||
filament_colour = #3A80CA
|
||||
first_layer_bed_temperature = 100
|
||||
|
@ -2777,7 +2827,7 @@ filament_cost = 27.3
|
|||
filament_density = 1.04
|
||||
bridge_fan_speed = 50
|
||||
cooling = 1
|
||||
extrusion_multiplier = 0.9
|
||||
extrusion_multiplier = 1
|
||||
fan_always_on = 1
|
||||
fan_below_layer_time = 10
|
||||
filament_colour = #FFFFD7
|
||||
|
@ -2963,6 +3013,13 @@ initial_exposure_time = 30
|
|||
material_type = Tough
|
||||
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]
|
||||
inherits = *common 0.025*
|
||||
exposure_time = 15
|
||||
|
@ -3323,6 +3380,13 @@ initial_exposure_time = 30
|
|||
material_type = Tough
|
||||
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]
|
||||
inherits = *common 0.05*
|
||||
exposure_time = 9
|
||||
|
@ -3835,18 +3899,21 @@ min_layer_height = 0.1
|
|||
inherits = Original Prusa i3 MK2S
|
||||
printer_model = MK2.5
|
||||
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
|
||||
|
||||
[printer:Original Prusa i3 MK2.5 0.25 nozzle]
|
||||
inherits = Original Prusa i3 MK2S 0.25 nozzle
|
||||
printer_model = MK2.5
|
||||
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
|
||||
|
||||
[printer:Original Prusa i3 MK2.5 0.6 nozzle]
|
||||
inherits = Original Prusa i3 MK2S 0.6 nozzle
|
||||
printer_model = MK2.5
|
||||
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
|
||||
|
||||
[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_y = 500
|
||||
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_y = 10
|
||||
machine_max_jerk_z = 0.2
|
||||
|
@ -3904,7 +3971,7 @@ machine_max_feedrate_e = 120
|
|||
machine_max_feedrate_x = 500
|
||||
machine_max_feedrate_y = 500
|
||||
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_y = 10
|
||||
machine_max_jerk_z = 0.2
|
||||
|
@ -3954,7 +4021,7 @@ machine_max_feedrate_e = 120
|
|||
machine_max_feedrate_x = 500
|
||||
machine_max_feedrate_y = 500
|
||||
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_y = 10
|
||||
machine_max_jerk_z = 0.2
|
||||
|
@ -4003,7 +4070,7 @@ machine_max_feedrate_e = 120
|
|||
machine_max_feedrate_x = 500
|
||||
machine_max_feedrate_y = 500
|
||||
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_y = 10
|
||||
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_y = 200,100
|
||||
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_y = 8,8
|
||||
machine_max_jerk_z = 0.4,0.4
|
||||
|
@ -4261,7 +4328,7 @@ retract_lift_below = 179
|
|||
retract_layer_change = 0
|
||||
silent_mode = 0
|
||||
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
|
||||
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 =
|
||||
|
@ -4280,7 +4347,7 @@ deretract_speed = 40
|
|||
wipe = 1
|
||||
retract_before_wipe = 70%
|
||||
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]
|
||||
inherits = Original Prusa MINI
|
||||
|
@ -4295,7 +4362,7 @@ retract_speed = 70
|
|||
deretract_speed = 40
|
||||
wipe = 1
|
||||
retract_before_wipe = 70%
|
||||
retract_before_travel = 1
|
||||
retract_before_travel = 1.5
|
||||
|
||||
[printer:Original Prusa SL1]
|
||||
printer_technology = SLA
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#add_subdirectory(slasupporttree)
|
||||
#add_subdirectory(openvdb)
|
||||
add_subdirectory(meshboolean)
|
||||
add_subdirectory(opencsg)
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
if (SLIC3R_STATIC)
|
||||
set(CGAL_Boost_USE_STATIC_LIBS ON)
|
||||
endif ()
|
||||
|
||||
find_package(CGAL REQUIRED)
|
||||
|
||||
add_executable(meshboolean MeshBoolean.cpp)
|
||||
|
||||
target_link_libraries(meshboolean libslic3r CGAL::CGAL)
|
||||
target_link_libraries(meshboolean libslic3r)
|
||||
|
||||
if (WIN32)
|
||||
prusaslicer_copy_dlls(meshboolean)
|
||||
|
|
|
@ -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 <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>
|
||||
|
||||
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[])
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
|
29
sandboxes/opencsg/CMakeLists.txt
Normal 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
|
||||
)
|
498
sandboxes/opencsg/Engine.cpp
Normal 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
|
493
sandboxes/opencsg/Engine.hpp
Normal 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
|
68
sandboxes/opencsg/ShaderCSGDisplay.cpp
Normal 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
|
27
sandboxes/opencsg/ShaderCSGDisplay.hpp
Normal 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
|
@ -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() {}
|
|
@ -1,2 +1,7 @@
|
|||
add_executable(openvdb_example openvdb_example.cpp)
|
||||
target_link_libraries(openvdb_example libslic3r)
|
||||
if(TARGET OpenVDB::openvdb)
|
||||
add_executable(openvdb_example openvdb_example.cpp)
|
||||
|
||||
target_link_libraries(openvdb_example libslic3r)
|
||||
target_link_libraries(openvdb_example OpenVDB::openvdb)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -162,9 +162,25 @@ int CLI::run(int argc, char **argv)
|
|||
|
||||
// Initialize full print configs for both the FFF and SLA technologies.
|
||||
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);
|
||||
// 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.
|
||||
bool user_center_specified = false;
|
||||
|
@ -591,7 +607,7 @@ bool CLI::setup(int argc, char **argv)
|
|||
// 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 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"));
|
||||
|
||||
|
@ -641,18 +657,10 @@ bool CLI::export_models(IO::ExportFormat format)
|
|||
const std::string path = this->output_filepath(model, format);
|
||||
bool success = false;
|
||||
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;
|
||||
#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::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;
|
||||
#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;
|
||||
}
|
||||
if (success)
|
||||
|
|
|
@ -635,13 +635,30 @@ inline bool igl::copyleft::cgal::SelfIntersectMesh<
|
|||
{
|
||||
using namespace std;
|
||||
|
||||
// must be co-planar
|
||||
if(
|
||||
A.supporting_plane() != B.supporting_plane() &&
|
||||
A.supporting_plane() != B.supporting_plane().opposite())
|
||||
{
|
||||
return false;
|
||||
auto opposite_vertex = [](const Index a0, const Index a1) {
|
||||
// get opposite index of A
|
||||
int a2=-1;
|
||||
for(int c=0;c<3;++c)
|
||||
if(c!=a0 && c!=a1) {
|
||||
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
|
||||
// (triangle). Either
|
||||
// - 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
|
||||
// (intersects) triangle B
|
||||
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
|
||||
{
|
||||
// get opposite index
|
||||
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;
|
||||
return CGAL::do_intersect(A.vertex(a2),B);
|
||||
};
|
||||
|
||||
// Determine if edge opposite vertex va in triangle A intersects edge
|
||||
|
@ -681,8 +686,8 @@ inline bool igl::copyleft::cgal::SelfIntersectMesh<
|
|||
};
|
||||
|
||||
if(
|
||||
!opposite_point_inside(A,shared[0].first,shared[1].first,B) &&
|
||||
!opposite_point_inside(B,shared[0].second,shared[1].second,A) &&
|
||||
!opposite_point_inside(A,a2,B) &&
|
||||
!opposite_point_inside(B,b2,A) &&
|
||||
!opposite_edges_intersect(A,shared[0].first,B,shared[1].second) &&
|
||||
!opposite_edges_intersect(A,shared[1].first,B,shared[0].second))
|
||||
{
|
||||
|
|
|
@ -9,6 +9,11 @@ if (MINGW)
|
|||
add_compile_options(-Wa,-mbig-obj)
|
||||
endif ()
|
||||
|
||||
set(OpenVDBUtils_SOURCES "")
|
||||
if (TARGET OpenVDB::openvdb)
|
||||
set(OpenVDBUtils_SOURCES OpenVDBUtils.cpp OpenVDBUtils.hpp)
|
||||
endif()
|
||||
|
||||
add_library(libslic3r STATIC
|
||||
pchheader.cpp
|
||||
pchheader.hpp
|
||||
|
@ -116,6 +121,8 @@ add_library(libslic3r STATIC
|
|||
Line.hpp
|
||||
Model.cpp
|
||||
Model.hpp
|
||||
CustomGCode.cpp
|
||||
CustomGCode.hpp
|
||||
Arrange.hpp
|
||||
Arrange.cpp
|
||||
MotionPlanner.cpp
|
||||
|
@ -149,9 +156,9 @@ add_library(libslic3r STATIC
|
|||
ShortestPath.cpp
|
||||
ShortestPath.hpp
|
||||
SLAPrint.cpp
|
||||
SLAPrintSteps.cpp
|
||||
SLAPrintSteps.hpp
|
||||
SLAPrint.hpp
|
||||
SLA/SLAAutoSupports.hpp
|
||||
SLA/SLAAutoSupports.cpp
|
||||
Slicing.cpp
|
||||
Slicing.hpp
|
||||
SlicingAdaptive.cpp
|
||||
|
@ -180,35 +187,80 @@ add_library(libslic3r STATIC
|
|||
MinAreaBoundingBox.cpp
|
||||
miniz_extension.hpp
|
||||
miniz_extension.cpp
|
||||
SLA/SLACommon.hpp
|
||||
SLA/SLABoilerPlate.hpp
|
||||
SLA/SLAPad.hpp
|
||||
SLA/SLAPad.cpp
|
||||
SLA/SLASupportTreeBuilder.hpp
|
||||
SLA/SLASupportTreeBuildsteps.hpp
|
||||
SLA/SLASupportTreeBuildsteps.cpp
|
||||
SLA/SLASupportTreeBuilder.cpp
|
||||
SLA/SLAConcurrency.hpp
|
||||
SLA/SLASupportTree.hpp
|
||||
SLA/SLASupportTree.cpp
|
||||
SLA/SLASupportTreeIGL.cpp
|
||||
SLA/SLARotfinder.hpp
|
||||
SLA/SLARotfinder.cpp
|
||||
SLA/SLABoostAdapter.hpp
|
||||
SLA/SLASpatIndex.hpp
|
||||
SLA/SLARaster.hpp
|
||||
SLA/SLARaster.cpp
|
||||
SLA/SLARasterWriter.hpp
|
||||
SLA/SLARasterWriter.cpp
|
||||
SimplifyMesh.hpp
|
||||
SimplifyMeshImpl.hpp
|
||||
SimplifyMesh.cpp
|
||||
${OpenVDBUtils_SOURCES}
|
||||
SLA/Common.hpp
|
||||
SLA/Common.cpp
|
||||
SLA/Pad.hpp
|
||||
SLA/Pad.cpp
|
||||
SLA/SupportTreeBuilder.hpp
|
||||
SLA/SupportTreeBuildsteps.hpp
|
||||
SLA/SupportTreeBuildsteps.cpp
|
||||
SLA/SupportTreeBuilder.cpp
|
||||
SLA/Concurrency.hpp
|
||||
SLA/SupportTree.hpp
|
||||
SLA/SupportTree.cpp
|
||||
# SLA/SupportTreeIGL.cpp
|
||||
SLA/Rotfinder.hpp
|
||||
SLA/Rotfinder.cpp
|
||||
SLA/BoostAdapter.hpp
|
||||
SLA/SpatIndex.hpp
|
||||
SLA/Raster.hpp
|
||||
SLA/Raster.cpp
|
||||
SLA/RasterWriter.hpp
|
||||
SLA/RasterWriter.cpp
|
||||
SLA/ConcaveHull.hpp
|
||||
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_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||
add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE)
|
||||
if (SLIC3R_STATIC)
|
||||
set(CGAL_Boost_USE_STATIC_LIBS ON CACHE BOOL "" FORCE)
|
||||
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_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNEST2D_INCLUDES} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
@ -228,10 +280,14 @@ target_link_libraries(libslic3r
|
|||
qhull
|
||||
semver
|
||||
TBB::tbb
|
||||
# OpenVDB::openvdb
|
||||
libslic3r_cgal
|
||||
${CMAKE_DL_LIBS}
|
||||
)
|
||||
|
||||
if (TARGET OpenVDB::openvdb)
|
||||
target_link_libraries(libslic3r OpenVDB::openvdb)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(libslic3r Psapi.lib)
|
||||
endif()
|
||||
|
@ -239,3 +295,7 @@ endif()
|
|||
if(SLIC3R_PROFILE)
|
||||
target_link_libraries(slic3r Shiny)
|
||||
endif()
|
||||
|
||||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||
add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE)
|
||||
endif ()
|
||||
|
|
|
@ -757,6 +757,12 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
|
|||
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)
|
||||
{
|
||||
std::vector<const char*> args;
|
||||
|
|
|
@ -1494,8 +1494,49 @@ protected:
|
|||
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.
|
||||
class ConfigBase
|
||||
class ConfigBase : public ConfigOptionResolver
|
||||
{
|
||||
public:
|
||||
// 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.
|
||||
|
||||
ConfigBase() {}
|
||||
virtual ~ConfigBase() {}
|
||||
~ConfigBase() override {}
|
||||
|
||||
// Virtual overridables:
|
||||
public:
|
||||
|
@ -1513,6 +1554,7 @@ public:
|
|||
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.
|
||||
virtual t_config_option_keys keys() const = 0;
|
||||
|
||||
protected:
|
||||
// Verify whether the opt_key has not been obsoleted or renamed.
|
||||
// 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 {}
|
||||
|
||||
public:
|
||||
using ConfigOptionResolver::option;
|
||||
using ConfigOptionResolver::option_throw;
|
||||
|
||||
// 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)
|
||||
{ return this->optptr(opt_key, create); }
|
||||
|
||||
|
@ -1537,10 +1577,6 @@ public:
|
|||
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 *opt = this->optptr(opt_key, create);
|
||||
|
@ -1549,9 +1585,6 @@ public:
|
|||
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>
|
||||
TYPE* option_throw(const t_config_option_key &opt_key, bool create = false)
|
||||
{
|
||||
|
@ -1561,10 +1594,6 @@ public:
|
|||
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.
|
||||
// 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.
|
||||
|
@ -1735,6 +1764,8 @@ public:
|
|||
{ return dynamic_cast<T*>(this->option(opt_key, create)); }
|
||||
template<class T> const T* opt(const t_config_option_key &opt_key) const
|
||||
{ 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.
|
||||
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.
|
||||
|
|
72
src/libslic3r/CustomGCode.cpp
Normal 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
|
86
src/libslic3r/CustomGCode.hpp
Normal 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_ */
|
|
@ -657,4 +657,23 @@ bool remove_sticks(ExPolygon &poly)
|
|||
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
|
||||
|
|
|
@ -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 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 ExPolygon &ex);
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
#include "Flow.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "Print.hpp"
|
||||
#include <cmath>
|
||||
#include <assert.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
// Mark string for localization and translate.
|
||||
#define L(s) Slic3r::I18N::translate(s)
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// 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) {
|
||||
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
|
||||
// and other context properties.
|
||||
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;
|
||||
} else if (! width.percent && width.value == 0.) {
|
||||
// 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 {
|
||||
// If user set a manual value, use it.
|
||||
w = float(width.get_abs_value(height));
|
||||
|
|
|
@ -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
|
||||
// to fit a region with integer number of lines.
|
||||
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);
|
||||
|
|
|
@ -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_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config_ranges.xml";
|
||||
const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
|
||||
const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt";
|
||||
const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml";
|
||||
|
||||
const char* MODEL_TAG = "model";
|
||||
|
@ -385,6 +386,7 @@ namespace Slic3r {
|
|||
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
|
||||
typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap;
|
||||
typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
|
||||
typedef std::map<int, std::vector<sla::DrainHole>> IdToSlaDrainHolesMap;
|
||||
|
||||
// Version of the 3mf file
|
||||
unsigned int m_version;
|
||||
|
@ -403,6 +405,7 @@ namespace Slic3r {
|
|||
IdToLayerHeightsProfileMap m_layer_heights_profiles;
|
||||
IdToLayerConfigRangesMap m_layer_config_ranges;
|
||||
IdToSlaSupportPointsMap m_sla_support_points;
|
||||
IdToSlaDrainHolesMap m_sla_drain_holes;
|
||||
std::string m_curr_metadata_name;
|
||||
std::string m_curr_characters;
|
||||
std::string m_name;
|
||||
|
@ -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_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_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);
|
||||
|
||||
|
@ -629,6 +633,11 @@ namespace Slic3r {
|
|||
// extract sla support points file
|
||||
_extract_sla_support_points_from_archive(archive, stat);
|
||||
}
|
||||
else if (boost::algorithm::iequals(name, SLA_DRAIN_HOLES_FILE))
|
||||
{
|
||||
// extract sla support points file
|
||||
_extract_sla_drain_holes_from_archive(archive, stat);
|
||||
}
|
||||
else if (boost::algorithm::iequals(name, PRINT_CONFIG_FILE))
|
||||
{
|
||||
// extract slic3r print config file
|
||||
|
@ -684,6 +693,11 @@ namespace Slic3r {
|
|||
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);
|
||||
if (obj_metadata != m_objects_metadata.end())
|
||||
{
|
||||
|
@ -955,8 +969,9 @@ namespace Slic3r {
|
|||
|
||||
// Info on format versioning - see 3mf.hpp
|
||||
int version = 0;
|
||||
if (!objects.empty() && objects[0].find("support_points_format_version=") != std::string::npos) {
|
||||
objects[0].erase(objects[0].begin(), objects[0].begin() + 30); // removes the string
|
||||
std::string key("support_points_format_version=");
|
||||
if (!objects.empty() && objects[0].find(key) != std::string::npos) {
|
||||
objects[0].erase(objects[0].begin(), objects[0].begin() + long(key.size())); // removes the string
|
||||
version = std::stoi(objects[0]);
|
||||
objects.erase(objects.begin()); // pop the header
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -1092,6 +1191,14 @@ namespace Slic3r {
|
|||
|
||||
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")
|
||||
continue;
|
||||
pt::ptree tree = code.second;
|
||||
|
@ -1100,7 +1207,7 @@ namespace Slic3r {
|
|||
int extruder = tree.get<int> ("<xmlattr>.extruder" );
|
||||
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::map<int, ObjectData> IdToObjectDataMap;
|
||||
|
||||
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
|
||||
bool m_fullpath_sources{ true };
|
||||
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
|
||||
|
||||
public:
|
||||
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
|
||||
#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);
|
||||
#else
|
||||
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources);
|
||||
#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:
|
||||
#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_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config);
|
||||
bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data);
|
||||
bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
};
|
||||
|
||||
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
|
||||
#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)
|
||||
{
|
||||
|
@ -1916,21 +2013,6 @@ namespace Slic3r {
|
|||
return _save_model_to_file(filename, model, config);
|
||||
}
|
||||
#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
|
||||
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;
|
||||
}
|
||||
|
||||
if (!_add_sla_drain_holes_file_to_archive(archive, model))
|
||||
{
|
||||
close_zip_writer(&archive);
|
||||
boost::filesystem::remove(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml").
|
||||
// All custom gcode per height of whole Model are stored here
|
||||
if (!_add_custom_gcode_per_print_z_file_to_archive(archive, model))
|
||||
|
@ -2475,6 +2565,50 @@ namespace Slic3r {
|
|||
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)
|
||||
{
|
||||
char buffer[1024];
|
||||
|
@ -2565,12 +2699,8 @@ namespace Slic3r {
|
|||
// stores volume's source data
|
||||
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());
|
||||
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_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";
|
||||
|
@ -2615,7 +2745,7 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
|
|||
pt::ptree tree;
|
||||
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", "");
|
||||
// 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 );
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
@ -2659,37 +2795,21 @@ bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool c
|
|||
return res;
|
||||
}
|
||||
|
||||
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data)
|
||||
#else
|
||||
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources)
|
||||
#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))
|
||||
return false;
|
||||
|
||||
_3MF_Exporter exporter;
|
||||
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources, thumbnail_data);
|
||||
#else
|
||||
bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources);
|
||||
#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)
|
||||
exporter.log_errors();
|
||||
|
|
|
@ -20,6 +20,10 @@ namespace Slic3r {
|
|||
support_points_format_version = 1
|
||||
};
|
||||
|
||||
enum {
|
||||
drain_holes_format_version = 1
|
||||
};
|
||||
|
||||
class Model;
|
||||
class DynamicPrintConfig;
|
||||
#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.
|
||||
// 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
|
||||
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr);
|
||||
#else
|
||||
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources);
|
||||
#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
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "../Utils.hpp"
|
||||
#include "../I18N.hpp"
|
||||
#include "../Geometry.hpp"
|
||||
#include "../CustomGCode.hpp"
|
||||
|
||||
#include "AMF.hpp"
|
||||
|
||||
|
@ -156,6 +157,7 @@ struct AMFParserContext
|
|||
NODE_TYPE_PRINTABLE, // amf/constellation/instance/mirrorz
|
||||
NODE_TYPE_CUSTOM_GCODE, // amf/custom_code_per_height
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -308,13 +310,19 @@ void AMFParserContext::startElement(const char *name, const char **atts)
|
|||
else
|
||||
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;
|
||||
m_value[0] = get_attribute(atts, "height");
|
||||
m_value[0] = get_attribute(atts, "print_z");
|
||||
m_value[1] = get_attribute(atts, "gcode");
|
||||
m_value[2] = get_attribute(atts, "extruder");
|
||||
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;
|
||||
case 3:
|
||||
if (m_path[2] == NODE_TYPE_MESH) {
|
||||
|
@ -632,18 +640,29 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
break;
|
||||
|
||||
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];
|
||||
int extruder = atoi(m_value[2].c_str());
|
||||
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)
|
||||
val.clear();
|
||||
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:
|
||||
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());
|
||||
|
@ -1003,11 +1022,7 @@ bool load_amf(const char* path, DynamicPrintConfig* config, Model* model, bool c
|
|||
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)
|
||||
#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))
|
||||
return false;
|
||||
|
@ -1161,12 +1176,8 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
|
|||
stream << "</metadata>\n";
|
||||
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());
|
||||
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_volume_id\">" << volume->source.volume_idx << "</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", "");
|
||||
|
||||
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", "");
|
||||
// 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>.gcode" , code.gcode );
|
||||
code_tree.put("<xmlattr>.extruder" , code.extruder );
|
||||
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())
|
||||
{
|
||||
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
|
||||
boost::replace_all(out, "><code", ">\n <code");
|
||||
boost::replace_all(out, "><mode", ">\n <mode");
|
||||
boost::replace_all(out, "><", ">\n<");
|
||||
|
||||
stream << out << "\n";
|
||||
|
|
|
@ -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.
|
||||
// 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);
|
||||
#else
|
||||
extern bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config);
|
||||
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
|
|
|
@ -355,6 +355,35 @@ bool objparse(const char *path, ObjData &data)
|
|||
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>
|
||||
bool savevector(FILE *pFile, const std::vector<T> &v)
|
||||
{
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <istream>
|
||||
|
||||
namespace ObjParser {
|
||||
|
||||
|
@ -97,6 +98,7 @@ struct ObjData {
|
|||
};
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "GCode/WipeTower.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "libslic3r.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
|
@ -164,12 +165,12 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP
|
|||
cnt = (cnt + 1) / 2;
|
||||
}
|
||||
// And collect copies of the objects.
|
||||
for (const Point © : object->copies()) {
|
||||
for (const PrintInstance &instance : object->instances()) {
|
||||
// All the layers were reduced to the 1st item of polygons_per_layer.
|
||||
size_t i = islands.size();
|
||||
polygons_append(islands, polygons_per_layer.front());
|
||||
for (; i < islands.size(); ++ i)
|
||||
islands[i].translate(copy);
|
||||
islands[i].translate(instance.shift);
|
||||
}
|
||||
}
|
||||
return islands;
|
||||
|
@ -965,7 +966,7 @@ namespace DoExport {
|
|||
skirts.emplace_back(std::move(s));
|
||||
}
|
||||
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
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(
|
||||
|
@ -1038,8 +1039,8 @@ namespace DoExport {
|
|||
std::string filament_stats_string_out;
|
||||
|
||||
print_statistics.clear();
|
||||
print_statistics.estimated_normal_print_time = normal_time_estimator.get_time_dhms();
|
||||
print_statistics.estimated_silent_print_time = silent_time_estimator_enabled ? silent_time_estimator.get_time_dhms() : "N/A";
|
||||
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_dhm/*s*/() : "N/A";
|
||||
print_statistics.estimated_normal_color_print_times = normal_time_estimator.get_color_times_dhms(true);
|
||||
if (silent_time_estimator_enabled)
|
||||
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
|
||||
void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
#else
|
||||
|
@ -1125,7 +1161,7 @@ void GCode::_do_export(Print& print, FILE* file)
|
|||
for (auto layer : object->support_layers())
|
||||
zs.push_back(layer->print_z);
|
||||
std::sort(zs.begin(), zs.end());
|
||||
m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin()));
|
||||
m_layer_count += (unsigned int)(object->instances().size() * (std::unique(zs.begin(), zs.end()) - zs.begin()));
|
||||
}
|
||||
} else {
|
||||
// Print all objects with the same print_z together.
|
||||
|
@ -1218,13 +1254,18 @@ void GCode::_do_export(Print& print, FILE* file)
|
|||
ToolOrdering tool_ordering;
|
||||
unsigned int initial_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;
|
||||
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) {
|
||||
// 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.
|
||||
for (; initial_print_object_id < print.objects().size(); ++initial_print_object_id) {
|
||||
tool_ordering = ToolOrdering(*print.objects()[initial_print_object_id], initial_extruder_id);
|
||||
if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
|
||||
print_object_instance_sequential_active = print_object_instances_ordering.begin();
|
||||
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
|
||||
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;
|
||||
}
|
||||
// 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.
|
||||
// Therefore initialize the printing extruders from there.
|
||||
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) {
|
||||
// Nothing to print!
|
||||
|
@ -1324,17 +1367,11 @@ void GCode::_do_export(Print& print, FILE* file)
|
|||
|
||||
// Do all objects for each layer.
|
||||
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;
|
||||
for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) {
|
||||
const PrintObject &object = *objects[object_id];
|
||||
for (const Point © : object.copies()) {
|
||||
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
|
||||
if (object_id != initial_print_object_id || © != object.copies().data()) {
|
||||
// Don't initialize for the first object and first copy.
|
||||
const PrintObject *prev_object = (*print_object_instance_sequential_active)->print_object;
|
||||
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
|
||||
const PrintObject &object = *(*print_object_instance_sequential_active)->print_object;
|
||||
if (&object != prev_object || tool_ordering.first_extruder() != final_extruder_id) {
|
||||
tool_ordering = ToolOrdering(object, final_extruder_id);
|
||||
unsigned int new_extruder_id = tool_ordering.first_extruder();
|
||||
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);
|
||||
}
|
||||
print.throw_if_canceled();
|
||||
this->set_origin(unscale(copy));
|
||||
this->set_origin(unscale((*print_object_instance_sequential_active)->shift));
|
||||
if (finished_objects > 0) {
|
||||
// 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.
|
||||
|
@ -1374,7 +1411,7 @@ void GCode::_do_export(Print& print, FILE* file)
|
|||
for (const LayerToPrint <p : layers_to_print) {
|
||||
std::vector<LayerToPrint> lrs;
|
||||
lrs.emplace_back(std::move(ltp));
|
||||
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), nullptr, © - 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();
|
||||
}
|
||||
#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.
|
||||
// Reset it when starting another object from 1st layer.
|
||||
m_second_layer_things_done = false;
|
||||
}
|
||||
prev_object = &object;
|
||||
}
|
||||
} 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.
|
||||
// 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);
|
||||
|
@ -1694,7 +1729,7 @@ std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
|
|||
std::vector<GCode::ObjectByExtruder> &objects_by_extruder,
|
||||
const std::vector<LayerToPrint> &layers,
|
||||
// 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.
|
||||
const size_t single_object_instance_idx)
|
||||
{
|
||||
|
@ -1723,13 +1758,13 @@ std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
|
|||
if (! sorted.empty()) {
|
||||
const Print &print = *sorted.front().first->print();
|
||||
out.reserve(sorted.size());
|
||||
for (const std::pair<size_t, size_t> &instance_id : *ordering) {
|
||||
const PrintObject &print_object = *print.objects()[instance_id.first];
|
||||
for (const PrintInstance *instance : *ordering) {
|
||||
const PrintObject &print_object = *instance->print_object;
|
||||
std::pair<const PrintObject*, ObjectByExtruder*> key(&print_object, nullptr);
|
||||
auto it = std::lower_bound(sorted.begin(), sorted.end(), key);
|
||||
if (it != sorted.end() && it->first == &print_object)
|
||||
// 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(
|
||||
const Model::CustomGCode *custom_gcode,
|
||||
const CustomGCode::Item *custom_gcode,
|
||||
// ID of the first extruder printing this layer.
|
||||
unsigned int first_extruder_id,
|
||||
bool single_material_print)
|
||||
bool single_extruder_printer)
|
||||
{
|
||||
std::string gcode;
|
||||
|
||||
|
@ -1752,30 +1787,38 @@ namespace ProcessLayer
|
|||
assert(custom_gcode->gcode != ToolChangeCode);
|
||||
|
||||
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;
|
||||
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;
|
||||
else if (custom_code == PausePrintCode)
|
||||
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
|
||||
if (custom_code == ColorChangeCode) // color change
|
||||
if (color_change || tool_change)
|
||||
{
|
||||
// Color Change or Tool Change as Color Change.
|
||||
// add tag for analyzer
|
||||
gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n";
|
||||
// add tag for time estimator
|
||||
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
|
||||
) {
|
||||
//! FIXME_in_fw show message during print pause
|
||||
gcode += "M601\n"; // pause print
|
||||
gcode += "M117 Change filament for Extruder " + std::to_string(m600_extruder_before_layer) + "\n";
|
||||
}
|
||||
else
|
||||
gcode += custom_code + "\n";
|
||||
else {
|
||||
gcode += ColorChangeCode;
|
||||
gcode += "\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1836,6 +1879,8 @@ namespace Skirt {
|
|||
const Print &print,
|
||||
const std::vector<GCode::LayerToPrint> &layers,
|
||||
const LayerTools &layer_tools,
|
||||
// First non-empty support layer.
|
||||
const SupportLayer *support_layer,
|
||||
// Heights (print_z) at which the skirt has already been extruded.
|
||||
std::vector<coordf_t> &skirt_done)
|
||||
{
|
||||
|
@ -1849,7 +1894,7 @@ namespace Skirt {
|
|||
// This print_z has not been extruded yet (sequential print)
|
||||
skirt_done.back() < layer_tools.print_z - EPSILON &&
|
||||
// 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
|
||||
// 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);
|
||||
|
@ -1879,7 +1924,7 @@ void GCode::process_layer(
|
|||
const std::vector<LayerToPrint> &layers,
|
||||
const LayerTools &layer_tools,
|
||||
// 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.
|
||||
// Otherwise print a single copy of a single object.
|
||||
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.
|
||||
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_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.
|
||||
std::map<unsigned int, std::vector<ObjectByExtruder>> by_extruder;
|
||||
|
@ -2084,14 +2129,14 @@ void GCode::process_layer(
|
|||
|
||||
// Let's recover vector of extruder overrides:
|
||||
const WipingExtrusions::ExtruderPerCopy *entity_overrides = nullptr;
|
||||
if (is_anything_overridden) {
|
||||
printing_extruders.clear();
|
||||
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
|
||||
// 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();
|
||||
}
|
||||
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) {
|
||||
printing_extruders.emplace_back(correct_extruder_id);
|
||||
} else {
|
||||
|
@ -2102,10 +2147,10 @@ void GCode::process_layer(
|
|||
extruder :
|
||||
// at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation)
|
||||
static_cast<unsigned int>(- extruder - 1));
|
||||
}
|
||||
Slic3r::sort_remove_duplicates(printing_extruders);
|
||||
}
|
||||
} 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:
|
||||
for (unsigned int extruder : printing_extruders)
|
||||
|
@ -2205,7 +2250,7 @@ void GCode::process_layer(
|
|||
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";
|
||||
// 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);
|
||||
if (m_last_obj_copy != this_object_copy)
|
||||
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) {
|
||||
last_pos = m_layer->object()->bounding_box().center();
|
||||
last_pos(1) += coord_t(3. * m_layer->object()->bounding_box().radius());
|
||||
// Object is centered around (0,0) in its current coordinate system.
|
||||
last_pos.x() = 0;
|
||||
last_pos.y() += coord_t(3. * m_layer->object()->bounding_box().radius());
|
||||
last_pos_weight = 5.f;
|
||||
}
|
||||
|
||||
|
|
|
@ -231,7 +231,7 @@ private:
|
|||
const std::vector<LayerToPrint> &layers,
|
||||
const LayerTools &layer_tools,
|
||||
// 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.
|
||||
// Otherwise print a single copy of a single object.
|
||||
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.
|
||||
const std::vector<LayerToPrint> &layers,
|
||||
// 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.
|
||||
const size_t single_object_instance_idx);
|
||||
|
||||
|
@ -430,6 +430,8 @@ private:
|
|||
friend class WipeTowerIntegration;
|
||||
};
|
||||
|
||||
std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Print& print);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -121,9 +121,9 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
|
|||
if (support_layer)
|
||||
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
|
||||
bbox_this.merge(extrusionentity_extents(extrusion_entity));
|
||||
for (const Point &offset : print_object.copies()) {
|
||||
for (const PrintInstance &instance : print_object.instances()) {
|
||||
BoundingBoxf bbox_translated(bbox_this);
|
||||
bbox_translated.translate(unscale(offset));
|
||||
bbox_translated.translate(unscale(instance.shift));
|
||||
bbox.merge(bbox_translated);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
// in order to update positions.
|
||||
if (!this->enable) {
|
||||
this->_reader.parse_buffer(gcode);
|
||||
if (! this->enable) {
|
||||
m_reader.parse_buffer(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.
|
||||
GCodeReader r = this->_reader; // clone
|
||||
GCodeReader r = m_reader; // clone
|
||||
r.parse_buffer(gcode, [&total_layer_length, &layer_height, &z, &set_z]
|
||||
(GCodeReader &reader, const GCodeReader::GCodeLine &line) {
|
||||
if (line.cmd_is("G1")) {
|
||||
|
@ -50,7 +50,7 @@ std::string SpiralVase::process_layer(const std::string &gcode)
|
|||
z -= layer_height;
|
||||
|
||||
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) {
|
||||
if (line.cmd_is("G1")) {
|
||||
if (line.has_z()) {
|
||||
|
|
|
@ -7,20 +7,19 @@
|
|||
namespace Slic3r {
|
||||
|
||||
class SpiralVase {
|
||||
public:
|
||||
bool enable;
|
||||
public:
|
||||
bool enable = false;
|
||||
|
||||
SpiralVase(const PrintConfig &config)
|
||||
: enable(false), _config(&config)
|
||||
SpiralVase(const PrintConfig &config) : m_config(&config)
|
||||
{
|
||||
this->_reader.z() = (float)this->_config->z_offset;
|
||||
this->_reader.apply_config(*this->_config);
|
||||
m_reader.z() = (float)m_config->z_offset;
|
||||
m_reader.apply_config(*m_config);
|
||||
};
|
||||
std::string process_layer(const std::string &gcode);
|
||||
|
||||
private:
|
||||
const PrintConfig* _config;
|
||||
GCodeReader _reader;
|
||||
private:
|
||||
const PrintConfig *m_config;
|
||||
GCodeReader m_reader;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
std::vector<std::pair<double, unsigned int>> per_layer_extruder_switches;
|
||||
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).
|
||||
// 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.
|
||||
|
@ -462,15 +463,23 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
|
|||
// Only valid for non-sequential print.
|
||||
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())
|
||||
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);
|
||||
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.
|
||||
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:
|
||||
for (auto it_lt = m_layer_tools.rbegin(); it_lt != m_layer_tools.rend(); ++ it_lt) {
|
||||
LayerTools < = *it_lt;
|
||||
|
@ -483,16 +492,23 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
|
|||
// Custom G-codes were processed.
|
||||
break;
|
||||
// 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.
|
||||
coordf_t print_z_below = 0.;
|
||||
if (auto it_lt_below = it_lt; ++ it_lt_below != m_layer_tools.rend())
|
||||
print_z_below = it_lt_below->print_z;
|
||||
if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) {
|
||||
// The custom G-code applies to the current layer.
|
||||
if ( tool_changes_as_color_changes || custom_gcode.gcode != ColorChangeCode ||
|
||||
(custom_gcode.extruder <= num_extruders && extruder_printing_above[unsigned(custom_gcode.extruder - 1)]))
|
||||
bool color_change = custom_gcode.gcode == ColorChangeCode;
|
||||
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.
|
||||
(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;
|
||||
// Consume that custom G-code event.
|
||||
++ 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);
|
||||
if (this_layer == nullptr)
|
||||
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
|
||||
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);
|
||||
if (this_layer == nullptr)
|
||||
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 region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
|
||||
|
|
|
@ -114,7 +114,7 @@ public:
|
|||
size_t wipe_tower_partitions = 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.
|
||||
const Model::CustomGCode *custom_gcode = nullptr;
|
||||
const CustomGCode::Item *custom_gcode = nullptr;
|
||||
|
||||
WipingExtrusions& wiping_extrusions() {
|
||||
m_wiping_extrusions.set_layer_tools_ptr(this);
|
||||
|
|
|
@ -707,6 +707,11 @@ namespace Slic3r {
|
|||
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
|
||||
{
|
||||
return _get_time_minutes(get_time());
|
||||
|
@ -1616,6 +1621,29 @@ namespace Slic3r {
|
|||
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)
|
||||
{
|
||||
return std::to_string((int)(::roundf(time_in_secs / 60.0f)));
|
||||
|
|
|
@ -363,6 +363,9 @@ namespace Slic3r {
|
|||
// Returns the estimated time, in format DDd HHh MMm SSs
|
||||
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)
|
||||
std::string get_time_minutes() const;
|
||||
|
||||
|
@ -473,6 +476,8 @@ namespace Slic3r {
|
|||
|
||||
// Returns the given time is seconds in format DDd HHh MMm SSs
|
||||
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)
|
||||
static std::string _get_time_minutes(float time_in_secs);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "GCodeWriter.hpp"
|
||||
#include "CustomGCode.hpp"
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
|
|
@ -10,11 +10,6 @@
|
|||
|
||||
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 {
|
||||
public:
|
||||
GCodeConfig config;
|
||||
|
|
|
@ -105,6 +105,7 @@ public:
|
|||
coordf_t slice_z; // Z used for slicing in unscaled coordinates
|
||||
coordf_t print_z; // Z used for printing in unscaled coordinates
|
||||
coordf_t height; // layer height in unscaled coordinates
|
||||
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
|
||||
// (with possibly differing extruder ID and slicing parameters) and merged.
|
||||
|
|
|
@ -362,8 +362,10 @@ void LayerRegion::prepare_fill_surfaces()
|
|||
alter fill_surfaces boundaries on which our idempotency relies since that's
|
||||
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 (this->region()->config().top_solid_layers == 0) {
|
||||
if (! spiral_vase && this->region()->config().top_solid_layers == 0) {
|
||||
for (Surface &surface : this->fill_surfaces.surfaces)
|
||||
if (surface.is_top())
|
||||
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
|
||||
if (this->region()->config().fill_density.value > 0) {
|
||||
if (! spiral_vase && this->region()->config().fill_density.value > 0) {
|
||||
// scaling an area requires two calls!
|
||||
double min_area = scale_(scale_(this->region()->config().solid_infill_below_area.value));
|
||||
for (Surface &surface : this->fill_surfaces.surfaces)
|
||||
|
|
|
@ -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 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
|
||||
template<bool B, class T>
|
||||
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;
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
||||
#endif // MTUTILS_HPP
|
||||
|
|
276
src/libslic3r/MeshBoolean.cpp
Normal 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
|
49
src/libslic3r/MeshBoolean.hpp
Normal 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_
|
|
@ -126,7 +126,8 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
|
|||
if (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;
|
||||
}
|
||||
|
@ -163,7 +164,8 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
|
|||
if (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;
|
||||
}
|
||||
|
@ -618,6 +620,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
|
|||
assert(this->config.id() == rhs.config.id());
|
||||
this->sla_support_points = rhs.sla_support_points;
|
||||
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_height_profile = rhs.layer_height_profile;
|
||||
this->printable = rhs.printable;
|
||||
|
@ -658,6 +661,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
|
|||
assert(this->config.id() == rhs.config.id());
|
||||
this->sla_support_points = std::move(rhs.sla_support_points);
|
||||
this->sla_points_status = std::move(rhs.sla_points_status);
|
||||
this->sla_drain_holes = std::move(rhs.sla_drain_holes);
|
||||
this->layer_config_ranges = std::move(rhs.layer_config_ranges); // #ys_FIXME_experiment
|
||||
this->layer_height_profile = std::move(rhs.layer_height_profile);
|
||||
this->origin_translation = std::move(rhs.origin_translation);
|
||||
|
@ -903,11 +907,9 @@ const BoundingBoxf3& ModelObject::raw_bounding_box() const
|
|||
|
||||
const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true);
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
{
|
||||
if (v->is_model_part())
|
||||
m_raw_bounding_box.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix()));
|
||||
}
|
||||
}
|
||||
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) {
|
||||
upper->set_model(nullptr);
|
||||
upper->sla_support_points.clear();
|
||||
upper->sla_drain_holes.clear();
|
||||
upper->sla_points_status = sla::PointsStatus::NoPoints;
|
||||
upper->clear_volumes();
|
||||
upper->input_file = "";
|
||||
upper->input_file.clear();
|
||||
}
|
||||
|
||||
if (keep_lower) {
|
||||
lower->set_model(nullptr);
|
||||
lower->sla_support_points.clear();
|
||||
lower->sla_drain_holes.clear();
|
||||
lower->sla_points_status = sla::PointsStatus::NoPoints;
|
||||
lower->clear_volumes();
|
||||
lower->input_file = "";
|
||||
lower->input_file.clear();
|
||||
}
|
||||
|
||||
// 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_lower) { lower->add_volume(*volume); }
|
||||
}
|
||||
else {
|
||||
else if (! volume->mesh().empty()) {
|
||||
|
||||
TriangleMesh upper_mesh, lower_mesh;
|
||||
|
||||
// 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);
|
||||
volume->reset_mesh();
|
||||
|
||||
mesh.require_shared_vertices();
|
||||
|
||||
// Perform cut
|
||||
TriangleMeshSlicer tms(&mesh);
|
||||
tms.cut(float(z), &upper_mesh, &lower_mesh);
|
||||
|
@ -1841,19 +1848,6 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
|
|||
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
|
||||
// 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)
|
||||
|
@ -1942,28 +1936,6 @@ extern bool model_has_advanced_features(const Model &model)
|
|||
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
|
||||
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
|
||||
void check_model_ids_validity(const Model &model)
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
#include "Point.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Slicing.hpp"
|
||||
#include "SLA/SLACommon.hpp"
|
||||
#include "SLA/SupportPoint.hpp"
|
||||
#include "SLA/Hollowing.hpp"
|
||||
#include "TriangleMesh.hpp"
|
||||
#include "Arrange.hpp"
|
||||
#include "CustomGCode.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
@ -198,11 +200,14 @@ public:
|
|||
// This vector holds position of selected support points for SLA. The data are
|
||||
// saved in mesh coordinates to allow using them for several instances.
|
||||
// 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
|
||||
// the SLA gizmo and the backend).
|
||||
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
|
||||
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
|
||||
|
@ -372,7 +377,7 @@ private:
|
|||
template<class Archive> void serialize(Archive &ar) {
|
||||
ar(cereal::base_class<ObjectBase>(this));
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
@ -669,6 +674,7 @@ public:
|
|||
set_rotation(Z, rotation);
|
||||
set_offset(X, unscale<double>(offs(X)));
|
||||
set_offset(Y, unscale<double>(offs(Y)));
|
||||
this->object->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -749,48 +755,7 @@ public:
|
|||
ModelWipeTower wipe_tower;
|
||||
|
||||
// Extensions for color print
|
||||
struct CustomGCode
|
||||
{
|
||||
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;
|
||||
CustomGCode::Info custom_gcode_per_print_z;
|
||||
|
||||
// Default constructor assigns a new ID to the model.
|
||||
Model() { assert(this->id().valid()); }
|
||||
|
@ -872,10 +837,6 @@ private:
|
|||
#undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE
|
||||
#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
|
||||
// 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);
|
||||
|
@ -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);
|
||||
// If the model has advanced features, then it cannot be processed in simple mode.
|
||||
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
|
||||
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
|
||||
|
|
135
src/libslic3r/OpenVDBUtils.cpp
Normal 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
|
45
src/libslic3r/OpenVDBUtils.hpp
Normal 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
|
|
@ -312,6 +312,10 @@ void PerimeterGenerator::process()
|
|||
for (ExPolygon &ex : expp)
|
||||
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 {
|
||||
//FIXME Is this offset correct if the line width of the inner perimeters differs
|
||||
// from the line width of the infill?
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "PlaceholderParser.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#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_new = config_new.option(opt_key);
|
||||
assert(opt_new != nullptr);
|
||||
if (opt_old == nullptr)
|
||||
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;
|
||||
return opt_old != nullptr && *opt_new == *opt_old;
|
||||
}
|
||||
|
||||
std::vector<std::string> PlaceholderParser::config_diff(const DynamicPrintConfig &rhs)
|
||||
|
@ -126,14 +123,7 @@ bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs)
|
|||
bool modified = false;
|
||||
for (const t_config_option_key &opt_key : rhs.keys()) {
|
||||
if (! opts_equal(m_config, rhs, opt_key)) {
|
||||
// Store a copy of the config option.
|
||||
// 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());
|
||||
this->set(opt_key, rhs.option(opt_key)->clone());
|
||||
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)
|
||||
{
|
||||
for (const t_config_option_key &opt_key : keys) {
|
||||
// Store a copy of the config option.
|
||||
// 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());
|
||||
}
|
||||
for (const t_config_option_key &opt_key : keys)
|
||||
this->set(opt_key, rhs.option(opt_key)->clone());
|
||||
}
|
||||
|
||||
void PlaceholderParser::apply_config(DynamicPrintConfig &&rhs)
|
||||
|
@ -635,7 +617,7 @@ namespace client
|
|||
return os;
|
||||
}
|
||||
|
||||
struct MyContext {
|
||||
struct MyContext : public ConfigOptionResolver {
|
||||
const DynamicConfig *external_config = nullptr;
|
||||
const DynamicConfig *config = 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; }
|
||||
|
||||
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;
|
||||
if (config_override != nullptr)
|
||||
|
@ -662,6 +644,8 @@ namespace client
|
|||
return opt;
|
||||
}
|
||||
|
||||
const ConfigOption* resolve_symbol(const std::string &opt_key) const { return this->optptr(opt_key); }
|
||||
|
||||
template <typename Iterator>
|
||||
static void legacy_variable_expansion(
|
||||
const MyContext *ctx,
|
||||
|
@ -758,7 +742,43 @@ namespace client
|
|||
case coPoint: output.set_s(opt.opt->serialize()); break;
|
||||
case coBool: output.set_b(opt.opt->getBool()); break;
|
||||
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:
|
||||
ctx->throw_exception("Unknown scalar variable type", opt.it_range);
|
||||
}
|
||||
|
|
|
@ -24,8 +24,7 @@
|
|||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
//! macro used to mark string used at localization,
|
||||
//! return same string
|
||||
// Mark string for localization and translate.
|
||||
#define L(s) Slic3r::I18N::translate(s)
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -174,7 +173,11 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||
steps.emplace_back(psSkirt);
|
||||
} else if (
|
||||
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);
|
||||
} else if (
|
||||
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 == "infill_first"
|
||||
|| opt_key == "single_extruder_multi_material"
|
||||
|| opt_key == "spiral_vase"
|
||||
|| opt_key == "temperature"
|
||||
|| opt_key == "wipe_tower"
|
||||
|| opt_key == "wipe_tower_width"
|
||||
|
@ -326,7 +328,7 @@ unsigned int Print::num_object_instances() const
|
|||
{
|
||||
unsigned int instances = 0;
|
||||
for (const PrintObject *print_object : m_objects)
|
||||
instances += (unsigned int)print_object->copies().size();
|
||||
instances += (unsigned int)print_object->instances().size();
|
||||
return instances;
|
||||
}
|
||||
|
||||
|
@ -447,33 +449,30 @@ static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &
|
|||
return true;
|
||||
}
|
||||
|
||||
struct PrintInstances
|
||||
struct PrintObjectTrafoAndInstances
|
||||
{
|
||||
Transform3d trafo;
|
||||
Points copies;
|
||||
bool operator<(const PrintInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); }
|
||||
PrintInstances instances;
|
||||
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
|
||||
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;
|
||||
PrintInstances trafo;
|
||||
trafo.copies.assign(1, Point());
|
||||
std::set<PrintObjectTrafoAndInstances> trafos;
|
||||
PrintObjectTrafoAndInstances trafo;
|
||||
for (ModelInstance *model_instance : model_object.instances)
|
||||
if (model_instance->is_printable()) {
|
||||
trafo.trafo = model_instance->get_matrix();
|
||||
// Set the Z axis of the transformation.
|
||||
trafo.copies.front() = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
|
||||
auto shift = 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()[13] = 0;
|
||||
auto it = trafos.find(trafo);
|
||||
if (it == trafos.end())
|
||||
trafos.emplace(trafo);
|
||||
else
|
||||
const_cast<PrintInstances&>(*it).copies.emplace_back(trafo.copies.front());
|
||||
// Search or insert a trafo.
|
||||
auto it = trafos.emplace(trafo).first;
|
||||
const_cast<PrintObjectTrafoAndInstances&>(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift });
|
||||
}
|
||||
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.
|
||||
|
@ -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.
|
||||
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_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) {
|
||||
// Skip any CustomGCode items, which are not tool changes.
|
||||
++ it_a;
|
||||
|
@ -530,7 +529,6 @@ void Print::config_diffs(
|
|||
const DynamicPrintConfig &new_full_config,
|
||||
t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys ®ion_diff,
|
||||
t_config_option_keys &full_config_diff,
|
||||
DynamicPrintConfig &placeholder_parser_overrides,
|
||||
DynamicPrintConfig &filament_overrides) const
|
||||
{
|
||||
// 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);
|
||||
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.
|
||||
// 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()) {
|
||||
const ConfigOption *opt_old = m_full_print_config.option(opt_key);
|
||||
const ConfigOption *opt_new = new_full_config.option(opt_key);
|
||||
if (opt_old == nullptr || *opt_new != *opt_old)
|
||||
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.
|
||||
t_config_option_keys print_diff, object_diff, region_diff, full_config_diff;
|
||||
DynamicPrintConfig placeholder_parser_overrides, filament_overrides;
|
||||
this->config_diffs(new_full_config, print_diff, object_diff, region_diff, full_config_diff, placeholder_parser_overrides, filament_overrides);
|
||||
DynamicPrintConfig 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.
|
||||
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.
|
||||
size_t num_extruders = m_config.nozzle_diameter.size();
|
||||
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));
|
||||
m_placeholder_parser.apply_config(std::move(placeholder_parser_overrides));
|
||||
// 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("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);
|
||||
} else {
|
||||
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.
|
||||
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.
|
||||
|
@ -839,7 +832,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
|
||||
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.
|
||||
// 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 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);
|
||||
|
@ -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.
|
||||
model_object.name = model_object_new.name;
|
||||
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.instances.reserve(model_object_new.instances.size());
|
||||
for (const ModelInstance *model_instance : model_object_new.instances) {
|
||||
model_object.instances.emplace_back(new ModelInstance(*model_instance));
|
||||
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
|
||||
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()) {
|
||||
// Simple case, just generate new instances.
|
||||
for (const PrintInstances &print_instances : new_print_instances) {
|
||||
PrintObject *print_object = new PrintObject(this, model_object, false);
|
||||
print_object->set_trafo(print_instances.trafo);
|
||||
print_object->set_copies(print_instances.copies);
|
||||
for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) {
|
||||
PrintObject *print_object = new PrintObject(this, model_object, print_instances.trafo, std::move(print_instances.instances));
|
||||
print_object->config_apply(config);
|
||||
print_objects_new.emplace_back(print_object);
|
||||
// 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); });
|
||||
// Merge the old / new lists.
|
||||
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);
|
||||
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.
|
||||
PrintObject *print_object = new PrintObject(this, model_object, false);
|
||||
print_object->set_trafo(new_instances.trafo);
|
||||
print_object->set_copies(new_instances.copies);
|
||||
PrintObject *print_object = new PrintObject(this, model_object, new_instances.trafo, std::move(new_instances.instances));
|
||||
print_object->config_apply(config);
|
||||
print_objects_new.emplace_back(print_object);
|
||||
// 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;
|
||||
} else {
|
||||
// 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)
|
||||
update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED);
|
||||
print_objects_new.emplace_back((*it_old)->print_object);
|
||||
|
@ -1144,6 +1152,62 @@ bool Print::has_skirt() const
|
|||
|| 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.
|
||||
std::string Print::validate() const
|
||||
{
|
||||
|
@ -1154,53 +1218,16 @@ std::string Print::validate() const
|
|||
return L("The supplied settings will cause an empty print.");
|
||||
|
||||
if (m_config.complete_objects) {
|
||||
// Check horizontal clearance.
|
||||
{
|
||||
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 © : print_object->copies()) {
|
||||
Polygon convex_hull = convex_hull0;
|
||||
convex_hull.translate(copy);
|
||||
if (! intersection(convex_hulls_other, convex_hull).empty())
|
||||
if (! sequential_print_horizontal_clearance_valid(*this))
|
||||
return L("Some objects are too close; your extruder will collide with them.");
|
||||
polygons_append(convex_hulls_other, convex_hull);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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))
|
||||
if (! sequential_print_vertical_clearance_valid(*this))
|
||||
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) {
|
||||
size_t total_copies_count = 0;
|
||||
for (const PrintObject *object : m_objects)
|
||||
total_copies_count += object->copies().size();
|
||||
total_copies_count += object->instances().size();
|
||||
// #4043
|
||||
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.");
|
||||
|
@ -1411,16 +1438,17 @@ std::string Print::validate() const
|
|||
return std::string();
|
||||
}
|
||||
|
||||
#if 0
|
||||
// the bounding box of objects placed in copies position
|
||||
// (without taking skirt/brim/support material into account)
|
||||
BoundingBox Print::bounding_box() const
|
||||
{
|
||||
BoundingBox bb;
|
||||
for (const PrintObject *object : m_objects)
|
||||
for (Point copy : object->m_copies) {
|
||||
bb.merge(copy);
|
||||
copy += to_2d(object->size);
|
||||
bb.merge(copy);
|
||||
for (const PrintInstance &instance : object->instances()) {
|
||||
BoundingBox bb2(object->bounding_box());
|
||||
bb.merge(bb2.min + instance.shift);
|
||||
bb.merge(bb2.max + instance.shift);
|
||||
}
|
||||
return bb;
|
||||
}
|
||||
|
@ -1465,6 +1493,7 @@ BoundingBox Print::total_bounding_box() const
|
|||
|
||||
return bb;
|
||||
}
|
||||
#endif
|
||||
|
||||
double Print::skirt_first_layer_height() const
|
||||
{
|
||||
|
@ -1562,6 +1591,8 @@ void Print::process()
|
|||
} 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.
|
||||
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);
|
||||
}
|
||||
|
@ -1657,10 +1688,10 @@ void Print::_make_skirt()
|
|||
append(object_points, extrusion_entity->as_polyline().points);
|
||||
}
|
||||
// Repeat points for each object copy.
|
||||
for (const Point &shift : object->m_copies) {
|
||||
for (const PrintInstance &instance : object->instances()) {
|
||||
Points copy_points = object_points;
|
||||
for (Point &pt : copy_points)
|
||||
pt += shift;
|
||||
pt += instance.shift;
|
||||
append(points, copy_points);
|
||||
}
|
||||
}
|
||||
|
@ -1778,11 +1809,11 @@ void Print::_make_brim()
|
|||
object_islands.push_back(expoly.contour);
|
||||
if (! object->support_layers().empty())
|
||||
object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));
|
||||
islands.reserve(islands.size() + object_islands.size() * object->m_copies.size());
|
||||
for (const Point &pt : object->m_copies)
|
||||
islands.reserve(islands.size() + object_islands.size() * object->instances().size());
|
||||
for (const PrintInstance &instance : object->instances())
|
||||
for (Polygon &poly : object_islands) {
|
||||
islands.push_back(poly);
|
||||
islands.back().translate(pt);
|
||||
islands.back().translate(instance.shift);
|
||||
}
|
||||
}
|
||||
Polygons loops;
|
||||
|
|
|
@ -24,6 +24,7 @@ class PrintObject;
|
|||
class ModelObject;
|
||||
class GCode;
|
||||
class GCodePreviewData;
|
||||
enum class SlicingMode : uint32_t;
|
||||
|
||||
// Print step IDs for keeping track of the print state.
|
||||
enum PrintStep {
|
||||
|
@ -84,7 +85,7 @@ private:
|
|||
|
||||
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() {}
|
||||
~PrintRegion() = default;
|
||||
};
|
||||
|
||||
|
||||
|
@ -92,6 +93,21 @@ typedef std::vector<Layer*> LayerPtrs;
|
|||
typedef std::vector<SupportLayer*> SupportLayerPtrs;
|
||||
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>
|
||||
{
|
||||
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
|
||||
std::vector<std::vector<std::pair<t_layer_height_range, int>>> region_volumes;
|
||||
|
||||
// this is set to true when LayerRegion->slices is split in top/internal/bottom
|
||||
// so that next call to make_perimeters() performs a union() before computing loops
|
||||
bool typed_slices;
|
||||
|
||||
Vec3crd size; // XYZ in scaled coordinates
|
||||
|
||||
// Size of an object: XYZ in scaled coordinates. The size might not be quite snug in XY plane.
|
||||
const Vec3crd& size() const { return m_size; }
|
||||
const PrintObjectConfig& config() const { return m_config; }
|
||||
const LayerPtrs& layers() const { return m_layers; }
|
||||
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
|
||||
const Transform3d& trafo() const { return m_trafo; }
|
||||
const Points& copies() const { return m_copies; }
|
||||
const Point copy_center(size_t idx) const { return m_copies[idx] + m_copies_shift + Point(this->size.x() / 2, this->size.y() / 2); }
|
||||
const PrintInstances& instances() const { return m_instances; }
|
||||
|
||||
// since the object is aligned to origin, bounding box coincides with size
|
||||
BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }
|
||||
// Bounding box is used to align the object infill patterns, and to calculate attractor for the rear seam.
|
||||
// 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
|
||||
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_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
|
||||
|
||||
protected:
|
||||
private:
|
||||
// to be called from Print only.
|
||||
friend class Print;
|
||||
|
||||
PrintObject(Print* print, ModelObject* model_object, bool add_instances = true);
|
||||
~PrintObject() {}
|
||||
PrintObject(Print* print, ModelObject* model_object, const Transform3d& trafo, PrintInstances&& instances);
|
||||
~PrintObject() = default;
|
||||
|
||||
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 set_trafo(const Transform3d& trafo) { m_trafo = trafo; }
|
||||
PrintBase::ApplyStatus set_copies(const Points &points);
|
||||
PrintBase::ApplyStatus set_instances(PrintInstances &&instances);
|
||||
// Invalidates the step, and its depending steps in PrintObject and Print.
|
||||
bool invalidate_step(PrintObjectStep step);
|
||||
// Invalidates all PrintObject and Print steps.
|
||||
|
@ -219,25 +235,30 @@ private:
|
|||
void combine_infill();
|
||||
void _generate_support_material();
|
||||
|
||||
// XYZ in scaled coordinates
|
||||
Vec3crd m_size;
|
||||
PrintObjectConfig m_config;
|
||||
// Translation in Z + Rotation + Scaling / Mirroring.
|
||||
Transform3d m_trafo = Transform3d::Identity();
|
||||
// Slic3r::Point objects in scaled G-code coordinates
|
||||
Points m_copies;
|
||||
// scaled coordinates to add to copies (to compensate for the alignment
|
||||
// operated when creating the object but still preserving a coherent API
|
||||
// for external callers)
|
||||
Point m_copies_shift;
|
||||
std::vector<PrintInstance> m_instances;
|
||||
// The mesh is being centered before thrown to Clipper, so that the Clipper's fixed coordinates require less bits.
|
||||
// This is the adjustment of the the Object's coordinate system towards PrintObject's coordinate system.
|
||||
Point m_center_offset;
|
||||
|
||||
SlicingParameters m_slicing_params;
|
||||
LayerPtrs m_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_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
|
||||
std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const ModelVolume &volume) const;
|
||||
std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, const ModelVolume &volume) const;
|
||||
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, SlicingMode mode, 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
|
||||
|
@ -325,7 +346,7 @@ private: // Prevents erroneous use by other classes.
|
|||
typedef PrintBaseWithState<PrintStep, psCount> Inherited;
|
||||
|
||||
public:
|
||||
Print() {}
|
||||
Print() = default;
|
||||
virtual ~Print() { this->clear(); }
|
||||
|
||||
PrinterTechnology technology() const noexcept { return ptFFF; }
|
||||
|
@ -361,8 +382,6 @@ public:
|
|||
|
||||
// Returns an empty string if valid, otherwise returns an error message.
|
||||
std::string validate() const override;
|
||||
BoundingBox bounding_box() const;
|
||||
BoundingBox total_bounding_box() const;
|
||||
double skirt_first_layer_height() const;
|
||||
Flow brim_flow() const;
|
||||
Flow skirt_flow() const;
|
||||
|
@ -417,7 +436,6 @@ private:
|
|||
const DynamicPrintConfig &new_full_config,
|
||||
t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys ®ion_diff,
|
||||
t_config_option_keys &full_config_diff,
|
||||
DynamicPrintConfig &placeholder_parser_overrides,
|
||||
DynamicPrintConfig &filament_overrides) const;
|
||||
|
||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
|
|
|
@ -168,6 +168,17 @@ void PrintConfigDef::init_fff_params()
|
|||
def->min = 0;
|
||||
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->label = L("Bridge");
|
||||
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 "
|
||||
"and its speed will be calculated by interpolating the minimum and maximum speeds.");
|
||||
def->sidetext = L("approximate seconds");
|
||||
def->width = 6;
|
||||
def->min = 0;
|
||||
def->max = 1000;
|
||||
def->mode = comExpert;
|
||||
|
@ -718,8 +728,9 @@ void PrintConfigDef::init_fff_params()
|
|||
def->gui_type = "f_enum_open";
|
||||
def->gui_flags = "show_value";
|
||||
def->enum_values.push_back("PLA");
|
||||
def->enum_values.push_back("ABS");
|
||||
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("HIPS");
|
||||
def->enum_values.push_back("EDGE");
|
||||
|
@ -1092,7 +1103,6 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
const int machine_limits_opt_width = 7;
|
||||
{
|
||||
struct AxisDefault {
|
||||
std::string name;
|
||||
|
@ -1124,7 +1134,6 @@ void PrintConfigDef::init_fff_params()
|
|||
(void)L("Maximum feedrate of the E axis");
|
||||
def->sidetext = L("mm/s");
|
||||
def->min = 0;
|
||||
def->width = machine_limits_opt_width;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloats(axis.max_feedrate));
|
||||
// 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");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->width = machine_limits_opt_width;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloats(axis.max_acceleration));
|
||||
// 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");
|
||||
def->sidetext = L("mm/s");
|
||||
def->min = 0;
|
||||
def->width = machine_limits_opt_width;
|
||||
def->mode = comAdvanced;
|
||||
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->sidetext = L("mm/s");
|
||||
def->min = 0;
|
||||
def->width = machine_limits_opt_width;
|
||||
def->mode = comAdvanced;
|
||||
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->sidetext = L("mm/s");
|
||||
def->min = 0;
|
||||
def->width = machine_limits_opt_width;
|
||||
def->mode = comAdvanced;
|
||||
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->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->width = machine_limits_opt_width;
|
||||
def->mode = comAdvanced;
|
||||
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->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->width = machine_limits_opt_width;
|
||||
def->mode = comAdvanced;
|
||||
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 "
|
||||
"speed will be scaled down to extend duration to this value.");
|
||||
def->sidetext = L("approximate seconds");
|
||||
def->width = 6;
|
||||
def->min = 0;
|
||||
def->max = 1000;
|
||||
def->mode = comExpert;
|
||||
|
@ -1781,6 +1783,13 @@ void PrintConfigDef::init_fff_params()
|
|||
def->shortcut.push_back("bottom_solid_layers");
|
||||
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->label = L("Spiral vase");
|
||||
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->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->label = L("Travel");
|
||||
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->mode = comExpert;
|
||||
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)
|
||||
|
|
|
@ -46,12 +46,6 @@ enum SeamPosition {
|
|||
spRandom, spNearest, spAligned, spRear
|
||||
};
|
||||
|
||||
/*
|
||||
enum FilamentType {
|
||||
ftPLA, ftABS, ftPET, ftHIPS, ftFLEX, ftSCAFF, ftEDGE, ftNGEN, ftPVA
|
||||
};
|
||||
*/
|
||||
|
||||
enum SLAMaterial {
|
||||
slamTough,
|
||||
slamFlex,
|
||||
|
@ -149,24 +143,6 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<SeamPosition>::ge
|
|||
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() {
|
||||
static const t_config_enum_values keys_map = {
|
||||
{ "landscape", sladoLandscape},
|
||||
|
@ -354,6 +330,9 @@ protected:
|
|||
#define STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \
|
||||
public: \
|
||||
/* 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 \
|
||||
{ return s_cache_##CLASS_NAME.optptr(opt_key, this); } \
|
||||
/* Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. */ \
|
||||
|
@ -487,6 +466,7 @@ class PrintRegionConfig : public StaticPrintConfig
|
|||
public:
|
||||
ConfigOptionFloat bridge_angle;
|
||||
ConfigOptionInt bottom_solid_layers;
|
||||
ConfigOptionFloat bottom_solid_min_thickness;
|
||||
ConfigOptionFloat bridge_flow_ratio;
|
||||
ConfigOptionFloat bridge_speed;
|
||||
ConfigOptionBool ensure_vertical_shell_thickness;
|
||||
|
@ -522,6 +502,7 @@ public:
|
|||
ConfigOptionBool thin_walls;
|
||||
ConfigOptionFloatOrPercent top_infill_extrusion_width;
|
||||
ConfigOptionInt top_solid_layers;
|
||||
ConfigOptionFloat top_solid_min_thickness;
|
||||
ConfigOptionFloatOrPercent top_solid_infill_speed;
|
||||
ConfigOptionBool wipe_into_infill;
|
||||
|
||||
|
@ -530,6 +511,7 @@ protected:
|
|||
{
|
||||
OPT_PTR(bridge_angle);
|
||||
OPT_PTR(bottom_solid_layers);
|
||||
OPT_PTR(bottom_solid_min_thickness);
|
||||
OPT_PTR(bridge_flow_ratio);
|
||||
OPT_PTR(bridge_speed);
|
||||
OPT_PTR(ensure_vertical_shell_thickness);
|
||||
|
@ -563,6 +545,7 @@ protected:
|
|||
OPT_PTR(top_infill_extrusion_width);
|
||||
OPT_PTR(top_solid_infill_speed);
|
||||
OPT_PTR(top_solid_layers);
|
||||
OPT_PTR(top_solid_min_thickness);
|
||||
OPT_PTR(wipe_into_infill);
|
||||
}
|
||||
};
|
||||
|
@ -1017,7 +1000,7 @@ public:
|
|||
ConfigOptionFloat support_base_height /*= 1.0*/;
|
||||
|
||||
// The minimum distance of the pillar base from the model in mm.
|
||||
ConfigOptionFloat support_base_safety_distance; /*= 1.0*/;
|
||||
ConfigOptionFloat support_base_safety_distance; /*= 1.0*/
|
||||
|
||||
// The default angle for connecting support sticks and junctions.
|
||||
ConfigOptionFloat support_critical_angle /*= 45*/;
|
||||
|
@ -1062,7 +1045,7 @@ public:
|
|||
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
// according to the support_base_safety_distance parameter.
|
||||
// - 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
|
||||
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:
|
||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||
{
|
||||
|
@ -1121,6 +1126,10 @@ protected:
|
|||
OPT_PTR(pad_object_connector_stride);
|
||||
OPT_PTR(pad_object_connector_width);
|
||||
OPT_PTR(pad_object_connector_penetration);
|
||||
OPT_PTR(hollowing_enable);
|
||||
OPT_PTR(hollowing_min_thickness);
|
||||
OPT_PTR(hollowing_quality);
|
||||
OPT_PTR(hollowing_closing_distance);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -40,52 +40,54 @@
|
|||
|
||||
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),
|
||||
typed_slices(false),
|
||||
size(Vec3crd::Zero())
|
||||
m_trafo(trafo)
|
||||
{
|
||||
// Compute the translation to be applied to our meshes so that we work with smaller coordinates
|
||||
{
|
||||
// Translate meshes so that our toolpath generation algorithms work with smaller
|
||||
// XY coordinates; this translation is an optimization and not strictly required.
|
||||
// A cloned mesh will be aligned to 0 before slicing in slice_region() since we
|
||||
// don't assume it's already aligned and we don't alter the original position in model.
|
||||
// We store the XY translation so that we can place copies correctly in the output G-code
|
||||
// (copies are expressed in G-code coordinates and this translation is not publicly exposed).
|
||||
const BoundingBoxf3 modobj_bbox = model_object->raw_bounding_box();
|
||||
m_copies_shift = Point::new_scale(modobj_bbox.min(0), modobj_bbox.min(1));
|
||||
// Scale the object size and store it
|
||||
this->size = (modobj_bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
|
||||
// Compute centering offet to be applied to our meshes so that we work with smaller coordinates
|
||||
// requiring less bits to represent Clipper coordinates.
|
||||
|
||||
// Snug bounding box of a rotated and scaled object by the 1st instantion, without the instance translation applied.
|
||||
// All the instances share the transformation matrix with the exception of translation in XY and rotation by Z,
|
||||
// therefore a bounding box from 1st instance of a ModelObject is good enough for calculating the object center,
|
||||
// snug height and an approximate bounding box in XY.
|
||||
BoundingBoxf3 bbox = model_object->raw_bounding_box();
|
||||
Vec3d bbox_center = bbox.center();
|
||||
// We may need to rotate the bbox / bbox_center from the original instance to the current instance.
|
||||
double z_diff = Geometry::rotation_diff_z(model_object->instances.front()->get_rotation(), instances.front().model_instance->get_rotation());
|
||||
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) {
|
||||
Points copies;
|
||||
copies.reserve(m_model_object->instances.size());
|
||||
for (const ModelInstance *mi : m_model_object->instances) {
|
||||
assert(mi->is_printable());
|
||||
const Vec3d& offset = mi->get_offset();
|
||||
copies.emplace_back(Point::new_scale(offset(0), offset(1)));
|
||||
}
|
||||
this->set_copies(copies);
|
||||
}
|
||||
// Center of the transformed mesh (without translation).
|
||||
m_center_offset = Point::new_scale(bbox_center.x(), bbox_center.y());
|
||||
// Size of the transformed mesh. This bounding may not be snug in XY plane, but it is snug in Z.
|
||||
m_size = (bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
|
||||
|
||||
this->set_instances(std::move(instances));
|
||||
}
|
||||
|
||||
PrintBase::ApplyStatus PrintObject::set_copies(const Points &points)
|
||||
PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances)
|
||||
{
|
||||
// Order copies with a nearest-neighbor search.
|
||||
std::vector<Point> copies;
|
||||
copies.reserve(points.size());
|
||||
for (const Point &pt : points)
|
||||
copies.emplace_back(pt + m_copies_shift);
|
||||
for (PrintInstance &i : instances)
|
||||
// Add the center offset, which will be subtracted from the mesh when slicing.
|
||||
i.shift += m_center_offset;
|
||||
// Invalidate and set copies.
|
||||
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;
|
||||
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;
|
||||
m_copies = copies;
|
||||
m_instances = std::move(instances);
|
||||
for (PrintInstance &i : m_instances)
|
||||
i.print_object = this;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
@ -151,12 +153,12 @@ void PrintObject::make_perimeters()
|
|||
BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info();
|
||||
|
||||
// merge slices if they were split into types
|
||||
if (this->typed_slices) {
|
||||
if (m_typed_slices) {
|
||||
for (Layer *layer : m_layers) {
|
||||
layer->merge_slices();
|
||||
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
|
||||
|
@ -463,8 +465,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||
|| opt_key == "raft_layers"
|
||||
|| opt_key == "slice_closing_radius") {
|
||||
steps.emplace_back(posSlice);
|
||||
}
|
||||
else if (
|
||||
} else if (
|
||||
opt_key == "clip_multipart_objects"
|
||||
|| opt_key == "elefant_foot_compensation"
|
||||
|| 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 == "solid_infill_every_layers"
|
||||
|| opt_key == "bottom_solid_layers"
|
||||
|| opt_key == "bottom_solid_min_thickness"
|
||||
|| opt_key == "top_solid_layers"
|
||||
|| opt_key == "top_solid_min_thickness"
|
||||
|| opt_key == "solid_infill_below_area"
|
||||
|| opt_key == "infill_extruder"
|
||||
|| opt_key == "solid_infill_extruder"
|
||||
|
@ -620,6 +623,14 @@ bool PrintObject::has_support_material() const
|
|||
|| 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).
|
||||
// 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.
|
||||
|
@ -638,7 +649,9 @@ void PrintObject::detect_surfaces_type()
|
|||
// are completely hidden inside a collective body of intersecting parts.
|
||||
// This is useful if one of the parts is to be dissolved, or if it is transparent and the internal shells
|
||||
// should be visible.
|
||||
bool interface_shells = 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) {
|
||||
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.
|
||||
std::vector<Surfaces> surfaces_new;
|
||||
if (interface_shells)
|
||||
surfaces_new.assign(m_layers.size(), Surfaces());
|
||||
surfaces_new.assign(num_layers, Surfaces());
|
||||
|
||||
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) {
|
||||
// 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 =
|
||||
|
@ -669,7 +687,7 @@ void PrintObject::detect_surfaces_type()
|
|||
m_print->throw_if_canceled();
|
||||
// BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z;
|
||||
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)
|
||||
// unless internal shells are requested
|
||||
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;
|
||||
if (upper_layer) {
|
||||
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);
|
||||
surfaces_append(top,
|
||||
//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(
|
||||
diff(
|
||||
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),
|
||||
-offset, offset),
|
||||
stBottom);
|
||||
|
@ -795,18 +813,25 @@ void PrintObject::detect_surfaces_type()
|
|||
|
||||
if (interface_shells) {
|
||||
// Move surfaces_new to layerm->slices.surfaces
|
||||
for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer)
|
||||
m_layers[idx_layer]->get_region(idx_region)->slices.surfaces = std::move(surfaces_new[idx_layer]);
|
||||
for (size_t idx_layer = 0; idx_layer < num_layers; ++ 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";
|
||||
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
|
||||
tbb::parallel_for(
|
||||
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) {
|
||||
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();
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
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
|
||||
|
||||
// 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()
|
||||
|
@ -912,18 +937,33 @@ void PrintObject::discover_vertical_shells()
|
|||
Polygons bottom_surfaces;
|
||||
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;
|
||||
if (top_bottom_surfaces_all_regions) {
|
||||
// This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
|
||||
// is calculated over all materials.
|
||||
// Is the "ensure vertical wall thickness" applicable to any region?
|
||||
bool has_extra_layers = false;
|
||||
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
|
||||
const PrintRegion ®ion = *m_print->get_region(idx_region);
|
||||
if (region.config().ensure_vertical_shell_thickness.value &&
|
||||
(region.config().top_solid_layers.value > 1 || region.config().bottom_solid_layers.value > 1)) {
|
||||
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++idx_region) {
|
||||
const PrintRegionConfig &config = m_print->get_region(idx_region)->config();
|
||||
if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) {
|
||||
has_extra_layers = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! has_extra_layers)
|
||||
|
@ -931,9 +971,9 @@ void PrintObject::discover_vertical_shells()
|
|||
return;
|
||||
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - start : cache top / bottom";
|
||||
//FIXME Improve the heuristics for a grain size.
|
||||
size_t grain_size = std::max(m_layers.size() / 16, size_t(1));
|
||||
size_t grain_size = std::max(num_layers / 16, size_t(1));
|
||||
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) {
|
||||
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
|
||||
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));
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
{
|
||||
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices));
|
||||
svg.draw(layer.slices, "blue");
|
||||
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.lslices));
|
||||
svg.draw(layer.lslices, "blue");
|
||||
svg.draw(union_ex(cache.holes), "red");
|
||||
svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05));
|
||||
svg.Close();
|
||||
|
@ -1004,21 +1044,19 @@ void PrintObject::discover_vertical_shells()
|
|||
if (! region.config().ensure_vertical_shell_thickness.value)
|
||||
// This region will be handled by discover_horizontal_shells().
|
||||
continue;
|
||||
int n_extra_top_layers = std::max(0, region.config().top_solid_layers.value - 1);
|
||||
int n_extra_bottom_layers = std::max(0, region.config().bottom_solid_layers.value - 1);
|
||||
if (n_extra_top_layers + n_extra_bottom_layers == 0)
|
||||
if (! has_extra_layers_fn(region.config()))
|
||||
// Zero or 1 layer, there is no additional vertical wall thickness enforced.
|
||||
continue;
|
||||
|
||||
//FIXME Improve the heuristics for a grain size.
|
||||
size_t grain_size = std::max(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) {
|
||||
// This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness
|
||||
// is calculated over a single material.
|
||||
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : cache top / bottom";
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<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) {
|
||||
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
|
||||
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";
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size(), grain_size),
|
||||
[this, idx_region, n_extra_top_layers, n_extra_bottom_layers, &cache_top_botom_regions]
|
||||
tbb::blocked_range<size_t>(0, num_layers, grain_size),
|
||||
[this, idx_region, &cache_top_botom_regions]
|
||||
(const tbb::blocked_range<size_t>& range) {
|
||||
// printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end());
|
||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
||||
|
@ -1060,6 +1098,7 @@ void PrintObject::discover_vertical_shells()
|
|||
|
||||
Layer *layer = m_layers[idx_layer];
|
||||
LayerRegion *layerm = layer->m_regions[idx_region];
|
||||
const PrintRegionConfig ®ion_config = layerm->region()->config();
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
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 */
|
||||
// Reset the top / bottom inflated regions caches of entries, which are out of the moving window.
|
||||
bool hole_first = true;
|
||||
for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n)
|
||||
if (n >= 0 && n < (int)m_layers.size()) {
|
||||
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[n];
|
||||
if (hole_first) {
|
||||
hole_first = false;
|
||||
polygons_append(holes, cache.holes);
|
||||
}
|
||||
else if (! holes.empty()) {
|
||||
polygons_append(holes, cache_top_botom_regions[idx_layer].holes);
|
||||
{
|
||||
// Gather top regions projected to this layer.
|
||||
coordf_t print_z = layer->print_z;
|
||||
int n_top_layers = region_config.top_solid_layers.value;
|
||||
for (int i = int(idx_layer) + 1;
|
||||
i < int(cache_top_botom_regions.size()) &&
|
||||
(i < int(idx_layer) + n_top_layers ||
|
||||
m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON);
|
||||
++ i) {
|
||||
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
|
||||
if (! holes.empty())
|
||||
holes = intersection(holes, cache.holes);
|
||||
}
|
||||
size_t n_shell_old = shell.size();
|
||||
if (n > int(idx_layer))
|
||||
// Collect top surfaces.
|
||||
if (! cache.top_surfaces.empty()) {
|
||||
polygons_append(shell, cache.top_surfaces);
|
||||
else if (n < int(idx_layer))
|
||||
// Collect bottom and bottom bridge surfaces.
|
||||
// Running the union_ using the Clipper library piece by piece is cheaper
|
||||
// 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);
|
||||
// Running the union_ using the Clipper library piece by piece is cheaper
|
||||
// than running the union_ all at once.
|
||||
if (n_shell_old < shell.size())
|
||||
shell = union_(shell, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
{
|
||||
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)
|
||||
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)
|
||||
|
@ -1535,7 +1591,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
|||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info();
|
||||
|
||||
this->typed_slices = false;
|
||||
m_typed_slices = false;
|
||||
|
||||
#ifdef SLIC3R_PROFILE
|
||||
// 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.
|
||||
bool clipped = 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)) {
|
||||
// Cheap path: Slice regions without mutual clipping.
|
||||
// The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
|
||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
|
||||
// slicing in parallel
|
||||
std::vector<ExPolygons> expolygons_by_layer = this->slice_region(region_id, slice_zs);
|
||||
std::vector<ExPolygons> expolygons_by_layer = this->slice_region(region_id, slice_zs, slicing_mode);
|
||||
m_print->throw_if_canceled();
|
||||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start";
|
||||
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
|
||||
|
@ -1646,7 +1703,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
|||
else
|
||||
ranges.emplace_back(volumes_and_ranges[j].first);
|
||||
// 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;
|
||||
} else
|
||||
++ 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.
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
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.
|
||||
|
@ -1915,7 +1972,7 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std
|
|||
if (volume->is_modifier())
|
||||
volumes.emplace_back(volume);
|
||||
}
|
||||
out = this->slice_volumes(slice_zs, volumes);
|
||||
out = this->slice_volumes(slice_zs, SlicingMode::Regular, volumes);
|
||||
} else {
|
||||
// Some modifier in this region was split to layer spans.
|
||||
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)
|
||||
ranges.emplace_back(volumes_and_ranges[j].first);
|
||||
// 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()) {
|
||||
out = std::move(this_slices);
|
||||
merge.assign(out.size(), false);
|
||||
|
@ -1972,10 +2029,10 @@ std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType
|
|||
zs.reserve(this->layers().size());
|
||||
for (const Layer *l : this->layers())
|
||||
zs.emplace_back((float)l->slice_z);
|
||||
return this->slice_volumes(zs, volumes);
|
||||
return this->slice_volumes(zs, 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;
|
||||
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) {
|
||||
mesh.transform(m_trafo, true);
|
||||
// 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
|
||||
const Print *print = this->print();
|
||||
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();
|
||||
TriangleMeshSlicer mslicer;
|
||||
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();
|
||||
}
|
||||
}
|
||||
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;
|
||||
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) {
|
||||
mesh.transform(m_trafo, true);
|
||||
// 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
|
||||
TriangleMeshSlicer mslicer;
|
||||
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.
|
||||
mesh.require_shared_vertices();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
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;
|
||||
if (! z.empty() && ! ranges.empty()) {
|
||||
if (ranges.size() == 1 && z.front() >= ranges.front().first && z.back() < ranges.front().second) {
|
||||
// All layers fit into a single range.
|
||||
out = this->slice_volume(z, volume);
|
||||
out = this->slice_volume(z, mode, volume);
|
||||
} else {
|
||||
std::vector<float> z_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));
|
||||
}
|
||||
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());
|
||||
i = 0;
|
||||
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 i = 0; i < m_layers.size(); ++ i) {
|
||||
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 ®ion_config = layerm->region()->config();
|
||||
if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 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)
|
||||
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) {
|
||||
m_print->throw_if_canceled();
|
||||
SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge;
|
||||
|
@ -2321,10 +2381,15 @@ void PrintObject::discover_horizontal_shells()
|
|||
continue;
|
||||
// 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;
|
||||
for (int n = (type == stTop) ? i-1 : i+1; std::abs(n - (int)i) < solid_layers; (type == stTop) ? -- n : ++ n) {
|
||||
if (n < 0 || n >= int(m_layers.size()))
|
||||
continue;
|
||||
// Scatter top / bottom regions to other layers. Scattering process is inherently serial, it is difficult to parallelize without locking.
|
||||
for (int n = (type == stTop) ? int(i) - 1 : int(i) + 1;
|
||||
(type == stTop) ?
|
||||
(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;
|
||||
// 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];
|
||||
|
@ -2412,7 +2477,8 @@ void PrintObject::discover_horizontal_shells()
|
|||
// is grown, and that little space is an internal solid shell so
|
||||
// it triggers this too_narrow logic.)
|
||||
internal));
|
||||
solid = new_internal_solid;
|
||||
// see https://github.com/prusa3d/PrusaSlicer/pull/3426
|
||||
// solid = new_internal_solid;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef SLABOOSTADAPTER_HPP
|
||||
#define SLABOOSTADAPTER_HPP
|
||||
#ifndef SLA_BOOSTADAPTER_HPP
|
||||
#define SLA_BOOSTADAPTER_HPP
|
||||
|
||||
#include "SLA/SLABoilerPlate.hpp"
|
||||
#include <libslic3r/SLA/Common.hpp>
|
||||
#include <boost/geometry.hpp>
|
||||
|
||||
namespace boost {
|
30
src/libslic3r/SLA/Clustering.hpp
Normal 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
|
769
src/libslic3r/SLA/Common.cpp
Normal 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
|
32
src/libslic3r/SLA/Common.hpp
Normal 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
|
|
@ -1,7 +1,9 @@
|
|||
#include "ConcaveHull.hpp"
|
||||
#include <libslic3r/SLA/ConcaveHull.hpp>
|
||||
#include <libslic3r/SLA/SpatIndex.hpp>
|
||||
|
||||
#include <libslic3r/MTUtils.hpp>
|
||||
#include <libslic3r/ClipperUtils.hpp>
|
||||
#include "SLASpatIndex.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
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
|
||||
// mode
|
||||
ClipperLib::Paths fast_offset(const ClipperLib::Paths &paths,
|
||||
static ClipperLib::Paths fast_offset(const ClipperLib::Paths &paths,
|
||||
coord_t delta,
|
||||
ClipperLib::JoinType jointype)
|
||||
{
|
||||
|
@ -73,7 +75,7 @@ Points ConcaveHull::calculate_centroids() const
|
|||
Points centroids = reserve_vector<Point>(m_polys.size());
|
||||
std::transform(m_polys.begin(), m_polys.end(),
|
||||
std::back_inserter(centroids),
|
||||
[this](const Polygon &poly) { return centroid(poly); });
|
||||
[](const Polygon &poly) { return centroid(poly); });
|
||||
|
||||
return centroids;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef CONCAVEHULL_HPP
|
||||
#define CONCAVEHULL_HPP
|
||||
#ifndef SLA_CONCAVEHULL_HPP
|
||||
#define SLA_CONCAVEHULL_HPP
|
||||
|
||||
#include <libslic3r/ExPolygon.hpp>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef SLACONCURRENCY_H
|
||||
#define SLACONCURRENCY_H
|
||||
#ifndef SLA_CONCURRENCY_H
|
||||
#define SLA_CONCURRENCY_H
|
||||
|
||||
#include <tbb/spin_mutex.h>
|
||||
#include <tbb/mutex.h>
|
149
src/libslic3r/SLA/Contour3D.cpp
Normal 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
|
45
src/libslic3r/SLA/Contour3D.hpp
Normal 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
|
160
src/libslic3r/SLA/EigenMesh3D.hpp
Normal 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
|
277
src/libslic3r/SLA/Hollowing.cpp
Normal 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
|
75
src/libslic3r/SLA/Hollowing.hpp
Normal 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
|