diff --git a/CMakeLists.txt b/CMakeLists.txt index f31c0eac1e..e4e686645f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 "$/$") + else () + set (_out_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) diff --git a/cmake/modules/CheckAtomic.cmake b/cmake/modules/CheckAtomic.cmake new file mode 100644 index 0000000000..c045e30b22 --- /dev/null +++ b/cmake/modules/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 +std::atomic 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 +#include +std::atomic 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 works. +CHECK_CXX_SOURCE_COMPILES(" +#ifdef _MSC_VER +#include +#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/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake index b75bc6ed01..bddc346b9f 100644 --- a/cmake/modules/FindOpenVDB.cmake +++ b/cmake/modules/FindOpenVDB.cmake @@ -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,25 +521,34 @@ 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}" - IMPORTED_LOCATION_DEBUG "${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}" - ) - else () - set_target_properties(OpenVDB::${COMPONENT} PROPERTIES - IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}" - ) - endif () + 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}" + ) + endif () + endif () if (OPENVDB_USE_STATIC_LIBS) set_target_properties(OpenVDB::${COMPONENT} PROPERTIES @@ -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() diff --git a/cmake/modules/Findcereal.cmake b/cmake/modules/Findcereal.cmake new file mode 100644 index 0000000000..b4829757e0 --- /dev/null +++ b/cmake/modules/Findcereal.cmake @@ -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() diff --git a/cmake/modules/OpenVDBUtils.cmake b/cmake/modules/OpenVDBUtils.cmake index bb3ce6e65d..f64eda6f2c 100644 --- a/cmake/modules/OpenVDBUtils.cmake +++ b/cmake/modules/OpenVDBUtils.cmake @@ -125,7 +125,9 @@ function(OPENVDB_ABI_VERSION_FROM_PRINT OPENVDB_PRINT) cmake_parse_arguments(_VDB "QUIET" "ABI" "" ${ARGN}) if(NOT EXISTS ${OPENVDB_PRINT}) - message(WARNING "vdb_print not found! ${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}) - message(WARNING "vdb_print returned with status ${_VDB_PRINT_RETURN_STATUS}") + if(NOT OpenVDB_FIND_QUIETLY) + message(WARNING "vdb_print returned with status ${_VDB_PRINT_RETURN_STATUS}") + endif() return() endif() diff --git a/deps/CGAL/CGAL.cmake b/deps/CGAL/CGAL.cmake index 285d819ab3..f0584c5b58 100644 --- a/deps/CGAL/CGAL.cmake +++ b/deps/CGAL/CGAL.cmake @@ -17,4 +17,4 @@ ExternalProject_Add_Step(dep_CGAL dep_CGAL_relocation_fix DEPENDEES install COMMAND ${CMAKE_COMMAND} -E remove CGALConfig-installation-dirs.cmake WORKING_DIRECTORY "${DESTDIR}/usr/local/${CMAKE_INSTALL_LIBDIR}/cmake/CGAL" -) \ No newline at end of file +) diff --git a/deps/GMP/GMP.cmake b/deps/GMP/GMP.cmake index 8bcf948592..4e8228cbac 100644 --- a/deps/GMP/GMP.cmake +++ b/deps/GMP/GMP.cmake @@ -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 diff --git a/deps/openvdb-mods.patch b/deps/openvdb-mods.patch index 023cb53087..d80d0ffde1 100644 --- a/deps/openvdb-mods.patch +++ b/deps/openvdb-mods.patch @@ -1,24 +1,25 @@ -From dbe038fce8a15ddc9a5c83ec5156d7bc9e178015 Mon Sep 17 00:00:00 2001 +From d359098d9989ac7dbd149611d6ac941529fb4157 Mon Sep 17 00:00:00 2001 From: tamasmeszaros -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 --- 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 ++std::atomic 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 ++#include ++std::atomic 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 works. ++CHECK_CXX_SOURCE_COMPILES(" ++#ifdef _MSC_VER ++#include ++#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 "") @@ -1004,7 +1118,7 @@ index 63a2eda..6211071 100644 ) + + if (OPENVDB_USE_STATIC_LIBS) -+ set_target_properties(OpenVDB::${COMPONENT} PROPERTIES ++ set_target_properties(OpenVDB::${COMPONENT} PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "OPENVDB_STATICLIB;OPENVDB_OPENEXR_STATICLIB" + ) + endif() @@ -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,19 +1249,26 @@ 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...]] -+# [OPTIONAL_COMPONENTS components...]) ++# [OPTIONAL_COMPONENTS components...]) +# -+# where the allowed components are tbbmalloc and tbb_preview. Users may modify ++# where the allowed components are tbbmalloc and tbb_preview. Users may modify +# the behavior of this module with the following variables: +# +# * TBB_ROOT_DIR - The base directory the of TBB installation. +# * TBB_INCLUDE_DIR - The directory that contains the TBB headers files. +# * TBB_LIBRARY - The directory that contains the TBB library files. -+# * TBB__LIBRARY - The path of the TBB the corresponding TBB library. -+# These libraries, if specified, override the ++# * TBB__LIBRARY - The path of the TBB the corresponding TBB library. ++# These libraries, if specified, override the +# corresponding library search results, where +# may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug, +# tbb_preview, or tbb_preview_debug. @@ -1167,7 +1281,7 @@ index bdf9c81..c6bdec9 100644 +# Users may modify the behavior of this module with the following environment +# variables: +# -+# * TBB_INSTALL_DIR ++# * TBB_INSTALL_DIR +# * TBBROOT +# * LIBRARY_PATH +# @@ -1180,15 +1294,15 @@ index bdf9c81..c6bdec9 100644 +# * TBB_VERSION - The full version string +# * TBB_VERSION_MAJOR - The major version +# * TBB_VERSION_MINOR - The minor version -+# * TBB_INTERFACE_VERSION - The interface version number defined in ++# * TBB_INTERFACE_VERSION - The interface version number defined in +# tbb/tbb_stddef.h. -+# * TBB__LIBRARY_RELEASE - The path of the TBB release version of ++# * TBB__LIBRARY_RELEASE - The path of the TBB release version of +# , where may be tbb, tbb_debug, -+# tbbmalloc, tbbmalloc_debug, tbb_preview, or ++# tbbmalloc, tbbmalloc_debug, tbb_preview, or +# tbb_preview_debug. -+# * TBB__LIBRARY_DEGUG - The path of the TBB release version of ++# * TBB__LIBRARY_DEGUG - The path of the TBB release version of +# , where may be tbb, tbb_debug, -+# tbbmalloc, tbbmalloc_debug, tbb_preview, or ++# tbbmalloc, tbbmalloc_debug, tbb_preview, or +# tbb_preview_debug. +# +# The following varibles should be used to build and link with TBB: @@ -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 /gcc4.1 or -+ # /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 /gcc4.1 or ++ # /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() +- endif() +- endif() ++ ################################## ++ # Find the TBB include dir ++ ################################## + -+ # 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) ++ find_path(TBB_INCLUDE_DIRS tbb/tbb.h ++ HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR} ++ PATHS ${TBB_DEFAULT_SEARCH_DIR} ++ PATH_SUFFIXES include) + - endif() -- endif() -+ endforeach() ++ ################################## ++ # 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};$<$,$>:${TBB_DEFINITIONS_DEBUG}>;$<$:${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}") -+ 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}) ++ ++ 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};$<$,$>:${TBB_DEFINITIONS_DEBUG}>;$<$:${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 diff --git a/resources/icons/edit_gcode_f.svg b/resources/icons/edit_gcode_f.svg new file mode 100644 index 0000000000..980060d9bb --- /dev/null +++ b/resources/icons/edit_gcode_f.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/resources/icons/error_tick_f.svg b/resources/icons/error_tick_f.svg new file mode 100644 index 0000000000..adf876e3c6 --- /dev/null +++ b/resources/icons/error_tick_f.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/resources/icons/export_to_sd.svg b/resources/icons/export_to_sd.svg index c836b00a17..516cec4355 100644 --- a/resources/icons/export_to_sd.svg +++ b/resources/icons/export_to_sd.svg @@ -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"> + + + + + + @@ -33,111 +47,120 @@ - - + transform="translate(-265.33929,12.851203)" + id="layer1-0"> + transform="matrix(1.31769,0,0,1.31769,-167.28747,-111.35623)" + id="layer1-3"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="g4685" + transform="matrix(0.00352778,0,0,-0.00352778,105.26858,151.76571)"> + 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="path4687" /> + 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="path4689" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/hollow.svg b/resources/icons/hollow.svg new file mode 100644 index 0000000000..13e96ada96 --- /dev/null +++ b/resources/icons/hollow.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/hollowing.svg b/resources/icons/hollowing.svg new file mode 100644 index 0000000000..65c7592c83 --- /dev/null +++ b/resources/icons/hollowing.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/mode_advanced.svg b/resources/icons/mode_advanced.svg new file mode 100644 index 0000000000..79f68f5b9d --- /dev/null +++ b/resources/icons/mode_advanced.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/resources/icons/mode_expert.svg b/resources/icons/mode_expert.svg new file mode 100644 index 0000000000..aaa6d0e4da --- /dev/null +++ b/resources/icons/mode_expert.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/resources/icons/mode_simple.svg b/resources/icons/mode_simple.svg new file mode 100644 index 0000000000..b94b7112cd --- /dev/null +++ b/resources/icons/mode_simple.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/resources/icons/pause_print_f.svg b/resources/icons/pause_print_f.svg new file mode 100644 index 0000000000..ee1c734708 --- /dev/null +++ b/resources/icons/pause_print_f.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/resources/icons/sla_printer.svg b/resources/icons/sla_printer.svg new file mode 100644 index 0000000000..ab63083e30 --- /dev/null +++ b/resources/icons/sla_printer.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + diff --git a/resources/icons/white/hollowing.svg b/resources/icons/white/hollowing.svg new file mode 100644 index 0000000000..65c7592c83 --- /dev/null +++ b/resources/icons/white/hollowing.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/white/sla_printer.svg b/resources/icons/white/sla_printer.svg new file mode 100644 index 0000000000..3d47e7da62 --- /dev/null +++ b/resources/icons/white/sla_printer.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + diff --git a/resources/localization/PrusaSlicer.pot b/resources/localization/PrusaSlicer.pot index 77539a7a35..6236ed8a5b 100644 --- a/resources/localization/PrusaSlicer.pot +++ b/resources/localization/PrusaSlicer.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-12-06 13:53+0100\n" +"POT-Creation-Date: 2020-02-12 14:01+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -38,7 +38,7 @@ msgstr "" msgid "About %s" msgstr "" -#: src/slic3r/GUI/AboutDialog.cpp:231 src/slic3r/GUI/MainFrame.cpp:63 +#: src/slic3r/GUI/AboutDialog.cpp:231 src/slic3r/GUI/MainFrame.cpp:64 msgid "Version" msgstr "" @@ -64,50 +64,38 @@ msgid "" "numerous others." msgstr "" -#: src/slic3r/GUI/AppConfig.cpp:105 +#: src/slic3r/GUI/AppConfig.cpp:118 msgid "" "Error parsing PrusaSlicer config file, it is probably corrupted. Try to " "manually delete the file to recover from the error. Your user profiles will " "not be affected." msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:100 -msgid "" -"To except of redundant tool manipulation, \n" -"Color change(s) for unused extruder(s) was(were) deleted" -msgstr "" - -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:101 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3292 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3367 src/slic3r/GUI/Plater.cpp:135 -msgid "Info" -msgstr "" - -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:110 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:118 msgid "" "Copying of the temporary G-code to the output G-code failed. Maybe the SD " "card is write locked?" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:111 -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:461 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:120 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:470 msgid "Running post-processing scripts" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:113 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:122 msgid "G-code file exported to %1%" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:117 -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:167 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:126 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:176 msgid "Slicing complete" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:163 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:172 msgid "Masked SLA file exported to %1%" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:205 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:214 #, possible-c-format msgid "" "%s has encountered an error. It was likely caused by running out of memory. " @@ -115,158 +103,160 @@ msgid "" "and we would be glad if you reported it." msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:463 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:472 msgid "Copying of the temporary G-code to the output G-code failed" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:488 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:497 msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:68 src/slic3r/GUI/GUI_ObjectList.cpp:1932 +#: src/slic3r/GUI/BedShapeDialog.cpp:66 src/slic3r/GUI/GUI_ObjectList.cpp:2033 msgid "Shape" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:75 +#: src/slic3r/GUI/BedShapeDialog.cpp:73 msgid "Rectangular" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:79 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:232 src/slic3r/GUI/Plater.cpp:154 -#: src/slic3r/GUI/Tab.cpp:2292 +#: src/slic3r/GUI/BedShapeDialog.cpp:77 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:232 src/slic3r/GUI/Plater.cpp:162 +#: src/slic3r/GUI/Tab.cpp:2320 msgid "Size" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:80 +#: src/slic3r/GUI/BedShapeDialog.cpp:78 msgid "Size in X and Y of the rectangular plate." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:86 +#: src/slic3r/GUI/BedShapeDialog.cpp:84 msgid "Origin" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:87 +#: src/slic3r/GUI/BedShapeDialog.cpp:85 msgid "" "Distance of the 0,0 G-code coordinate from the front left corner of the " "rectangle." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:91 +#: src/slic3r/GUI/BedShapeDialog.cpp:89 msgid "Circular" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:94 src/slic3r/GUI/ConfigWizard.cpp:218 -#: src/slic3r/GUI/ConfigWizard.cpp:926 src/slic3r/GUI/ConfigWizard.cpp:940 +#: src/slic3r/GUI/BedShapeDialog.cpp:92 src/slic3r/GUI/ConfigWizard.cpp:218 +#: src/slic3r/GUI/ConfigWizard.cpp:971 src/slic3r/GUI/ConfigWizard.cpp:985 +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:87 #: src/slic3r/GUI/GUI_ObjectLayers.cpp:135 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:333 -#: src/slic3r/GUI/WipeTowerDialog.cpp:85 src/slic3r/GUI/wxExtensions.cpp:628 -#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:87 -#: src/libslic3r/PrintConfig.cpp:75 src/libslic3r/PrintConfig.cpp:82 -#: src/libslic3r/PrintConfig.cpp:91 src/libslic3r/PrintConfig.cpp:225 -#: src/libslic3r/PrintConfig.cpp:300 src/libslic3r/PrintConfig.cpp:308 -#: src/libslic3r/PrintConfig.cpp:358 src/libslic3r/PrintConfig.cpp:368 -#: src/libslic3r/PrintConfig.cpp:494 src/libslic3r/PrintConfig.cpp:505 -#: src/libslic3r/PrintConfig.cpp:523 src/libslic3r/PrintConfig.cpp:702 -#: src/libslic3r/PrintConfig.cpp:1228 src/libslic3r/PrintConfig.cpp:1289 -#: src/libslic3r/PrintConfig.cpp:1307 src/libslic3r/PrintConfig.cpp:1325 -#: src/libslic3r/PrintConfig.cpp:1379 src/libslic3r/PrintConfig.cpp:1389 -#: src/libslic3r/PrintConfig.cpp:1511 src/libslic3r/PrintConfig.cpp:1519 -#: src/libslic3r/PrintConfig.cpp:1560 src/libslic3r/PrintConfig.cpp:1568 -#: src/libslic3r/PrintConfig.cpp:1578 src/libslic3r/PrintConfig.cpp:1586 -#: src/libslic3r/PrintConfig.cpp:1594 src/libslic3r/PrintConfig.cpp:1677 -#: src/libslic3r/PrintConfig.cpp:1903 src/libslic3r/PrintConfig.cpp:1974 -#: src/libslic3r/PrintConfig.cpp:2008 src/libslic3r/PrintConfig.cpp:2203 -#: src/libslic3r/PrintConfig.cpp:2210 src/libslic3r/PrintConfig.cpp:2217 -#: src/libslic3r/PrintConfig.cpp:2247 src/libslic3r/PrintConfig.cpp:2257 -#: src/libslic3r/PrintConfig.cpp:2267 src/libslic3r/PrintConfig.cpp:2452 -#: src/libslic3r/PrintConfig.cpp:2591 src/libslic3r/PrintConfig.cpp:2600 -#: src/libslic3r/PrintConfig.cpp:2609 src/libslic3r/PrintConfig.cpp:2619 -#: src/libslic3r/PrintConfig.cpp:2663 src/libslic3r/PrintConfig.cpp:2673 -#: src/libslic3r/PrintConfig.cpp:2685 src/libslic3r/PrintConfig.cpp:2705 -#: src/libslic3r/PrintConfig.cpp:2715 src/libslic3r/PrintConfig.cpp:2725 -#: src/libslic3r/PrintConfig.cpp:2743 src/libslic3r/PrintConfig.cpp:2758 -#: src/libslic3r/PrintConfig.cpp:2772 src/libslic3r/PrintConfig.cpp:2783 -#: src/libslic3r/PrintConfig.cpp:2796 src/libslic3r/PrintConfig.cpp:2841 -#: src/libslic3r/PrintConfig.cpp:2851 src/libslic3r/PrintConfig.cpp:2860 -#: src/libslic3r/PrintConfig.cpp:2870 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:94 +#: src/slic3r/GUI/WipeTowerDialog.cpp:85 src/libslic3r/PrintConfig.cpp:75 +#: src/libslic3r/PrintConfig.cpp:82 src/libslic3r/PrintConfig.cpp:91 +#: src/libslic3r/PrintConfig.cpp:178 src/libslic3r/PrintConfig.cpp:236 +#: src/libslic3r/PrintConfig.cpp:311 src/libslic3r/PrintConfig.cpp:319 +#: src/libslic3r/PrintConfig.cpp:369 src/libslic3r/PrintConfig.cpp:379 +#: src/libslic3r/PrintConfig.cpp:505 src/libslic3r/PrintConfig.cpp:516 +#: src/libslic3r/PrintConfig.cpp:534 src/libslic3r/PrintConfig.cpp:712 +#: src/libslic3r/PrintConfig.cpp:1231 src/libslic3r/PrintConfig.cpp:1292 +#: src/libslic3r/PrintConfig.cpp:1310 src/libslic3r/PrintConfig.cpp:1328 +#: src/libslic3r/PrintConfig.cpp:1384 src/libslic3r/PrintConfig.cpp:1394 +#: src/libslic3r/PrintConfig.cpp:1516 src/libslic3r/PrintConfig.cpp:1524 +#: src/libslic3r/PrintConfig.cpp:1565 src/libslic3r/PrintConfig.cpp:1573 +#: src/libslic3r/PrintConfig.cpp:1583 src/libslic3r/PrintConfig.cpp:1591 +#: src/libslic3r/PrintConfig.cpp:1599 src/libslic3r/PrintConfig.cpp:1682 +#: src/libslic3r/PrintConfig.cpp:1914 src/libslic3r/PrintConfig.cpp:1985 +#: src/libslic3r/PrintConfig.cpp:2019 src/libslic3r/PrintConfig.cpp:2147 +#: src/libslic3r/PrintConfig.cpp:2226 src/libslic3r/PrintConfig.cpp:2233 +#: src/libslic3r/PrintConfig.cpp:2240 src/libslic3r/PrintConfig.cpp:2270 +#: src/libslic3r/PrintConfig.cpp:2280 src/libslic3r/PrintConfig.cpp:2290 +#: src/libslic3r/PrintConfig.cpp:2475 src/libslic3r/PrintConfig.cpp:2614 +#: src/libslic3r/PrintConfig.cpp:2623 src/libslic3r/PrintConfig.cpp:2632 +#: src/libslic3r/PrintConfig.cpp:2642 src/libslic3r/PrintConfig.cpp:2686 +#: src/libslic3r/PrintConfig.cpp:2696 src/libslic3r/PrintConfig.cpp:2708 +#: src/libslic3r/PrintConfig.cpp:2728 src/libslic3r/PrintConfig.cpp:2738 +#: src/libslic3r/PrintConfig.cpp:2748 src/libslic3r/PrintConfig.cpp:2766 +#: src/libslic3r/PrintConfig.cpp:2781 src/libslic3r/PrintConfig.cpp:2795 +#: src/libslic3r/PrintConfig.cpp:2806 src/libslic3r/PrintConfig.cpp:2819 +#: src/libslic3r/PrintConfig.cpp:2864 src/libslic3r/PrintConfig.cpp:2874 +#: src/libslic3r/PrintConfig.cpp:2883 src/libslic3r/PrintConfig.cpp:2893 +#: src/libslic3r/PrintConfig.cpp:2909 msgid "mm" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:95 src/libslic3r/PrintConfig.cpp:699 +#: src/slic3r/GUI/BedShapeDialog.cpp:93 src/libslic3r/PrintConfig.cpp:709 msgid "Diameter" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:96 +#: src/slic3r/GUI/BedShapeDialog.cpp:94 msgid "" "Diameter of the print bed. It is assumed that origin (0,0) is located in the " "center." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:100 src/slic3r/GUI/GUI_Preview.cpp:248 +#: src/slic3r/GUI/BedShapeDialog.cpp:98 src/slic3r/GUI/GUI_Preview.cpp:251 #: src/libslic3r/ExtrusionEntity.cpp:322 msgid "Custom" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:104 +#: src/slic3r/GUI/BedShapeDialog.cpp:102 msgid "Load shape from STL..." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:157 +#: src/slic3r/GUI/BedShapeDialog.cpp:155 msgid "Settings" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:174 +#: src/slic3r/GUI/BedShapeDialog.cpp:172 msgid "Texture" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:184 src/slic3r/GUI/BedShapeDialog.cpp:263 +#: src/slic3r/GUI/BedShapeDialog.cpp:182 src/slic3r/GUI/BedShapeDialog.cpp:261 msgid "Load..." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:192 src/slic3r/GUI/BedShapeDialog.cpp:271 -#: src/slic3r/GUI/Tab.cpp:3080 +#: src/slic3r/GUI/BedShapeDialog.cpp:190 src/slic3r/GUI/BedShapeDialog.cpp:269 +#: src/slic3r/GUI/Tab.cpp:3108 msgid "Remove" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:225 src/slic3r/GUI/BedShapeDialog.cpp:304 +#: src/slic3r/GUI/BedShapeDialog.cpp:223 src/slic3r/GUI/BedShapeDialog.cpp:302 msgid "Not found: " msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:253 +#: src/slic3r/GUI/BedShapeDialog.cpp:251 msgid "Model" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:489 +#: src/slic3r/GUI/BedShapeDialog.cpp:487 msgid "Choose an STL file to import bed shape from:" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:496 src/slic3r/GUI/BedShapeDialog.cpp:545 -#: src/slic3r/GUI/BedShapeDialog.cpp:570 +#: src/slic3r/GUI/BedShapeDialog.cpp:494 src/slic3r/GUI/BedShapeDialog.cpp:543 +#: src/slic3r/GUI/BedShapeDialog.cpp:566 msgid "Invalid file format." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:507 +#: src/slic3r/GUI/BedShapeDialog.cpp:505 msgid "Error! Invalid model" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:515 +#: src/slic3r/GUI/BedShapeDialog.cpp:513 msgid "The selected file contains no geometry." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:519 +#: src/slic3r/GUI/BedShapeDialog.cpp:517 msgid "" "The selected file contains several disjoint areas. This is not supported." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:534 +#: src/slic3r/GUI/BedShapeDialog.cpp:532 msgid "Choose a file to import bed texture from (PNG/SVG):" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:559 +#: src/slic3r/GUI/BedShapeDialog.cpp:555 msgid "Choose an STL file to import bed model from:" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.hpp:59 src/slic3r/GUI/ConfigWizard.cpp:885 +#: src/slic3r/GUI/BedShapeDialog.hpp:59 src/slic3r/GUI/ConfigWizard.cpp:930 msgid "Bed Shape" msgstr "" @@ -320,7 +310,7 @@ msgid "" msgstr "" #: src/slic3r/GUI/ConfigManipulation.cpp:49 -#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 src/slic3r/GUI/Tab.cpp:1039 +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 src/slic3r/GUI/Tab.cpp:1051 #: src/libslic3r/PrintConfig.cpp:71 msgid "Layer height" msgstr "" @@ -332,11 +322,11 @@ msgid "" "The first layer height will be reset to 0.01." msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:61 src/libslic3r/PrintConfig.cpp:878 +#: src/slic3r/GUI/ConfigManipulation.cpp:61 src/libslic3r/PrintConfig.cpp:889 msgid "First layer height" msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:75 +#: src/slic3r/GUI/ConfigManipulation.cpp:81 #, no-c-format msgid "" "The Spiral Vase mode requires:\n" @@ -344,18 +334,19 @@ msgid "" "- no top solid layers\n" "- 0% fill density\n" "- no support material\n" -"- inactive Ensure vertical shell thickness" +"- Ensure vertical shell thickness enabled\n" +"- Detect thin walls disabled" msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:82 +#: src/slic3r/GUI/ConfigManipulation.cpp:89 msgid "Shall I adjust those settings in order to enable Spiral Vase?" msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:83 +#: src/slic3r/GUI/ConfigManipulation.cpp:90 msgid "Spiral Vase" msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:107 +#: src/slic3r/GUI/ConfigManipulation.cpp:115 msgid "" "The Wipe Tower currently supports the non-soluble supports only\n" "if they are printed with the current extruder without triggering a tool " @@ -364,74 +355,74 @@ msgid "" "to be set to 0)." msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:111 +#: src/slic3r/GUI/ConfigManipulation.cpp:119 msgid "Shall I adjust those settings in order to enable the Wipe Tower?" msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:112 -#: src/slic3r/GUI/ConfigManipulation.cpp:132 +#: src/slic3r/GUI/ConfigManipulation.cpp:120 +#: src/slic3r/GUI/ConfigManipulation.cpp:140 msgid "Wipe Tower" msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:128 +#: src/slic3r/GUI/ConfigManipulation.cpp:136 msgid "" "For the Wipe Tower to work with the soluble supports, the support layers\n" "need to be synchronized with the object layers." msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:131 +#: src/slic3r/GUI/ConfigManipulation.cpp:139 msgid "Shall I synchronize support layers in order to enable the Wipe Tower?" msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:151 +#: src/slic3r/GUI/ConfigManipulation.cpp:159 msgid "" "Supports work better, if the following feature is enabled:\n" "- Detect bridging perimeters" msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:154 +#: src/slic3r/GUI/ConfigManipulation.cpp:162 msgid "Shall I adjust those settings for supports?" msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:155 +#: src/slic3r/GUI/ConfigManipulation.cpp:163 msgid "Support Generator" msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:200 +#: src/slic3r/GUI/ConfigManipulation.cpp:208 msgid "The %1% infill pattern is not supposed to work at 100%% density." msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:202 +#: src/slic3r/GUI/ConfigManipulation.cpp:210 msgid "Shall I switch to rectilinear fill pattern?" msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:203 -#: src/slic3r/GUI/GUI_ObjectList.cpp:35 src/slic3r/GUI/GUI_ObjectList.cpp:89 -#: src/slic3r/GUI/GUI_ObjectList.cpp:609 src/slic3r/GUI/Plater.cpp:516 -#: src/slic3r/GUI/Tab.cpp:1071 src/slic3r/GUI/Tab.cpp:1072 -#: src/libslic3r/PrintConfig.cpp:182 src/libslic3r/PrintConfig.cpp:405 -#: src/libslic3r/PrintConfig.cpp:425 src/libslic3r/PrintConfig.cpp:765 -#: src/libslic3r/PrintConfig.cpp:779 src/libslic3r/PrintConfig.cpp:816 -#: src/libslic3r/PrintConfig.cpp:970 src/libslic3r/PrintConfig.cpp:980 -#: src/libslic3r/PrintConfig.cpp:998 src/libslic3r/PrintConfig.cpp:1017 -#: src/libslic3r/PrintConfig.cpp:1036 src/libslic3r/PrintConfig.cpp:1724 -#: src/libslic3r/PrintConfig.cpp:1741 +#: src/slic3r/GUI/ConfigManipulation.cpp:211 +#: src/slic3r/GUI/GUI_ObjectList.cpp:35 src/slic3r/GUI/GUI_ObjectList.cpp:94 +#: src/slic3r/GUI/GUI_ObjectList.cpp:612 src/slic3r/GUI/Plater.cpp:524 +#: src/slic3r/GUI/Tab.cpp:1093 src/slic3r/GUI/Tab.cpp:1094 +#: src/libslic3r/PrintConfig.cpp:193 src/libslic3r/PrintConfig.cpp:416 +#: src/libslic3r/PrintConfig.cpp:436 src/libslic3r/PrintConfig.cpp:776 +#: src/libslic3r/PrintConfig.cpp:790 src/libslic3r/PrintConfig.cpp:827 +#: src/libslic3r/PrintConfig.cpp:981 src/libslic3r/PrintConfig.cpp:991 +#: src/libslic3r/PrintConfig.cpp:1009 src/libslic3r/PrintConfig.cpp:1028 +#: src/libslic3r/PrintConfig.cpp:1047 src/libslic3r/PrintConfig.cpp:1728 +#: src/libslic3r/PrintConfig.cpp:1745 msgid "Infill" msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:304 +#: src/slic3r/GUI/ConfigManipulation.cpp:317 msgid "Head penetration should not be greater than the head width." msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:306 +#: src/slic3r/GUI/ConfigManipulation.cpp:319 msgid "Invalid Head penetration" msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:317 +#: src/slic3r/GUI/ConfigManipulation.cpp:330 msgid "Pinhead diameter should be smaller than the pillar diameter." msgstr "" -#: src/slic3r/GUI/ConfigManipulation.cpp:319 +#: src/slic3r/GUI/ConfigManipulation.cpp:332 msgid "Invalid pinhead diameter" msgstr "" @@ -463,7 +454,7 @@ msgstr "" msgid "PrusaSlicer version" msgstr "" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:51 src/slic3r/GUI/Preset.cpp:1408 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:51 src/slic3r/GUI/Preset.cpp:1471 msgid "print" msgstr "" @@ -471,11 +462,11 @@ msgstr "" msgid "filaments" msgstr "" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:53 src/slic3r/GUI/Preset.cpp:1412 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:53 src/slic3r/GUI/Preset.cpp:1475 msgid "printer" msgstr "" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:57 src/slic3r/GUI/Tab.cpp:961 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:57 src/slic3r/GUI/Tab.cpp:973 msgid "vendor" msgstr "" @@ -528,90 +519,90 @@ msgstr "" msgid "Standard" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:290 src/slic3r/GUI/ConfigWizard.cpp:545 -#: src/slic3r/GUI/Tab.cpp:3130 +#: src/slic3r/GUI/ConfigWizard.cpp:290 src/slic3r/GUI/ConfigWizard.cpp:573 +#: src/slic3r/GUI/Tab.cpp:3158 msgid "All" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:291 src/slic3r/GUI/ConfigWizard.cpp:546 -#: src/slic3r/GUI/Plater.cpp:488 src/slic3r/GUI/Plater.cpp:628 +#: src/slic3r/GUI/ConfigWizard.cpp:291 src/slic3r/GUI/ConfigWizard.cpp:574 +#: src/slic3r/GUI/Plater.cpp:496 src/slic3r/GUI/Plater.cpp:636 #: src/libslic3r/ExtrusionEntity.cpp:309 msgid "None" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:412 +#: src/slic3r/GUI/ConfigWizard.cpp:427 #, possible-c-format msgid "Welcome to the %s Configuration Assistant" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:414 +#: src/slic3r/GUI/ConfigWizard.cpp:429 #, possible-c-format msgid "Welcome to the %s Configuration Wizard" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:416 +#: src/slic3r/GUI/ConfigWizard.cpp:431 msgid "Welcome" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:418 +#: src/slic3r/GUI/ConfigWizard.cpp:433 #, possible-c-format msgid "" "Hello, welcome to %s! This %s helps you with the initial configuration; just " "a few settings and you will be ready to print." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:423 +#: src/slic3r/GUI/ConfigWizard.cpp:438 msgid "" "Remove user profiles - install from scratch (a snapshot will be taken " "beforehand)" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:466 +#: src/slic3r/GUI/ConfigWizard.cpp:481 #, possible-c-format msgid "%s Family" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:537 +#: src/slic3r/GUI/ConfigWizard.cpp:565 msgid "Vendor:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:538 +#: src/slic3r/GUI/ConfigWizard.cpp:566 msgid "Profile:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:575 src/slic3r/GUI/ConfigWizard.cpp:603 +#: src/slic3r/GUI/ConfigWizard.cpp:603 src/slic3r/GUI/ConfigWizard.cpp:631 msgid "(All)" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:704 +#: src/slic3r/GUI/ConfigWizard.cpp:732 msgid "Custom Printer Setup" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:704 +#: src/slic3r/GUI/ConfigWizard.cpp:732 msgid "Custom Printer" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:706 +#: src/slic3r/GUI/ConfigWizard.cpp:734 msgid "Define a custom printer profile" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:708 +#: src/slic3r/GUI/ConfigWizard.cpp:736 msgid "Custom profile name:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:732 +#: src/slic3r/GUI/ConfigWizard.cpp:760 msgid "Automatic updates" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:732 +#: src/slic3r/GUI/ConfigWizard.cpp:760 msgid "Updates" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:740 src/slic3r/GUI/Preferences.cpp:69 +#: src/slic3r/GUI/ConfigWizard.cpp:768 src/slic3r/GUI/Preferences.cpp:64 msgid "Check for application updates" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:744 +#: src/slic3r/GUI/ConfigWizard.cpp:772 #, possible-c-format msgid "" "If enabled, %s checks for new application versions online. When a new " @@ -620,11 +611,11 @@ msgid "" "notification mechanisms, no automatic installation is done." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:750 src/slic3r/GUI/Preferences.cpp:77 +#: src/slic3r/GUI/ConfigWizard.cpp:778 src/slic3r/GUI/Preferences.cpp:82 msgid "Update built-in Presets automatically" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:754 +#: src/slic3r/GUI/ConfigWizard.cpp:782 #, possible-c-format msgid "" "If enabled, %s downloads updates of built-in system presets in the " @@ -633,23 +624,42 @@ msgid "" "startup." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:757 +#: src/slic3r/GUI/ConfigWizard.cpp:785 msgid "" "Updates are never applied without user's consent and never overwrite user's " "customized settings." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:762 +#: src/slic3r/GUI/ConfigWizard.cpp:790 msgid "" "Additionally a backup snapshot of the whole configuration is created before " "an update is applied." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:769 +#: src/slic3r/GUI/ConfigWizard.cpp:798 src/slic3r/GUI/GUI_ObjectList.cpp:1654 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3930 src/slic3r/GUI/Plater.cpp:3209 +#: src/slic3r/GUI/Plater.cpp:3943 src/slic3r/GUI/Plater.cpp:3972 +msgid "Reload from disk" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:801 +msgid "" +"Export full pathnames of models and parts sources into 3mf and amf files" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:805 +msgid "" +"If enabled, allows the Reload from disk command to automatically find and " +"load the files when invoked.\n" +"If not enabled, the Reload from disk command will ask to select each file " +"using an open file dialog." +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:814 msgid "View mode" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:771 +#: src/slic3r/GUI/ConfigWizard.cpp:816 msgid "" "PrusaSlicer's user interfaces comes in three variants:\n" "Simple, Advanced, and Expert.\n" @@ -658,202 +668,506 @@ msgid "" "fine-tuning, they are suitable for advanced and expert users, respectively." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:776 +#: src/slic3r/GUI/ConfigWizard.cpp:821 msgid "Simple mode" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:777 +#: src/slic3r/GUI/ConfigWizard.cpp:822 msgid "Advanced mode" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:778 +#: src/slic3r/GUI/ConfigWizard.cpp:823 msgid "Expert mode" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:812 +#: src/slic3r/GUI/ConfigWizard.cpp:857 msgid "Other Vendors" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:816 +#: src/slic3r/GUI/ConfigWizard.cpp:861 #, possible-c-format msgid "Pick another vendor supported by %s" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:847 +#: src/slic3r/GUI/ConfigWizard.cpp:892 msgid "Firmware Type" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:847 src/slic3r/GUI/Tab.cpp:1917 +#: src/slic3r/GUI/ConfigWizard.cpp:892 src/slic3r/GUI/Tab.cpp:1945 msgid "Firmware" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:851 +#: src/slic3r/GUI/ConfigWizard.cpp:896 msgid "Choose the type of firmware used by your printer." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:885 +#: src/slic3r/GUI/ConfigWizard.cpp:930 msgid "Bed Shape and Size" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:888 +#: src/slic3r/GUI/ConfigWizard.cpp:933 msgid "Set the shape of your printer's bed." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:908 +#: src/slic3r/GUI/ConfigWizard.cpp:953 msgid "Filament and Nozzle Diameters" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:908 +#: src/slic3r/GUI/ConfigWizard.cpp:953 msgid "Print Diameters" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:922 +#: src/slic3r/GUI/ConfigWizard.cpp:967 msgid "Enter the diameter of your printer's hot end nozzle." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:925 +#: src/slic3r/GUI/ConfigWizard.cpp:970 msgid "Nozzle Diameter:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:935 +#: src/slic3r/GUI/ConfigWizard.cpp:980 msgid "Enter the diameter of your filament." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:936 +#: src/slic3r/GUI/ConfigWizard.cpp:981 msgid "" "Good precision is required, so use a caliper and do multiple measurements " "along the filament, then compute the average." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:939 +#: src/slic3r/GUI/ConfigWizard.cpp:984 msgid "Filament Diameter:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:973 +#: src/slic3r/GUI/ConfigWizard.cpp:1018 msgid "Extruder and Bed Temperatures" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:973 +#: src/slic3r/GUI/ConfigWizard.cpp:1018 msgid "Temperatures" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:989 +#: src/slic3r/GUI/ConfigWizard.cpp:1034 msgid "Enter the temperature needed for extruding your filament." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:990 +#: src/slic3r/GUI/ConfigWizard.cpp:1035 msgid "A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:993 +#: src/slic3r/GUI/ConfigWizard.cpp:1038 msgid "Extrusion Temperature:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:994 src/slic3r/GUI/ConfigWizard.cpp:1008 +#: src/slic3r/GUI/ConfigWizard.cpp:1039 src/slic3r/GUI/ConfigWizard.cpp:1053 msgid "°C" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1003 +#: src/slic3r/GUI/ConfigWizard.cpp:1048 msgid "" "Enter the bed temperature needed for getting your filament to stick to your " "heated bed." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1004 +#: src/slic3r/GUI/ConfigWizard.cpp:1049 msgid "" "A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have " "no heated bed." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1007 +#: src/slic3r/GUI/ConfigWizard.cpp:1052 msgid "Bed Temperature:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1426 src/slic3r/GUI/ConfigWizard.cpp:1862 +#: src/slic3r/GUI/ConfigWizard.cpp:1474 src/slic3r/GUI/ConfigWizard.cpp:2014 msgid "Filaments" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1426 src/slic3r/GUI/ConfigWizard.cpp:1864 +#: src/slic3r/GUI/ConfigWizard.cpp:1474 src/slic3r/GUI/ConfigWizard.cpp:2016 msgid "SLA Materials" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1480 +#: src/slic3r/GUI/ConfigWizard.cpp:1528 msgid "FFF Technology Printers" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1485 +#: src/slic3r/GUI/ConfigWizard.cpp:1533 msgid "SLA Technology Printers" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1625 +#: src/slic3r/GUI/ConfigWizard.cpp:1751 src/slic3r/GUI/DoubleSlider.cpp:1878 +#: src/slic3r/GUI/DoubleSlider.cpp:1899 src/slic3r/GUI/GUI.cpp:240 +msgid "Notice" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:1760 msgid "You have to select at least one filament for selected printers" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1631 +#: src/slic3r/GUI/ConfigWizard.cpp:1761 +msgid "Do you want to automatic select default filaments?" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:1771 msgid "You have to select at least one material for selected printers" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1831 +#: src/slic3r/GUI/ConfigWizard.cpp:1772 +msgid "Do you want to automatic select default materials?" +msgstr "" + +#: src/slic3r/GUI/ConfigWizard.cpp:1979 msgid "Select all standard printers" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1834 +#: src/slic3r/GUI/ConfigWizard.cpp:1982 msgid "< &Back" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1835 +#: src/slic3r/GUI/ConfigWizard.cpp:1983 msgid "&Next >" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1836 +#: src/slic3r/GUI/ConfigWizard.cpp:1984 msgid "&Finish" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1837 src/slic3r/GUI/FirmwareDialog.cpp:151 -#: src/slic3r/GUI/ProgressStatusBar.cpp:27 +#: src/slic3r/GUI/ConfigWizard.cpp:1985 src/slic3r/GUI/FirmwareDialog.cpp:151 +#: src/slic3r/GUI/ProgressStatusBar.cpp:26 msgid "Cancel" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1850 +#: src/slic3r/GUI/ConfigWizard.cpp:1998 msgid "Prusa FFF Technology Printers" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1853 +#: src/slic3r/GUI/ConfigWizard.cpp:2001 msgid "Prusa MSLA Technology Printers" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1862 +#: src/slic3r/GUI/ConfigWizard.cpp:2014 msgid "Filament Profiles Selection" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1862 src/slic3r/GUI/GUI_ObjectList.cpp:3415 +#: src/slic3r/GUI/ConfigWizard.cpp:2014 src/slic3r/GUI/GUI_ObjectList.cpp:3527 msgid "Type:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1864 +#: src/slic3r/GUI/ConfigWizard.cpp:2016 msgid "SLA Material Profiles Selection" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1864 +#: src/slic3r/GUI/ConfigWizard.cpp:2016 msgid "Layer height:" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1962 +#: src/slic3r/GUI/ConfigWizard.cpp:2112 msgid "Configuration Assistant" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1963 +#: src/slic3r/GUI/ConfigWizard.cpp:2113 msgid "Configuration &Assistant" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1965 +#: src/slic3r/GUI/ConfigWizard.cpp:2115 msgid "Configuration Wizard" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1966 +#: src/slic3r/GUI/ConfigWizard.cpp:2116 msgid "Configuration &Wizard" msgstr "" +#: src/slic3r/GUI/DoubleSlider.cpp:79 +msgid "Place bearings in slots and resume printing" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:923 +msgid "One layer mode" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:925 +msgid "Discard all custom changes" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:928 +#, possible-c-format +msgid "Jump to height %s or Set extruder sequence for the entire print" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:930 src/slic3r/GUI/DoubleSlider.cpp:1501 +#: src/slic3r/GUI/DoubleSlider.cpp:1623 +msgid "Jump to height" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:933 +msgid "Edit current color - Right click the colored slider segment" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:941 +msgid "Print mode" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:955 +msgid "Add extruder change - Left click" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:957 +msgid "" +"Add color change - Left click for predefined color orShift + Left click for " +"custom color selection" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:959 +msgid "Add color change - Left click" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:960 +msgid "or press \"+\" key" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:962 +msgid "To add another code use Ctrl + Left click" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:963 +msgid "To add another code use Right click" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:972 +msgid "Color change (\"%1%\")" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:973 +msgid "Color change (\"%1%\") for Extruder %2%" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:976 +msgid "Pause print (\"%1%\")" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:978 +msgid "Extruder (tool) is changed to Extruder \"%1%\"" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:979 +msgid "\"%1%\"" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:985 +msgid "Note" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:987 +msgid "" +"G-code associated to this tick mark is in a conflict with print mode.\n" +"Editing it will cause changes of Slider data." +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:990 +msgid "" +"There is a color change for extruder that won't be used till the end of " +"print job.\n" +"This code won't be processed during G-code generation." +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:993 +msgid "" +"There is an extruder change set to the same extruder.\n" +"This code won't be processed during G-code generation." +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:996 +msgid "" +"There is a color change for extruder that has not been used before.\n" +"Check your settings to avoid redundant color changes." +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1001 +msgid "Delete tick mark - Left click or press \"-\" key" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1003 +msgid "Edit tick mark - Ctrl + Left click" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1004 +msgid "Edit tick mark - Right click" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1102 src/slic3r/GUI/DoubleSlider.cpp:1138 +#: src/slic3r/GUI/GLCanvas3D.cpp:982 src/slic3r/GUI/Tab.cpp:2316 +#: src/libslic3r/GCode/PreviewData.cpp:445 +#, possible-c-format +msgid "Extruder %d" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1103 +msgid "active" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1112 +msgid "Switch code to Change extruder" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1112 src/slic3r/GUI/GUI_ObjectList.cpp:1661 +msgid "Change extruder" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1113 +msgid "Change extruder (N/A)" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1115 +msgid "Use another extruder" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1139 +msgid "used" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1147 +msgid "Switch code to Color change (%1%) for:" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1148 +msgid "Add color change (%1%) for:" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1449 +msgid "Add color change" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1459 +msgid "Add pause print" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1462 +msgid "Add custom G-code" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1480 +msgid "Edit color" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1481 +msgid "Edit pause print message" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1482 +msgid "Edit custom G-code" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1488 +msgid "Delete color change" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1489 +msgid "Delete tool change" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1490 +msgid "Delete pause print" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1491 +msgid "Delete custom G-code" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1504 +msgid "Set extruder sequence for the entire print" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1590 +msgid "Enter custom G-code used on current layer" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1591 +msgid "Custom G-code on current layer (%1% mm)." +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1606 +msgid "Enter short message shown on Printer display when a print is paused" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1607 +msgid "Message for pause print on current layer (%1% mm)." +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1622 +msgid "Enter the height you want to jump to" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1872 +msgid "" +"The last color change data was saved for a single extruder printer profile." +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1873 +msgid "" +"The last color change data was saved for a multiple extruder printer profile." +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1875 +msgid "Your current changes will delete all saved color changes." +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1876 src/slic3r/GUI/DoubleSlider.cpp:1897 +msgid "Are you sure you want to continue?" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1889 +msgid "The last color change data was saved for a multi extruder printing." +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1890 +msgid "" +"Select YES if you want to delete all saved tool changes, \n" +"\tNO if you want all tool changes switch to color changes, \n" +"\tor CANCEL to leave it unchanged" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1893 +msgid "Do you want to delete all saved tool changes?" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1895 +msgid "" +"The last color change data was saved for a multi extruder printing with tool " +"changes for whole print." +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:1896 +msgid "Your current changes will delete all saved extruder (tool) changes." +msgstr "" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:23 +msgid "Set extruder sequence" +msgstr "" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:39 +msgid "Set extruder change for every" +msgstr "" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:52 +#: src/libslic3r/PrintConfig.cpp:352 src/libslic3r/PrintConfig.cpp:994 +#: src/libslic3r/PrintConfig.cpp:1505 src/libslic3r/PrintConfig.cpp:1690 +#: src/libslic3r/PrintConfig.cpp:1750 src/libslic3r/PrintConfig.cpp:1930 +#: src/libslic3r/PrintConfig.cpp:1976 +msgid "layers" +msgstr "" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:136 +msgid "Set extruder(tool) sequence" +msgstr "" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:182 +msgid "Remove extruder from sequence" +msgstr "" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:192 +msgid "Add extruder to sequence" +msgstr "" + #: src/slic3r/GUI/Field.cpp:131 msgid "default value" msgstr "" @@ -862,7 +1176,7 @@ msgstr "" msgid "parameter name" msgstr "" -#: src/slic3r/GUI/Field.cpp:145 src/slic3r/GUI/OptionsGroup.cpp:570 +#: src/slic3r/GUI/Field.cpp:145 src/slic3r/GUI/OptionsGroup.cpp:574 msgid "N/A" msgstr "" @@ -964,8 +1278,8 @@ msgstr "" msgid "Firmware image:" msgstr "" -#: src/slic3r/GUI/FirmwareDialog.cpp:805 src/slic3r/GUI/Tab.cpp:1635 -#: src/slic3r/GUI/Tab.cpp:1691 +#: src/slic3r/GUI/FirmwareDialog.cpp:805 src/slic3r/GUI/Tab.cpp:1660 +#: src/slic3r/GUI/Tab.cpp:1719 msgid "Browse" msgstr "" @@ -998,6 +1312,7 @@ msgid "Advanced: Output log" msgstr "" #: src/slic3r/GUI/FirmwareDialog.cpp:852 +#: src/slic3r/GUI/Mouse3DController.cpp:387 #: src/slic3r/GUI/PrintHostDialogs.cpp:161 msgid "Close" msgstr "" @@ -1016,293 +1331,304 @@ msgstr "" msgid "Cancelling..." msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:240 src/slic3r/GUI/GLCanvas3D.cpp:4365 +#: src/slic3r/GUI/GLCanvas3D.cpp:239 src/slic3r/GUI/GLCanvas3D.cpp:4622 msgid "Variable layer height" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:243 +#: src/slic3r/GUI/GLCanvas3D.cpp:242 msgid "Left mouse button:" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:246 +#: src/slic3r/GUI/GLCanvas3D.cpp:245 msgid "Add detail" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:249 +#: src/slic3r/GUI/GLCanvas3D.cpp:248 msgid "Right mouse button:" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:252 +#: src/slic3r/GUI/GLCanvas3D.cpp:251 msgid "Remove detail" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:255 +#: src/slic3r/GUI/GLCanvas3D.cpp:254 msgid "Shift + Left mouse button:" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:258 +#: src/slic3r/GUI/GLCanvas3D.cpp:257 msgid "Reset to base" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:261 +#: src/slic3r/GUI/GLCanvas3D.cpp:260 msgid "Shift + Right mouse button:" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:264 +#: src/slic3r/GUI/GLCanvas3D.cpp:263 msgid "Smoothing" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:267 +#: src/slic3r/GUI/GLCanvas3D.cpp:266 msgid "Mouse wheel:" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:270 +#: src/slic3r/GUI/GLCanvas3D.cpp:269 msgid "Increase/decrease edit area" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:273 +#: src/slic3r/GUI/GLCanvas3D.cpp:272 msgid "Adaptive" msgstr "" #: src/slic3r/GUI/GLCanvas3D.cpp:278 -msgid "Cusp (mm)" +msgid "Quality / Speed" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:286 +#: src/slic3r/GUI/GLCanvas3D.cpp:282 +msgid "Higher print quality versus higher print speed." +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:293 msgid "Smooth" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:291 src/libslic3r/PrintConfig.cpp:500 +#: src/slic3r/GUI/GLCanvas3D.cpp:299 src/libslic3r/PrintConfig.cpp:511 msgid "Radius" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:300 +#: src/slic3r/GUI/GLCanvas3D.cpp:309 msgid "Keep min" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:307 +#: src/slic3r/GUI/GLCanvas3D.cpp:318 msgid "Reset" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:695 +#: src/slic3r/GUI/GLCanvas3D.cpp:604 msgid "Variable layer height - Manual edit" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:794 +#: src/slic3r/GUI/GLCanvas3D.cpp:690 msgid "An object outside the print area was detected" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:795 +#: src/slic3r/GUI/GLCanvas3D.cpp:691 msgid "A toolpath outside the print area was detected" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:796 +#: src/slic3r/GUI/GLCanvas3D.cpp:692 msgid "SLA supports outside the print area were detected" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:797 -msgid "Some objects are not visible when editing supports" +#: src/slic3r/GUI/GLCanvas3D.cpp:693 +msgid "Some objects are not visible" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:799 +#: src/slic3r/GUI/GLCanvas3D.cpp:695 msgid "" "An object outside the print area was detected\n" "Resolve the current problem to continue slicing" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:1014 src/slic3r/GUI/GLCanvas3D.cpp:1042 +#: src/slic3r/GUI/GLCanvas3D.cpp:909 src/slic3r/GUI/GLCanvas3D.cpp:938 msgid "Default print color" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:1043 src/slic3r/GUI/GLCanvas3D.cpp:1052 -#: src/slic3r/GUI/GLCanvas3D.cpp:1091 +#: src/slic3r/GUI/GLCanvas3D.cpp:939 src/slic3r/GUI/GLCanvas3D.cpp:948 +#: src/slic3r/GUI/GLCanvas3D.cpp:987 msgid "Pause print or custom G-code" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:1064 +#: src/slic3r/GUI/GLCanvas3D.cpp:960 #, possible-c-format msgid "up to %.2f mm" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:1068 +#: src/slic3r/GUI/GLCanvas3D.cpp:964 #, possible-c-format msgid "above %.2f mm" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:1072 +#: src/slic3r/GUI/GLCanvas3D.cpp:968 #, possible-c-format msgid "%.2f - %.2f mm" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:1086 src/slic3r/GUI/Tab.cpp:2288 -#: src/slic3r/GUI/wxExtensions.cpp:3170 src/slic3r/GUI/wxExtensions.cpp:3421 -#: src/libslic3r/GCode/PreviewData.cpp:451 -#, possible-c-format -msgid "Extruder %d" -msgstr "" - -#: src/slic3r/GUI/GLCanvas3D.cpp:1099 +#: src/slic3r/GUI/GLCanvas3D.cpp:995 #, possible-c-format msgid "Color change for Extruder %d at %.2f mm" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:1673 +#: src/slic3r/GUI/GLCanvas3D.cpp:1306 +msgid "Seq." +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1705 msgid "Variable layer height - Reset" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:1681 +#: src/slic3r/GUI/GLCanvas3D.cpp:1713 msgid "Variable layer height - Adaptive" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:1689 +#: src/slic3r/GUI/GLCanvas3D.cpp:1721 msgid "Variable layer height - Smooth all" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:2026 +#: src/slic3r/GUI/GLCanvas3D.cpp:2075 msgid "Mirror Object" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3297 +#: src/slic3r/GUI/GLCanvas3D.cpp:2945 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:573 +msgid "Gizmo-Move" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3025 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:575 +msgid "Gizmo-Rotate" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3538 msgid "Move Object" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3843 +#: src/slic3r/GUI/GLCanvas3D.cpp:4085 msgid "Undo History" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3843 +#: src/slic3r/GUI/GLCanvas3D.cpp:4085 msgid "Redo History" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3861 +#: src/slic3r/GUI/GLCanvas3D.cpp:4103 #, possible-c-format msgid "Undo %1$d Action" msgid_plural "Undo %1$d Actions" msgstr[0] "" msgstr[1] "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3861 +#: src/slic3r/GUI/GLCanvas3D.cpp:4103 #, possible-c-format msgid "Redo %1$d Action" msgid_plural "Redo %1$d Actions" msgstr[0] "" msgstr[1] "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4259 +#: src/slic3r/GUI/GLCanvas3D.cpp:4516 msgid "Add..." msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4267 src/slic3r/GUI/GUI_ObjectList.cpp:1592 -#: src/slic3r/GUI/Plater.cpp:3712 src/slic3r/GUI/Plater.cpp:3734 -#: src/slic3r/GUI/Tab.cpp:3080 +#: src/slic3r/GUI/GLCanvas3D.cpp:4524 src/slic3r/GUI/GUI_ObjectList.cpp:1692 +#: src/slic3r/GUI/Plater.cpp:3940 src/slic3r/GUI/Plater.cpp:3962 +#: src/slic3r/GUI/Tab.cpp:3108 msgid "Delete" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4276 src/slic3r/GUI/Plater.cpp:4410 +#: src/slic3r/GUI/GLCanvas3D.cpp:4533 src/slic3r/GUI/Plater.cpp:4684 msgid "Delete all" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4285 src/slic3r/GUI/KBShortcutsDialog.cpp:137 -#: src/slic3r/GUI/Plater.cpp:2758 +#: src/slic3r/GUI/GLCanvas3D.cpp:4542 src/slic3r/GUI/KBShortcutsDialog.cpp:137 +#: src/slic3r/GUI/Plater.cpp:2738 msgid "Arrange" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4285 src/slic3r/GUI/KBShortcutsDialog.cpp:138 +#: src/slic3r/GUI/GLCanvas3D.cpp:4542 src/slic3r/GUI/KBShortcutsDialog.cpp:138 msgid "Arrange selection" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4297 +#: src/slic3r/GUI/GLCanvas3D.cpp:4554 msgid "Copy" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4306 +#: src/slic3r/GUI/GLCanvas3D.cpp:4563 msgid "Paste" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4318 src/slic3r/GUI/Plater.cpp:3569 -#: src/slic3r/GUI/Plater.cpp:3581 src/slic3r/GUI/Plater.cpp:3721 +#: src/slic3r/GUI/GLCanvas3D.cpp:4575 src/slic3r/GUI/Plater.cpp:3797 +#: src/slic3r/GUI/Plater.cpp:3809 src/slic3r/GUI/Plater.cpp:3949 msgid "Add instance" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4329 src/slic3r/GUI/Plater.cpp:3723 +#: src/slic3r/GUI/GLCanvas3D.cpp:4586 src/slic3r/GUI/Plater.cpp:3951 msgid "Remove instance" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4342 +#: src/slic3r/GUI/GLCanvas3D.cpp:4599 msgid "Split to objects" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4352 src/slic3r/GUI/GUI_ObjectList.cpp:1424 +#: src/slic3r/GUI/GLCanvas3D.cpp:4609 src/slic3r/GUI/GUI_ObjectList.cpp:1485 msgid "Split to parts" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4416 src/slic3r/GUI/MainFrame.cpp:571 +#: src/slic3r/GUI/GLCanvas3D.cpp:4673 src/slic3r/GUI/MainFrame.cpp:581 msgid "Undo" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4416 src/slic3r/GUI/GLCanvas3D.cpp:4449 +#: src/slic3r/GUI/GLCanvas3D.cpp:4673 src/slic3r/GUI/GLCanvas3D.cpp:4706 msgid "Click right mouse button to open History" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4433 +#: src/slic3r/GUI/GLCanvas3D.cpp:4690 msgid "Next Undo action: %1%" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4449 src/slic3r/GUI/MainFrame.cpp:574 +#: src/slic3r/GUI/GLCanvas3D.cpp:4706 src/slic3r/GUI/MainFrame.cpp:584 msgid "Redo" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4465 +#: src/slic3r/GUI/GLCanvas3D.cpp:4722 msgid "Next Redo action: %1%" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:6380 +#: src/slic3r/GUI/GLCanvas3D.cpp:6659 msgid "Selection-Add from rectangle" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:6399 +#: src/slic3r/GUI/GLCanvas3D.cpp:6678 msgid "Selection-Remove from rectangle" msgstr "" -#: src/slic3r/GUI/GLCanvas3DManager.cpp:283 +#: src/slic3r/GUI/GLCanvas3DManager.cpp:284 #, possible-c-format msgid "" "PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n" "while OpenGL version %s, render %s, vendor %s was detected." msgstr "" -#: src/slic3r/GUI/GLCanvas3DManager.cpp:286 +#: src/slic3r/GUI/GLCanvas3DManager.cpp:287 msgid "You may need to update your graphics card driver." msgstr "" -#: src/slic3r/GUI/GLCanvas3DManager.cpp:289 +#: src/slic3r/GUI/GLCanvas3DManager.cpp:290 msgid "" "As a workaround, you may run PrusaSlicer with a software rendered 3D " "graphics by running prusa-slicer.exe with the --sw_renderer parameter." msgstr "" -#: src/slic3r/GUI/GLCanvas3DManager.cpp:291 +#: src/slic3r/GUI/GLCanvas3DManager.cpp:292 msgid "Unsupported OpenGL version" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:40 -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:145 src/libslic3r/PrintConfig.cpp:3329 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:42 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:144 src/libslic3r/PrintConfig.cpp:3387 msgid "Cut" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:150 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:168 msgid "Keep upper part" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:151 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:169 msgid "Keep lower part" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:152 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:170 msgid "Rotate lower part upwards" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:155 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:175 msgid "Perform cut" msgstr "" @@ -1310,42 +1636,100 @@ msgstr "" msgid "Place on face" msgstr "" +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:41 +msgid "Hollow this object" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:42 +msgid "Preview hollowed and drilled model" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:43 +msgid "Offset" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:44 +msgid "Quality" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:45 +msgid "Closing distance" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:46 +msgid "Hole diameter" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:47 +msgid "Hole depth" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:48 +msgid "Remove selected holes" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:49 +msgid "Remove all holes" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:50 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:58 +msgid "Clipping of view" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:51 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:59 +msgid "Reset direction" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:52 +msgid "Show supports" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:413 +msgid "Add drainage hole" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:530 +msgid "Delete drainage hole" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:791 +msgid "Hollowing parameter change" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:863 +msgid "Change drainage hole diameter" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:953 +msgid "Hollowing and drilling" +msgstr "" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:1033 +msgid "Move drainage hole" +msgstr "" + #: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:48 msgid "Move" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:177 -msgid "Position (mm)" -msgstr "" - -#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:177 -msgid "Displacement (mm)" -msgstr "" - #: src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp:449 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:480 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:499 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:517 -#: src/libslic3r/PrintConfig.cpp:3378 +#: src/libslic3r/PrintConfig.cpp:3436 msgid "Rotate" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp:482 -msgid "Rotation (deg)" -msgstr "" - #: src/slic3r/GUI/Gizmos/GLGizmoScale.cpp:47 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:230 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:500 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:518 -#: src/libslic3r/PrintConfig.cpp:3393 +#: src/libslic3r/PrintConfig.cpp:3451 msgid "Scale" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoScale.cpp:292 -msgid "Scale (%)" -msgstr "" - #: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:48 msgid "Head diameter" msgstr "" @@ -1355,7 +1739,7 @@ msgid "Lock supports under new islands" msgstr "" #: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:50 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1286 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1350 msgid "Remove selected points" msgstr "" @@ -1364,12 +1748,12 @@ msgid "Remove all points" msgstr "" #: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:52 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1289 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1353 msgid "Apply changes" msgstr "" #: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:53 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1290 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1354 msgid "Discard changes" msgstr "" @@ -1378,12 +1762,12 @@ msgid "Minimal points distance" msgstr "" #: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:55 -#: src/libslic3r/PrintConfig.cpp:2732 +#: src/libslic3r/PrintConfig.cpp:2755 msgid "Support points density" msgstr "" #: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:56 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1292 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1356 msgid "Auto-generate points" msgstr "" @@ -1391,169 +1775,149 @@ msgstr "" msgid "Manual editing" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:58 -msgid "Clipping of view" -msgstr "" - -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:59 -msgid "Reset direction" -msgstr "" - -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:442 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:479 msgid "Add support point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:578 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:615 msgid "Delete support point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:754 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:804 msgid "Change point head diameter" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:820 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:872 msgid "Support parameter change" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:929 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:978 msgid "SLA Support Points" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:950 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:999 msgid "SLA gizmo turned on" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:972 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1024 msgid "Do you want to save your manually edited support points?" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:973 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1025 msgid "Save changes?" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:985 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1037 msgid "SLA gizmo turned off" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1022 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1076 msgid "Move support point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1121 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1175 msgid "Support points edit" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1190 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1247 msgid "Autogeneration will erase all manually edited points." msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1191 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1248 msgid "Are you sure you want to do it?" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1192 src/slic3r/GUI/GUI.cpp:246 -#: src/slic3r/GUI/Tab.cpp:3040 src/slic3r/GUI/WipeTowerDialog.cpp:45 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1249 src/slic3r/GUI/GUI.cpp:246 +#: src/slic3r/GUI/Tab.cpp:3068 src/slic3r/GUI/WipeTowerDialog.cpp:45 #: src/slic3r/GUI/WipeTowerDialog.cpp:366 msgid "Warning" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1195 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1252 msgid "Autogenerate support points" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1249 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1313 msgid "SLA gizmo keyboard shortcuts" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1260 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1324 msgid "Note: some shortcuts work in (non)editing mode only." msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1278 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1281 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1282 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1342 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1345 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1346 msgid "Left click" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1278 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1342 msgid "Add point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1279 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1343 msgid "Right click" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1279 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1343 msgid "Remove point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1280 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1283 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1284 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1344 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1347 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1348 msgid "Drag" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1280 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1344 msgid "Move point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1281 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1345 msgid "Add point to selection" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1282 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1346 msgid "Remove point from selection" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1283 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1347 msgid "Select by rectangle" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1284 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1348 msgid "Deselect by rectangle" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1285 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1349 msgid "Select all points" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1287 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1351 msgid "Mouse wheel" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1287 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1351 msgid "Move clipping plane" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1288 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1352 msgid "Reset clipping plane" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1291 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1355 msgid "Switch to editing mode" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:477 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:500 msgid "Gizmo-Place on Face" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:550 -msgid "Gizmo-Move" -msgstr "" - -#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:552 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:574 msgid "Gizmo-Scale" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:554 -msgid "Gizmo-Rotate" -msgstr "" - -#: src/slic3r/GUI/GUI.cpp:240 -msgid "Notice" -msgstr "" - -#: src/slic3r/GUI/GUI_App.cpp:137 +#: src/slic3r/GUI/GUI_App.cpp:138 #, possible-c-format msgid "" "%s has encountered an error. It was likely caused by running out of memory. " @@ -1563,175 +1927,183 @@ msgid "" "The application will now terminate." msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:140 +#: src/slic3r/GUI/GUI_App.cpp:141 msgid "Fatal error" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:450 +#: src/slic3r/GUI/GUI_App.cpp:442 msgid "Changing of an application language" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:458 src/slic3r/GUI/GUI_App.cpp:467 +#: src/slic3r/GUI/GUI_App.cpp:450 src/slic3r/GUI/GUI_App.cpp:459 msgid "Recreating" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:471 +#: src/slic3r/GUI/GUI_App.cpp:466 msgid "Loading of current presets" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:479 +#: src/slic3r/GUI/GUI_App.cpp:474 msgid "Loading of a mode view" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:560 +#: src/slic3r/GUI/GUI_App.cpp:555 msgid "Choose one file (3MF/AMF):" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:572 +#: src/slic3r/GUI/GUI_App.cpp:567 msgid "Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:634 +#: src/slic3r/GUI/GUI_App.cpp:629 msgid "Select the language" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:634 +#: src/slic3r/GUI/GUI_App.cpp:629 msgid "Language" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:802 +#: src/slic3r/GUI/GUI_App.cpp:797 #, possible-c-format msgid "Run %s" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:805 +#: src/slic3r/GUI/GUI_App.cpp:800 msgid "&Configuration Snapshots" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:805 +#: src/slic3r/GUI/GUI_App.cpp:800 msgid "Inspect / activate configuration snapshots" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:806 +#: src/slic3r/GUI/GUI_App.cpp:801 msgid "Take Configuration &Snapshot" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:806 +#: src/slic3r/GUI/GUI_App.cpp:801 msgid "Capture a configuration snapshot" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:809 +#: src/slic3r/GUI/GUI_App.cpp:802 +msgid "Check for updates" +msgstr "" + +#: src/slic3r/GUI/GUI_App.cpp:802 +msgid "Check for configuration updates" +msgstr "" + +#: src/slic3r/GUI/GUI_App.cpp:804 msgid "&Preferences" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:815 +#: src/slic3r/GUI/GUI_App.cpp:810 msgid "Application preferences" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:818 src/slic3r/GUI/wxExtensions.cpp:3824 +#: src/slic3r/GUI/GUI_App.cpp:813 src/slic3r/GUI/wxExtensions.cpp:753 msgid "Simple" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:818 +#: src/slic3r/GUI/GUI_App.cpp:813 msgid "Simple View Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:819 src/slic3r/GUI/GUI_ObjectList.cpp:97 -#: src/slic3r/GUI/GUI_ObjectList.cpp:617 src/slic3r/GUI/Tab.cpp:1067 -#: src/slic3r/GUI/Tab.cpp:1082 src/slic3r/GUI/Tab.cpp:1181 -#: src/slic3r/GUI/Tab.cpp:1184 src/slic3r/GUI/Tab.cpp:1450 -#: src/slic3r/GUI/Tab.cpp:1937 src/slic3r/GUI/Tab.cpp:3614 -#: src/slic3r/GUI/wxExtensions.cpp:3825 src/libslic3r/PrintConfig.cpp:88 -#: src/libslic3r/PrintConfig.cpp:202 src/libslic3r/PrintConfig.cpp:365 -#: src/libslic3r/PrintConfig.cpp:1026 src/libslic3r/PrintConfig.cpp:2253 +#: src/slic3r/GUI/GUI_App.cpp:814 src/slic3r/GUI/GUI_ObjectList.cpp:102 +#: src/slic3r/GUI/GUI_ObjectList.cpp:620 src/slic3r/GUI/Tab.cpp:1089 +#: src/slic3r/GUI/Tab.cpp:1104 src/slic3r/GUI/Tab.cpp:1203 +#: src/slic3r/GUI/Tab.cpp:1206 src/slic3r/GUI/Tab.cpp:1472 +#: src/slic3r/GUI/Tab.cpp:1965 src/slic3r/GUI/Tab.cpp:3646 +#: src/slic3r/GUI/wxExtensions.cpp:754 src/libslic3r/PrintConfig.cpp:88 +#: src/libslic3r/PrintConfig.cpp:213 src/libslic3r/PrintConfig.cpp:376 +#: src/libslic3r/PrintConfig.cpp:1037 src/libslic3r/PrintConfig.cpp:2276 msgid "Advanced" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:819 +#: src/slic3r/GUI/GUI_App.cpp:814 msgid "Advanced View Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:820 src/slic3r/GUI/wxExtensions.cpp:3826 +#: src/slic3r/GUI/GUI_App.cpp:815 src/slic3r/GUI/wxExtensions.cpp:755 msgid "Expert" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:820 +#: src/slic3r/GUI/GUI_App.cpp:815 msgid "Expert View Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:825 +#: src/slic3r/GUI/GUI_App.cpp:820 msgid "Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:825 +#: src/slic3r/GUI/GUI_App.cpp:820 #, possible-c-format msgid "%s View Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:827 +#: src/slic3r/GUI/GUI_App.cpp:822 msgid "Change Application &Language" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:829 +#: src/slic3r/GUI/GUI_App.cpp:824 msgid "Flash printer &firmware" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:829 +#: src/slic3r/GUI/GUI_App.cpp:824 msgid "Upload a firmware image into an Arduino based printer" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:841 +#: src/slic3r/GUI/GUI_App.cpp:839 msgid "Taking configuration snapshot" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:841 +#: src/slic3r/GUI/GUI_App.cpp:839 msgid "Snapshot name" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:884 +#: src/slic3r/GUI/GUI_App.cpp:882 msgid "" "Switching the language will trigger application restart.\n" "You will lose content of the plater." msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:886 +#: src/slic3r/GUI/GUI_App.cpp:884 msgid "Do you want to proceed?" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:887 +#: src/slic3r/GUI/GUI_App.cpp:885 msgid "Language selection" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:910 +#: src/slic3r/GUI/GUI_App.cpp:908 msgid "&Configuration" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:934 +#: src/slic3r/GUI/GUI_App.cpp:932 msgid "The presets on the following tabs were modified" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:934 src/slic3r/GUI/Tab.cpp:2902 +#: src/slic3r/GUI/GUI_App.cpp:932 src/slic3r/GUI/Tab.cpp:2930 msgid "Discard changes and continue anyway?" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:937 +#: src/slic3r/GUI/GUI_App.cpp:935 msgid "Unsaved Presets" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:1083 src/slic3r/GUI/Tab.cpp:2914 +#: src/slic3r/GUI/GUI_App.cpp:1084 src/slic3r/GUI/Tab.cpp:2942 msgid "It's impossible to print multi-part object(s) with SLA technology." msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:1084 +#: src/slic3r/GUI/GUI_App.cpp:1085 msgid "Please check and fix your object list." msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:1085 src/slic3r/GUI/Plater.cpp:2317 -#: src/slic3r/GUI/Tab.cpp:2916 +#: src/slic3r/GUI/GUI_App.cpp:1086 src/slic3r/GUI/Plater.cpp:2297 +#: src/slic3r/GUI/Tab.cpp:2944 msgid "Attention!" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:1102 +#: src/slic3r/GUI/GUI_App.cpp:1103 msgid "Select a gcode file:" msgstr "" @@ -1751,37 +2123,38 @@ msgstr "" msgid "Add layer range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:34 src/slic3r/GUI/GUI_ObjectList.cpp:88 -#: src/slic3r/GUI/GUI_ObjectList.cpp:608 src/libslic3r/PrintConfig.cpp:72 -#: src/libslic3r/PrintConfig.cpp:165 src/libslic3r/PrintConfig.cpp:397 -#: src/libslic3r/PrintConfig.cpp:459 src/libslic3r/PrintConfig.cpp:467 -#: src/libslic3r/PrintConfig.cpp:879 src/libslic3r/PrintConfig.cpp:1064 -#: src/libslic3r/PrintConfig.cpp:1369 src/libslic3r/PrintConfig.cpp:1436 -#: src/libslic3r/PrintConfig.cpp:1617 src/libslic3r/PrintConfig.cpp:2063 -#: src/libslic3r/PrintConfig.cpp:2122 +#: src/slic3r/GUI/GUI_ObjectList.cpp:34 src/slic3r/GUI/GUI_ObjectList.cpp:93 +#: src/slic3r/GUI/GUI_ObjectList.cpp:611 src/libslic3r/PrintConfig.cpp:72 +#: src/libslic3r/PrintConfig.cpp:165 src/libslic3r/PrintConfig.cpp:174 +#: src/libslic3r/PrintConfig.cpp:408 src/libslic3r/PrintConfig.cpp:470 +#: src/libslic3r/PrintConfig.cpp:478 src/libslic3r/PrintConfig.cpp:890 +#: src/libslic3r/PrintConfig.cpp:1075 src/libslic3r/PrintConfig.cpp:1374 +#: src/libslic3r/PrintConfig.cpp:1441 src/libslic3r/PrintConfig.cpp:1622 +#: src/libslic3r/PrintConfig.cpp:2074 src/libslic3r/PrintConfig.cpp:2133 +#: src/libslic3r/PrintConfig.cpp:2142 msgid "Layers and Perimeters" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:36 src/slic3r/GUI/GUI_ObjectList.cpp:90 -#: src/slic3r/GUI/GUI_ObjectList.cpp:610 src/slic3r/GUI/GUI_Preview.cpp:245 -#: src/slic3r/GUI/Tab.cpp:1100 src/slic3r/GUI/Tab.cpp:1101 -#: src/libslic3r/ExtrusionEntity.cpp:319 src/libslic3r/PrintConfig.cpp:349 -#: src/libslic3r/PrintConfig.cpp:1497 src/libslic3r/PrintConfig.cpp:1855 -#: src/libslic3r/PrintConfig.cpp:1861 src/libslic3r/PrintConfig.cpp:1869 -#: src/libslic3r/PrintConfig.cpp:1881 src/libslic3r/PrintConfig.cpp:1891 -#: src/libslic3r/PrintConfig.cpp:1899 src/libslic3r/PrintConfig.cpp:1914 -#: src/libslic3r/PrintConfig.cpp:1935 src/libslic3r/PrintConfig.cpp:1947 -#: src/libslic3r/PrintConfig.cpp:1963 src/libslic3r/PrintConfig.cpp:1972 -#: src/libslic3r/PrintConfig.cpp:1981 src/libslic3r/PrintConfig.cpp:1992 -#: src/libslic3r/PrintConfig.cpp:2006 src/libslic3r/PrintConfig.cpp:2014 -#: src/libslic3r/PrintConfig.cpp:2015 src/libslic3r/PrintConfig.cpp:2024 -#: src/libslic3r/PrintConfig.cpp:2032 src/libslic3r/PrintConfig.cpp:2046 +#: src/slic3r/GUI/GUI_ObjectList.cpp:36 src/slic3r/GUI/GUI_ObjectList.cpp:95 +#: src/slic3r/GUI/GUI_ObjectList.cpp:613 src/slic3r/GUI/GUI_Preview.cpp:248 +#: src/slic3r/GUI/Tab.cpp:1122 src/slic3r/GUI/Tab.cpp:1123 +#: src/libslic3r/ExtrusionEntity.cpp:319 src/libslic3r/PrintConfig.cpp:360 +#: src/libslic3r/PrintConfig.cpp:1502 src/libslic3r/PrintConfig.cpp:1866 +#: src/libslic3r/PrintConfig.cpp:1872 src/libslic3r/PrintConfig.cpp:1880 +#: src/libslic3r/PrintConfig.cpp:1892 src/libslic3r/PrintConfig.cpp:1902 +#: src/libslic3r/PrintConfig.cpp:1910 src/libslic3r/PrintConfig.cpp:1925 +#: src/libslic3r/PrintConfig.cpp:1946 src/libslic3r/PrintConfig.cpp:1958 +#: src/libslic3r/PrintConfig.cpp:1974 src/libslic3r/PrintConfig.cpp:1983 +#: src/libslic3r/PrintConfig.cpp:1992 src/libslic3r/PrintConfig.cpp:2003 +#: src/libslic3r/PrintConfig.cpp:2017 src/libslic3r/PrintConfig.cpp:2025 +#: src/libslic3r/PrintConfig.cpp:2026 src/libslic3r/PrintConfig.cpp:2035 +#: src/libslic3r/PrintConfig.cpp:2043 src/libslic3r/PrintConfig.cpp:2057 msgid "Support material" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:39 src/slic3r/GUI/GUI_ObjectList.cpp:94 -#: src/slic3r/GUI/GUI_ObjectList.cpp:614 src/libslic3r/PrintConfig.cpp:2229 -#: src/libslic3r/PrintConfig.cpp:2237 +#: src/slic3r/GUI/GUI_ObjectList.cpp:39 src/slic3r/GUI/GUI_ObjectList.cpp:99 +#: src/slic3r/GUI/GUI_ObjectList.cpp:617 src/libslic3r/PrintConfig.cpp:2252 +#: src/libslic3r/PrintConfig.cpp:2260 msgid "Wipe options" msgstr "" @@ -1805,505 +2178,512 @@ msgstr "" msgid "Add support blocker" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:91 src/slic3r/GUI/GUI_ObjectList.cpp:611 -#: src/slic3r/GUI/GUI_Preview.cpp:223 src/slic3r/GUI/Tab.cpp:1125 -#: src/libslic3r/PrintConfig.cpp:214 src/libslic3r/PrintConfig.cpp:447 -#: src/libslic3r/PrintConfig.cpp:908 src/libslic3r/PrintConfig.cpp:1037 -#: src/libslic3r/PrintConfig.cpp:1426 src/libslic3r/PrintConfig.cpp:1663 -#: src/libslic3r/PrintConfig.cpp:1712 src/libslic3r/PrintConfig.cpp:1764 -#: src/libslic3r/PrintConfig.cpp:2107 +#: src/slic3r/GUI/GUI_ObjectList.cpp:96 src/slic3r/GUI/GUI_ObjectList.cpp:614 +#: src/slic3r/GUI/GUI_Preview.cpp:226 src/slic3r/GUI/Tab.cpp:1147 +#: src/libslic3r/PrintConfig.cpp:225 src/libslic3r/PrintConfig.cpp:458 +#: src/libslic3r/PrintConfig.cpp:919 src/libslic3r/PrintConfig.cpp:1048 +#: src/libslic3r/PrintConfig.cpp:1431 src/libslic3r/PrintConfig.cpp:1668 +#: src/libslic3r/PrintConfig.cpp:1716 src/libslic3r/PrintConfig.cpp:1768 +#: src/libslic3r/PrintConfig.cpp:2118 msgid "Speed" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:92 src/slic3r/GUI/GUI_ObjectList.cpp:612 -#: src/slic3r/GUI/Tab.cpp:1160 src/slic3r/GUI/Tab.cpp:1808 -#: src/libslic3r/PrintConfig.cpp:477 src/libslic3r/PrintConfig.cpp:991 -#: src/libslic3r/PrintConfig.cpp:1404 src/libslic3r/PrintConfig.cpp:1733 -#: src/libslic3r/PrintConfig.cpp:1927 src/libslic3r/PrintConfig.cpp:1954 +#: src/slic3r/GUI/GUI_ObjectList.cpp:97 src/slic3r/GUI/GUI_ObjectList.cpp:615 +#: src/slic3r/GUI/Tab.cpp:1182 src/slic3r/GUI/Tab.cpp:1836 +#: src/libslic3r/PrintConfig.cpp:488 src/libslic3r/PrintConfig.cpp:1002 +#: src/libslic3r/PrintConfig.cpp:1409 src/libslic3r/PrintConfig.cpp:1737 +#: src/libslic3r/PrintConfig.cpp:1938 src/libslic3r/PrintConfig.cpp:1965 msgid "Extruders" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:93 src/slic3r/GUI/GUI_ObjectList.cpp:613 -#: src/libslic3r/PrintConfig.cpp:436 src/libslic3r/PrintConfig.cpp:544 -#: src/libslic3r/PrintConfig.cpp:866 src/libslic3r/PrintConfig.cpp:999 -#: src/libslic3r/PrintConfig.cpp:1413 src/libslic3r/PrintConfig.cpp:1753 -#: src/libslic3r/PrintConfig.cpp:1936 src/libslic3r/PrintConfig.cpp:2095 +#: src/slic3r/GUI/GUI_ObjectList.cpp:98 src/slic3r/GUI/GUI_ObjectList.cpp:616 +#: src/libslic3r/PrintConfig.cpp:447 src/libslic3r/PrintConfig.cpp:555 +#: src/libslic3r/PrintConfig.cpp:877 src/libslic3r/PrintConfig.cpp:1010 +#: src/libslic3r/PrintConfig.cpp:1418 src/libslic3r/PrintConfig.cpp:1757 +#: src/libslic3r/PrintConfig.cpp:1947 src/libslic3r/PrintConfig.cpp:2106 msgid "Extrusion Width" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:99 src/slic3r/GUI/GUI_ObjectList.cpp:619 -#: src/slic3r/GUI/Plater.cpp:484 src/slic3r/GUI/Tab.cpp:3564 -#: src/slic3r/GUI/Tab.cpp:3565 src/libslic3r/PrintConfig.cpp:2582 -#: src/libslic3r/PrintConfig.cpp:2589 src/libslic3r/PrintConfig.cpp:2598 -#: src/libslic3r/PrintConfig.cpp:2607 src/libslic3r/PrintConfig.cpp:2617 -#: src/libslic3r/PrintConfig.cpp:2643 src/libslic3r/PrintConfig.cpp:2650 -#: src/libslic3r/PrintConfig.cpp:2661 src/libslic3r/PrintConfig.cpp:2671 -#: src/libslic3r/PrintConfig.cpp:2680 src/libslic3r/PrintConfig.cpp:2693 -#: src/libslic3r/PrintConfig.cpp:2703 src/libslic3r/PrintConfig.cpp:2712 -#: src/libslic3r/PrintConfig.cpp:2722 src/libslic3r/PrintConfig.cpp:2733 -#: src/libslic3r/PrintConfig.cpp:2741 +#: src/slic3r/GUI/GUI_ObjectList.cpp:104 src/slic3r/GUI/GUI_ObjectList.cpp:622 +#: src/slic3r/GUI/Plater.cpp:492 src/slic3r/GUI/Tab.cpp:3589 +#: src/slic3r/GUI/Tab.cpp:3590 src/libslic3r/PrintConfig.cpp:2605 +#: src/libslic3r/PrintConfig.cpp:2612 src/libslic3r/PrintConfig.cpp:2621 +#: src/libslic3r/PrintConfig.cpp:2630 src/libslic3r/PrintConfig.cpp:2640 +#: src/libslic3r/PrintConfig.cpp:2666 src/libslic3r/PrintConfig.cpp:2673 +#: src/libslic3r/PrintConfig.cpp:2684 src/libslic3r/PrintConfig.cpp:2694 +#: src/libslic3r/PrintConfig.cpp:2703 src/libslic3r/PrintConfig.cpp:2716 +#: src/libslic3r/PrintConfig.cpp:2726 src/libslic3r/PrintConfig.cpp:2735 +#: src/libslic3r/PrintConfig.cpp:2745 src/libslic3r/PrintConfig.cpp:2756 +#: src/libslic3r/PrintConfig.cpp:2764 msgid "Supports" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:100 src/slic3r/GUI/GUI_ObjectList.cpp:620 -#: src/slic3r/GUI/Plater.cpp:624 src/slic3r/GUI/Tab.cpp:3596 -#: src/slic3r/GUI/Tab.cpp:3597 src/libslic3r/PrintConfig.cpp:2749 -#: src/libslic3r/PrintConfig.cpp:2756 src/libslic3r/PrintConfig.cpp:2770 -#: src/libslic3r/PrintConfig.cpp:2781 src/libslic3r/PrintConfig.cpp:2791 -#: src/libslic3r/PrintConfig.cpp:2813 src/libslic3r/PrintConfig.cpp:2824 -#: src/libslic3r/PrintConfig.cpp:2831 src/libslic3r/PrintConfig.cpp:2838 -#: src/libslic3r/PrintConfig.cpp:2849 src/libslic3r/PrintConfig.cpp:2858 -#: src/libslic3r/PrintConfig.cpp:2867 +#: src/slic3r/GUI/GUI_ObjectList.cpp:105 src/slic3r/GUI/GUI_ObjectList.cpp:623 +#: src/slic3r/GUI/Plater.cpp:632 src/slic3r/GUI/Tab.cpp:3621 +#: src/slic3r/GUI/Tab.cpp:3622 src/libslic3r/PrintConfig.cpp:2772 +#: src/libslic3r/PrintConfig.cpp:2779 src/libslic3r/PrintConfig.cpp:2793 +#: src/libslic3r/PrintConfig.cpp:2804 src/libslic3r/PrintConfig.cpp:2814 +#: src/libslic3r/PrintConfig.cpp:2836 src/libslic3r/PrintConfig.cpp:2847 +#: src/libslic3r/PrintConfig.cpp:2854 src/libslic3r/PrintConfig.cpp:2861 +#: src/libslic3r/PrintConfig.cpp:2872 src/libslic3r/PrintConfig.cpp:2881 +#: src/libslic3r/PrintConfig.cpp:2890 msgid "Pad" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:262 +#: src/slic3r/GUI/GUI_ObjectList.cpp:106 src/slic3r/GUI/Tab.cpp:3639 +#: src/slic3r/GUI/Tab.cpp:3640 src/libslic3r/SLA/Hollowing.cpp:46 +#: src/libslic3r/SLA/Hollowing.cpp:58 src/libslic3r/SLA/Hollowing.cpp:67 +#: src/libslic3r/SLA/Hollowing.cpp:76 src/libslic3r/PrintConfig.cpp:2900 +#: src/libslic3r/PrintConfig.cpp:2907 src/libslic3r/PrintConfig.cpp:2917 +#: src/libslic3r/PrintConfig.cpp:2926 +msgid "Hollowing" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:268 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:153 msgid "Name" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:270 src/slic3r/GUI/Tab.cpp:1414 -#: src/slic3r/GUI/wxExtensions.cpp:549 src/libslic3r/PrintConfig.cpp:476 +#: src/slic3r/GUI/GUI_ObjectList.cpp:276 src/slic3r/GUI/Tab.cpp:1436 +#: src/slic3r/GUI/wxExtensions.cpp:598 src/libslic3r/PrintConfig.cpp:487 msgid "Extruder" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:274 src/slic3r/GUI/GUI_ObjectList.cpp:386 +#: src/slic3r/GUI/GUI_ObjectList.cpp:280 src/slic3r/GUI/GUI_ObjectList.cpp:392 msgid "Editing" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:331 +#: src/slic3r/GUI/GUI_ObjectList.cpp:337 #, possible-c-format msgid "Auto-repaired (%d errors):" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:338 +#: src/slic3r/GUI/GUI_ObjectList.cpp:344 msgid "degenerate facets" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:339 +#: src/slic3r/GUI/GUI_ObjectList.cpp:345 msgid "edges fixed" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:340 +#: src/slic3r/GUI/GUI_ObjectList.cpp:346 msgid "facets removed" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:341 +#: src/slic3r/GUI/GUI_ObjectList.cpp:347 msgid "facets added" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:342 +#: src/slic3r/GUI/GUI_ObjectList.cpp:348 msgid "facets reversed" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:343 +#: src/slic3r/GUI/GUI_ObjectList.cpp:349 msgid "backwards edges" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:351 +#: src/slic3r/GUI/GUI_ObjectList.cpp:357 msgid "Right button click the icon to fix STL through Netfabb" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:388 +#: src/slic3r/GUI/GUI_ObjectList.cpp:394 msgid "Right button click the icon to change the object settings" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:390 +#: src/slic3r/GUI/GUI_ObjectList.cpp:396 msgid "Click the icon to change the object settings" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:394 +#: src/slic3r/GUI/GUI_ObjectList.cpp:400 msgid "Right button click the icon to change the object printable property" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:396 +#: src/slic3r/GUI/GUI_ObjectList.cpp:402 msgid "Click the icon to change the object printable property" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:449 src/slic3r/GUI/GUI_ObjectList.cpp:461 -#: src/slic3r/GUI/GUI_ObjectList.cpp:907 src/slic3r/GUI/GUI_ObjectList.cpp:3822 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3832 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3867 src/slic3r/GUI/wxExtensions.cpp:734 -#: src/slic3r/GUI/wxExtensions.cpp:791 src/slic3r/GUI/wxExtensions.cpp:816 -#: src/slic3r/GUI/wxExtensions.cpp:1024 src/slic3r/GUI/wxExtensions.cpp:2240 +#: src/slic3r/GUI/GUI_ObjectList.cpp:455 src/slic3r/GUI/GUI_ObjectList.cpp:467 +#: src/slic3r/GUI/GUI_ObjectList.cpp:915 src/slic3r/GUI/GUI_ObjectList.cpp:3941 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3951 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3986 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:200 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:257 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:282 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:490 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:1725 msgid "default" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:528 +#: src/slic3r/GUI/GUI_ObjectList.cpp:534 msgid "Change Extruder" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:543 +#: src/slic3r/GUI/GUI_ObjectList.cpp:549 msgid "Rename Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:543 +#: src/slic3r/GUI/GUI_ObjectList.cpp:549 msgid "Rename Sub-object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1068 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3643 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1089 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3755 msgid "Instances to Separated Objects" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1086 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1104 msgid "Volumes in Object reordered" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1086 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1104 msgid "Object reordered" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1141 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1460 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1466 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1723 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1180 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1528 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1534 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1823 #, possible-c-format msgid "Quick Add Settings (%s)" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1218 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1263 msgid "Select showing settings" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1267 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1312 msgid "Add Settings for Layers" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1268 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1313 msgid "Add Settings for Sub-object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1269 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1314 msgid "Add Settings for Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1330 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1384 msgid "Add Settings Bundle for Height range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1331 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1385 msgid "Add Settings Bundle for Sub-object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1332 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1386 msgid "Add Settings Bundle for Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1371 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1425 msgid "Load" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1376 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1404 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1407 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1430 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1462 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1466 msgid "Box" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1376 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1430 msgid "Cylinder" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1376 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1430 msgid "Sphere" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1376 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1430 msgid "Slab" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1431 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1498 msgid "Height range Modifier" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1439 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1507 msgid "Add settings" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1507 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1582 msgid "Change type" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1514 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1671 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1589 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1601 msgid "Set as a Separated Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1520 -msgid "Printable" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:1527 -msgid "Rename" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:1538 -msgid "Fix through the Netfabb" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:1548 src/slic3r/GUI/Plater.cpp:3747 -msgid "Export as STL" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:1555 src/slic3r/GUI/Plater.cpp:3161 -#: src/slic3r/GUI/Plater.cpp:3715 src/slic3r/GUI/Plater.cpp:3744 -msgid "Reload from disk" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:1555 src/slic3r/GUI/Plater.cpp:3715 -msgid "Reload the selected volumes from disk" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:1561 src/slic3r/GUI/wxExtensions.cpp:3176 -#: src/slic3r/GUI/wxExtensions.cpp:3432 -msgid "Change extruder" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:1580 src/slic3r/GUI/wxExtensions.cpp:3170 -#: src/slic3r/GUI/wxExtensions.cpp:3421 src/libslic3r/PrintConfig.cpp:314 -msgid "Default" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:1586 -msgid "Select new extruder for the object/part" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:1598 -msgid "Scale to print volume" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:1598 -msgid "Scale the selected object to fit the print volume" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:1671 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1601 msgid "Set as a Separated Objects" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1678 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1924 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1611 +msgid "Printable" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1626 +msgid "Rename" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1637 +msgid "Fix through the Netfabb" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1647 src/slic3r/GUI/Plater.cpp:3975 +msgid "Export as STL" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1654 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3930 src/slic3r/GUI/Plater.cpp:3943 +msgid "Reload the selected volumes from disk" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1680 src/libslic3r/PrintConfig.cpp:325 +msgid "Default" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1686 +msgid "Select new extruder for the object/part" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1698 +msgid "Scale to print volume" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1698 +msgid "Scale the selected object to fit the print volume" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1767 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2025 msgid "Add Shape" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1752 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1853 msgid "Load Part" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1791 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1892 msgid "Error!" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1866 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1967 msgid "Add Generic Subobject" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1895 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1996 msgid "Generic" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2013 -#: src/slic3r/GUI/GUI_ObjectList.cpp:2115 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2114 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2216 msgid "Last instance of an object cannot be deleted." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2025 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2126 msgid "Delete Settings" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2049 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2150 msgid "Delete All Instances from Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2065 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2166 msgid "Delete Height Range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2096 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2197 msgid "From Object List You can't delete the last solid part from object." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2100 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2201 msgid "Delete Subobject" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2119 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2220 msgid "Delete Instance" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2143 src/slic3r/GUI/Plater.cpp:2914 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2244 src/slic3r/GUI/Plater.cpp:2962 msgid "" "The selected object couldn't be split because it contains only one part." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2147 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2248 msgid "Split to Parts" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2195 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2304 msgid "Add Layers" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2321 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2430 msgid "Group manipulation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2333 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2442 msgid "Object manipulation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2346 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2455 msgid "Object Settings to modify" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2350 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2459 msgid "Part Settings to modify" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2355 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2464 msgid "Layer range Settings to modify" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2361 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2470 msgid "Part manipulation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2367 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2476 msgid "Instance manipulation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2374 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2483 msgid "Height ranges" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2374 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2483 msgid "Settings for height range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2560 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2669 msgid "Delete Selected Item" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2697 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2806 msgid "Delete Selected" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2763 -#: src/slic3r/GUI/GUI_ObjectList.cpp:2792 -#: src/slic3r/GUI/GUI_ObjectList.cpp:2810 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2872 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2901 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2919 msgid "Add Height Range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2869 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2979 msgid "Edit Height Range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3153 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3265 msgid "Selection-Remove from list" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3161 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3273 msgid "Selection-Add from list" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3279 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3391 msgid "Object or Instance" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3280 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3413 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3392 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3525 msgid "Part" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3280 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3392 msgid "Layer" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3282 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3394 msgid "Unsupported selection" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3283 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3395 #, possible-c-format msgid "You started your selection with %s Item." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3284 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3396 #, possible-c-format msgid "In this mode you can select only other %s Items%s" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3287 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3399 msgid "of a current Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3408 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3404 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3479 src/slic3r/GUI/Plater.cpp:143 +msgid "Info" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3520 msgid "You can't change a type of the last solid part of the object." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3413 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3525 msgid "Modifier" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3413 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3525 msgid "Support Enforcer" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3413 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3525 msgid "Support Blocker" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3415 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3527 msgid "Select type of part" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3420 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3532 msgid "Change Part Type" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3665 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3777 msgid "Enter new name" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3665 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3777 msgid "Renaming" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3681 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3788 src/slic3r/GUI/Tab.cpp:3412 -#: src/slic3r/GUI/Tab.cpp:3416 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3793 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3900 src/slic3r/GUI/Tab.cpp:3440 +#: src/slic3r/GUI/Tab.cpp:3444 msgid "The supplied name is not valid;" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3682 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3789 src/slic3r/GUI/Tab.cpp:3413 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3794 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3901 src/slic3r/GUI/Tab.cpp:3441 msgid "the following characters are not allowed:" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3812 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3926 msgid "Set extruder for selected items" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3813 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3927 msgid "Select extruder number for selected objects and/or parts" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3826 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3945 msgid "Select extruder number:" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3827 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3946 msgid "This extruder will be set for selected items" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3852 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3971 msgid "Change Extruders" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3942 src/slic3r/GUI/Selection.cpp:1473 +#: src/slic3r/GUI/GUI_ObjectList.cpp:4068 src/slic3r/GUI/Selection.cpp:1474 msgid "Set Printable" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3942 src/slic3r/GUI/Selection.cpp:1473 +#: src/slic3r/GUI/GUI_ObjectList.cpp:4068 src/slic3r/GUI/Selection.cpp:1474 msgid "Set Unprintable" msgstr "" @@ -2321,7 +2701,7 @@ msgstr "" msgid "Select coordinate space, in which the transformation will be performed." msgstr "" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:155 src/libslic3r/GCode.cpp:634 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:155 src/libslic3r/GCode.cpp:638 msgid "Object name" msgstr "" @@ -2332,8 +2712,8 @@ msgstr "" #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:216 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:458 -#: src/slic3r/GUI/Mouse3DController.cpp:274 -#: src/slic3r/GUI/Mouse3DController.cpp:287 +#: src/slic3r/GUI/Mouse3DController.cpp:321 +#: src/slic3r/GUI/Mouse3DController.cpp:344 msgid "Rotation" msgstr "" @@ -2422,121 +2802,125 @@ msgstr "" msgid "Change Option %s" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:217 +#: src/slic3r/GUI/GUI_Preview.cpp:220 msgid "View" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:220 src/slic3r/GUI/GUI_Preview.cpp:577 -#: src/libslic3r/GCode/PreviewData.cpp:360 +#: src/slic3r/GUI/GUI_Preview.cpp:223 src/slic3r/GUI/GUI_Preview.cpp:577 +#: src/libslic3r/GCode/PreviewData.cpp:345 msgid "Feature type" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:221 src/libslic3r/PrintConfig.cpp:489 +#: src/slic3r/GUI/GUI_Preview.cpp:224 src/libslic3r/PrintConfig.cpp:500 msgid "Height" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:222 src/libslic3r/PrintConfig.cpp:2215 +#: src/slic3r/GUI/GUI_Preview.cpp:225 src/libslic3r/PrintConfig.cpp:2238 msgid "Width" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:224 src/slic3r/GUI/Tab.cpp:1437 +#: src/slic3r/GUI/GUI_Preview.cpp:227 src/slic3r/GUI/Tab.cpp:1459 msgid "Fan speed" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:225 +#: src/slic3r/GUI/GUI_Preview.cpp:228 msgid "Volumetric flow rate" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:226 src/slic3r/GUI/GUI_Preview.cpp:334 -#: src/slic3r/GUI/GUI_Preview.cpp:523 src/slic3r/GUI/GUI_Preview.cpp:576 -#: src/slic3r/GUI/GUI_Preview.cpp:772 src/libslic3r/GCode/PreviewData.cpp:372 +#: src/slic3r/GUI/GUI_Preview.cpp:229 src/slic3r/GUI/GUI_Preview.cpp:337 +#: src/slic3r/GUI/GUI_Preview.cpp:521 src/slic3r/GUI/GUI_Preview.cpp:576 +#: src/slic3r/GUI/GUI_Preview.cpp:831 src/libslic3r/GCode/PreviewData.cpp:357 msgid "Tool" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:227 src/slic3r/GUI/GUI_Preview.cpp:574 -#: src/libslic3r/GCode/PreviewData.cpp:374 +#: src/slic3r/GUI/GUI_Preview.cpp:230 src/slic3r/GUI/GUI_Preview.cpp:574 +#: src/libslic3r/GCode/PreviewData.cpp:359 msgid "Color Print" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:230 +#: src/slic3r/GUI/GUI_Preview.cpp:233 msgid "Show" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:233 src/slic3r/GUI/GUI_Preview.cpp:234 +#: src/slic3r/GUI/GUI_Preview.cpp:236 src/slic3r/GUI/GUI_Preview.cpp:237 msgid "Feature types" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:236 src/libslic3r/ExtrusionEntity.cpp:310 +#: src/slic3r/GUI/GUI_Preview.cpp:239 src/libslic3r/ExtrusionEntity.cpp:310 msgid "Perimeter" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:237 src/libslic3r/ExtrusionEntity.cpp:311 +#: src/slic3r/GUI/GUI_Preview.cpp:240 src/libslic3r/ExtrusionEntity.cpp:311 msgid "External perimeter" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:238 src/libslic3r/ExtrusionEntity.cpp:312 +#: src/slic3r/GUI/GUI_Preview.cpp:241 src/libslic3r/ExtrusionEntity.cpp:312 msgid "Overhang perimeter" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:239 src/libslic3r/ExtrusionEntity.cpp:313 +#: src/slic3r/GUI/GUI_Preview.cpp:242 src/libslic3r/ExtrusionEntity.cpp:313 msgid "Internal infill" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:240 src/libslic3r/ExtrusionEntity.cpp:314 -#: src/libslic3r/PrintConfig.cpp:1752 src/libslic3r/PrintConfig.cpp:1763 +#: src/slic3r/GUI/GUI_Preview.cpp:243 src/libslic3r/ExtrusionEntity.cpp:314 +#: src/libslic3r/PrintConfig.cpp:1756 src/libslic3r/PrintConfig.cpp:1767 msgid "Solid infill" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:241 src/libslic3r/ExtrusionEntity.cpp:315 -#: src/libslic3r/PrintConfig.cpp:2094 src/libslic3r/PrintConfig.cpp:2106 +#: src/slic3r/GUI/GUI_Preview.cpp:244 src/libslic3r/ExtrusionEntity.cpp:315 +#: src/libslic3r/PrintConfig.cpp:2105 src/libslic3r/PrintConfig.cpp:2117 msgid "Top solid infill" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:242 src/libslic3r/ExtrusionEntity.cpp:316 +#: src/slic3r/GUI/GUI_Preview.cpp:245 src/libslic3r/ExtrusionEntity.cpp:316 msgid "Bridge infill" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:243 src/libslic3r/ExtrusionEntity.cpp:317 -#: src/libslic3r/PrintConfig.cpp:907 +#: src/slic3r/GUI/GUI_Preview.cpp:246 src/libslic3r/ExtrusionEntity.cpp:317 +#: src/libslic3r/PrintConfig.cpp:918 msgid "Gap fill" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:244 src/slic3r/GUI/Tab.cpp:1091 +#: src/slic3r/GUI/GUI_Preview.cpp:247 src/slic3r/GUI/Tab.cpp:1113 #: src/libslic3r/ExtrusionEntity.cpp:318 msgid "Skirt" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:246 src/libslic3r/ExtrusionEntity.cpp:320 -#: src/libslic3r/PrintConfig.cpp:1980 +#: src/slic3r/GUI/GUI_Preview.cpp:249 src/libslic3r/ExtrusionEntity.cpp:320 +#: src/libslic3r/PrintConfig.cpp:1991 msgid "Support material interface" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:247 src/slic3r/GUI/Tab.cpp:1171 +#: src/slic3r/GUI/GUI_Preview.cpp:250 src/slic3r/GUI/Tab.cpp:1193 #: src/libslic3r/ExtrusionEntity.cpp:321 msgid "Wipe tower" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:252 src/libslic3r/PrintConfig.cpp:2129 +#: src/slic3r/GUI/GUI_Preview.cpp:255 src/libslic3r/PrintConfig.cpp:2152 msgid "Travel" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:253 +#: src/slic3r/GUI/GUI_Preview.cpp:256 msgid "Retractions" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:254 +#: src/slic3r/GUI/GUI_Preview.cpp:257 msgid "Unretractions" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:255 +#: src/slic3r/GUI/GUI_Preview.cpp:258 msgid "Shells" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:256 +#: src/slic3r/GUI/GUI_Preview.cpp:259 msgid "Legend" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:14 src/slic3r/GUI/MainFrame.cpp:684 +#: src/slic3r/GUI/Job.hpp:123 +msgid "ERROR: not enough resources to execute a new job." +msgstr "" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:14 src/slic3r/GUI/MainFrame.cpp:710 msgid "Keyboard Shortcuts" msgstr "" @@ -2552,8 +2936,8 @@ msgstr "" msgid "Load Config from .ini/amf/3mf/gcode" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:110 src/slic3r/GUI/Plater.cpp:858 -#: src/slic3r/GUI/Plater.cpp:5142 src/libslic3r/PrintConfig.cpp:3280 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:110 src/slic3r/GUI/Plater.cpp:888 +#: src/slic3r/GUI/Plater.cpp:5555 src/libslic3r/PrintConfig.cpp:3338 msgid "Export G-code" msgstr "" @@ -2719,670 +3103,699 @@ msgstr "" msgid "Zoom out" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:160 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:161 +msgid "Show/Hide object/instance labels" +msgstr "" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:163 msgid "Show/Hide 3Dconnexion devices settings dialog" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:161 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:164 msgid "Unselect gizmo / Clear selection" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:167 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:170 msgid "Plater Shortcuts" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:182 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:194 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 msgid "Arrow Up" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:182 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:184 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:187 msgid "Upper Layer" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:183 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:195 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:186 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:198 msgid "Arrow Down" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:183 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:186 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:188 msgid "Lower Layer" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:186 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:189 msgid "Show/Hide (L)egend" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:188 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:191 msgid "Preview Shortcuts" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:194 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 msgid "Move current slider thumb Up" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:195 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:198 msgid "Move current slider thumb Down" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:196 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:199 msgid "Arrow Left" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:196 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:199 msgid "Set upper thumb to current slider thumb" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:200 msgid "Arrow Right" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:200 msgid "Set lower thumb to current slider thumb" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:198 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:201 msgid "Add color change marker for current layer" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:199 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:202 msgid "Delete color change marker for current layer" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:201 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:204 msgid "Layers Slider Shortcuts" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:65 +#: src/slic3r/GUI/MainFrame.cpp:66 msgid "" " - Remember to check for updates at http://github.com/prusa3d/PrusaSlicer/" "releases" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:160 +#: src/slic3r/GUI/MainFrame.cpp:174 msgid "based on Slic3r" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:190 +#: src/slic3r/GUI/MainFrame.cpp:204 msgid "Plater" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:401 +#: src/slic3r/GUI/MainFrame.cpp:405 msgid "&New Project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:401 +#: src/slic3r/GUI/MainFrame.cpp:405 msgid "Start a new project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:404 +#: src/slic3r/GUI/MainFrame.cpp:408 msgid "&Open Project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:404 +#: src/slic3r/GUI/MainFrame.cpp:408 msgid "Open a project file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:409 +#: src/slic3r/GUI/MainFrame.cpp:413 msgid "Recent projects" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:418 +#: src/slic3r/GUI/MainFrame.cpp:422 msgid "The selected project is no more available" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:418 src/slic3r/GUI/MainFrame.cpp:761 +#: src/slic3r/GUI/MainFrame.cpp:422 src/slic3r/GUI/MainFrame.cpp:787 #: src/slic3r/GUI/PrintHostDialogs.cpp:231 msgid "Error" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:442 +#: src/slic3r/GUI/MainFrame.cpp:446 msgid "&Save Project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:442 +#: src/slic3r/GUI/MainFrame.cpp:446 msgid "Save current project file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:446 src/slic3r/GUI/MainFrame.cpp:448 +#: src/slic3r/GUI/MainFrame.cpp:450 src/slic3r/GUI/MainFrame.cpp:452 msgid "Save Project &as" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:446 src/slic3r/GUI/MainFrame.cpp:448 +#: src/slic3r/GUI/MainFrame.cpp:450 src/slic3r/GUI/MainFrame.cpp:452 msgid "Save current project file as" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:456 +#: src/slic3r/GUI/MainFrame.cpp:460 msgid "Import STL/OBJ/AM&F/3MF" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:456 +#: src/slic3r/GUI/MainFrame.cpp:460 msgid "Load a model" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:460 +#: src/slic3r/GUI/MainFrame.cpp:464 msgid "Import &Config" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:460 +#: src/slic3r/GUI/MainFrame.cpp:464 msgid "Load exported configuration file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:462 +#: src/slic3r/GUI/MainFrame.cpp:467 msgid "Import Config from &project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:462 +#: src/slic3r/GUI/MainFrame.cpp:467 msgid "Load configuration from project file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:465 +#: src/slic3r/GUI/MainFrame.cpp:471 msgid "Import Config &Bundle" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:465 +#: src/slic3r/GUI/MainFrame.cpp:471 msgid "Load presets from a bundle" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:467 +#: src/slic3r/GUI/MainFrame.cpp:474 msgid "&Import" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:470 src/slic3r/GUI/MainFrame.cpp:725 +#: src/slic3r/GUI/MainFrame.cpp:477 src/slic3r/GUI/MainFrame.cpp:751 msgid "Export &G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:470 +#: src/slic3r/GUI/MainFrame.cpp:477 msgid "Export current plate as G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:474 src/slic3r/GUI/MainFrame.cpp:726 +#: src/slic3r/GUI/MainFrame.cpp:481 src/slic3r/GUI/MainFrame.cpp:752 msgid "S&end G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:474 +#: src/slic3r/GUI/MainFrame.cpp:481 msgid "Send to print current plate as G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:479 +#: src/slic3r/GUI/MainFrame.cpp:486 msgid "Export plate as &STL" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:479 +#: src/slic3r/GUI/MainFrame.cpp:486 msgid "Export current plate as STL" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:482 +#: src/slic3r/GUI/MainFrame.cpp:489 msgid "Export plate as STL &including supports" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:482 +#: src/slic3r/GUI/MainFrame.cpp:489 msgid "Export current plate as STL including supports" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:485 +#: src/slic3r/GUI/MainFrame.cpp:492 msgid "Export plate as &AMF" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:485 +#: src/slic3r/GUI/MainFrame.cpp:492 msgid "Export current plate as AMF" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:489 +#: src/slic3r/GUI/MainFrame.cpp:496 msgid "Export &toolpaths as OBJ" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:489 +#: src/slic3r/GUI/MainFrame.cpp:496 msgid "Export toolpaths as OBJ" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:493 +#: src/slic3r/GUI/MainFrame.cpp:500 msgid "Export &Config" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:493 +#: src/slic3r/GUI/MainFrame.cpp:500 msgid "Export current configuration to file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:495 +#: src/slic3r/GUI/MainFrame.cpp:503 msgid "Export Config &Bundle" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:495 +#: src/slic3r/GUI/MainFrame.cpp:503 msgid "Export all presets to file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:497 +#: src/slic3r/GUI/MainFrame.cpp:506 msgid "&Export" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:503 +#: src/slic3r/GUI/MainFrame.cpp:512 msgid "Quick Slice" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:503 +#: src/slic3r/GUI/MainFrame.cpp:512 msgid "Slice a file into a G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:509 +#: src/slic3r/GUI/MainFrame.cpp:518 msgid "Quick Slice and Save As" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:509 +#: src/slic3r/GUI/MainFrame.cpp:518 msgid "Slice a file into a G-code, save as" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:515 +#: src/slic3r/GUI/MainFrame.cpp:524 msgid "Repeat Last Quick Slice" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:515 +#: src/slic3r/GUI/MainFrame.cpp:524 msgid "Repeat last quick slice" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:523 +#: src/slic3r/GUI/MainFrame.cpp:532 msgid "(Re)Slice No&w" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:523 +#: src/slic3r/GUI/MainFrame.cpp:532 msgid "Start new slicing process" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:527 +#: src/slic3r/GUI/MainFrame.cpp:536 msgid "&Repair STL file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:527 +#: src/slic3r/GUI/MainFrame.cpp:536 msgid "Automatically repair an STL file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:530 +#: src/slic3r/GUI/MainFrame.cpp:540 msgid "&Quit" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:530 +#: src/slic3r/GUI/MainFrame.cpp:540 #, possible-c-format msgid "Quit %s" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:555 +#: src/slic3r/GUI/MainFrame.cpp:565 msgid "&Select all" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:556 +#: src/slic3r/GUI/MainFrame.cpp:566 msgid "Selects all objects" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:558 +#: src/slic3r/GUI/MainFrame.cpp:568 msgid "D&eselect all" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:559 +#: src/slic3r/GUI/MainFrame.cpp:569 msgid "Deselects all objects" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:562 +#: src/slic3r/GUI/MainFrame.cpp:572 msgid "&Delete selected" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:563 +#: src/slic3r/GUI/MainFrame.cpp:573 msgid "Deletes the current selection" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:565 +#: src/slic3r/GUI/MainFrame.cpp:575 msgid "Delete &all" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:566 +#: src/slic3r/GUI/MainFrame.cpp:576 msgid "Deletes all objects" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:570 +#: src/slic3r/GUI/MainFrame.cpp:580 msgid "&Undo" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:573 +#: src/slic3r/GUI/MainFrame.cpp:583 msgid "&Redo" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:578 +#: src/slic3r/GUI/MainFrame.cpp:588 msgid "&Copy" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:579 +#: src/slic3r/GUI/MainFrame.cpp:589 msgid "Copy selection to clipboard" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:581 +#: src/slic3r/GUI/MainFrame.cpp:591 msgid "&Paste" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:582 +#: src/slic3r/GUI/MainFrame.cpp:592 msgid "Paste clipboard" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:591 +#: src/slic3r/GUI/MainFrame.cpp:596 +msgid "Re&load from disk" +msgstr "" + +#: src/slic3r/GUI/MainFrame.cpp:597 +msgid "Reload the plater from disk" +msgstr "" + +#: src/slic3r/GUI/MainFrame.cpp:606 msgid "&Plater Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:591 +#: src/slic3r/GUI/MainFrame.cpp:606 msgid "Show the plater" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:598 +#: src/slic3r/GUI/MainFrame.cpp:614 msgid "P&rint Settings Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:598 +#: src/slic3r/GUI/MainFrame.cpp:614 msgid "Show the print settings" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:600 src/slic3r/GUI/MainFrame.cpp:728 +#: src/slic3r/GUI/MainFrame.cpp:617 src/slic3r/GUI/MainFrame.cpp:754 msgid "&Filament Settings Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:600 +#: src/slic3r/GUI/MainFrame.cpp:617 msgid "Show the filament settings" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:603 +#: src/slic3r/GUI/MainFrame.cpp:621 msgid "Print&er Settings Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:603 +#: src/slic3r/GUI/MainFrame.cpp:621 msgid "Show the printer settings" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:607 +#: src/slic3r/GUI/MainFrame.cpp:626 msgid "3&D" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:607 +#: src/slic3r/GUI/MainFrame.cpp:626 msgid "Show the 3D editing view" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:610 +#: src/slic3r/GUI/MainFrame.cpp:629 msgid "Pre&view" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:610 +#: src/slic3r/GUI/MainFrame.cpp:629 msgid "Show the 3D slices preview" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:629 +#: src/slic3r/GUI/MainFrame.cpp:648 msgid "Print &Host Upload Queue" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:629 +#: src/slic3r/GUI/MainFrame.cpp:648 msgid "Display the Print Host Upload Queue window" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:638 +#: src/slic3r/GUI/MainFrame.cpp:658 msgid "Iso" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:638 +#: src/slic3r/GUI/MainFrame.cpp:658 msgid "Iso View" msgstr "" #. TRN To be shown in the main menu View->Top #. TRN To be shown in Print Settings "Top solid layers" -#: src/slic3r/GUI/MainFrame.cpp:642 src/libslic3r/PrintConfig.cpp:2121 +#: src/slic3r/GUI/MainFrame.cpp:662 src/libslic3r/PrintConfig.cpp:2132 +#: src/libslic3r/PrintConfig.cpp:2141 msgid "Top" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:642 +#: src/slic3r/GUI/MainFrame.cpp:662 msgid "Top View" msgstr "" #. TRN To be shown in the main menu View->Bottom #. TRN To be shown in Print Settings "Bottom solid layers" -#: src/slic3r/GUI/MainFrame.cpp:645 src/libslic3r/PrintConfig.cpp:164 +#. TRN To be shown in Print Settings "Top solid layers" +#: src/slic3r/GUI/MainFrame.cpp:665 src/libslic3r/PrintConfig.cpp:164 +#: src/libslic3r/PrintConfig.cpp:173 msgid "Bottom" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:645 +#: src/slic3r/GUI/MainFrame.cpp:665 msgid "Bottom View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:647 +#: src/slic3r/GUI/MainFrame.cpp:667 msgid "Front" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:647 +#: src/slic3r/GUI/MainFrame.cpp:667 msgid "Front View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:649 src/libslic3r/PrintConfig.cpp:1627 +#: src/slic3r/GUI/MainFrame.cpp:669 src/libslic3r/PrintConfig.cpp:1632 msgid "Rear" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:649 +#: src/slic3r/GUI/MainFrame.cpp:669 msgid "Rear View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:651 +#: src/slic3r/GUI/MainFrame.cpp:671 msgid "Left" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:651 +#: src/slic3r/GUI/MainFrame.cpp:671 msgid "Left View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:653 +#: src/slic3r/GUI/MainFrame.cpp:673 msgid "Right" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:653 +#: src/slic3r/GUI/MainFrame.cpp:673 msgid "Right View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:660 +#: src/slic3r/GUI/MainFrame.cpp:677 +msgid "Show &labels" +msgstr "" + +#: src/slic3r/GUI/MainFrame.cpp:677 +msgid "Show object/instance labels in 3D scene" +msgstr "" + +#: src/slic3r/GUI/MainFrame.cpp:686 msgid "Prusa 3D &Drivers" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:660 +#: src/slic3r/GUI/MainFrame.cpp:686 msgid "Open the Prusa3D drivers download page in your browser" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:662 +#: src/slic3r/GUI/MainFrame.cpp:688 msgid "Software &Releases" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:662 +#: src/slic3r/GUI/MainFrame.cpp:688 msgid "Open the software releases page in your browser" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:668 +#: src/slic3r/GUI/MainFrame.cpp:694 #, possible-c-format msgid "%s &Website" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:669 +#: src/slic3r/GUI/MainFrame.cpp:695 #, possible-c-format msgid "Open the %s website in your browser" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:675 +#: src/slic3r/GUI/MainFrame.cpp:701 msgid "System &Info" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:675 +#: src/slic3r/GUI/MainFrame.cpp:701 msgid "Show system information" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:677 +#: src/slic3r/GUI/MainFrame.cpp:703 msgid "Show &Configuration Folder" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:677 +#: src/slic3r/GUI/MainFrame.cpp:703 msgid "Show user configuration folder (datadir)" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:679 +#: src/slic3r/GUI/MainFrame.cpp:705 msgid "Report an I&ssue" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:679 +#: src/slic3r/GUI/MainFrame.cpp:705 #, possible-c-format msgid "Report an issue on %s" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:681 +#: src/slic3r/GUI/MainFrame.cpp:707 #, possible-c-format msgid "&About %s" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:681 +#: src/slic3r/GUI/MainFrame.cpp:707 msgid "Show about dialog" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:684 +#: src/slic3r/GUI/MainFrame.cpp:710 msgid "Show the list of the keyboard shortcuts" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:697 +#: src/slic3r/GUI/MainFrame.cpp:723 msgid "&File" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:698 +#: src/slic3r/GUI/MainFrame.cpp:724 msgid "&Edit" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:699 +#: src/slic3r/GUI/MainFrame.cpp:725 msgid "&Window" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:700 +#: src/slic3r/GUI/MainFrame.cpp:726 msgid "&View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:703 +#: src/slic3r/GUI/MainFrame.cpp:729 msgid "&Help" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:725 +#: src/slic3r/GUI/MainFrame.cpp:751 msgid "E&xport" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:726 +#: src/slic3r/GUI/MainFrame.cpp:752 msgid "S&end to print" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:728 +#: src/slic3r/GUI/MainFrame.cpp:754 msgid "Mate&rial Settings Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:749 +#: src/slic3r/GUI/MainFrame.cpp:775 msgid "Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:760 +#: src/slic3r/GUI/MainFrame.cpp:786 msgid "No previously sliced file." msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:766 +#: src/slic3r/GUI/MainFrame.cpp:792 msgid "Previously sliced file (" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:766 +#: src/slic3r/GUI/MainFrame.cpp:792 msgid ") not found." msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:767 +#: src/slic3r/GUI/MainFrame.cpp:793 msgid "File Not Found" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:802 +#: src/slic3r/GUI/MainFrame.cpp:828 #, possible-c-format msgid "Save %s file as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:802 +#: src/slic3r/GUI/MainFrame.cpp:828 msgid "SVG" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:802 +#: src/slic3r/GUI/MainFrame.cpp:828 msgid "G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:814 +#: src/slic3r/GUI/MainFrame.cpp:840 msgid "Save zip file as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:823 src/slic3r/GUI/Plater.cpp:3058 -#: src/slic3r/GUI/Plater.cpp:4781 src/slic3r/GUI/Tab.cpp:1201 -#: src/slic3r/GUI/Tab.cpp:3615 +#: src/slic3r/GUI/MainFrame.cpp:849 src/slic3r/GUI/Plater.cpp:3105 +#: src/slic3r/GUI/Plater.cpp:5137 src/slic3r/GUI/Tab.cpp:1223 +#: src/slic3r/GUI/Tab.cpp:3647 msgid "Slicing" msgstr "" #. TRN "Processing input_file_basename" -#: src/slic3r/GUI/MainFrame.cpp:825 +#: src/slic3r/GUI/MainFrame.cpp:851 #, possible-c-format msgid "Processing %s" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:848 +#: src/slic3r/GUI/MainFrame.cpp:874 msgid " was successfully sliced." msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:850 +#: src/slic3r/GUI/MainFrame.cpp:876 msgid "Slicing Done!" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:865 +#: src/slic3r/GUI/MainFrame.cpp:891 msgid "Select the STL file to repair:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:875 +#: src/slic3r/GUI/MainFrame.cpp:901 msgid "Save OBJ file (less prone to coordinate errors than STL) as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:887 +#: src/slic3r/GUI/MainFrame.cpp:913 msgid "Your file was repaired." msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:887 src/libslic3r/PrintConfig.cpp:3374 +#: src/slic3r/GUI/MainFrame.cpp:913 src/libslic3r/PrintConfig.cpp:3432 msgid "Repair" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:901 +#: src/slic3r/GUI/MainFrame.cpp:927 msgid "Save configuration as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:920 src/slic3r/GUI/MainFrame.cpp:982 +#: src/slic3r/GUI/MainFrame.cpp:946 src/slic3r/GUI/MainFrame.cpp:1008 msgid "Select configuration to load:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:956 +#: src/slic3r/GUI/MainFrame.cpp:982 msgid "Save presets bundle as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:1003 +#: src/slic3r/GUI/MainFrame.cpp:1029 #, possible-c-format msgid "%d presets successfully imported." msgstr "" -#: src/slic3r/GUI/Mouse3DController.cpp:255 +#: src/slic3r/GUI/Mouse3DController.cpp:291 msgid "3Dconnexion settings" msgstr "" -#: src/slic3r/GUI/Mouse3DController.cpp:259 +#: src/slic3r/GUI/Mouse3DController.cpp:306 msgid "Device:" msgstr "" -#: src/slic3r/GUI/Mouse3DController.cpp:266 +#: src/slic3r/GUI/Mouse3DController.cpp:313 msgid "Speed:" msgstr "" -#: src/slic3r/GUI/Mouse3DController.cpp:270 -#: src/slic3r/GUI/Mouse3DController.cpp:283 +#: src/slic3r/GUI/Mouse3DController.cpp:317 +#: src/slic3r/GUI/Mouse3DController.cpp:337 +#: src/slic3r/GUI/Mouse3DController.cpp:339 msgid "Translation" msgstr "" -#: src/slic3r/GUI/Mouse3DController.cpp:279 +#: src/slic3r/GUI/Mouse3DController.cpp:326 +#: src/slic3r/GUI/Mouse3DController.cpp:337 +msgid "Zoom" +msgstr "" + +#: src/slic3r/GUI/Mouse3DController.cpp:332 msgid "Deadzone:" msgstr "" @@ -3396,607 +3809,677 @@ msgstr "" msgid "%s has encountered an error" msgstr "" -#: src/slic3r/GUI/OptionsGroup.cpp:249 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:56 +msgid "Instances" +msgstr "" + +#: src/slic3r/GUI/ObjectDataViewModel.cpp:60 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:216 +#, possible-c-format +msgid "Instance %d" +msgstr "" + +#: src/slic3r/GUI/ObjectDataViewModel.cpp:67 src/slic3r/GUI/Tab.cpp:3496 +#: src/slic3r/GUI/Tab.cpp:3585 +msgid "Layers" +msgstr "" + +#: src/slic3r/GUI/ObjectDataViewModel.cpp:94 +msgid "Range" +msgstr "" + +#: src/slic3r/GUI/OptionsGroup.cpp:253 msgctxt "Layers" msgid "Top" msgstr "" -#: src/slic3r/GUI/OptionsGroup.cpp:249 +#: src/slic3r/GUI/OptionsGroup.cpp:253 msgctxt "Layers" msgid "Bottom" msgstr "" -#: src/slic3r/GUI/Plater.cpp:155 +#: src/slic3r/GUI/Plater.cpp:163 msgid "Volume" msgstr "" -#: src/slic3r/GUI/Plater.cpp:156 +#: src/slic3r/GUI/Plater.cpp:164 msgid "Facets" msgstr "" -#: src/slic3r/GUI/Plater.cpp:157 +#: src/slic3r/GUI/Plater.cpp:165 msgid "Materials" msgstr "" -#: src/slic3r/GUI/Plater.cpp:160 +#: src/slic3r/GUI/Plater.cpp:168 msgid "Manifold" msgstr "" -#: src/slic3r/GUI/Plater.cpp:210 +#: src/slic3r/GUI/Plater.cpp:218 msgid "Sliced Info" msgstr "" -#: src/slic3r/GUI/Plater.cpp:229 src/slic3r/GUI/Plater.cpp:1179 +#: src/slic3r/GUI/Plater.cpp:237 src/slic3r/GUI/Plater.cpp:1226 msgid "Used Filament (m)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:230 +#: src/slic3r/GUI/Plater.cpp:238 msgid "Used Filament (mm³)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:231 +#: src/slic3r/GUI/Plater.cpp:239 msgid "Used Filament (g)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:232 +#: src/slic3r/GUI/Plater.cpp:240 msgid "Used Material (unit)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:233 +#: src/slic3r/GUI/Plater.cpp:241 msgid "Cost (money)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:234 src/slic3r/GUI/Plater.cpp:1166 -#: src/slic3r/GUI/Plater.cpp:1208 +#: src/slic3r/GUI/Plater.cpp:242 src/slic3r/GUI/Plater.cpp:1213 +#: src/slic3r/GUI/Plater.cpp:1255 msgid "Estimated printing time" msgstr "" -#: src/slic3r/GUI/Plater.cpp:235 +#: src/slic3r/GUI/Plater.cpp:243 msgid "Number of tool changes" msgstr "" -#: src/slic3r/GUI/Plater.cpp:332 +#: src/slic3r/GUI/Plater.cpp:340 msgid "Click to edit preset" msgstr "" -#: src/slic3r/GUI/Plater.cpp:487 +#: src/slic3r/GUI/Plater.cpp:495 msgid "Select what kind of support do you need" msgstr "" -#: src/slic3r/GUI/Plater.cpp:489 src/libslic3r/PrintConfig.cpp:1890 -#: src/libslic3r/PrintConfig.cpp:2642 +#: src/slic3r/GUI/Plater.cpp:497 src/libslic3r/PrintConfig.cpp:1901 +#: src/libslic3r/PrintConfig.cpp:2665 msgid "Support on build plate only" msgstr "" -#: src/slic3r/GUI/Plater.cpp:490 src/slic3r/GUI/Plater.cpp:613 +#: src/slic3r/GUI/Plater.cpp:498 src/slic3r/GUI/Plater.cpp:621 msgid "For support enforcers only" msgstr "" -#: src/slic3r/GUI/Plater.cpp:491 +#: src/slic3r/GUI/Plater.cpp:499 msgid "Everywhere" msgstr "" -#: src/slic3r/GUI/Plater.cpp:523 src/slic3r/GUI/Tab.cpp:1097 +#: src/slic3r/GUI/Plater.cpp:531 src/slic3r/GUI/Tab.cpp:1119 msgid "Brim" msgstr "" -#: src/slic3r/GUI/Plater.cpp:525 +#: src/slic3r/GUI/Plater.cpp:533 msgid "" "This flag enables the brim that will be printed around each object on the " "first layer." msgstr "" -#: src/slic3r/GUI/Plater.cpp:533 +#: src/slic3r/GUI/Plater.cpp:541 msgid "Purging volumes" msgstr "" -#: src/slic3r/GUI/Plater.cpp:627 +#: src/slic3r/GUI/Plater.cpp:635 msgid "Select what kind of pad do you need" msgstr "" -#: src/slic3r/GUI/Plater.cpp:629 +#: src/slic3r/GUI/Plater.cpp:637 msgid "Below object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:630 +#: src/slic3r/GUI/Plater.cpp:638 msgid "Around object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:802 +#: src/slic3r/GUI/Plater.cpp:812 msgid "Print settings" msgstr "" -#: src/slic3r/GUI/Plater.cpp:803 src/slic3r/GUI/Tab.cpp:1405 -#: src/slic3r/GUI/Tab.cpp:1406 +#: src/slic3r/GUI/Plater.cpp:813 src/slic3r/GUI/Tab.cpp:1427 +#: src/slic3r/GUI/Tab.cpp:1428 msgid "Filament" msgstr "" -#: src/slic3r/GUI/Plater.cpp:804 +#: src/slic3r/GUI/Plater.cpp:814 msgid "SLA print settings" msgstr "" -#: src/slic3r/GUI/Plater.cpp:805 src/slic3r/GUI/Preset.cpp:1411 +#: src/slic3r/GUI/Plater.cpp:815 src/slic3r/GUI/Preset.cpp:1474 msgid "SLA material" msgstr "" -#: src/slic3r/GUI/Plater.cpp:806 +#: src/slic3r/GUI/Plater.cpp:816 msgid "Printer" msgstr "" -#: src/slic3r/GUI/Plater.cpp:856 src/slic3r/GUI/Plater.cpp:5143 +#: src/slic3r/GUI/Plater.cpp:875 src/slic3r/GUI/Plater.cpp:5556 msgid "Send to printer" msgstr "" -#: src/slic3r/GUI/Plater.cpp:859 src/slic3r/GUI/Plater.cpp:3058 -#: src/slic3r/GUI/Plater.cpp:4784 +#: src/slic3r/GUI/Plater.cpp:876 +msgid "Remove device" +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:877 +msgid "Export to SD card / Flash drive" +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:889 src/slic3r/GUI/Plater.cpp:3105 +#: src/slic3r/GUI/Plater.cpp:5140 msgid "Slice now" msgstr "" -#: src/slic3r/GUI/Plater.cpp:999 +#: src/slic3r/GUI/Plater.cpp:1039 msgid "Hold Shift to Slice & Export G-code" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1102 +#: src/slic3r/GUI/Plater.cpp:1149 #, possible-c-format msgid "%d (%d shells)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1107 +#: src/slic3r/GUI/Plater.cpp:1154 #, possible-c-format msgid "Auto-repaired (%d errors)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1110 +#: src/slic3r/GUI/Plater.cpp:1157 #, possible-c-format msgid "" "%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d " "facets reversed, %d backwards edges" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1120 +#: src/slic3r/GUI/Plater.cpp:1167 msgid "Yes" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1141 +#: src/slic3r/GUI/Plater.cpp:1188 msgid "Used Material (ml)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1144 +#: src/slic3r/GUI/Plater.cpp:1191 msgid "object(s)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1144 +#: src/slic3r/GUI/Plater.cpp:1191 msgid "supports and pad" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1181 src/slic3r/GUI/Plater.cpp:1196 +#: src/slic3r/GUI/Plater.cpp:1228 src/slic3r/GUI/Plater.cpp:1243 msgid "objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1181 src/slic3r/GUI/Plater.cpp:1196 +#: src/slic3r/GUI/Plater.cpp:1228 src/slic3r/GUI/Plater.cpp:1243 msgid "wipe tower" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1194 src/libslic3r/PrintConfig.cpp:749 -#: src/libslic3r/PrintConfig.cpp:2478 src/libslic3r/PrintConfig.cpp:2479 +#: src/slic3r/GUI/Plater.cpp:1241 src/libslic3r/PrintConfig.cpp:760 +#: src/libslic3r/PrintConfig.cpp:2501 src/libslic3r/PrintConfig.cpp:2502 msgid "Cost" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1211 +#: src/slic3r/GUI/Plater.cpp:1258 msgid "normal mode" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1215 src/slic3r/GUI/Plater.cpp:1224 -#: src/libslic3r/PrintConfig.cpp:572 +#: src/slic3r/GUI/Plater.cpp:1262 src/slic3r/GUI/Plater.cpp:1271 +#: src/libslic3r/PrintConfig.cpp:582 msgid "Color" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1220 +#: src/slic3r/GUI/Plater.cpp:1267 msgid "stealth mode" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1324 +#: src/slic3r/GUI/Plater.cpp:1375 msgid "Load File" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1328 +#: src/slic3r/GUI/Plater.cpp:1379 msgid "Load Files" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1561 -msgid "ERROR: not enough resources to execute a new job." -msgstr "" - -#: src/slic3r/GUI/Plater.cpp:2158 +#: src/slic3r/GUI/Plater.cpp:2135 msgid "New Project" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2277 +#: src/slic3r/GUI/Plater.cpp:2257 msgid "Loading" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2287 +#: src/slic3r/GUI/Plater.cpp:2267 #, possible-c-format msgid "Processing input file %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2315 -msgid "" -"You cannot load SLA project with a multi-part object on the bed" +#: src/slic3r/GUI/Plater.cpp:2295 +msgid "You cannot load SLA project with a multi-part object on the bed" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2316 src/slic3r/GUI/Tab.cpp:2915 +#: src/slic3r/GUI/Plater.cpp:2296 src/slic3r/GUI/Tab.cpp:2943 msgid "Please check your object list before preset changing." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2361 +#: src/slic3r/GUI/Plater.cpp:2341 msgid "" "This file contains several objects positioned at multiple heights.\n" "Instead of considering them as multiple objects, should I consider\n" "this file as a single object having multiple parts?" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2364 src/slic3r/GUI/Plater.cpp:2417 +#: src/slic3r/GUI/Plater.cpp:2344 src/slic3r/GUI/Plater.cpp:2397 msgid "Multi-part object detected" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2371 +#: src/slic3r/GUI/Plater.cpp:2351 msgid "" "This file cannot be loaded in a simple mode. Do you want to switch to an " "advanced mode?" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2372 +#: src/slic3r/GUI/Plater.cpp:2352 msgid "Detected advanced data" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2394 +#: src/slic3r/GUI/Plater.cpp:2374 #, possible-c-format msgid "" "You can't to add the object(s) from %s because of one or some of them " "is(are) multi-part" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2414 +#: src/slic3r/GUI/Plater.cpp:2394 msgid "" "Multiple objects were loaded for a multi-material printer.\n" "Instead of considering them as multiple objects, should I consider\n" "these files to represent a single object having multiple parts?" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2430 +#: src/slic3r/GUI/Plater.cpp:2410 msgid "Loaded" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2532 +#: src/slic3r/GUI/Plater.cpp:2512 msgid "" "Your object appears to be too large, so it was automatically scaled down to " "fit your print bed." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2533 +#: src/slic3r/GUI/Plater.cpp:2513 msgid "Object too large?" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2595 +#: src/slic3r/GUI/Plater.cpp:2575 msgid "Export STL file:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2602 +#: src/slic3r/GUI/Plater.cpp:2582 msgid "Export AMF file:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2608 +#: src/slic3r/GUI/Plater.cpp:2588 msgid "Save file as:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2614 +#: src/slic3r/GUI/Plater.cpp:2594 msgid "Export OBJ file:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2716 +#: src/slic3r/GUI/Plater.cpp:2696 msgid "Delete Object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2727 +#: src/slic3r/GUI/Plater.cpp:2707 msgid "Reset Project" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2765 +#: src/slic3r/GUI/Plater.cpp:2744 +msgid "Hollow" +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:2751 msgid "Optimize Rotation" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2811 +#: src/slic3r/GUI/Plater.cpp:2797 msgid "Arranging" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2833 +#: src/slic3r/GUI/Plater.cpp:2819 msgid "Could not arrange model objects! Some geometries may be invalid." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2839 +#: src/slic3r/GUI/Plater.cpp:2825 msgid "Arranging canceled." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2840 +#: src/slic3r/GUI/Plater.cpp:2826 msgid "Arranging done." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2856 +#: src/slic3r/GUI/Plater.cpp:2842 msgid "Searching for optimal orientation" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2889 +#: src/slic3r/GUI/Plater.cpp:2875 msgid "Orientation search canceled." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2890 +#: src/slic3r/GUI/Plater.cpp:2876 msgid "Orientation found." msgstr "" #: src/slic3r/GUI/Plater.cpp:2906 +msgid "Indexing hollowed object" +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:2910 +msgid "Hollowing cancelled." +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:2911 +msgid "Hollowing done." +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:2913 +msgid "Hollowing failed." +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:2954 msgid "" "The selected object can't be split because it contains more than one volume/" "material." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2917 +#: src/slic3r/GUI/Plater.cpp:2965 msgid "Split to Objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3043 +#: src/slic3r/GUI/Plater.cpp:3090 msgid "Invalid data" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3052 +#: src/slic3r/GUI/Plater.cpp:3099 msgid "Ready to slice" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3090 src/slic3r/GUI/PrintHostDialogs.cpp:232 +#: src/slic3r/GUI/Plater.cpp:3137 src/slic3r/GUI/PrintHostDialogs.cpp:232 msgid "Cancelling" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3107 +#: src/slic3r/GUI/Plater.cpp:3154 msgid "Another export job is currently running." msgstr "" -#: src/slic3r/GUI/Plater.cpp:3276 +#: src/slic3r/GUI/Plater.cpp:3272 +msgid "Please select the file to reload" +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:3307 +msgid "It is not allowed to change the file to reload" +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:3307 +msgid "Do you want to retry" +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:3328 +msgid "Reload from: " +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:3436 +msgid "Unable to reload:" +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:3441 +msgid "Error during reload" +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:3461 +msgid "Reload all from disk" +msgstr "" + +#: src/slic3r/GUI/Plater.cpp:3482 msgid "Fix Throught NetFabb" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3467 +#: src/slic3r/GUI/Plater.cpp:3673 msgid "Export failed" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3472 src/slic3r/GUI/PrintHostDialogs.cpp:233 +#: src/slic3r/GUI/Plater.cpp:3678 src/slic3r/GUI/PrintHostDialogs.cpp:233 msgid "Cancelled" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3712 src/slic3r/GUI/Plater.cpp:3734 +#: src/slic3r/GUI/Plater.cpp:3940 src/slic3r/GUI/Plater.cpp:3962 msgid "Remove the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3721 +#: src/slic3r/GUI/Plater.cpp:3949 msgid "Add one more instance of the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3723 +#: src/slic3r/GUI/Plater.cpp:3951 msgid "Remove one instance of the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3725 +#: src/slic3r/GUI/Plater.cpp:3953 msgid "Set number of instances" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3725 +#: src/slic3r/GUI/Plater.cpp:3953 msgid "Change the number of instances of the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3744 +#: src/slic3r/GUI/Plater.cpp:3972 msgid "Reload the selected object from disk" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3747 +#: src/slic3r/GUI/Plater.cpp:3975 msgid "Export the selected object as STL file" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3772 +#: src/slic3r/GUI/Plater.cpp:4004 msgid "Along X axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3772 +#: src/slic3r/GUI/Plater.cpp:4004 msgid "Mirror the selected object along the X axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3774 +#: src/slic3r/GUI/Plater.cpp:4006 msgid "Along Y axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3774 +#: src/slic3r/GUI/Plater.cpp:4006 msgid "Mirror the selected object along the Y axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3776 +#: src/slic3r/GUI/Plater.cpp:4008 msgid "Along Z axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3776 +#: src/slic3r/GUI/Plater.cpp:4008 msgid "Mirror the selected object along the Z axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3779 +#: src/slic3r/GUI/Plater.cpp:4011 msgid "Mirror" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3779 +#: src/slic3r/GUI/Plater.cpp:4011 msgid "Mirror the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3791 +#: src/slic3r/GUI/Plater.cpp:4023 msgid "To objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3791 src/slic3r/GUI/Plater.cpp:3811 +#: src/slic3r/GUI/Plater.cpp:4023 src/slic3r/GUI/Plater.cpp:4043 msgid "Split the selected object into individual objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3793 +#: src/slic3r/GUI/Plater.cpp:4025 msgid "To parts" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3793 src/slic3r/GUI/Plater.cpp:3825 +#: src/slic3r/GUI/Plater.cpp:4025 src/slic3r/GUI/Plater.cpp:4057 msgid "Split the selected object into individual sub-parts" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3796 src/slic3r/GUI/Plater.cpp:3811 -#: src/slic3r/GUI/Plater.cpp:3825 src/libslic3r/PrintConfig.cpp:3398 +#: src/slic3r/GUI/Plater.cpp:4028 src/slic3r/GUI/Plater.cpp:4043 +#: src/slic3r/GUI/Plater.cpp:4057 src/libslic3r/PrintConfig.cpp:3456 msgid "Split" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3796 +#: src/slic3r/GUI/Plater.cpp:4028 msgid "Split the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3817 +#: src/slic3r/GUI/Plater.cpp:4049 msgid "Optimize orientation" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3817 +#: src/slic3r/GUI/Plater.cpp:4049 msgid "Optimize the rotation of the object for better print results." msgstr "" -#: src/slic3r/GUI/Plater.cpp:3857 +#: src/slic3r/GUI/Plater.cpp:4108 msgid "3D editor view" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3865 src/slic3r/GUI/Tab.cpp:2358 +#: src/slic3r/GUI/Plater.cpp:4116 src/slic3r/GUI/Tab.cpp:2386 msgid "Preview" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4144 +#: src/slic3r/GUI/Plater.cpp:4413 msgid "" "%1% printer was active at the time the target Undo / Redo snapshot was " "taken. Switching to %1% printer requires reloading of %1% presets." msgstr "" -#: src/slic3r/GUI/Plater.cpp:4319 +#: src/slic3r/GUI/Plater.cpp:4588 msgid "Load Project" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4347 +#: src/slic3r/GUI/Plater.cpp:4616 msgid "Import Object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4351 +#: src/slic3r/GUI/Plater.cpp:4620 msgid "Import Objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4410 -msgid "All objects will be removed, continue ?" +#: src/slic3r/GUI/Plater.cpp:4684 +msgid "All objects will be removed, continue?" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4418 +#: src/slic3r/GUI/Plater.cpp:4692 msgid "Delete Selected Objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4426 +#: src/slic3r/GUI/Plater.cpp:4700 msgid "Increase Instances" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4461 +#: src/slic3r/GUI/Plater.cpp:4735 msgid "Decrease Instances" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4497 +#: src/slic3r/GUI/Plater.cpp:4771 #, possible-c-format msgid "Set numbers of copies to %d" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4527 +#: src/slic3r/GUI/Plater.cpp:4801 msgid "Cut by Plane" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4559 +#: src/slic3r/GUI/Plater.cpp:4854 msgid "Save G-code file as:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4559 +#: src/slic3r/GUI/Plater.cpp:4854 msgid "Save SL1 file as:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4671 +#: src/slic3r/GUI/Plater.cpp:5001 #, possible-c-format msgid "STL file exported to %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4687 +#: src/slic3r/GUI/Plater.cpp:5022 #, possible-c-format msgid "AMF file exported to %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4690 +#: src/slic3r/GUI/Plater.cpp:5025 #, possible-c-format msgid "Error exporting AMF file %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4722 +#: src/slic3r/GUI/Plater.cpp:5068 #, possible-c-format msgid "3MF file exported to %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4727 +#: src/slic3r/GUI/Plater.cpp:5073 #, possible-c-format msgid "Error exporting 3MF file %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:5142 +#: src/slic3r/GUI/Plater.cpp:5555 msgid "Export" msgstr "" -#: src/slic3r/GUI/Plater.cpp:5143 +#: src/slic3r/GUI/Plater.cpp:5556 msgid "Send G-code" msgstr "" -#: src/slic3r/GUI/Plater.cpp:5227 +#: src/slic3r/GUI/Plater.cpp:5640 msgid "Paste From Clipboard" msgstr "" -#: src/slic3r/GUI/Preferences.cpp:22 src/slic3r/GUI/Tab.cpp:1766 -#: src/slic3r/GUI/Tab.cpp:2010 +#: src/slic3r/GUI/Preferences.cpp:22 src/slic3r/GUI/Tab.cpp:1794 +#: src/slic3r/GUI/Tab.cpp:2038 msgid "General" msgstr "" -#: src/slic3r/GUI/Preferences.cpp:44 +#: src/slic3r/GUI/Preferences.cpp:39 msgid "Remember output directory" msgstr "" -#: src/slic3r/GUI/Preferences.cpp:46 +#: src/slic3r/GUI/Preferences.cpp:41 msgid "" "If this is enabled, Slic3r will prompt the last output directory instead of " "the one containing the input files." msgstr "" -#: src/slic3r/GUI/Preferences.cpp:52 +#: src/slic3r/GUI/Preferences.cpp:47 msgid "Auto-center parts" msgstr "" -#: src/slic3r/GUI/Preferences.cpp:54 +#: src/slic3r/GUI/Preferences.cpp:49 msgid "" "If this is enabled, Slic3r will auto-center objects around the print bed " "center." msgstr "" -#: src/slic3r/GUI/Preferences.cpp:60 +#: src/slic3r/GUI/Preferences.cpp:55 msgid "Background processing" msgstr "" -#: src/slic3r/GUI/Preferences.cpp:62 +#: src/slic3r/GUI/Preferences.cpp:57 msgid "" "If this is enabled, Slic3r will pre-process objects as soon as they're " "loaded in order to save time when exporting G-code." msgstr "" -#: src/slic3r/GUI/Preferences.cpp:71 +#: src/slic3r/GUI/Preferences.cpp:66 msgid "" "If enabled, PrusaSlicer will check for the new versions of itself online. " "When a new version becomes available a notification is displayed at the next " @@ -4004,7 +4487,17 @@ msgid "" "notification mechanisms, no automatic installation is done." msgstr "" -#: src/slic3r/GUI/Preferences.cpp:79 +#: src/slic3r/GUI/Preferences.cpp:73 +msgid "Export sources full pathnames to 3mf and amf" +msgstr "" + +#: src/slic3r/GUI/Preferences.cpp:75 +msgid "" +"If enabled, allows the Reload from disk command to automatically find and " +"load the files when invoked." +msgstr "" + +#: src/slic3r/GUI/Preferences.cpp:84 msgid "" "If enabled, Slic3r downloads updates of built-in system presets in the " "background. These updates are downloaded into a separate temporary location. " @@ -4012,215 +4505,260 @@ msgid "" "startup." msgstr "" -#: src/slic3r/GUI/Preferences.cpp:84 +#: src/slic3r/GUI/Preferences.cpp:89 msgid "Suppress \" - default - \" presets" msgstr "" -#: src/slic3r/GUI/Preferences.cpp:86 +#: src/slic3r/GUI/Preferences.cpp:91 msgid "" "Suppress \" - default - \" presets in the Print / Filament / Printer " "selections once there are any other valid presets available." msgstr "" -#: src/slic3r/GUI/Preferences.cpp:92 +#: src/slic3r/GUI/Preferences.cpp:97 msgid "Show incompatible print and filament presets" msgstr "" -#: src/slic3r/GUI/Preferences.cpp:94 +#: src/slic3r/GUI/Preferences.cpp:99 msgid "" "When checked, the print and filament presets are shown in the preset editor " "even if they are marked as incompatible with the active printer" msgstr "" -#: src/slic3r/GUI/Preferences.cpp:101 +#: src/slic3r/GUI/Preferences.cpp:106 msgid "Use Retina resolution for the 3D scene" msgstr "" -#: src/slic3r/GUI/Preferences.cpp:103 +#: src/slic3r/GUI/Preferences.cpp:108 msgid "" "If enabled, the 3D scene will be rendered in Retina resolution. If you are " "experiencing 3D performance problems, disabling this option may help." msgstr "" -#: src/slic3r/GUI/Preferences.cpp:110 +#: src/slic3r/GUI/Preferences.cpp:115 +msgid "Camera" +msgstr "" + +#: src/slic3r/GUI/Preferences.cpp:121 msgid "Use perspective camera" msgstr "" -#: src/slic3r/GUI/Preferences.cpp:112 +#: src/slic3r/GUI/Preferences.cpp:123 msgid "" "If enabled, use perspective camera. If not enabled, use orthographic camera." msgstr "" -#: src/slic3r/GUI/Preferences.cpp:117 +#: src/slic3r/GUI/Preferences.cpp:129 +msgid "Use free camera" +msgstr "" + +#: src/slic3r/GUI/Preferences.cpp:131 +msgid "If enabled, use free camera. If not enabled, use constrained camera." +msgstr "" + +#: src/slic3r/GUI/Preferences.cpp:137 +msgid "GUI" +msgstr "" + +#: src/slic3r/GUI/Preferences.cpp:147 msgid "Use custom size for toolbar icons" msgstr "" -#: src/slic3r/GUI/Preferences.cpp:119 +#: src/slic3r/GUI/Preferences.cpp:149 msgid "If enabled, you can change size of toolbar icons manually." msgstr "" -#: src/slic3r/GUI/Preferences.cpp:144 +#: src/slic3r/GUI/Preferences.cpp:176 #, possible-c-format msgid "You need to restart %s to make the changes effective." msgstr "" -#: src/slic3r/GUI/Preferences.cpp:192 +#: src/slic3r/GUI/Preferences.cpp:226 msgid "Icon size in a respect to the default size" msgstr "" -#: src/slic3r/GUI/Preferences.cpp:207 +#: src/slic3r/GUI/Preferences.cpp:241 msgid "Select toolbar icon size in respect to the default one." msgstr "" -#: src/slic3r/GUI/Preset.cpp:237 +#: src/slic3r/GUI/Preset.cpp:247 msgid "modified" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1034 src/slic3r/GUI/Preset.cpp:1081 -#: src/slic3r/GUI/Preset.cpp:1157 src/slic3r/GUI/Preset.cpp:1191 -#: src/slic3r/GUI/PresetBundle.cpp:1576 src/slic3r/GUI/PresetBundle.cpp:1660 +#: src/slic3r/GUI/Preset.cpp:1059 src/slic3r/GUI/Preset.cpp:1114 +#: src/slic3r/GUI/Preset.cpp:1192 src/slic3r/GUI/Preset.cpp:1234 +#: src/slic3r/GUI/PresetBundle.cpp:1576 src/slic3r/GUI/PresetBundle.cpp:1665 msgid "System presets" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1085 src/slic3r/GUI/Preset.cpp:1195 -#: src/slic3r/GUI/PresetBundle.cpp:1665 +#: src/slic3r/GUI/Preset.cpp:1118 src/slic3r/GUI/Preset.cpp:1238 +#: src/slic3r/GUI/PresetBundle.cpp:1670 msgid "User presets" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1116 src/slic3r/GUI/Tab.cpp:243 -msgid "Add a new printer" -msgstr "" - -#: src/slic3r/GUI/Preset.cpp:1118 +#: src/slic3r/GUI/Preset.cpp:1151 msgid "Add/Remove materials" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1409 +#: src/slic3r/GUI/Preset.cpp:1153 +msgid "Add/Remove printers" +msgstr "" + +#: src/slic3r/GUI/Preset.cpp:1472 msgid "filament" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1410 +#: src/slic3r/GUI/Preset.cpp:1473 msgid "SLA print" msgstr "" -#: src/slic3r/GUI/PresetBundle.cpp:1676 +#: src/slic3r/GUI/PresetBundle.cpp:1697 msgid "Add/Remove filaments" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:28 +#: src/slic3r/GUI/PresetHints.cpp:29 msgid "" "If estimated layer time is below ~%1%s, fan will run at %2%%% and print " "speed will be reduced so that no less than %3%s are spent on that layer " "(however, speed will never be reduced below %4%mm/s)." msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:35 +#: src/slic3r/GUI/PresetHints.cpp:36 msgid "" "If estimated layer time is greater, but still below ~%1%s, fan will run at a " "proportionally decreasing speed between %2%%% and %3%%%." msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:39 +#: src/slic3r/GUI/PresetHints.cpp:40 msgid "During the other layers, fan" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:41 +#: src/slic3r/GUI/PresetHints.cpp:42 msgid "Fan" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:47 +#: src/slic3r/GUI/PresetHints.cpp:48 msgid "will always run at %1%%%" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:50 +#: src/slic3r/GUI/PresetHints.cpp:51 msgid "except for the first %1% layers." msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:52 +#: src/slic3r/GUI/PresetHints.cpp:53 msgid "except for the first layer." msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:54 +#: src/slic3r/GUI/PresetHints.cpp:55 msgid "will be turned off." msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:155 +#: src/slic3r/GUI/PresetHints.cpp:156 msgid "external perimeters" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:164 +#: src/slic3r/GUI/PresetHints.cpp:165 msgid "perimeters" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:173 +#: src/slic3r/GUI/PresetHints.cpp:174 msgid "infill" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:183 +#: src/slic3r/GUI/PresetHints.cpp:184 msgid "solid infill" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:191 +#: src/slic3r/GUI/PresetHints.cpp:192 msgid "top solid infill" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:202 +#: src/slic3r/GUI/PresetHints.cpp:203 msgid "support" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:212 +#: src/slic3r/GUI/PresetHints.cpp:213 msgid "support interface" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:218 +#: src/slic3r/GUI/PresetHints.cpp:219 msgid "First layer volumetric" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:218 +#: src/slic3r/GUI/PresetHints.cpp:219 msgid "Bridging volumetric" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:218 +#: src/slic3r/GUI/PresetHints.cpp:219 msgid "Volumetric" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:219 +#: src/slic3r/GUI/PresetHints.cpp:220 msgid "flow rate is maximized" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:222 +#: src/slic3r/GUI/PresetHints.cpp:223 msgid "by the print profile maximum" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:223 +#: src/slic3r/GUI/PresetHints.cpp:224 msgid "when printing" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:224 +#: src/slic3r/GUI/PresetHints.cpp:225 msgid "with a volumetric rate" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:228 +#: src/slic3r/GUI/PresetHints.cpp:229 #, possible-c-format msgid "%3.2f mm³/s at filament speed %3.2f mm/s." msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:246 +#: src/slic3r/GUI/PresetHints.cpp:247 msgid "" "Recommended object thin wall thickness: Not available due to invalid layer " "height." msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:262 +#: src/slic3r/GUI/PresetHints.cpp:263 #, possible-c-format msgid "Recommended object thin wall thickness for layer height %.2f and" msgstr "" -#: src/slic3r/GUI/PresetHints.cpp:268 +#: src/slic3r/GUI/PresetHints.cpp:269 #, possible-c-format msgid "%d lines: %.2f mm" msgstr "" +#: src/slic3r/GUI/PresetHints.cpp:300 +msgid "" +"Top / bottom shell thickness hint: Not available due to invalid layer height." +msgstr "" + +#: src/slic3r/GUI/PresetHints.cpp:313 +msgid "Top shell is %1% mm thick for layer height %2% mm." +msgstr "" + +#: src/slic3r/GUI/PresetHints.cpp:316 +msgid "Minimum top shell thickness is %1% mm." +msgstr "" + +#: src/slic3r/GUI/PresetHints.cpp:319 +msgid "Top is open." +msgstr "" + +#: src/slic3r/GUI/PresetHints.cpp:332 +msgid "Bottom shell is %1% mm thick for layer height %2% mm." +msgstr "" + +#: src/slic3r/GUI/PresetHints.cpp:335 +msgid "Minimum bottom shell thickness is %1% mm." +msgstr "" + +#: src/slic3r/GUI/PresetHints.cpp:338 +msgid "Bottom is open." +msgstr "" + #: src/slic3r/GUI/PrintHostDialogs.cpp:33 msgid "Send G-Code to printer host" msgstr "" @@ -4295,12 +4833,12 @@ msgid "Time" msgstr "" #: src/slic3r/GUI/RammingChart.cpp:76 src/slic3r/GUI/WipeTowerDialog.cpp:83 -#: src/libslic3r/PrintConfig.cpp:634 src/libslic3r/PrintConfig.cpp:678 -#: src/libslic3r/PrintConfig.cpp:693 src/libslic3r/PrintConfig.cpp:2385 -#: src/libslic3r/PrintConfig.cpp:2394 src/libslic3r/PrintConfig.cpp:2495 -#: src/libslic3r/PrintConfig.cpp:2503 src/libslic3r/PrintConfig.cpp:2511 -#: src/libslic3r/PrintConfig.cpp:2518 src/libslic3r/PrintConfig.cpp:2526 -#: src/libslic3r/PrintConfig.cpp:2534 +#: src/libslic3r/PrintConfig.cpp:644 src/libslic3r/PrintConfig.cpp:688 +#: src/libslic3r/PrintConfig.cpp:703 src/libslic3r/PrintConfig.cpp:2408 +#: src/libslic3r/PrintConfig.cpp:2417 src/libslic3r/PrintConfig.cpp:2518 +#: src/libslic3r/PrintConfig.cpp:2526 src/libslic3r/PrintConfig.cpp:2534 +#: src/libslic3r/PrintConfig.cpp:2541 src/libslic3r/PrintConfig.cpp:2549 +#: src/libslic3r/PrintConfig.cpp:2557 msgid "s" msgstr "" @@ -4308,8 +4846,8 @@ msgstr "" msgid "Volumetric speed" msgstr "" -#: src/slic3r/GUI/RammingChart.cpp:81 src/libslic3r/PrintConfig.cpp:591 -#: src/libslic3r/PrintConfig.cpp:1247 +#: src/slic3r/GUI/RammingChart.cpp:81 src/libslic3r/PrintConfig.cpp:601 +#: src/libslic3r/PrintConfig.cpp:1250 msgid "mm³/s" msgstr "" @@ -4349,11 +4887,11 @@ msgstr "" msgid "Scale To Fit" msgstr "" -#: src/slic3r/GUI/Selection.cpp:1474 +#: src/slic3r/GUI/Selection.cpp:1475 msgid "Set Printable Instance" msgstr "" -#: src/slic3r/GUI/Selection.cpp:1474 +#: src/slic3r/GUI/Selection.cpp:1475 msgid "Set Unprintable Instance" msgstr "" @@ -4365,7 +4903,7 @@ msgstr "" msgid "Copy to Clipboard" msgstr "" -#: src/slic3r/GUI/Tab.cpp:52 src/libslic3r/PrintConfig.cpp:244 +#: src/slic3r/GUI/Tab.cpp:52 src/libslic3r/PrintConfig.cpp:255 msgid "Compatible printers" msgstr "" @@ -4373,7 +4911,7 @@ msgstr "" msgid "Select the printers this profile is compatible with." msgstr "" -#: src/slic3r/GUI/Tab.cpp:58 src/libslic3r/PrintConfig.cpp:259 +#: src/slic3r/GUI/Tab.cpp:58 src/libslic3r/PrintConfig.cpp:270 msgid "Compatible print profiles" msgstr "" @@ -4397,289 +4935,297 @@ msgid "" "or click this button." msgstr "" -#: src/slic3r/GUI/Tab.cpp:943 +#: src/slic3r/GUI/Tab.cpp:243 +msgid "Add a new printer" +msgstr "" + +#: src/slic3r/GUI/Tab.cpp:955 msgid "This is a default preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:945 +#: src/slic3r/GUI/Tab.cpp:957 msgid "This is a system preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:947 +#: src/slic3r/GUI/Tab.cpp:959 msgid "Current preset is inherited from the default preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:950 +#: src/slic3r/GUI/Tab.cpp:962 #, possible-c-format msgid "" "Current preset is inherited from:\n" "\t%s" msgstr "" -#: src/slic3r/GUI/Tab.cpp:954 +#: src/slic3r/GUI/Tab.cpp:966 msgid "It can't be deleted or modified." msgstr "" -#: src/slic3r/GUI/Tab.cpp:955 +#: src/slic3r/GUI/Tab.cpp:967 msgid "" "Any modifications should be saved as a new preset inherited from this one." msgstr "" -#: src/slic3r/GUI/Tab.cpp:956 +#: src/slic3r/GUI/Tab.cpp:968 msgid "To do that please specify a new name for the preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:960 +#: src/slic3r/GUI/Tab.cpp:972 msgid "Additional information:" msgstr "" -#: src/slic3r/GUI/Tab.cpp:966 +#: src/slic3r/GUI/Tab.cpp:978 msgid "printer model" msgstr "" -#: src/slic3r/GUI/Tab.cpp:974 +#: src/slic3r/GUI/Tab.cpp:986 msgid "default print profile" msgstr "" -#: src/slic3r/GUI/Tab.cpp:977 +#: src/slic3r/GUI/Tab.cpp:989 msgid "default filament profile" msgstr "" -#: src/slic3r/GUI/Tab.cpp:991 +#: src/slic3r/GUI/Tab.cpp:1003 msgid "default SLA material profile" msgstr "" -#: src/slic3r/GUI/Tab.cpp:995 +#: src/slic3r/GUI/Tab.cpp:1007 msgid "default SLA print profile" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1003 +#: src/slic3r/GUI/Tab.cpp:1015 msgid "full profile name" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1004 +#: src/slic3r/GUI/Tab.cpp:1016 msgid "symbolic profile name" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1038 src/slic3r/GUI/Tab.cpp:3558 +#: src/slic3r/GUI/Tab.cpp:1050 src/slic3r/GUI/Tab.cpp:3583 msgid "Layers and perimeters" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1043 +#: src/slic3r/GUI/Tab.cpp:1055 msgid "Vertical shells" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1054 +#: src/slic3r/GUI/Tab.cpp:1066 msgid "Horizontal shells" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1055 src/libslic3r/PrintConfig.cpp:1776 +#: src/slic3r/GUI/Tab.cpp:1067 src/libslic3r/PrintConfig.cpp:1780 msgid "Solid layers" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1060 +#: src/slic3r/GUI/Tab.cpp:1071 +msgid "Minimum shell thickness" +msgstr "" + +#: src/slic3r/GUI/Tab.cpp:1082 msgid "Quality (slower slicing)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1078 +#: src/slic3r/GUI/Tab.cpp:1100 msgid "Reducing printing time" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1090 +#: src/slic3r/GUI/Tab.cpp:1112 msgid "Skirt and brim" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1107 +#: src/slic3r/GUI/Tab.cpp:1129 msgid "Raft" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1111 +#: src/slic3r/GUI/Tab.cpp:1133 msgid "Options for support material and raft" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1126 +#: src/slic3r/GUI/Tab.cpp:1148 msgid "Speed for print moves" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1138 +#: src/slic3r/GUI/Tab.cpp:1160 msgid "Speed for non-print moves" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1141 +#: src/slic3r/GUI/Tab.cpp:1163 msgid "Modifiers" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1144 +#: src/slic3r/GUI/Tab.cpp:1166 msgid "Acceleration control (advanced)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1151 +#: src/slic3r/GUI/Tab.cpp:1173 msgid "Autospeed (advanced)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1159 +#: src/slic3r/GUI/Tab.cpp:1181 msgid "Multiple Extruders" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1167 +#: src/slic3r/GUI/Tab.cpp:1189 msgid "Ooze prevention" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1185 +#: src/slic3r/GUI/Tab.cpp:1207 msgid "Extrusion width" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1195 +#: src/slic3r/GUI/Tab.cpp:1217 msgid "Overlap" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1198 +#: src/slic3r/GUI/Tab.cpp:1220 msgid "Flow" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1207 +#: src/slic3r/GUI/Tab.cpp:1229 msgid "Other" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1210 src/slic3r/GUI/Tab.cpp:3618 +#: src/slic3r/GUI/Tab.cpp:1232 src/slic3r/GUI/Tab.cpp:3650 msgid "Output options" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1211 +#: src/slic3r/GUI/Tab.cpp:1233 msgid "Sequential printing" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1213 +#: src/slic3r/GUI/Tab.cpp:1235 msgid "Extruder clearance (mm)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1222 src/slic3r/GUI/Tab.cpp:3619 +#: src/slic3r/GUI/Tab.cpp:1240 src/slic3r/GUI/Tab.cpp:3651 msgid "Output file" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1229 src/libslic3r/PrintConfig.cpp:1448 +#: src/slic3r/GUI/Tab.cpp:1247 src/libslic3r/PrintConfig.cpp:1453 msgid "Post-processing scripts" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1235 src/slic3r/GUI/Tab.cpp:1236 -#: src/slic3r/GUI/Tab.cpp:1517 src/slic3r/GUI/Tab.cpp:1518 -#: src/slic3r/GUI/Tab.cpp:1982 src/slic3r/GUI/Tab.cpp:1983 -#: src/slic3r/GUI/Tab.cpp:2096 src/slic3r/GUI/Tab.cpp:2097 -#: src/slic3r/GUI/Tab.cpp:3495 src/slic3r/GUI/Tab.cpp:3496 +#: src/slic3r/GUI/Tab.cpp:1253 src/slic3r/GUI/Tab.cpp:1254 +#: src/slic3r/GUI/Tab.cpp:1542 src/slic3r/GUI/Tab.cpp:1543 +#: src/slic3r/GUI/Tab.cpp:2010 src/slic3r/GUI/Tab.cpp:2011 +#: src/slic3r/GUI/Tab.cpp:2124 src/slic3r/GUI/Tab.cpp:2125 +#: src/slic3r/GUI/Tab.cpp:3520 src/slic3r/GUI/Tab.cpp:3521 msgid "Notes" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1242 src/slic3r/GUI/Tab.cpp:1525 -#: src/slic3r/GUI/Tab.cpp:1989 src/slic3r/GUI/Tab.cpp:2103 -#: src/slic3r/GUI/Tab.cpp:3503 src/slic3r/GUI/Tab.cpp:3624 +#: src/slic3r/GUI/Tab.cpp:1260 src/slic3r/GUI/Tab.cpp:1550 +#: src/slic3r/GUI/Tab.cpp:2017 src/slic3r/GUI/Tab.cpp:2131 +#: src/slic3r/GUI/Tab.cpp:3528 src/slic3r/GUI/Tab.cpp:3656 msgid "Dependencies" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1243 src/slic3r/GUI/Tab.cpp:1526 -#: src/slic3r/GUI/Tab.cpp:1990 src/slic3r/GUI/Tab.cpp:2104 -#: src/slic3r/GUI/Tab.cpp:3504 src/slic3r/GUI/Tab.cpp:3625 +#: src/slic3r/GUI/Tab.cpp:1261 src/slic3r/GUI/Tab.cpp:1551 +#: src/slic3r/GUI/Tab.cpp:2018 src/slic3r/GUI/Tab.cpp:2132 +#: src/slic3r/GUI/Tab.cpp:3529 src/slic3r/GUI/Tab.cpp:3657 msgid "Profile dependencies" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1303 src/slic3r/GUI/Tab.cpp:1358 +#: src/slic3r/GUI/Tab.cpp:1325 src/slic3r/GUI/Tab.cpp:1380 msgid "Filament Overrides" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1304 src/slic3r/GUI/Tab.cpp:1363 -#: src/slic3r/GUI/Tab.cpp:2338 +#: src/slic3r/GUI/Tab.cpp:1326 src/slic3r/GUI/Tab.cpp:1385 +#: src/slic3r/GUI/Tab.cpp:2366 msgid "Retraction" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1413 src/libslic3r/PrintConfig.cpp:2056 +#: src/slic3r/GUI/Tab.cpp:1435 src/libslic3r/PrintConfig.cpp:2067 msgid "Temperature" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1419 +#: src/slic3r/GUI/Tab.cpp:1441 msgid "Bed" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1424 +#: src/slic3r/GUI/Tab.cpp:1446 msgid "Cooling" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1425 src/libslic3r/PrintConfig.cpp:1350 -#: src/libslic3r/PrintConfig.cpp:2177 +#: src/slic3r/GUI/Tab.cpp:1447 src/libslic3r/PrintConfig.cpp:1355 +#: src/libslic3r/PrintConfig.cpp:2200 msgid "Enable" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1436 +#: src/slic3r/GUI/Tab.cpp:1458 msgid "Fan settings" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1445 +#: src/slic3r/GUI/Tab.cpp:1467 msgid "Cooling thresholds" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1451 +#: src/slic3r/GUI/Tab.cpp:1473 msgid "Filament properties" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1455 +#: src/slic3r/GUI/Tab.cpp:1480 msgid "Print speed override" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1465 +#: src/slic3r/GUI/Tab.cpp:1490 msgid "Wipe tower parameters" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1468 +#: src/slic3r/GUI/Tab.cpp:1493 msgid "Toolchange parameters with single extruder MM printers" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1482 +#: src/slic3r/GUI/Tab.cpp:1507 msgid "Ramming settings" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1504 src/slic3r/GUI/Tab.cpp:1945 +#: src/slic3r/GUI/Tab.cpp:1529 src/slic3r/GUI/Tab.cpp:1973 msgid "Custom G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1505 src/slic3r/GUI/Tab.cpp:1946 -#: src/libslic3r/PrintConfig.cpp:1802 src/libslic3r/PrintConfig.cpp:1817 +#: src/slic3r/GUI/Tab.cpp:1530 src/slic3r/GUI/Tab.cpp:1974 +#: src/libslic3r/PrintConfig.cpp:1813 src/libslic3r/PrintConfig.cpp:1828 msgid "Start G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1511 src/slic3r/GUI/Tab.cpp:1952 -#: src/libslic3r/PrintConfig.cpp:374 src/libslic3r/PrintConfig.cpp:384 +#: src/slic3r/GUI/Tab.cpp:1536 src/slic3r/GUI/Tab.cpp:1980 +#: src/libslic3r/PrintConfig.cpp:385 src/libslic3r/PrintConfig.cpp:395 msgid "End G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1568 +#: src/slic3r/GUI/Tab.cpp:1593 msgid "Volumetric flow hints not available" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1654 src/slic3r/GUI/Tab.cpp:1885 +#: src/slic3r/GUI/Tab.cpp:1679 src/slic3r/GUI/Tab.cpp:1913 msgid "Test" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1664 +#: src/slic3r/GUI/Tab.cpp:1689 msgid "Could not get a valid Printer Host reference" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1670 src/slic3r/GUI/Tab.cpp:1898 +#: src/slic3r/GUI/Tab.cpp:1695 src/slic3r/GUI/Tab.cpp:1926 msgid "Success!" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1685 +#: src/slic3r/GUI/Tab.cpp:1713 msgid "" "HTTPS CA file is optional. It is only needed if you use HTTPS with a self-" "signed certificate." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1698 +#: src/slic3r/GUI/Tab.cpp:1726 msgid "Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1699 +#: src/slic3r/GUI/Tab.cpp:1727 msgid "Open CA certificate file" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1727 +#: src/slic3r/GUI/Tab.cpp:1755 #, possible-c-format msgid "" "HTTPS CA File:\n" @@ -4689,24 +5235,24 @@ msgid "" "Store / Keychain." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1767 src/slic3r/GUI/Tab.cpp:2011 +#: src/slic3r/GUI/Tab.cpp:1795 src/slic3r/GUI/Tab.cpp:2039 msgid "Size and coordinates" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1772 src/slic3r/GUI/Tab.cpp:2016 -#: src/slic3r/GUI/Tab.cpp:3132 +#: src/slic3r/GUI/Tab.cpp:1800 src/slic3r/GUI/Tab.cpp:2044 +#: src/slic3r/GUI/Tab.cpp:3160 msgid "Set" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1804 +#: src/slic3r/GUI/Tab.cpp:1832 msgid "Capabilities" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1809 +#: src/slic3r/GUI/Tab.cpp:1837 msgid "Number of extruders of the printer." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1837 +#: src/slic3r/GUI/Tab.cpp:1865 msgid "" "Single Extruder Multi Material is selected, \n" "and all extruders must have the same diameter.\n" @@ -4714,248 +5260,248 @@ msgid "" "nozzle diameter value?" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1840 src/slic3r/GUI/Tab.cpp:2308 -#: src/libslic3r/PrintConfig.cpp:1323 +#: src/slic3r/GUI/Tab.cpp:1868 src/slic3r/GUI/Tab.cpp:2336 +#: src/libslic3r/PrintConfig.cpp:1326 msgid "Nozzle diameter" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1870 +#: src/slic3r/GUI/Tab.cpp:1898 msgid "USB/Serial connection" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1871 src/libslic3r/PrintConfig.cpp:1656 +#: src/slic3r/GUI/Tab.cpp:1899 src/libslic3r/PrintConfig.cpp:1661 msgid "Serial port" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1876 +#: src/slic3r/GUI/Tab.cpp:1904 msgid "Rescan serial ports" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1898 +#: src/slic3r/GUI/Tab.cpp:1926 msgid "Connection to printer works correctly." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1901 +#: src/slic3r/GUI/Tab.cpp:1929 msgid "Connection failed." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1914 src/slic3r/GUI/Tab.cpp:2091 +#: src/slic3r/GUI/Tab.cpp:1942 src/slic3r/GUI/Tab.cpp:2119 msgid "Print Host upload" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1958 src/libslic3r/PrintConfig.cpp:143 +#: src/slic3r/GUI/Tab.cpp:1986 src/libslic3r/PrintConfig.cpp:143 msgid "Before layer change G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1964 src/libslic3r/PrintConfig.cpp:1069 +#: src/slic3r/GUI/Tab.cpp:1992 src/libslic3r/PrintConfig.cpp:1080 msgid "After layer change G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1970 src/libslic3r/PrintConfig.cpp:2082 +#: src/slic3r/GUI/Tab.cpp:1998 src/libslic3r/PrintConfig.cpp:2093 msgid "Tool change G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1976 +#: src/slic3r/GUI/Tab.cpp:2004 msgid "Between objects G-code (for sequential printing)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2048 +#: src/slic3r/GUI/Tab.cpp:2076 msgid "Display" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2063 +#: src/slic3r/GUI/Tab.cpp:2091 msgid "Tilt" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2064 +#: src/slic3r/GUI/Tab.cpp:2092 msgid "Tilt time" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2070 src/slic3r/GUI/Tab.cpp:3477 +#: src/slic3r/GUI/Tab.cpp:2098 src/slic3r/GUI/Tab.cpp:3503 msgid "Corrections" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2085 src/slic3r/GUI/Tab.cpp:3473 +#: src/slic3r/GUI/Tab.cpp:2113 src/slic3r/GUI/Tab.cpp:3499 msgid "Exposure" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2156 src/slic3r/GUI/Tab.cpp:2241 -#: src/libslic3r/PrintConfig.cpp:1119 src/libslic3r/PrintConfig.cpp:1137 -#: src/libslic3r/PrintConfig.cpp:1155 src/libslic3r/PrintConfig.cpp:1172 -#: src/libslic3r/PrintConfig.cpp:1183 src/libslic3r/PrintConfig.cpp:1194 -#: src/libslic3r/PrintConfig.cpp:1205 +#: src/slic3r/GUI/Tab.cpp:2184 src/slic3r/GUI/Tab.cpp:2269 +#: src/libslic3r/PrintConfig.cpp:1129 src/libslic3r/PrintConfig.cpp:1146 +#: src/libslic3r/PrintConfig.cpp:1163 src/libslic3r/PrintConfig.cpp:1179 +#: src/libslic3r/PrintConfig.cpp:1189 src/libslic3r/PrintConfig.cpp:1199 +#: src/libslic3r/PrintConfig.cpp:1209 msgid "Machine limits" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2170 +#: src/slic3r/GUI/Tab.cpp:2198 msgid "Values in this column are for Normal mode" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2171 +#: src/slic3r/GUI/Tab.cpp:2199 msgid "Normal" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2176 +#: src/slic3r/GUI/Tab.cpp:2204 msgid "Values in this column are for Stealth mode" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2177 +#: src/slic3r/GUI/Tab.cpp:2205 msgid "Stealth" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2185 +#: src/slic3r/GUI/Tab.cpp:2213 msgid "Maximum feedrates" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2190 +#: src/slic3r/GUI/Tab.cpp:2218 msgid "Maximum accelerations" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2197 +#: src/slic3r/GUI/Tab.cpp:2225 msgid "Jerk limits" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2202 +#: src/slic3r/GUI/Tab.cpp:2230 msgid "Minimum feedrates" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2266 src/slic3r/GUI/Tab.cpp:2274 +#: src/slic3r/GUI/Tab.cpp:2294 src/slic3r/GUI/Tab.cpp:2302 msgid "Single extruder MM setup" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2275 +#: src/slic3r/GUI/Tab.cpp:2303 msgid "Single extruder multimaterial parameters" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2306 +#: src/slic3r/GUI/Tab.cpp:2334 msgid "" "This is a single extruder multimaterial printer, diameters of all extruders " "will be set to the new value. Do you want to proceed?" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2330 +#: src/slic3r/GUI/Tab.cpp:2358 msgid "Layer height limits" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2335 +#: src/slic3r/GUI/Tab.cpp:2363 msgid "Position (for multi-extruder printers)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2341 +#: src/slic3r/GUI/Tab.cpp:2369 msgid "Only lift Z" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2354 +#: src/slic3r/GUI/Tab.cpp:2382 msgid "" "Retraction when tool is disabled (advanced settings for multi-extruder " "setups)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2362 +#: src/slic3r/GUI/Tab.cpp:2390 msgid "Reset to Filament Color" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2543 +#: src/slic3r/GUI/Tab.cpp:2571 msgid "" "The Wipe option is not available when using the Firmware Retraction mode.\n" "\n" "Shall I disable it in order to enable Firmware Retraction?" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2545 +#: src/slic3r/GUI/Tab.cpp:2573 msgid "Firmware Retraction" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2875 +#: src/slic3r/GUI/Tab.cpp:2903 #, possible-c-format msgid "Default preset (%s)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2876 +#: src/slic3r/GUI/Tab.cpp:2904 #, possible-c-format msgid "Preset (%s)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2893 +#: src/slic3r/GUI/Tab.cpp:2921 msgid "has the following unsaved changes:" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2896 +#: src/slic3r/GUI/Tab.cpp:2924 msgid "is not compatible with printer" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2897 +#: src/slic3r/GUI/Tab.cpp:2925 msgid "is not compatible with print profile" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2899 +#: src/slic3r/GUI/Tab.cpp:2927 msgid "and it has the following unsaved changes:" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2903 +#: src/slic3r/GUI/Tab.cpp:2931 msgid "Unsaved Changes" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3001 +#: src/slic3r/GUI/Tab.cpp:3029 msgctxt "PresetName" msgid "%1% - Copy" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3024 +#: src/slic3r/GUI/Tab.cpp:3052 msgid "The supplied name is empty. It can't be saved." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3029 +#: src/slic3r/GUI/Tab.cpp:3057 msgid "Cannot overwrite a system profile." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3033 +#: src/slic3r/GUI/Tab.cpp:3061 msgid "Cannot overwrite an external profile." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3038 +#: src/slic3r/GUI/Tab.cpp:3066 msgid "Preset with name \"%1%\" already exists." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3039 +#: src/slic3r/GUI/Tab.cpp:3067 msgid "Replace?" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3077 +#: src/slic3r/GUI/Tab.cpp:3105 msgid "remove" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3077 +#: src/slic3r/GUI/Tab.cpp:3105 msgid "delete" msgstr "" #. TRN remove/delete -#: src/slic3r/GUI/Tab.cpp:3079 +#: src/slic3r/GUI/Tab.cpp:3107 msgid "Are you sure you want to %1% the selected preset?" msgstr "" #. TRN Remove/Delete -#: src/slic3r/GUI/Tab.cpp:3082 +#: src/slic3r/GUI/Tab.cpp:3110 msgid "%1% Preset" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3208 +#: src/slic3r/GUI/Tab.cpp:3236 msgid "LOCKED LOCK" msgstr "" #. TRN Description for "LOCKED LOCK" -#: src/slic3r/GUI/Tab.cpp:3210 +#: src/slic3r/GUI/Tab.cpp:3238 msgid "" "indicates that the settings are the same as the system (or default) values " "for the current option group" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3212 +#: src/slic3r/GUI/Tab.cpp:3240 msgid "UNLOCKED LOCK" msgstr "" #. TRN Description for "UNLOCKED LOCK" -#: src/slic3r/GUI/Tab.cpp:3214 +#: src/slic3r/GUI/Tab.cpp:3242 msgid "" "indicates that some settings were changed and are not equal to the system " "(or default) values for the current option group.\n" @@ -4963,23 +5509,23 @@ msgid "" "to the system (or default) values." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3219 +#: src/slic3r/GUI/Tab.cpp:3247 msgid "WHITE BULLET" msgstr "" #. TRN Description for "WHITE BULLET" -#: src/slic3r/GUI/Tab.cpp:3221 +#: src/slic3r/GUI/Tab.cpp:3249 msgid "" "for the left button: \tindicates a non-system (or non-default) preset,\n" "for the right button: \tindicates that the settings hasn't been modified." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3224 +#: src/slic3r/GUI/Tab.cpp:3252 msgid "BACK ARROW" msgstr "" #. TRN Description for "BACK ARROW" -#: src/slic3r/GUI/Tab.cpp:3226 +#: src/slic3r/GUI/Tab.cpp:3254 msgid "" "indicates that the settings were changed and are not equal to the last saved " "preset for the current option group.\n" @@ -4987,13 +5533,13 @@ msgid "" "to the last saved preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3236 +#: src/slic3r/GUI/Tab.cpp:3264 msgid "" "LOCKED LOCK icon indicates that the settings are the same as the system (or " "default) values for the current option group" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3238 +#: src/slic3r/GUI/Tab.cpp:3266 msgid "" "UNLOCKED LOCK icon indicates that some settings were changed and are not " "equal to the system (or default) values for the current option group.\n" @@ -5001,17 +5547,17 @@ msgid "" "default) values." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3241 +#: src/slic3r/GUI/Tab.cpp:3269 msgid "WHITE BULLET icon indicates a non system (or non default) preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3244 +#: src/slic3r/GUI/Tab.cpp:3272 msgid "" "WHITE BULLET icon indicates that the settings are the same as in the last " "saved preset for the current option group." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3246 +#: src/slic3r/GUI/Tab.cpp:3274 msgid "" "BACK ARROW icon indicates that the settings were changed and are not equal " "to the last saved preset for the current option group.\n" @@ -5019,26 +5565,26 @@ msgid "" "preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3252 +#: src/slic3r/GUI/Tab.cpp:3280 msgid "" "LOCKED LOCK icon indicates that the value is the same as the system (or " "default) value." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3253 +#: src/slic3r/GUI/Tab.cpp:3281 msgid "" "UNLOCKED LOCK icon indicates that the value was changed and is not equal to " "the system (or default) value.\n" "Click to reset current value to the system (or default) value." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3259 +#: src/slic3r/GUI/Tab.cpp:3287 msgid "" "WHITE BULLET icon indicates that the value is the same as in the last saved " "preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3260 +#: src/slic3r/GUI/Tab.cpp:3288 msgid "" "BACK ARROW icon indicates that the value was changed and is not equal to the " "last saved preset.\n" @@ -5046,61 +5592,56 @@ msgid "" msgstr "" #. TRN Preset -#: src/slic3r/GUI/Tab.cpp:3373 +#: src/slic3r/GUI/Tab.cpp:3401 #, possible-c-format msgid "Save %s as:" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3417 +#: src/slic3r/GUI/Tab.cpp:3445 msgid "the following suffix is not allowed:" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3421 +#: src/slic3r/GUI/Tab.cpp:3449 msgid "The supplied name is not available." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3434 src/slic3r/GUI/Tab.cpp:3436 +#: src/slic3r/GUI/Tab.cpp:3462 src/slic3r/GUI/Tab.cpp:3464 msgid "Material" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3470 src/slic3r/GUI/Tab.cpp:3560 -#: src/slic3r/GUI/wxExtensions.cpp:601 -msgid "Layers" -msgstr "" - -#: src/slic3r/GUI/Tab.cpp:3568 +#: src/slic3r/GUI/Tab.cpp:3593 msgid "Support head" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3573 +#: src/slic3r/GUI/Tab.cpp:3598 msgid "Support pillar" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3587 +#: src/slic3r/GUI/Tab.cpp:3612 msgid "Connection of the support sticks and junctions" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3592 +#: src/slic3r/GUI/Tab.cpp:3617 msgid "Automatic generation" msgstr "" -#: src/slic3r/GUI/Tab.hpp:327 src/slic3r/GUI/Tab.hpp:427 +#: src/slic3r/GUI/Tab.hpp:327 src/slic3r/GUI/Tab.hpp:430 msgid "Print Settings" msgstr "" -#: src/slic3r/GUI/Tab.hpp:352 +#: src/slic3r/GUI/Tab.hpp:354 msgid "Filament Settings" msgstr "" -#: src/slic3r/GUI/Tab.hpp:388 +#: src/slic3r/GUI/Tab.hpp:390 msgid "Printer Settings" msgstr "" -#: src/slic3r/GUI/Tab.hpp:412 +#: src/slic3r/GUI/Tab.hpp:415 msgid "Material Settings" msgstr "" -#: src/slic3r/GUI/Tab.hpp:439 +#: src/slic3r/GUI/Tab.hpp:442 msgid "Save preset" msgstr "" @@ -5126,6 +5667,7 @@ msgid "Changelog && Download" msgstr "" #: src/slic3r/GUI/UpdateDialogs.cpp:60 src/slic3r/GUI/UpdateDialogs.cpp:125 +#: src/slic3r/GUI/UpdateDialogs.cpp:183 msgid "Open changelog page" msgstr "" @@ -5137,7 +5679,7 @@ msgstr "" msgid "Don't notify about new releases any more" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:89 src/slic3r/GUI/UpdateDialogs.cpp:205 +#: src/slic3r/GUI/UpdateDialogs.cpp:89 src/slic3r/GUI/UpdateDialogs.cpp:266 msgid "Configuration update" msgstr "" @@ -5155,21 +5697,41 @@ msgid "" "Updated configuration bundles:" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:113 +#: src/slic3r/GUI/UpdateDialogs.cpp:113 src/slic3r/GUI/UpdateDialogs.cpp:173 msgid "Comment:" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:149 +#: src/slic3r/GUI/UpdateDialogs.cpp:148 src/slic3r/GUI/UpdateDialogs.cpp:210 #, possible-c-format msgid "%s incompatibility" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:150 +#: src/slic3r/GUI/UpdateDialogs.cpp:148 +msgid "Configuration update is necessary to install" +msgstr "" + +#: src/slic3r/GUI/UpdateDialogs.cpp:151 +#, possible-c-format +msgid "" +"%s will now start updates. Otherwise it won't be able to start.\n" +"\n" +"Note that a full configuration snapshot will be created first. It can then " +"be restored at any time should there be a problem with the new version.\n" +"\n" +"Updated configuration bundles:" +msgstr "" + +#: src/slic3r/GUI/UpdateDialogs.cpp:191 src/slic3r/GUI/UpdateDialogs.cpp:246 +#, possible-c-format +msgid "Exit %s" +msgstr "" + +#: src/slic3r/GUI/UpdateDialogs.cpp:211 #, possible-c-format msgid "%s configuration is incompatible" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:155 +#: src/slic3r/GUI/UpdateDialogs.cpp:216 #, possible-c-format msgid "" "This version of %s is not compatible with currently installed configuration " @@ -5182,25 +5744,20 @@ msgid "" "existing configuration before installing files compatible with this %s." msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:164 +#: src/slic3r/GUI/UpdateDialogs.cpp:225 #, possible-c-format msgid "This %s version: %s" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:169 +#: src/slic3r/GUI/UpdateDialogs.cpp:230 msgid "Incompatible bundles:" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:185 -#, possible-c-format -msgid "Exit %s" -msgstr "" - -#: src/slic3r/GUI/UpdateDialogs.cpp:188 +#: src/slic3r/GUI/UpdateDialogs.cpp:249 msgid "Re-configure" msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:209 +#: src/slic3r/GUI/UpdateDialogs.cpp:270 #, possible-c-format msgid "" "%s now uses an updated configuration structure.\n" @@ -5216,10 +5773,23 @@ msgid "" "choose whether to enable automatic preset updates." msgstr "" -#: src/slic3r/GUI/UpdateDialogs.cpp:225 +#: src/slic3r/GUI/UpdateDialogs.cpp:286 msgid "For more information please visit our wiki page:" msgstr "" +#: src/slic3r/GUI/UpdateDialogs.cpp:303 +msgid "Configuration updates" +msgstr "" + +#: src/slic3r/GUI/UpdateDialogs.cpp:303 +msgid "No updates aviable" +msgstr "" + +#: src/slic3r/GUI/UpdateDialogs.cpp:308 +#, possible-c-format +msgid "%s has no configuration updates aviable." +msgstr "" + #: src/slic3r/GUI/WipeTowerDialog.cpp:15 msgid "Ramming customization" msgstr "" @@ -5309,242 +5879,53 @@ msgstr "" msgid "Show advanced settings" msgstr "" -#: src/slic3r/GUI/wxExtensions.cpp:590 -msgid "Instances" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:594 src/slic3r/GUI/wxExtensions.cpp:750 -#, possible-c-format -msgid "Instance %d" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:628 -msgid "Range" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:2325 -msgid "Place bearings in slots and resume" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3075 -msgid "One layer mode" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3078 -msgid "Discard all custom changes" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3080 -msgid "Set extruder sequence for whole print" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3086 -msgid "For add color change use left mouse button click" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3087 -msgid "For add change extruder use left mouse button click" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3088 -msgid "For add another code use right mouse button click" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3090 -msgid "" -"For Delete color change use left mouse button click\n" -"For Edit color use right mouse button click" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3092 -msgid "Delete color change for Extruder %1%" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3095 -msgid "Delete extruder change to \"%1%\"" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3096 -msgid "" -"For Delete \"%1%\" code use left mouse button click\n" -"For Edit \"%1%\" code use right mouse button click" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3176 src/slic3r/GUI/wxExtensions.cpp:3432 -msgid "Use another extruder" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3435 -msgid "Add color change (%1%) for:" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3441 -msgid "Add color change" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3444 -msgid "Add pause print" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3447 -msgid "Add custom G-code" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3460 -msgid "Edit color" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3461 -msgid "Edit pause print message" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3462 -msgid "Edit custom G-code" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3465 -msgid "Delete color change" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3466 -msgid "Delete pause print" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3467 -msgid "Delete custom G-code" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3499 -msgid "Enter custom G-code used on current layer" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3500 -msgid "Custom Gcode on current layer (%1% mm)." -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3513 -msgid "Enter short message shown on Printer display during pause print" -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3514 -msgid "Message for pause print on current layer (%1% mm)." -msgstr "" - -#: src/slic3r/GUI/wxExtensions.cpp:3774 +#: src/slic3r/GUI/wxExtensions.cpp:703 #, possible-c-format msgid "Switch to the %s mode" msgstr "" -#: src/slic3r/GUI/wxExtensions.cpp:3775 +#: src/slic3r/GUI/wxExtensions.cpp:704 #, possible-c-format msgid "Current mode is %s" msgstr "" -#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:23 -msgid "Set extruder sequence" -msgstr "" - -#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:39 -msgid "Set extruder change for every" -msgstr "" - -#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:52 -#: src/libslic3r/PrintConfig.cpp:341 src/libslic3r/PrintConfig.cpp:983 -#: src/libslic3r/PrintConfig.cpp:1500 src/libslic3r/PrintConfig.cpp:1685 -#: src/libslic3r/PrintConfig.cpp:1746 src/libslic3r/PrintConfig.cpp:1919 -#: src/libslic3r/PrintConfig.cpp:1965 -msgid "layers" -msgstr "" - -#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:136 -msgid "Set extruder(tool) sequence" -msgstr "" - -#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:182 -msgid "Remove extruder from sequence" -msgstr "" - -#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:192 -msgid "Add extruder to sequence" -msgstr "" - -#: src/slic3r/Utils/Duet.cpp:51 -msgid "Connection to Duet works correctly." -msgstr "" - -#: src/slic3r/Utils/Duet.cpp:56 -msgid "Could not connect to Duet" -msgstr "" - -#: src/slic3r/Utils/Duet.cpp:84 src/slic3r/Utils/Duet.cpp:154 -#: src/slic3r/Utils/FlashAir.cpp:115 src/slic3r/Utils/FlashAir.cpp:132 -msgid "Unknown error occured" -msgstr "" - -#: src/slic3r/Utils/Duet.cpp:148 -msgid "Wrong password" -msgstr "" - -#: src/slic3r/Utils/Duet.cpp:151 -msgid "Could not get resources to create a new connection" -msgstr "" - -#: src/slic3r/Utils/OctoPrint.cpp:70 +#: src/slic3r/Utils/AstroBox.cpp:68 src/slic3r/Utils/OctoPrint.cpp:68 #, possible-c-format msgid "Mismatched type of print host: %s" msgstr "" -#: src/slic3r/Utils/OctoPrint.cpp:85 -msgid "Connection to OctoPrint works correctly." +#: src/slic3r/Utils/AstroBox.cpp:83 +msgid "Connection to AstroBox works correctly." msgstr "" -#: src/slic3r/Utils/OctoPrint.cpp:91 -msgid "Could not connect to OctoPrint" +#: src/slic3r/Utils/AstroBox.cpp:89 +msgid "Could not connect to AstroBox" msgstr "" -#: src/slic3r/Utils/OctoPrint.cpp:91 -msgid "Note: OctoPrint version at least 1.1.0 is required." +#: src/slic3r/Utils/AstroBox.cpp:89 +msgid "Note: AstroBox version at least 1.1.0 is required." msgstr "" -#: src/slic3r/Utils/OctoPrint.cpp:196 -msgid "Connection to Prusa SL1 works correctly." +#: src/slic3r/Utils/Duet.cpp:49 +msgid "Connection to Duet works correctly." msgstr "" -#: src/slic3r/Utils/OctoPrint.cpp:201 -msgid "Could not connect to Prusa SLA" +#: src/slic3r/Utils/Duet.cpp:54 +msgid "Could not connect to Duet" msgstr "" -#: src/slic3r/Utils/FlashAir.cpp:60 -msgid "Upload not enabled on FlashAir card." +#: src/slic3r/Utils/Duet.cpp:82 src/slic3r/Utils/Duet.cpp:137 +#: src/slic3r/Utils/FlashAir.cpp:119 src/slic3r/Utils/FlashAir.cpp:140 +#: src/slic3r/Utils/FlashAir.cpp:156 +msgid "Unknown error occured" msgstr "" -#: src/slic3r/Utils/FlashAir.cpp:70 -msgid "Connection to FlashAir works correctly and upload is enabled." +#: src/slic3r/Utils/Duet.cpp:131 +msgid "Wrong password" msgstr "" -#: src/slic3r/Utils/FlashAir.cpp:75 -msgid "Could not connect to FlashAir" -msgstr "" - -#: src/slic3r/Utils/FlashAir.cpp:75 -msgid "" -"Note: FlashAir with firmware 2.00.02 or newer and activated upload function " -"is required." -msgstr "" - -#: src/slic3r/Utils/PresetUpdater.cpp:632 -#, possible-c-format -msgid "requires min. %s and max. %s" -msgstr "" - -#: src/slic3r/Utils/PresetUpdater.cpp:637 -#, possible-c-format -msgid "requires min. %s" -msgstr "" - -#: src/slic3r/Utils/PresetUpdater.cpp:639 -#, possible-c-format -msgid "requires max. %s" +#: src/slic3r/Utils/Duet.cpp:134 +msgid "Could not get resources to create a new connection" msgstr "" #: src/slic3r/Utils/FixModelByWin10.cpp:219 @@ -5565,7 +5946,7 @@ msgid "Mesh repair failed." msgstr "" #: src/slic3r/Utils/FixModelByWin10.cpp:251 -#: src/slic3r/Utils/FixModelByWin10.cpp:378 +#: src/slic3r/Utils/FixModelByWin10.cpp:385 msgid "Loading repaired model" msgstr "" @@ -5583,52 +5964,106 @@ msgstr "" msgid "Exporting model..." msgstr "" -#: src/slic3r/Utils/FixModelByWin10.cpp:368 +#: src/slic3r/Utils/FixModelByWin10.cpp:369 +#: src/slic3r/Utils/FixModelByWin10.cpp:374 msgid "Export of a temporary 3mf file failed" msgstr "" -#: src/slic3r/Utils/FixModelByWin10.cpp:383 +#: src/slic3r/Utils/FixModelByWin10.cpp:390 msgid "Import of the repaired 3mf file failed" msgstr "" -#: src/slic3r/Utils/FixModelByWin10.cpp:385 +#: src/slic3r/Utils/FixModelByWin10.cpp:392 msgid "Repaired 3MF file does not contain any object" msgstr "" -#: src/slic3r/Utils/FixModelByWin10.cpp:387 +#: src/slic3r/Utils/FixModelByWin10.cpp:394 msgid "Repaired 3MF file contains more than one object" msgstr "" -#: src/slic3r/Utils/FixModelByWin10.cpp:389 +#: src/slic3r/Utils/FixModelByWin10.cpp:396 msgid "Repaired 3MF file does not contain any volume" msgstr "" -#: src/slic3r/Utils/FixModelByWin10.cpp:391 +#: src/slic3r/Utils/FixModelByWin10.cpp:398 msgid "Repaired 3MF file contains more than one volume" msgstr "" -#: src/slic3r/Utils/FixModelByWin10.cpp:400 +#: src/slic3r/Utils/FixModelByWin10.cpp:407 msgid "Model repair finished" msgstr "" -#: src/slic3r/Utils/FixModelByWin10.cpp:406 +#: src/slic3r/Utils/FixModelByWin10.cpp:413 msgid "Model repair canceled" msgstr "" -#: src/slic3r/Utils/FixModelByWin10.cpp:423 +#: src/slic3r/Utils/FixModelByWin10.cpp:430 msgid "Model repaired successfully" msgstr "" -#: src/slic3r/Utils/FixModelByWin10.cpp:423 -#: src/slic3r/Utils/FixModelByWin10.cpp:426 +#: src/slic3r/Utils/FixModelByWin10.cpp:430 +#: src/slic3r/Utils/FixModelByWin10.cpp:433 msgid "Model Repair by the Netfabb service" msgstr "" -#: src/slic3r/Utils/FixModelByWin10.cpp:426 +#: src/slic3r/Utils/FixModelByWin10.cpp:433 msgid "Model repair failed:" msgstr "" -#: src/libslic3r/SLA/SLAPad.cpp:690 +#: src/slic3r/Utils/FlashAir.cpp:58 +msgid "Upload not enabled on FlashAir card." +msgstr "" + +#: src/slic3r/Utils/FlashAir.cpp:68 +msgid "Connection to FlashAir works correctly and upload is enabled." +msgstr "" + +#: src/slic3r/Utils/FlashAir.cpp:73 +msgid "Could not connect to FlashAir" +msgstr "" + +#: src/slic3r/Utils/FlashAir.cpp:73 +msgid "" +"Note: FlashAir with firmware 2.00.02 or newer and activated upload function " +"is required." +msgstr "" + +#: src/slic3r/Utils/OctoPrint.cpp:83 +msgid "Connection to OctoPrint works correctly." +msgstr "" + +#: src/slic3r/Utils/OctoPrint.cpp:89 +msgid "Could not connect to OctoPrint" +msgstr "" + +#: src/slic3r/Utils/OctoPrint.cpp:89 +msgid "Note: OctoPrint version at least 1.1.0 is required." +msgstr "" + +#: src/slic3r/Utils/OctoPrint.cpp:176 +msgid "Connection to Prusa SL1 works correctly." +msgstr "" + +#: src/slic3r/Utils/OctoPrint.cpp:181 +msgid "Could not connect to Prusa SLA" +msgstr "" + +#: src/slic3r/Utils/PresetUpdater.cpp:705 +#, possible-c-format +msgid "requires min. %s and max. %s" +msgstr "" + +#: src/slic3r/Utils/PresetUpdater.cpp:710 +#, possible-c-format +msgid "requires min. %s" +msgstr "" + +#: src/slic3r/Utils/PresetUpdater.cpp:713 +#, possible-c-format +msgid "requires max. %s" +msgstr "" + +#: src/libslic3r/SLA/Pad.cpp:691 msgid "Pad brim size is too small for the current configuration." msgstr "" @@ -5760,15 +6195,15 @@ msgstr "" msgid "Error with zip archive" msgstr "" -#: src/libslic3r/GCode.cpp:633 +#: src/libslic3r/GCode.cpp:637 msgid "Empty layers detected, the output would not be printable." msgstr "" -#: src/libslic3r/GCode.cpp:634 +#: src/libslic3r/GCode.cpp:638 msgid "Print z" msgstr "" -#: src/libslic3r/GCode.cpp:635 +#: src/libslic3r/GCode.cpp:639 msgid "" "This is usually caused by negligibly small extrusions or by a faulty model. " "Try to repair the model or change its orientation on the bed." @@ -5778,116 +6213,127 @@ msgstr "" msgid "Mixed" msgstr "" -#: src/libslic3r/Format/3mf.cpp:1519 +#: src/libslic3r/Flow.cpp:55 +msgid "" +"Cannot calculate extrusion width for %1%: Variable \"%2%\" not accessible." +msgstr "" + +#: src/libslic3r/Format/3mf.cpp:1630 msgid "" "The selected 3mf file has been saved with a newer version of %1% and is not " "compatible." msgstr "" -#: src/libslic3r/Format/AMF.cpp:917 +#: src/libslic3r/Format/AMF.cpp:934 msgid "" "The selected amf file has been saved with a newer version of %1% and is not " "compatible." msgstr "" -#: src/libslic3r/Print.cpp:1168 +#: src/libslic3r/Print.cpp:1215 msgid "All objects are outside of the print volume." msgstr "" -#: src/libslic3r/Print.cpp:1171 +#: src/libslic3r/Print.cpp:1218 msgid "The supplied settings will cause an empty print." msgstr "" -#: src/libslic3r/Print.cpp:1198 +#: src/libslic3r/Print.cpp:1222 msgid "Some objects are too close; your extruder will collide with them." msgstr "" -#: src/libslic3r/Print.cpp:1213 +#: src/libslic3r/Print.cpp:1224 msgid "" "Some objects are too tall and cannot be printed without extruder collisions." msgstr "" -#: src/libslic3r/Print.cpp:1223 +#: src/libslic3r/Print.cpp:1233 msgid "The Spiral Vase option can only be used when printing a single object." msgstr "" -#: src/libslic3r/Print.cpp:1230 +#: src/libslic3r/Print.cpp:1240 msgid "" "The Spiral Vase option can only be used when printing single material " "objects." msgstr "" -#: src/libslic3r/Print.cpp:1243 +#: src/libslic3r/Print.cpp:1253 msgid "" "The wipe tower is only supported if all extruders have the same nozzle " "diameter and use filaments of the same diameter." msgstr "" -#: src/libslic3r/Print.cpp:1248 +#: src/libslic3r/Print.cpp:1258 msgid "" "The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter " "and Repetier G-code flavors." msgstr "" -#: src/libslic3r/Print.cpp:1250 +#: src/libslic3r/Print.cpp:1260 msgid "" "The Wipe Tower is currently only supported with the relative extruder " "addressing (use_relative_e_distances=1)." msgstr "" -#: src/libslic3r/Print.cpp:1252 +#: src/libslic3r/Print.cpp:1262 msgid "Ooze prevention is currently not supported with the wipe tower enabled." msgstr "" -#: src/libslic3r/Print.cpp:1254 +#: src/libslic3r/Print.cpp:1264 msgid "" "The Wipe Tower currently does not support volumetric E (use_volumetric_e=0)." msgstr "" -#: src/libslic3r/Print.cpp:1275 +#: src/libslic3r/Print.cpp:1266 +msgid "" +"The Wipe Tower is currently not supported for multimaterial sequential " +"prints." +msgstr "" + +#: src/libslic3r/Print.cpp:1287 msgid "" "The Wipe Tower is only supported for multiple objects if they have equal " "layer heights" msgstr "" -#: src/libslic3r/Print.cpp:1277 +#: src/libslic3r/Print.cpp:1289 msgid "" "The Wipe Tower is only supported for multiple objects if they are printed " "over an equal number of raft layers" msgstr "" -#: src/libslic3r/Print.cpp:1279 +#: src/libslic3r/Print.cpp:1291 msgid "" "The Wipe Tower is only supported for multiple objects if they are printed " "with the same support_material_contact_distance" msgstr "" -#: src/libslic3r/Print.cpp:1281 +#: src/libslic3r/Print.cpp:1293 msgid "" "The Wipe Tower is only supported for multiple objects if they are sliced " "equally." msgstr "" -#: src/libslic3r/Print.cpp:1323 +#: src/libslic3r/Print.cpp:1335 msgid "" "The Wipe tower is only supported if all objects have the same variable layer " "height" msgstr "" -#: src/libslic3r/Print.cpp:1349 +#: src/libslic3r/Print.cpp:1361 msgid "" "One or more object were assigned an extruder that the printer does not have." msgstr "" -#: src/libslic3r/Print.cpp:1358 +#: src/libslic3r/Print.cpp:1370 msgid "%1%=%2% mm is too low to be printable at a layer height %3% mm" msgstr "" -#: src/libslic3r/Print.cpp:1361 +#: src/libslic3r/Print.cpp:1373 msgid "Excessive %1%=%2% mm to be printable with a nozzle diameter %3% mm" msgstr "" -#: src/libslic3r/Print.cpp:1372 +#: src/libslic3r/Print.cpp:1384 msgid "" "Printing with multiple extruders of differing nozzle diameters. If support " "is to be printed with the current extruder (support_material_extruder == 0 " @@ -5895,13 +6341,13 @@ msgid "" "same diameter." msgstr "" -#: src/libslic3r/Print.cpp:1380 +#: src/libslic3r/Print.cpp:1392 msgid "" "For the Wipe Tower to work with the soluble supports, the support layers " "need to be synchronized with the object layers." msgstr "" -#: src/libslic3r/Print.cpp:1384 +#: src/libslic3r/Print.cpp:1396 msgid "" "The Wipe Tower currently supports the non-soluble supports only if they are " "printed with the current extruder without triggering a tool change. (both " @@ -5909,105 +6355,129 @@ msgid "" "set to 0)." msgstr "" -#: src/libslic3r/Print.cpp:1406 +#: src/libslic3r/Print.cpp:1418 msgid "First layer height can't be greater than nozzle diameter" msgstr "" -#: src/libslic3r/Print.cpp:1411 +#: src/libslic3r/Print.cpp:1423 msgid "Layer height can't be greater than nozzle diameter" msgstr "" -#: src/libslic3r/Print.cpp:1566 +#: src/libslic3r/Print.cpp:1580 msgid "Infilling layers" msgstr "" -#: src/libslic3r/Print.cpp:1582 +#: src/libslic3r/Print.cpp:1602 msgid "Generating skirt" msgstr "" -#: src/libslic3r/Print.cpp:1590 +#: src/libslic3r/Print.cpp:1610 msgid "Generating brim" msgstr "" -#: src/libslic3r/Print.cpp:1614 +#: src/libslic3r/Print.cpp:1634 msgid "Exporting G-code" msgstr "" -#: src/libslic3r/Print.cpp:1618 +#: src/libslic3r/Print.cpp:1638 msgid "Generating G-code" msgstr "" -#: src/libslic3r/SLAPrint.cpp:66 -msgid "Slicing model" -msgstr "" - -#: src/libslic3r/SLAPrint.cpp:67 src/libslic3r/SLAPrint.cpp:905 -msgid "Generating support points" -msgstr "" - -#: src/libslic3r/SLAPrint.cpp:68 -msgid "Generating support tree" -msgstr "" - -#: src/libslic3r/SLAPrint.cpp:69 -msgid "Generating pad" -msgstr "" - -#: src/libslic3r/SLAPrint.cpp:70 -msgid "Slicing supports" -msgstr "" - -#: src/libslic3r/SLAPrint.cpp:87 -msgid "Merging slices and calculating statistics" -msgstr "" - -#: src/libslic3r/SLAPrint.cpp:88 -msgid "Rasterizing layers" -msgstr "" - -#: src/libslic3r/SLAPrint.cpp:670 +#: src/libslic3r/SLAPrint.cpp:613 msgid "" "Cannot proceed without support points! Add support points or disable support " "generation." msgstr "" -#: src/libslic3r/SLAPrint.cpp:682 +#: src/libslic3r/SLAPrint.cpp:625 msgid "" "Elevation is too low for object. Use the \"Pad around object\" feature to " "print the object without elevation." msgstr "" -#: src/libslic3r/SLAPrint.cpp:688 +#: src/libslic3r/SLAPrint.cpp:631 msgid "" "The endings of the support pillars will be deployed on the gap between the " "object and the pad. 'Support base safety distance' has to be greater than " "the 'Pad object gap' parameter to avoid this." msgstr "" -#: src/libslic3r/SLAPrint.cpp:703 +#: src/libslic3r/SLAPrint.cpp:646 msgid "Exposition time is out of printer profile bounds." msgstr "" -#: src/libslic3r/SLAPrint.cpp:710 +#: src/libslic3r/SLAPrint.cpp:653 msgid "Initial exposition time is out of printer profile bounds." msgstr "" -#: src/libslic3r/SLAPrint.cpp:794 +#: src/libslic3r/SLAPrint.cpp:760 +msgid "Slicing done" +msgstr "" + +#: src/libslic3r/SLAPrintSteps.cpp:41 +msgid "Hollowing model" +msgstr "" + +#: src/libslic3r/SLAPrintSteps.cpp:42 +msgid "Drilling holes into model." +msgstr "" + +#: src/libslic3r/SLAPrintSteps.cpp:43 +msgid "Slicing model" +msgstr "" + +#: src/libslic3r/SLAPrintSteps.cpp:44 src/libslic3r/SLAPrintSteps.cpp:325 +msgid "Generating support points" +msgstr "" + +#: src/libslic3r/SLAPrintSteps.cpp:45 +msgid "Generating support tree" +msgstr "" + +#: src/libslic3r/SLAPrintSteps.cpp:46 +msgid "Generating pad" +msgstr "" + +#: src/libslic3r/SLAPrintSteps.cpp:47 +msgid "Slicing supports" +msgstr "" + +#: src/libslic3r/SLAPrintSteps.cpp:62 +msgid "Merging slices and calculating statistics" +msgstr "" + +#: src/libslic3r/SLAPrintSteps.cpp:63 +msgid "Rasterizing layers" +msgstr "" + +#: src/libslic3r/SLAPrintSteps.cpp:154 +msgid "Too much overlapping holes." +msgstr "" + +#: src/libslic3r/SLAPrintSteps.cpp:163 +msgid "" +"Drilling holes into the mesh failed. This is usually caused by broken model. " +"Try to fix it first." +msgstr "" + +#: src/libslic3r/SLAPrintSteps.cpp:209 msgid "" "Slicing had to be stopped due to an internal error: Inconsistent slice index." msgstr "" -#: src/libslic3r/SLAPrint.cpp:964 src/libslic3r/SLAPrint.cpp:973 -#: src/libslic3r/SLAPrint.cpp:1018 +#: src/libslic3r/SLAPrintSteps.cpp:382 src/libslic3r/SLAPrintSteps.cpp:391 +#: src/libslic3r/SLAPrintSteps.cpp:430 msgid "Visualizing supports" msgstr "" -#: src/libslic3r/SLAPrint.cpp:1009 +#: src/libslic3r/SLAPrintSteps.cpp:422 msgid "No pad can be generated for this model with the current configuration" msgstr "" -#: src/libslic3r/SLAPrint.cpp:1554 -msgid "Slicing done" +#: src/libslic3r/SLAPrintSteps.cpp:596 +msgid "" +"There are unprintable objects. Try to adjust support settings to make the " +"objects printable." msgstr "" #: src/libslic3r/PrintBase.cpp:71 @@ -6103,7 +6573,7 @@ msgid "" "feature slows down both the print and the G-code generation." msgstr "" -#: src/libslic3r/PrintConfig.cpp:134 src/libslic3r/PrintConfig.cpp:2053 +#: src/libslic3r/PrintConfig.cpp:134 src/libslic3r/PrintConfig.cpp:2064 msgid "Other layers" msgstr "" @@ -6146,62 +6616,72 @@ msgstr "" msgid "Bottom solid layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:172 +#: src/libslic3r/PrintConfig.cpp:175 +msgid "" +"The number of bottom solid layers is increased above bottom_solid_layers if " +"necessary to satisfy minimum thickness of bottom shell." +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:177 +msgid "Minimum bottom shell thickness" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:183 msgid "Bridge" msgstr "" -#: src/libslic3r/PrintConfig.cpp:173 +#: src/libslic3r/PrintConfig.cpp:184 msgid "" "This is the acceleration your printer will use for bridges. Set zero to " "disable acceleration control for bridges." msgstr "" -#: src/libslic3r/PrintConfig.cpp:175 src/libslic3r/PrintConfig.cpp:318 -#: src/libslic3r/PrintConfig.cpp:851 src/libslic3r/PrintConfig.cpp:973 -#: src/libslic3r/PrintConfig.cpp:1143 src/libslic3r/PrintConfig.cpp:1196 -#: src/libslic3r/PrintConfig.cpp:1207 src/libslic3r/PrintConfig.cpp:1398 +#: src/libslic3r/PrintConfig.cpp:186 src/libslic3r/PrintConfig.cpp:329 +#: src/libslic3r/PrintConfig.cpp:862 src/libslic3r/PrintConfig.cpp:984 +#: src/libslic3r/PrintConfig.cpp:1152 src/libslic3r/PrintConfig.cpp:1201 +#: src/libslic3r/PrintConfig.cpp:1211 src/libslic3r/PrintConfig.cpp:1403 msgid "mm/s²" msgstr "" -#: src/libslic3r/PrintConfig.cpp:181 +#: src/libslic3r/PrintConfig.cpp:192 msgid "Bridging angle" msgstr "" -#: src/libslic3r/PrintConfig.cpp:183 +#: src/libslic3r/PrintConfig.cpp:194 msgid "" "Bridging angle override. If left to zero, the bridging angle will be " "calculated automatically. Otherwise the provided angle will be used for all " "bridges. Use 180° for zero angle." msgstr "" -#: src/libslic3r/PrintConfig.cpp:186 src/libslic3r/PrintConfig.cpp:769 -#: src/libslic3r/PrintConfig.cpp:1635 src/libslic3r/PrintConfig.cpp:1645 -#: src/libslic3r/PrintConfig.cpp:1883 src/libslic3r/PrintConfig.cpp:2038 -#: src/libslic3r/PrintConfig.cpp:2224 src/libslic3r/PrintConfig.cpp:2695 -#: src/libslic3r/PrintConfig.cpp:2816 +#: src/libslic3r/PrintConfig.cpp:197 src/libslic3r/PrintConfig.cpp:780 +#: src/libslic3r/PrintConfig.cpp:1640 src/libslic3r/PrintConfig.cpp:1650 +#: src/libslic3r/PrintConfig.cpp:1894 src/libslic3r/PrintConfig.cpp:2049 +#: src/libslic3r/PrintConfig.cpp:2247 src/libslic3r/PrintConfig.cpp:2718 +#: src/libslic3r/PrintConfig.cpp:2839 msgid "°" msgstr "" -#: src/libslic3r/PrintConfig.cpp:192 +#: src/libslic3r/PrintConfig.cpp:203 msgid "Bridges fan speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:193 +#: src/libslic3r/PrintConfig.cpp:204 msgid "This fan speed is enforced during all bridges and overhangs." msgstr "" -#: src/libslic3r/PrintConfig.cpp:194 src/libslic3r/PrintConfig.cpp:781 -#: src/libslic3r/PrintConfig.cpp:1216 src/libslic3r/PrintConfig.cpp:1279 -#: src/libslic3r/PrintConfig.cpp:1527 src/libslic3r/PrintConfig.cpp:2402 -#: src/libslic3r/PrintConfig.cpp:2735 +#: src/libslic3r/PrintConfig.cpp:205 src/libslic3r/PrintConfig.cpp:792 +#: src/libslic3r/PrintConfig.cpp:1219 src/libslic3r/PrintConfig.cpp:1282 +#: src/libslic3r/PrintConfig.cpp:1532 src/libslic3r/PrintConfig.cpp:2425 +#: src/libslic3r/PrintConfig.cpp:2758 msgid "%" msgstr "" -#: src/libslic3r/PrintConfig.cpp:201 +#: src/libslic3r/PrintConfig.cpp:212 msgid "Bridge flow ratio" msgstr "" -#: src/libslic3r/PrintConfig.cpp:203 +#: src/libslic3r/PrintConfig.cpp:214 msgid "" "This factor affects the amount of plastic for bridging. You can decrease it " "slightly to pull the extrudates and prevent sagging, although default " @@ -6209,83 +6689,83 @@ msgid "" "before tweaking this." msgstr "" -#: src/libslic3r/PrintConfig.cpp:213 +#: src/libslic3r/PrintConfig.cpp:224 msgid "Bridges" msgstr "" -#: src/libslic3r/PrintConfig.cpp:215 +#: src/libslic3r/PrintConfig.cpp:226 msgid "Speed for printing bridges." msgstr "" -#: src/libslic3r/PrintConfig.cpp:216 src/libslic3r/PrintConfig.cpp:599 -#: src/libslic3r/PrintConfig.cpp:607 src/libslic3r/PrintConfig.cpp:616 -#: src/libslic3r/PrintConfig.cpp:624 src/libslic3r/PrintConfig.cpp:651 -#: src/libslic3r/PrintConfig.cpp:670 src/libslic3r/PrintConfig.cpp:911 -#: src/libslic3r/PrintConfig.cpp:1039 src/libslic3r/PrintConfig.cpp:1125 -#: src/libslic3r/PrintConfig.cpp:1161 src/libslic3r/PrintConfig.cpp:1174 -#: src/libslic3r/PrintConfig.cpp:1185 src/libslic3r/PrintConfig.cpp:1238 -#: src/libslic3r/PrintConfig.cpp:1297 src/libslic3r/PrintConfig.cpp:1428 -#: src/libslic3r/PrintConfig.cpp:1602 src/libslic3r/PrintConfig.cpp:1611 -#: src/libslic3r/PrintConfig.cpp:2017 src/libslic3r/PrintConfig.cpp:2131 +#: src/libslic3r/PrintConfig.cpp:227 src/libslic3r/PrintConfig.cpp:609 +#: src/libslic3r/PrintConfig.cpp:617 src/libslic3r/PrintConfig.cpp:626 +#: src/libslic3r/PrintConfig.cpp:634 src/libslic3r/PrintConfig.cpp:661 +#: src/libslic3r/PrintConfig.cpp:680 src/libslic3r/PrintConfig.cpp:922 +#: src/libslic3r/PrintConfig.cpp:1050 src/libslic3r/PrintConfig.cpp:1135 +#: src/libslic3r/PrintConfig.cpp:1169 src/libslic3r/PrintConfig.cpp:1181 +#: src/libslic3r/PrintConfig.cpp:1191 src/libslic3r/PrintConfig.cpp:1241 +#: src/libslic3r/PrintConfig.cpp:1300 src/libslic3r/PrintConfig.cpp:1433 +#: src/libslic3r/PrintConfig.cpp:1607 src/libslic3r/PrintConfig.cpp:1616 +#: src/libslic3r/PrintConfig.cpp:2028 src/libslic3r/PrintConfig.cpp:2154 msgid "mm/s" msgstr "" -#: src/libslic3r/PrintConfig.cpp:223 +#: src/libslic3r/PrintConfig.cpp:234 msgid "Brim width" msgstr "" -#: src/libslic3r/PrintConfig.cpp:224 +#: src/libslic3r/PrintConfig.cpp:235 msgid "" "Horizontal width of the brim that will be printed around each object on the " "first layer." msgstr "" -#: src/libslic3r/PrintConfig.cpp:231 +#: src/libslic3r/PrintConfig.cpp:242 msgid "Clip multi-part objects" msgstr "" -#: src/libslic3r/PrintConfig.cpp:232 +#: src/libslic3r/PrintConfig.cpp:243 msgid "" "When printing multi-material objects, this settings will make Slic3r to clip " "the overlapping object parts one by the other (2nd part will be clipped by " "the 1st, 3rd part will be clipped by the 1st and 2nd etc)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:239 +#: src/libslic3r/PrintConfig.cpp:250 msgid "Colorprint height" msgstr "" -#: src/libslic3r/PrintConfig.cpp:240 +#: src/libslic3r/PrintConfig.cpp:251 msgid "Heights at which a filament change is to occur." msgstr "" -#: src/libslic3r/PrintConfig.cpp:250 +#: src/libslic3r/PrintConfig.cpp:261 msgid "Compatible printers condition" msgstr "" -#: src/libslic3r/PrintConfig.cpp:251 +#: src/libslic3r/PrintConfig.cpp:262 msgid "" "A boolean expression using the configuration values of an active printer " "profile. If this expression evaluates to true, this profile is considered " "compatible with the active printer profile." msgstr "" -#: src/libslic3r/PrintConfig.cpp:265 +#: src/libslic3r/PrintConfig.cpp:276 msgid "Compatible print profiles condition" msgstr "" -#: src/libslic3r/PrintConfig.cpp:266 +#: src/libslic3r/PrintConfig.cpp:277 msgid "" "A boolean expression using the configuration values of an active print " "profile. If this expression evaluates to true, this profile is considered " "compatible with the active print profile." msgstr "" -#: src/libslic3r/PrintConfig.cpp:283 +#: src/libslic3r/PrintConfig.cpp:294 msgid "Complete individual objects" msgstr "" -#: src/libslic3r/PrintConfig.cpp:284 +#: src/libslic3r/PrintConfig.cpp:295 msgid "" "When printing multiple objects or copies, this feature will complete each " "object before moving onto next one (and starting it from its bottom layer). " @@ -6293,107 +6773,107 @@ msgid "" "warn and prevent you from extruder collisions, but beware." msgstr "" -#: src/libslic3r/PrintConfig.cpp:292 +#: src/libslic3r/PrintConfig.cpp:303 msgid "Enable auto cooling" msgstr "" -#: src/libslic3r/PrintConfig.cpp:293 +#: src/libslic3r/PrintConfig.cpp:304 msgid "" "This flag enables the automatic cooling logic that adjusts print speed and " "fan speed according to layer printing time." msgstr "" -#: src/libslic3r/PrintConfig.cpp:298 +#: src/libslic3r/PrintConfig.cpp:309 msgid "Cooling tube position" msgstr "" -#: src/libslic3r/PrintConfig.cpp:299 +#: src/libslic3r/PrintConfig.cpp:310 msgid "Distance of the center-point of the cooling tube from the extruder tip." msgstr "" -#: src/libslic3r/PrintConfig.cpp:306 +#: src/libslic3r/PrintConfig.cpp:317 msgid "Cooling tube length" msgstr "" -#: src/libslic3r/PrintConfig.cpp:307 +#: src/libslic3r/PrintConfig.cpp:318 msgid "Length of the cooling tube to limit space for cooling moves inside it." msgstr "" -#: src/libslic3r/PrintConfig.cpp:315 +#: src/libslic3r/PrintConfig.cpp:326 msgid "" "This is the acceleration your printer will be reset to after the role-" "specific acceleration values are used (perimeter/infill). Set zero to " "prevent resetting acceleration at all." msgstr "" -#: src/libslic3r/PrintConfig.cpp:324 +#: src/libslic3r/PrintConfig.cpp:335 msgid "Default filament profile" msgstr "" -#: src/libslic3r/PrintConfig.cpp:325 +#: src/libslic3r/PrintConfig.cpp:336 msgid "" "Default filament profile associated with the current printer profile. On " "selection of the current printer profile, this filament profile will be " "activated." msgstr "" -#: src/libslic3r/PrintConfig.cpp:331 +#: src/libslic3r/PrintConfig.cpp:342 msgid "Default print profile" msgstr "" -#: src/libslic3r/PrintConfig.cpp:332 src/libslic3r/PrintConfig.cpp:2560 -#: src/libslic3r/PrintConfig.cpp:2571 +#: src/libslic3r/PrintConfig.cpp:343 src/libslic3r/PrintConfig.cpp:2583 +#: src/libslic3r/PrintConfig.cpp:2594 msgid "" "Default print profile associated with the current printer profile. On " "selection of the current printer profile, this print profile will be " "activated." msgstr "" -#: src/libslic3r/PrintConfig.cpp:338 +#: src/libslic3r/PrintConfig.cpp:349 msgid "Disable fan for the first" msgstr "" -#: src/libslic3r/PrintConfig.cpp:339 +#: src/libslic3r/PrintConfig.cpp:350 msgid "" "You can set this to a positive value to disable fan at all during the first " "layers, so that it does not make adhesion worse." msgstr "" -#: src/libslic3r/PrintConfig.cpp:348 +#: src/libslic3r/PrintConfig.cpp:359 msgid "Don't support bridges" msgstr "" -#: src/libslic3r/PrintConfig.cpp:350 +#: src/libslic3r/PrintConfig.cpp:361 msgid "" "Experimental option for preventing support material from being generated " "under bridged areas." msgstr "" -#: src/libslic3r/PrintConfig.cpp:356 +#: src/libslic3r/PrintConfig.cpp:367 msgid "Distance between copies" msgstr "" -#: src/libslic3r/PrintConfig.cpp:357 +#: src/libslic3r/PrintConfig.cpp:368 msgid "Distance used for the auto-arrange feature of the plater." msgstr "" -#: src/libslic3r/PrintConfig.cpp:364 +#: src/libslic3r/PrintConfig.cpp:375 msgid "Elephant foot compensation" msgstr "" -#: src/libslic3r/PrintConfig.cpp:366 +#: src/libslic3r/PrintConfig.cpp:377 msgid "" "The first layer will be shrunk in the XY plane by the configured value to " "compensate for the 1st layer squish aka an Elephant Foot effect." msgstr "" -#: src/libslic3r/PrintConfig.cpp:375 +#: src/libslic3r/PrintConfig.cpp:386 msgid "" "This end procedure is inserted at the end of the output file. Note that you " "can use placeholder variables for all PrusaSlicer settings." msgstr "" -#: src/libslic3r/PrintConfig.cpp:385 +#: src/libslic3r/PrintConfig.cpp:396 msgid "" "This end procedure is inserted at the end of the output file, before the " "printer end gcode (and before any toolchange from this filament in case of " @@ -6402,62 +6882,62 @@ msgid "" "in extruder order." msgstr "" -#: src/libslic3r/PrintConfig.cpp:396 +#: src/libslic3r/PrintConfig.cpp:407 msgid "Ensure vertical shell thickness" msgstr "" -#: src/libslic3r/PrintConfig.cpp:398 +#: src/libslic3r/PrintConfig.cpp:409 msgid "" "Add solid infill near sloping surfaces to guarantee the vertical shell " "thickness (top+bottom solid layers)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:404 +#: src/libslic3r/PrintConfig.cpp:415 msgid "Top fill pattern" msgstr "" -#: src/libslic3r/PrintConfig.cpp:406 +#: src/libslic3r/PrintConfig.cpp:417 msgid "" "Fill pattern for top infill. This only affects the top visible layer, and " "not its adjacent solid shells." msgstr "" -#: src/libslic3r/PrintConfig.cpp:414 src/libslic3r/PrintConfig.cpp:832 -#: src/libslic3r/PrintConfig.cpp:1998 +#: src/libslic3r/PrintConfig.cpp:425 src/libslic3r/PrintConfig.cpp:843 +#: src/libslic3r/PrintConfig.cpp:2009 msgid "Rectilinear" msgstr "" -#: src/libslic3r/PrintConfig.cpp:415 src/libslic3r/PrintConfig.cpp:838 +#: src/libslic3r/PrintConfig.cpp:426 src/libslic3r/PrintConfig.cpp:849 msgid "Concentric" msgstr "" -#: src/libslic3r/PrintConfig.cpp:416 src/libslic3r/PrintConfig.cpp:842 +#: src/libslic3r/PrintConfig.cpp:427 src/libslic3r/PrintConfig.cpp:853 msgid "Hilbert Curve" msgstr "" -#: src/libslic3r/PrintConfig.cpp:417 src/libslic3r/PrintConfig.cpp:843 +#: src/libslic3r/PrintConfig.cpp:428 src/libslic3r/PrintConfig.cpp:854 msgid "Archimedean Chords" msgstr "" -#: src/libslic3r/PrintConfig.cpp:418 src/libslic3r/PrintConfig.cpp:844 +#: src/libslic3r/PrintConfig.cpp:429 src/libslic3r/PrintConfig.cpp:855 msgid "Octagram Spiral" msgstr "" -#: src/libslic3r/PrintConfig.cpp:424 +#: src/libslic3r/PrintConfig.cpp:435 msgid "Bottom fill pattern" msgstr "" -#: src/libslic3r/PrintConfig.cpp:426 +#: src/libslic3r/PrintConfig.cpp:437 msgid "" "Fill pattern for bottom infill. This only affects the bottom external " "visible layer, and not its adjacent solid shells." msgstr "" -#: src/libslic3r/PrintConfig.cpp:435 src/libslic3r/PrintConfig.cpp:446 +#: src/libslic3r/PrintConfig.cpp:446 src/libslic3r/PrintConfig.cpp:457 msgid "External perimeters" msgstr "" -#: src/libslic3r/PrintConfig.cpp:437 +#: src/libslic3r/PrintConfig.cpp:448 msgid "" "Set this to a non-zero value to set a manual extrusion width for external " "perimeters. If left zero, default extrusion width will be used if set, " @@ -6465,43 +6945,43 @@ msgid "" "(for example 200%), it will be computed over layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:440 src/libslic3r/PrintConfig.cpp:549 -#: src/libslic3r/PrintConfig.cpp:871 src/libslic3r/PrintConfig.cpp:884 -#: src/libslic3r/PrintConfig.cpp:1004 src/libslic3r/PrintConfig.cpp:1030 -#: src/libslic3r/PrintConfig.cpp:1418 src/libslic3r/PrintConfig.cpp:1757 -#: src/libslic3r/PrintConfig.cpp:1872 src/libslic3r/PrintConfig.cpp:1940 -#: src/libslic3r/PrintConfig.cpp:2100 +#: src/libslic3r/PrintConfig.cpp:451 src/libslic3r/PrintConfig.cpp:560 +#: src/libslic3r/PrintConfig.cpp:882 src/libslic3r/PrintConfig.cpp:895 +#: src/libslic3r/PrintConfig.cpp:1015 src/libslic3r/PrintConfig.cpp:1041 +#: src/libslic3r/PrintConfig.cpp:1423 src/libslic3r/PrintConfig.cpp:1761 +#: src/libslic3r/PrintConfig.cpp:1883 src/libslic3r/PrintConfig.cpp:1951 +#: src/libslic3r/PrintConfig.cpp:2111 msgid "mm or %" msgstr "" -#: src/libslic3r/PrintConfig.cpp:448 +#: src/libslic3r/PrintConfig.cpp:459 msgid "" "This separate setting will affect the speed of external perimeters (the " "visible ones). If expressed as percentage (for example: 80%) it will be " "calculated on the perimeters speed setting above. Set to zero for auto." msgstr "" -#: src/libslic3r/PrintConfig.cpp:451 src/libslic3r/PrintConfig.cpp:893 -#: src/libslic3r/PrintConfig.cpp:1716 src/libslic3r/PrintConfig.cpp:1768 -#: src/libslic3r/PrintConfig.cpp:1984 src/libslic3r/PrintConfig.cpp:2113 +#: src/libslic3r/PrintConfig.cpp:462 src/libslic3r/PrintConfig.cpp:904 +#: src/libslic3r/PrintConfig.cpp:1720 src/libslic3r/PrintConfig.cpp:1772 +#: src/libslic3r/PrintConfig.cpp:1995 src/libslic3r/PrintConfig.cpp:2124 msgid "mm/s or %" msgstr "" -#: src/libslic3r/PrintConfig.cpp:458 +#: src/libslic3r/PrintConfig.cpp:469 msgid "External perimeters first" msgstr "" -#: src/libslic3r/PrintConfig.cpp:460 +#: src/libslic3r/PrintConfig.cpp:471 msgid "" "Print contour perimeters from the outermost one to the innermost one instead " "of the default inverse order." msgstr "" -#: src/libslic3r/PrintConfig.cpp:466 +#: src/libslic3r/PrintConfig.cpp:477 msgid "Extra perimeters if needed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:468 +#: src/libslic3r/PrintConfig.cpp:479 #, no-c-format msgid "" "Add more perimeters when needed for avoiding gaps in sloping walls. Slic3r " @@ -6509,14 +6989,14 @@ msgid "" "is supported." msgstr "" -#: src/libslic3r/PrintConfig.cpp:478 +#: src/libslic3r/PrintConfig.cpp:489 msgid "" "The extruder to use (unless more specific extruder settings are specified). " "This value overrides perimeter and infill extruders, but not the support " "extruders." msgstr "" -#: src/libslic3r/PrintConfig.cpp:490 +#: src/libslic3r/PrintConfig.cpp:501 msgid "" "Set this to the vertical distance between your nozzle tip and (usually) the " "X carriage rods. In other words, this is the height of the clearance " @@ -6524,26 +7004,26 @@ msgid "" "extruder can peek before colliding with other printed objects." msgstr "" -#: src/libslic3r/PrintConfig.cpp:501 +#: src/libslic3r/PrintConfig.cpp:512 msgid "" "Set this to the clearance radius around your extruder. If the extruder is " "not centered, choose the largest value for safety. This setting is used to " "check for collisions and to display the graphical preview in the plater." msgstr "" -#: src/libslic3r/PrintConfig.cpp:511 +#: src/libslic3r/PrintConfig.cpp:522 msgid "Extruder Color" msgstr "" -#: src/libslic3r/PrintConfig.cpp:512 src/libslic3r/PrintConfig.cpp:573 +#: src/libslic3r/PrintConfig.cpp:523 src/libslic3r/PrintConfig.cpp:583 msgid "This is only used in the Slic3r interface as a visual help." msgstr "" -#: src/libslic3r/PrintConfig.cpp:518 +#: src/libslic3r/PrintConfig.cpp:529 msgid "Extruder offset" msgstr "" -#: src/libslic3r/PrintConfig.cpp:519 +#: src/libslic3r/PrintConfig.cpp:530 msgid "" "If your firmware doesn't handle the extruder displacement you need the G-" "code to take it into account. This option lets you specify the displacement " @@ -6551,21 +7031,21 @@ msgid "" "coordinates (they will be subtracted from the XY coordinate)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:528 +#: src/libslic3r/PrintConfig.cpp:539 msgid "Extrusion axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:529 +#: src/libslic3r/PrintConfig.cpp:540 msgid "" "Use this option to set the axis letter associated to your printer's extruder " "(usually E but some printers use A)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:534 +#: src/libslic3r/PrintConfig.cpp:545 msgid "Extrusion multiplier" msgstr "" -#: src/libslic3r/PrintConfig.cpp:535 +#: src/libslic3r/PrintConfig.cpp:546 msgid "" "This factor changes the amount of flow proportionally. You may need to tweak " "this setting to get nice surface finish and correct single wall widths. " @@ -6573,11 +7053,11 @@ msgid "" "more, check filament diameter and your firmware E steps." msgstr "" -#: src/libslic3r/PrintConfig.cpp:543 +#: src/libslic3r/PrintConfig.cpp:554 msgid "Default extrusion width" msgstr "" -#: src/libslic3r/PrintConfig.cpp:545 +#: src/libslic3r/PrintConfig.cpp:556 msgid "" "Set this to a non-zero value to allow a manual extrusion width. If left to " "zero, Slic3r derives extrusion widths from the nozzle diameter (see the " @@ -6586,119 +7066,119 @@ msgid "" "height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:555 +#: src/libslic3r/PrintConfig.cpp:566 msgid "Keep fan always on" msgstr "" -#: src/libslic3r/PrintConfig.cpp:556 +#: src/libslic3r/PrintConfig.cpp:567 msgid "" "If this is enabled, fan will never be disabled and will be kept running at " "least at its minimum speed. Useful for PLA, harmful for ABS." msgstr "" -#: src/libslic3r/PrintConfig.cpp:561 +#: src/libslic3r/PrintConfig.cpp:572 msgid "Enable fan if layer print time is below" msgstr "" -#: src/libslic3r/PrintConfig.cpp:562 +#: src/libslic3r/PrintConfig.cpp:573 msgid "" "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." msgstr "" -#: src/libslic3r/PrintConfig.cpp:564 src/libslic3r/PrintConfig.cpp:1703 +#: src/libslic3r/PrintConfig.cpp:575 src/libslic3r/PrintConfig.cpp:1708 msgid "approximate seconds" msgstr "" -#: src/libslic3r/PrintConfig.cpp:578 +#: src/libslic3r/PrintConfig.cpp:588 msgid "Filament notes" msgstr "" -#: src/libslic3r/PrintConfig.cpp:579 +#: src/libslic3r/PrintConfig.cpp:589 msgid "You can put your notes regarding the filament here." msgstr "" -#: src/libslic3r/PrintConfig.cpp:587 src/libslic3r/PrintConfig.cpp:1244 +#: src/libslic3r/PrintConfig.cpp:597 src/libslic3r/PrintConfig.cpp:1247 msgid "Max volumetric speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:588 +#: src/libslic3r/PrintConfig.cpp:598 msgid "" "Maximum volumetric speed allowed for this filament. Limits the maximum " "volumetric speed of a print to the minimum of print and filament volumetric " "speed. Set to zero for no limit." msgstr "" -#: src/libslic3r/PrintConfig.cpp:597 +#: src/libslic3r/PrintConfig.cpp:607 msgid "Loading speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:598 +#: src/libslic3r/PrintConfig.cpp:608 msgid "Speed used for loading the filament on the wipe tower." msgstr "" -#: src/libslic3r/PrintConfig.cpp:605 +#: src/libslic3r/PrintConfig.cpp:615 msgid "Loading speed at the start" msgstr "" -#: src/libslic3r/PrintConfig.cpp:606 +#: src/libslic3r/PrintConfig.cpp:616 msgid "Speed used at the very beginning of loading phase." msgstr "" -#: src/libslic3r/PrintConfig.cpp:613 +#: src/libslic3r/PrintConfig.cpp:623 msgid "Unloading speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:614 +#: src/libslic3r/PrintConfig.cpp:624 msgid "" "Speed used for unloading the filament on the wipe tower (does not affect " "initial part of unloading just after ramming)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:622 +#: src/libslic3r/PrintConfig.cpp:632 msgid "Unloading speed at the start" msgstr "" -#: src/libslic3r/PrintConfig.cpp:623 +#: src/libslic3r/PrintConfig.cpp:633 msgid "" "Speed used for unloading the tip of the filament immediately after ramming." msgstr "" -#: src/libslic3r/PrintConfig.cpp:630 +#: src/libslic3r/PrintConfig.cpp:640 msgid "Delay after unloading" msgstr "" -#: src/libslic3r/PrintConfig.cpp:631 +#: src/libslic3r/PrintConfig.cpp:641 msgid "" "Time to wait after the filament is unloaded. May help to get reliable " "toolchanges with flexible materials that may need more time to shrink to " "original dimensions." msgstr "" -#: src/libslic3r/PrintConfig.cpp:640 +#: src/libslic3r/PrintConfig.cpp:650 msgid "Number of cooling moves" msgstr "" -#: src/libslic3r/PrintConfig.cpp:641 +#: src/libslic3r/PrintConfig.cpp:651 msgid "" "Filament is cooled by being moved back and forth in the cooling tubes. " "Specify desired number of these moves." msgstr "" -#: src/libslic3r/PrintConfig.cpp:649 +#: src/libslic3r/PrintConfig.cpp:659 msgid "Speed of the first cooling move" msgstr "" -#: src/libslic3r/PrintConfig.cpp:650 +#: src/libslic3r/PrintConfig.cpp:660 msgid "Cooling moves are gradually accelerating beginning at this speed." msgstr "" -#: src/libslic3r/PrintConfig.cpp:657 +#: src/libslic3r/PrintConfig.cpp:667 msgid "Minimal purge on wipe tower" msgstr "" -#: src/libslic3r/PrintConfig.cpp:658 +#: src/libslic3r/PrintConfig.cpp:668 msgid "" "After a tool change, the exact position of the newly loaded filament inside " "the nozzle may not be known, and the filament pressure is likely not yet " @@ -6707,63 +7187,63 @@ msgid "" "to produce successive infill or sacrificial object extrusions reliably." msgstr "" -#: src/libslic3r/PrintConfig.cpp:662 +#: src/libslic3r/PrintConfig.cpp:672 msgid "mm³" msgstr "" -#: src/libslic3r/PrintConfig.cpp:668 +#: src/libslic3r/PrintConfig.cpp:678 msgid "Speed of the last cooling move" msgstr "" -#: src/libslic3r/PrintConfig.cpp:669 +#: src/libslic3r/PrintConfig.cpp:679 msgid "Cooling moves are gradually accelerating towards this speed." msgstr "" -#: src/libslic3r/PrintConfig.cpp:676 +#: src/libslic3r/PrintConfig.cpp:686 msgid "Filament load time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:677 +#: src/libslic3r/PrintConfig.cpp:687 msgid "" "Time for the printer firmware (or the Multi Material Unit 2.0) to load a new " "filament during a tool change (when executing the T code). This time is " "added to the total print time by the G-code time estimator." msgstr "" -#: src/libslic3r/PrintConfig.cpp:684 +#: src/libslic3r/PrintConfig.cpp:694 msgid "Ramming parameters" msgstr "" -#: src/libslic3r/PrintConfig.cpp:685 +#: src/libslic3r/PrintConfig.cpp:695 msgid "" "This string is edited by RammingDialog and contains ramming specific " "parameters." msgstr "" -#: src/libslic3r/PrintConfig.cpp:691 +#: src/libslic3r/PrintConfig.cpp:701 msgid "Filament unload time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:692 +#: src/libslic3r/PrintConfig.cpp:702 msgid "" "Time for the printer firmware (or the Multi Material Unit 2.0) to unload a " "filament during a tool change (when executing the T code). This time is " "added to the total print time by the G-code time estimator." msgstr "" -#: src/libslic3r/PrintConfig.cpp:700 +#: src/libslic3r/PrintConfig.cpp:710 msgid "" "Enter your filament diameter here. Good precision is required, so use a " "caliper and do multiple measurements along the filament, then compute the " "average." msgstr "" -#: src/libslic3r/PrintConfig.cpp:707 src/libslic3r/PrintConfig.cpp:2471 -#: src/libslic3r/PrintConfig.cpp:2472 +#: src/libslic3r/PrintConfig.cpp:717 src/libslic3r/PrintConfig.cpp:2494 +#: src/libslic3r/PrintConfig.cpp:2495 msgid "Density" msgstr "" -#: src/libslic3r/PrintConfig.cpp:708 +#: src/libslic3r/PrintConfig.cpp:718 msgid "" "Enter your filament density here. This is only for statistical information. " "A decent way is to weigh a known length of filament and compute the ratio of " @@ -6771,117 +7251,117 @@ msgid "" "displacement." msgstr "" -#: src/libslic3r/PrintConfig.cpp:711 +#: src/libslic3r/PrintConfig.cpp:721 msgid "g/cm³" msgstr "" -#: src/libslic3r/PrintConfig.cpp:716 +#: src/libslic3r/PrintConfig.cpp:726 msgid "Filament type" msgstr "" -#: src/libslic3r/PrintConfig.cpp:717 +#: src/libslic3r/PrintConfig.cpp:727 msgid "The filament material type for use in custom G-codes." msgstr "" -#: src/libslic3r/PrintConfig.cpp:743 +#: src/libslic3r/PrintConfig.cpp:754 msgid "Soluble material" msgstr "" -#: src/libslic3r/PrintConfig.cpp:744 +#: src/libslic3r/PrintConfig.cpp:755 msgid "Soluble material is most likely used for a soluble support." msgstr "" -#: src/libslic3r/PrintConfig.cpp:750 +#: src/libslic3r/PrintConfig.cpp:761 msgid "" "Enter your filament cost per kg here. This is only for statistical " "information." msgstr "" -#: src/libslic3r/PrintConfig.cpp:751 +#: src/libslic3r/PrintConfig.cpp:762 msgid "money/kg" msgstr "" -#: src/libslic3r/PrintConfig.cpp:760 src/libslic3r/PrintConfig.cpp:2555 +#: src/libslic3r/PrintConfig.cpp:771 src/libslic3r/PrintConfig.cpp:2578 msgid "(Unknown)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:764 +#: src/libslic3r/PrintConfig.cpp:775 msgid "Fill angle" msgstr "" -#: src/libslic3r/PrintConfig.cpp:766 +#: src/libslic3r/PrintConfig.cpp:777 msgid "" "Default base angle for infill orientation. Cross-hatching will be applied to " "this. Bridges will be infilled using the best direction Slic3r can detect, " "so this setting does not affect them." msgstr "" -#: src/libslic3r/PrintConfig.cpp:778 +#: src/libslic3r/PrintConfig.cpp:789 msgid "Fill density" msgstr "" -#: src/libslic3r/PrintConfig.cpp:780 +#: src/libslic3r/PrintConfig.cpp:791 msgid "Density of internal infill, expressed in the range 0% - 100%." msgstr "" -#: src/libslic3r/PrintConfig.cpp:815 +#: src/libslic3r/PrintConfig.cpp:826 msgid "Fill pattern" msgstr "" -#: src/libslic3r/PrintConfig.cpp:817 +#: src/libslic3r/PrintConfig.cpp:828 msgid "Fill pattern for general low-density infill." msgstr "" -#: src/libslic3r/PrintConfig.cpp:833 +#: src/libslic3r/PrintConfig.cpp:844 msgid "Grid" msgstr "" -#: src/libslic3r/PrintConfig.cpp:834 +#: src/libslic3r/PrintConfig.cpp:845 msgid "Triangles" msgstr "" -#: src/libslic3r/PrintConfig.cpp:835 +#: src/libslic3r/PrintConfig.cpp:846 msgid "Stars" msgstr "" -#: src/libslic3r/PrintConfig.cpp:836 +#: src/libslic3r/PrintConfig.cpp:847 msgid "Cubic" msgstr "" -#: src/libslic3r/PrintConfig.cpp:837 +#: src/libslic3r/PrintConfig.cpp:848 msgid "Line" msgstr "" -#: src/libslic3r/PrintConfig.cpp:839 src/libslic3r/PrintConfig.cpp:2000 +#: src/libslic3r/PrintConfig.cpp:850 src/libslic3r/PrintConfig.cpp:2011 msgid "Honeycomb" msgstr "" -#: src/libslic3r/PrintConfig.cpp:840 +#: src/libslic3r/PrintConfig.cpp:851 msgid "3D Honeycomb" msgstr "" -#: src/libslic3r/PrintConfig.cpp:841 +#: src/libslic3r/PrintConfig.cpp:852 msgid "Gyroid" msgstr "" -#: src/libslic3r/PrintConfig.cpp:848 src/libslic3r/PrintConfig.cpp:857 -#: src/libslic3r/PrintConfig.cpp:865 src/libslic3r/PrintConfig.cpp:899 +#: src/libslic3r/PrintConfig.cpp:859 src/libslic3r/PrintConfig.cpp:868 +#: src/libslic3r/PrintConfig.cpp:876 src/libslic3r/PrintConfig.cpp:910 msgid "First layer" msgstr "" -#: src/libslic3r/PrintConfig.cpp:849 +#: src/libslic3r/PrintConfig.cpp:860 msgid "" "This is the acceleration your printer will use for first layer. Set zero to " "disable acceleration control for first layer." msgstr "" -#: src/libslic3r/PrintConfig.cpp:858 +#: src/libslic3r/PrintConfig.cpp:869 msgid "" "Heated build plate temperature for the first layer. Set this to zero to " "disable bed temperature control commands in the output." msgstr "" -#: src/libslic3r/PrintConfig.cpp:867 +#: src/libslic3r/PrintConfig.cpp:878 msgid "" "Set this to a non-zero value to set a manual extrusion width for first " "layer. You can use this to force fatter extrudates for better adhesion. If " @@ -6889,7 +7369,7 @@ msgid "" "layer height. If set to zero, it will use the default extrusion width." msgstr "" -#: src/libslic3r/PrintConfig.cpp:880 +#: src/libslic3r/PrintConfig.cpp:891 msgid "" "When printing with very low layer heights, you might still want to print a " "thicker bottom layer to improve adhesion and tolerance for non perfect build " @@ -6897,47 +7377,47 @@ msgid "" "example: 150%) over the default layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:889 +#: src/libslic3r/PrintConfig.cpp:900 msgid "First layer speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:890 +#: src/libslic3r/PrintConfig.cpp:901 msgid "" "If expressed as absolute value in mm/s, this speed will be applied to all " "the print moves of the first layer, regardless of their type. If expressed " "as a percentage (for example: 40%) it will scale the default speeds." msgstr "" -#: src/libslic3r/PrintConfig.cpp:900 +#: src/libslic3r/PrintConfig.cpp:911 msgid "" "Extruder temperature for first layer. If you want to control temperature " "manually during print, set this to zero to disable temperature control " "commands in the output file." msgstr "" -#: src/libslic3r/PrintConfig.cpp:909 +#: src/libslic3r/PrintConfig.cpp:920 msgid "" "Speed for filling small gaps using short zigzag moves. Keep this reasonably " "low to avoid too much shaking and resonance issues. Set zero to disable gaps " "filling." msgstr "" -#: src/libslic3r/PrintConfig.cpp:917 +#: src/libslic3r/PrintConfig.cpp:928 msgid "Verbose G-code" msgstr "" -#: src/libslic3r/PrintConfig.cpp:918 +#: src/libslic3r/PrintConfig.cpp:929 msgid "" "Enable this to get a commented G-code file, with each line explained by a " "descriptive text. If you print from SD card, the additional weight of the " "file could make your firmware slow down." msgstr "" -#: src/libslic3r/PrintConfig.cpp:925 +#: src/libslic3r/PrintConfig.cpp:936 msgid "G-code flavor" msgstr "" -#: src/libslic3r/PrintConfig.cpp:926 +#: src/libslic3r/PrintConfig.cpp:937 msgid "" "Some G/M-code commands, including temperature control and others, are not " "universal. Set this option to your printer's firmware to get a compatible " @@ -6945,15 +7425,15 @@ msgid "" "extrusion value at all." msgstr "" -#: src/libslic3r/PrintConfig.cpp:949 +#: src/libslic3r/PrintConfig.cpp:960 msgid "No extrusion" msgstr "" -#: src/libslic3r/PrintConfig.cpp:954 +#: src/libslic3r/PrintConfig.cpp:965 msgid "Label objects" msgstr "" -#: src/libslic3r/PrintConfig.cpp:955 +#: src/libslic3r/PrintConfig.cpp:966 msgid "" "Enable this to add comments into the G-Code labeling print moves with what " "object they belong to, which is useful for the Octoprint CancelObject " @@ -6961,46 +7441,46 @@ msgid "" "setup and Wipe into Object / Wipe into Infill." msgstr "" -#: src/libslic3r/PrintConfig.cpp:962 +#: src/libslic3r/PrintConfig.cpp:973 msgid "High extruder current on filament swap" msgstr "" -#: src/libslic3r/PrintConfig.cpp:963 +#: src/libslic3r/PrintConfig.cpp:974 msgid "" "It may be beneficial to increase the extruder motor current during the " "filament exchange sequence to allow for rapid ramming feed rates and to " "overcome resistance when loading a filament with an ugly shaped tip." msgstr "" -#: src/libslic3r/PrintConfig.cpp:971 +#: src/libslic3r/PrintConfig.cpp:982 msgid "" "This is the acceleration your printer will use for infill. Set zero to " "disable acceleration control for infill." msgstr "" -#: src/libslic3r/PrintConfig.cpp:979 +#: src/libslic3r/PrintConfig.cpp:990 msgid "Combine infill every" msgstr "" -#: src/libslic3r/PrintConfig.cpp:981 +#: src/libslic3r/PrintConfig.cpp:992 msgid "" "This feature allows to combine infill and speed up your print by extruding " "thicker infill layers while preserving thin perimeters, thus accuracy." msgstr "" -#: src/libslic3r/PrintConfig.cpp:984 +#: src/libslic3r/PrintConfig.cpp:995 msgid "Combine infill every n layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:990 +#: src/libslic3r/PrintConfig.cpp:1001 msgid "Infill extruder" msgstr "" -#: src/libslic3r/PrintConfig.cpp:992 +#: src/libslic3r/PrintConfig.cpp:1003 msgid "The extruder to use when printing infill." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1000 +#: src/libslic3r/PrintConfig.cpp:1011 msgid "" "Set this to a non-zero value to set a manual extrusion width for infill. If " "left zero, default extrusion width will be used if set, otherwise 1.125 x " @@ -7009,32 +7489,32 @@ msgid "" "example 90%) it will be computed over layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1010 +#: src/libslic3r/PrintConfig.cpp:1021 msgid "Infill before perimeters" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1011 +#: src/libslic3r/PrintConfig.cpp:1022 msgid "" "This option will switch the print order of perimeters and infill, making the " "latter first." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1016 +#: src/libslic3r/PrintConfig.cpp:1027 msgid "Only infill where needed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1018 +#: src/libslic3r/PrintConfig.cpp:1029 msgid "" "This option will limit infill to the areas actually needed for supporting " "ceilings (it will act as internal support material). If enabled, slows down " "the G-code generation due to the multiple checks involved." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1025 +#: src/libslic3r/PrintConfig.cpp:1036 msgid "Infill/perimeters overlap" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1027 +#: src/libslic3r/PrintConfig.cpp:1038 msgid "" "This setting applies an additional overlap between infill and perimeters for " "better bonding. Theoretically this shouldn't be needed, but backlash might " @@ -7042,30 +7522,30 @@ msgid "" "perimeter extrusion width." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1038 +#: src/libslic3r/PrintConfig.cpp:1049 msgid "Speed for printing the internal fill. Set to zero for auto." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1046 +#: src/libslic3r/PrintConfig.cpp:1057 msgid "Inherits profile" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1047 +#: src/libslic3r/PrintConfig.cpp:1058 msgid "Name of the profile, from which this profile inherits." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1060 +#: src/libslic3r/PrintConfig.cpp:1071 msgid "Interface shells" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1061 +#: src/libslic3r/PrintConfig.cpp:1072 msgid "" "Force the generation of solid shells between adjacent materials/volumes. " "Useful for multi-extruder prints with translucent materials or manual " "soluble support material." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1070 +#: src/libslic3r/PrintConfig.cpp:1081 msgid "" "This custom code is inserted at every layer change, right after the Z move " "and before the extruder moves to the first layer point. Note that you can " @@ -7073,11 +7553,11 @@ msgid "" "[layer_z]." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1081 +#: src/libslic3r/PrintConfig.cpp:1092 msgid "Supports remaining times" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1082 +#: src/libslic3r/PrintConfig.cpp:1093 msgid "" "Emit M73 P[percent printed] R[remaining time in minutes] at 1 minute " "intervals into the G-code to let the firmware show accurate remaining time. " @@ -7085,151 +7565,151 @@ msgid "" "firmware supports M73 Qxx Sxx for the silent mode." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1090 +#: src/libslic3r/PrintConfig.cpp:1101 msgid "Supports stealth mode" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1091 +#: src/libslic3r/PrintConfig.cpp:1102 msgid "The firmware supports stealth mode" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1115 +#: src/libslic3r/PrintConfig.cpp:1125 msgid "Maximum feedrate X" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1116 +#: src/libslic3r/PrintConfig.cpp:1126 msgid "Maximum feedrate Y" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1117 +#: src/libslic3r/PrintConfig.cpp:1127 msgid "Maximum feedrate Z" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1118 +#: src/libslic3r/PrintConfig.cpp:1128 msgid "Maximum feedrate E" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1121 +#: src/libslic3r/PrintConfig.cpp:1131 msgid "Maximum feedrate of the X axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1122 +#: src/libslic3r/PrintConfig.cpp:1132 msgid "Maximum feedrate of the Y axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1123 +#: src/libslic3r/PrintConfig.cpp:1133 msgid "Maximum feedrate of the Z axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1124 +#: src/libslic3r/PrintConfig.cpp:1134 msgid "Maximum feedrate of the E axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1133 +#: src/libslic3r/PrintConfig.cpp:1142 msgid "Maximum acceleration X" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1134 +#: src/libslic3r/PrintConfig.cpp:1143 msgid "Maximum acceleration Y" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1135 +#: src/libslic3r/PrintConfig.cpp:1144 msgid "Maximum acceleration Z" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1136 +#: src/libslic3r/PrintConfig.cpp:1145 msgid "Maximum acceleration E" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1139 +#: src/libslic3r/PrintConfig.cpp:1148 msgid "Maximum acceleration of the X axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1140 +#: src/libslic3r/PrintConfig.cpp:1149 msgid "Maximum acceleration of the Y axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1141 +#: src/libslic3r/PrintConfig.cpp:1150 msgid "Maximum acceleration of the Z axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1142 +#: src/libslic3r/PrintConfig.cpp:1151 msgid "Maximum acceleration of the E axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1151 +#: src/libslic3r/PrintConfig.cpp:1159 msgid "Maximum jerk X" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1152 +#: src/libslic3r/PrintConfig.cpp:1160 msgid "Maximum jerk Y" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1153 +#: src/libslic3r/PrintConfig.cpp:1161 msgid "Maximum jerk Z" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1154 +#: src/libslic3r/PrintConfig.cpp:1162 msgid "Maximum jerk E" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1157 +#: src/libslic3r/PrintConfig.cpp:1165 msgid "Maximum jerk of the X axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1158 +#: src/libslic3r/PrintConfig.cpp:1166 msgid "Maximum jerk of the Y axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1159 +#: src/libslic3r/PrintConfig.cpp:1167 msgid "Maximum jerk of the Z axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1160 +#: src/libslic3r/PrintConfig.cpp:1168 msgid "Maximum jerk of the E axis" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1171 +#: src/libslic3r/PrintConfig.cpp:1178 msgid "Minimum feedrate when extruding" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1173 +#: src/libslic3r/PrintConfig.cpp:1180 msgid "Minimum feedrate when extruding (M205 S)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1182 +#: src/libslic3r/PrintConfig.cpp:1188 msgid "Minimum travel feedrate" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1184 +#: src/libslic3r/PrintConfig.cpp:1190 msgid "Minimum travel feedrate (M205 T)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1193 +#: src/libslic3r/PrintConfig.cpp:1198 msgid "Maximum acceleration when extruding" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1195 +#: src/libslic3r/PrintConfig.cpp:1200 msgid "Maximum acceleration when extruding (M204 S)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1204 +#: src/libslic3r/PrintConfig.cpp:1208 msgid "Maximum acceleration when retracting" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1206 +#: src/libslic3r/PrintConfig.cpp:1210 msgid "Maximum acceleration when retracting (M204 T)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1214 src/libslic3r/PrintConfig.cpp:1223 +#: src/libslic3r/PrintConfig.cpp:1217 src/libslic3r/PrintConfig.cpp:1226 msgid "Max" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1215 +#: src/libslic3r/PrintConfig.cpp:1218 msgid "This setting represents the maximum speed of your fan." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1224 +#: src/libslic3r/PrintConfig.cpp:1227 #, no-c-format msgid "" "This is the highest printable layer height for this extruder, used to cap " @@ -7238,28 +7718,28 @@ msgid "" "adhesion. If set to 0, layer height is limited to 75% of the nozzle diameter." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1234 +#: src/libslic3r/PrintConfig.cpp:1237 msgid "Max print speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1235 +#: src/libslic3r/PrintConfig.cpp:1238 msgid "" "When setting other speed settings to 0 Slic3r will autocalculate the optimal " "speed in order to keep constant extruder pressure. This experimental setting " "is used to set the highest print speed you want to allow." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1245 +#: src/libslic3r/PrintConfig.cpp:1248 msgid "" "This experimental setting is used to set the maximum volumetric speed your " "extruder supports." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1254 +#: src/libslic3r/PrintConfig.cpp:1257 msgid "Max volumetric slope positive" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1255 src/libslic3r/PrintConfig.cpp:1266 +#: src/libslic3r/PrintConfig.cpp:1258 src/libslic3r/PrintConfig.cpp:1269 msgid "" "This experimental setting is used to limit the speed of change in extrusion " "rate. A value of 1.8 mm³/s² ensures, that a change from the extrusion rate " @@ -7267,95 +7747,95 @@ msgid "" "s) to 5.4 mm³/s (feedrate 60 mm/s) will take at least 2 seconds." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1259 src/libslic3r/PrintConfig.cpp:1270 +#: src/libslic3r/PrintConfig.cpp:1262 src/libslic3r/PrintConfig.cpp:1273 msgid "mm³/s²" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1265 +#: src/libslic3r/PrintConfig.cpp:1268 msgid "Max volumetric slope negative" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1277 src/libslic3r/PrintConfig.cpp:1286 +#: src/libslic3r/PrintConfig.cpp:1280 src/libslic3r/PrintConfig.cpp:1289 msgid "Min" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1278 +#: src/libslic3r/PrintConfig.cpp:1281 msgid "This setting represents the minimum PWM your fan needs to work." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1287 +#: src/libslic3r/PrintConfig.cpp:1290 msgid "" "This is the lowest printable layer height for this extruder and limits the " "resolution for variable layer height. Typical values are between 0.05 mm and " "0.1 mm." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1295 +#: src/libslic3r/PrintConfig.cpp:1298 msgid "Min print speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1296 +#: src/libslic3r/PrintConfig.cpp:1299 msgid "Slic3r will not scale speed down below this speed." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1303 +#: src/libslic3r/PrintConfig.cpp:1306 msgid "Minimal filament extrusion length" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1304 +#: src/libslic3r/PrintConfig.cpp:1307 msgid "" "Generate no less than the number of skirt loops required to consume the " "specified amount of filament on the bottom layer. For multi-extruder " "machines, this minimum applies to each extruder." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1313 +#: src/libslic3r/PrintConfig.cpp:1316 msgid "Configuration notes" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1314 +#: src/libslic3r/PrintConfig.cpp:1317 msgid "" "You can put here your personal notes. This text will be added to the G-code " "header comments." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1324 +#: src/libslic3r/PrintConfig.cpp:1327 msgid "" "This is the diameter of your extruder nozzle (for example: 0.5, 0.35 etc.)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1329 +#: src/libslic3r/PrintConfig.cpp:1332 msgid "Host Type" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1330 +#: src/libslic3r/PrintConfig.cpp:1333 msgid "" "Slic3r can upload G-code files to a printer host. This field must contain " "the kind of the host." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1343 +#: src/libslic3r/PrintConfig.cpp:1348 msgid "Only retract when crossing perimeters" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1344 +#: src/libslic3r/PrintConfig.cpp:1349 msgid "" "Disables retraction when the travel path does not exceed the upper layer's " "perimeters (and thus any ooze will be probably invisible)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1351 +#: src/libslic3r/PrintConfig.cpp:1356 msgid "" "This option will drop the temperature of the inactive extruders to prevent " "oozing. It will enable a tall skirt automatically and move extruders outside " "such skirt when changing temperatures." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1358 +#: src/libslic3r/PrintConfig.cpp:1363 msgid "Output filename format" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1359 +#: src/libslic3r/PrintConfig.cpp:1364 msgid "" "You can use all configuration options as variables inside this template. For " "example: [layer_height], [fill_density] etc. You can also use [timestamp], " @@ -7363,31 +7843,31 @@ msgid "" "[input_filename], [input_filename_base]." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1368 +#: src/libslic3r/PrintConfig.cpp:1373 msgid "Detect bridging perimeters" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1370 +#: src/libslic3r/PrintConfig.cpp:1375 msgid "" "Experimental option to adjust flow for overhangs (bridge flow will be used), " "to apply bridge speed to them and enable fan." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1376 +#: src/libslic3r/PrintConfig.cpp:1381 msgid "Filament parking position" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1377 +#: src/libslic3r/PrintConfig.cpp:1382 msgid "" "Distance of the extruder tip from the position where the filament is parked " "when unloaded. This should match the value in printer firmware." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1385 +#: src/libslic3r/PrintConfig.cpp:1390 msgid "Extra loading distance" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1386 +#: src/libslic3r/PrintConfig.cpp:1391 msgid "" "When set to zero, the distance the filament is moved from parking position " "during load is exactly the same as it was moved back during unload. When " @@ -7395,28 +7875,28 @@ msgid "" "than unloading." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1394 src/libslic3r/PrintConfig.cpp:1412 -#: src/libslic3r/PrintConfig.cpp:1425 src/libslic3r/PrintConfig.cpp:1435 +#: src/libslic3r/PrintConfig.cpp:1399 src/libslic3r/PrintConfig.cpp:1417 +#: src/libslic3r/PrintConfig.cpp:1430 src/libslic3r/PrintConfig.cpp:1440 msgid "Perimeters" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1395 +#: src/libslic3r/PrintConfig.cpp:1400 msgid "" "This is the acceleration your printer will use for perimeters. A high value " "like 9000 usually gives good results if your hardware is up to the job. Set " "zero to disable acceleration control for perimeters." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1403 +#: src/libslic3r/PrintConfig.cpp:1408 msgid "Perimeter extruder" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1405 +#: src/libslic3r/PrintConfig.cpp:1410 msgid "" "The extruder to use when printing perimeters and brim. First extruder is 1." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1414 +#: src/libslic3r/PrintConfig.cpp:1419 msgid "" "Set this to a non-zero value to set a manual extrusion width for perimeters. " "You may want to use thinner extrudates to get more accurate surfaces. If " @@ -7425,12 +7905,12 @@ msgid "" "it will be computed over layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1427 +#: src/libslic3r/PrintConfig.cpp:1432 msgid "" "Speed for perimeters (contours, aka vertical shells). Set to zero for auto." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1437 +#: src/libslic3r/PrintConfig.cpp:1442 msgid "" "This option sets the number of perimeters to generate for each layer. Note " "that Slic3r may increase this number automatically when it detects sloping " @@ -7438,11 +7918,11 @@ msgid "" "Perimeters option is enabled." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1441 +#: src/libslic3r/PrintConfig.cpp:1446 msgid "(minimum)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1449 +#: src/libslic3r/PrintConfig.cpp:1454 msgid "" "If you want to process the output G-code through custom scripts, just list " "their absolute paths here. Separate multiple scripts with a semicolon. " @@ -7451,55 +7931,55 @@ msgid "" "environment variables." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1461 +#: src/libslic3r/PrintConfig.cpp:1466 msgid "Printer type" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1462 +#: src/libslic3r/PrintConfig.cpp:1467 msgid "Type of the printer." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1467 +#: src/libslic3r/PrintConfig.cpp:1472 msgid "Printer notes" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1468 +#: src/libslic3r/PrintConfig.cpp:1473 msgid "You can put your notes regarding the printer here." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1476 +#: src/libslic3r/PrintConfig.cpp:1481 msgid "Printer vendor" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1477 +#: src/libslic3r/PrintConfig.cpp:1482 msgid "Name of the printer vendor." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1482 +#: src/libslic3r/PrintConfig.cpp:1487 msgid "Printer variant" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1483 +#: src/libslic3r/PrintConfig.cpp:1488 msgid "" "Name of the printer variant. For example, the printer variants may be " "differentiated by a nozzle diameter." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1496 +#: src/libslic3r/PrintConfig.cpp:1501 msgid "Raft layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1498 +#: src/libslic3r/PrintConfig.cpp:1503 msgid "" "The object will be raised by this number of layers, and support material " "will be generated under it." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1506 +#: src/libslic3r/PrintConfig.cpp:1511 msgid "Resolution" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1507 +#: src/libslic3r/PrintConfig.cpp:1512 msgid "" "Minimum detail resolution, used to simplify the input file for speeding up " "the slicing job and reducing memory usage. High-resolution models often " @@ -7507,278 +7987,278 @@ msgid "" "simplification and use full resolution from input." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1517 +#: src/libslic3r/PrintConfig.cpp:1522 msgid "Minimum travel after retraction" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1518 +#: src/libslic3r/PrintConfig.cpp:1523 msgid "" "Retraction is not triggered when travel moves are shorter than this length." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1524 +#: src/libslic3r/PrintConfig.cpp:1529 msgid "Retract amount before wipe" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1525 +#: src/libslic3r/PrintConfig.cpp:1530 msgid "" "With bowden extruders, it may be wise to do some amount of quick retract " "before doing the wipe movement." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1532 +#: src/libslic3r/PrintConfig.cpp:1537 msgid "Retract on layer change" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1533 +#: src/libslic3r/PrintConfig.cpp:1538 msgid "This flag enforces a retraction whenever a Z move is done." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1538 src/libslic3r/PrintConfig.cpp:1546 +#: src/libslic3r/PrintConfig.cpp:1543 src/libslic3r/PrintConfig.cpp:1551 msgid "Length" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1539 +#: src/libslic3r/PrintConfig.cpp:1544 msgid "Retraction Length" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1540 +#: src/libslic3r/PrintConfig.cpp:1545 msgid "" "When retraction is triggered, filament is pulled back by the specified " "amount (the length is measured on raw filament, before it enters the " "extruder)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1542 src/libslic3r/PrintConfig.cpp:1551 +#: src/libslic3r/PrintConfig.cpp:1547 src/libslic3r/PrintConfig.cpp:1556 msgid "mm (zero to disable)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1547 +#: src/libslic3r/PrintConfig.cpp:1552 msgid "Retraction Length (Toolchange)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1548 +#: src/libslic3r/PrintConfig.cpp:1553 msgid "" "When retraction is triggered before changing tool, filament is pulled back " "by the specified amount (the length is measured on raw filament, before it " "enters the extruder)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1556 +#: src/libslic3r/PrintConfig.cpp:1561 msgid "Lift Z" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1557 +#: src/libslic3r/PrintConfig.cpp:1562 msgid "" "If you set this to a positive value, Z is quickly raised every time a " "retraction is triggered. When using multiple extruders, only the setting for " "the first extruder will be considered." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1564 +#: src/libslic3r/PrintConfig.cpp:1569 msgid "Above Z" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1565 +#: src/libslic3r/PrintConfig.cpp:1570 msgid "Only lift Z above" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1566 +#: src/libslic3r/PrintConfig.cpp:1571 msgid "" "If you set this to a positive value, Z lift will only take place above the " "specified absolute Z. You can tune this setting for skipping lift on the " "first layers." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1573 +#: src/libslic3r/PrintConfig.cpp:1578 msgid "Below Z" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1574 +#: src/libslic3r/PrintConfig.cpp:1579 msgid "Only lift Z below" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1575 +#: src/libslic3r/PrintConfig.cpp:1580 msgid "" "If you set this to a positive value, Z lift will only take place below the " "specified absolute Z. You can tune this setting for limiting lift to the " "first layers." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1583 src/libslic3r/PrintConfig.cpp:1591 +#: src/libslic3r/PrintConfig.cpp:1588 src/libslic3r/PrintConfig.cpp:1596 msgid "Extra length on restart" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1584 +#: src/libslic3r/PrintConfig.cpp:1589 msgid "" "When the retraction is compensated after the travel move, the extruder will " "push this additional amount of filament. This setting is rarely needed." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1592 +#: src/libslic3r/PrintConfig.cpp:1597 msgid "" "When the retraction is compensated after changing tool, the extruder will " "push this additional amount of filament." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1599 src/libslic3r/PrintConfig.cpp:1600 +#: src/libslic3r/PrintConfig.cpp:1604 src/libslic3r/PrintConfig.cpp:1605 msgid "Retraction Speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1601 +#: src/libslic3r/PrintConfig.cpp:1606 msgid "The speed for retractions (it only applies to the extruder motor)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1607 src/libslic3r/PrintConfig.cpp:1608 +#: src/libslic3r/PrintConfig.cpp:1612 src/libslic3r/PrintConfig.cpp:1613 msgid "Deretraction Speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1609 +#: src/libslic3r/PrintConfig.cpp:1614 msgid "" "The speed for loading of a filament into extruder after retraction (it only " "applies to the extruder motor). If left to zero, the retraction speed is " "used." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1616 +#: src/libslic3r/PrintConfig.cpp:1621 msgid "Seam position" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1618 +#: src/libslic3r/PrintConfig.cpp:1623 msgid "Position of perimeters starting points." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1624 +#: src/libslic3r/PrintConfig.cpp:1629 msgid "Random" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1625 +#: src/libslic3r/PrintConfig.cpp:1630 msgid "Nearest" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1626 +#: src/libslic3r/PrintConfig.cpp:1631 msgid "Aligned" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1634 +#: src/libslic3r/PrintConfig.cpp:1639 msgid "Direction" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1636 +#: src/libslic3r/PrintConfig.cpp:1641 msgid "Preferred direction of the seam" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1637 +#: src/libslic3r/PrintConfig.cpp:1642 msgid "Seam preferred direction" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1644 +#: src/libslic3r/PrintConfig.cpp:1649 msgid "Jitter" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1646 +#: src/libslic3r/PrintConfig.cpp:1651 msgid "Seam preferred direction jitter" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1647 +#: src/libslic3r/PrintConfig.cpp:1652 msgid "Preferred direction of the seam - jitter" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1657 +#: src/libslic3r/PrintConfig.cpp:1662 msgid "USB/serial port for printer connection." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1664 +#: src/libslic3r/PrintConfig.cpp:1669 msgid "Serial port speed" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1665 +#: src/libslic3r/PrintConfig.cpp:1670 msgid "Speed (baud) of USB/serial port for printer connection." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1674 +#: src/libslic3r/PrintConfig.cpp:1679 msgid "Distance from object" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1675 +#: src/libslic3r/PrintConfig.cpp:1680 msgid "" "Distance between skirt and object(s). Set this to zero to attach the skirt " "to the object(s) and get a brim for better adhesion." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1682 +#: src/libslic3r/PrintConfig.cpp:1687 msgid "Skirt height" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1683 +#: src/libslic3r/PrintConfig.cpp:1688 msgid "" "Height of skirt expressed in layers. Set this to a tall value to use skirt " "as a shield against drafts." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1690 +#: src/libslic3r/PrintConfig.cpp:1695 msgid "Loops (minimum)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1691 +#: src/libslic3r/PrintConfig.cpp:1696 msgid "Skirt Loops" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1692 +#: src/libslic3r/PrintConfig.cpp:1697 msgid "" "Number of loops for the skirt. If the Minimum Extrusion Length option is " "set, the number of loops might be greater than the one configured here. Set " "this to zero to disable skirt completely." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1700 +#: src/libslic3r/PrintConfig.cpp:1705 msgid "Slow down if layer print time is below" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1701 +#: src/libslic3r/PrintConfig.cpp:1706 msgid "" "If layer print time is estimated below this number of seconds, print moves " "speed will be scaled down to extend duration to this value." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1711 +#: src/libslic3r/PrintConfig.cpp:1715 msgid "Small perimeters" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1713 +#: src/libslic3r/PrintConfig.cpp:1717 msgid "" "This separate setting will affect the speed of perimeters having radius <= " "6.5mm (usually holes). If expressed as percentage (for example: 80%) it will " "be calculated on the perimeters speed setting above. Set to zero for auto." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1723 +#: src/libslic3r/PrintConfig.cpp:1727 msgid "Solid infill threshold area" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1725 +#: src/libslic3r/PrintConfig.cpp:1729 msgid "" "Force solid infill for regions having a smaller area than the specified " "threshold." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1726 +#: src/libslic3r/PrintConfig.cpp:1730 msgid "mm²" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1732 +#: src/libslic3r/PrintConfig.cpp:1736 msgid "Solid infill extruder" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1734 +#: src/libslic3r/PrintConfig.cpp:1738 msgid "The extruder to use when printing solid infill." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1740 +#: src/libslic3r/PrintConfig.cpp:1744 msgid "Solid infill every" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1742 +#: src/libslic3r/PrintConfig.cpp:1746 msgid "" "This feature allows to force a solid layer every given number of layers. " "Zero to disable. You can set this to any value (for example 9999); Slic3r " @@ -7786,7 +8266,7 @@ msgid "" "according to nozzle diameter and layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1754 +#: src/libslic3r/PrintConfig.cpp:1758 msgid "" "Set this to a non-zero value to set a manual extrusion width for infill for " "solid surfaces. If left zero, default extrusion width will be used if set, " @@ -7794,22 +8274,26 @@ msgid "" "(for example 90%) it will be computed over layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1765 +#: src/libslic3r/PrintConfig.cpp:1769 msgid "" "Speed for printing solid regions (top/bottom/internal horizontal shells). " "This can be expressed as a percentage (for example: 80%) over the default " "infill speed above. Set to zero for auto." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1777 +#: src/libslic3r/PrintConfig.cpp:1781 msgid "Number of solid layers to generate on top and bottom surfaces." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1783 +#: src/libslic3r/PrintConfig.cpp:1787 src/libslic3r/PrintConfig.cpp:1788 +msgid "Minimum thickness of a top / bottom shell" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:1794 msgid "Spiral vase" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1784 +#: src/libslic3r/PrintConfig.cpp:1795 msgid "" "This feature will raise Z gradually while printing a single-walled object in " "order to remove any visible seam. This option requires a single perimeter, " @@ -7818,18 +8302,18 @@ msgid "" "when printing more than an object." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1792 +#: src/libslic3r/PrintConfig.cpp:1803 msgid "Temperature variation" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1793 +#: src/libslic3r/PrintConfig.cpp:1804 msgid "" "Temperature difference to be applied when an extruder is not active. Enables " "a full-height \"sacrificial\" skirt on which the nozzles are periodically " "wiped." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1803 +#: src/libslic3r/PrintConfig.cpp:1814 msgid "" "This start procedure is inserted at the beginning, after bed has reached the " "target temperature and extruder just started heating, and before extruder " @@ -7840,7 +8324,7 @@ msgid "" "put a \"M109 S[first_layer_temperature]\" command wherever you want." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1818 +#: src/libslic3r/PrintConfig.cpp:1829 msgid "" "This start procedure is inserted at the beginning, after any printer start " "gcode (and after any toolchange to this filament in case of multi-material " @@ -7853,29 +8337,29 @@ msgid "" "extruders, the gcode is processed in extruder order." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1834 +#: src/libslic3r/PrintConfig.cpp:1845 msgid "Single Extruder Multi Material" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1835 +#: src/libslic3r/PrintConfig.cpp:1846 msgid "The printer multiplexes filaments into a single hot end." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1840 +#: src/libslic3r/PrintConfig.cpp:1851 msgid "Prime all printing extruders" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1841 +#: src/libslic3r/PrintConfig.cpp:1852 msgid "" "If enabled, all printing extruders will be primed at the front edge of the " "print bed at the start of the print." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1846 +#: src/libslic3r/PrintConfig.cpp:1857 msgid "No sparse layers (EXPERIMENTAL)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1847 +#: src/libslic3r/PrintConfig.cpp:1858 msgid "" "If enabled, the wipe tower will not be printed on layers with no " "toolchanges. On layers with a toolchange, extruder will travel downward to " @@ -7883,75 +8367,75 @@ msgid "" "with the print." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1854 +#: src/libslic3r/PrintConfig.cpp:1865 msgid "Generate support material" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1856 +#: src/libslic3r/PrintConfig.cpp:1867 msgid "Enable support material generation." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1860 +#: src/libslic3r/PrintConfig.cpp:1871 msgid "Auto generated supports" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1862 +#: src/libslic3r/PrintConfig.cpp:1873 msgid "" "If checked, supports will be generated automatically based on the overhang " "threshold value. If unchecked, supports will be generated inside the " "\"Support Enforcer\" volumes only." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1868 +#: src/libslic3r/PrintConfig.cpp:1879 msgid "XY separation between an object and its support" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1870 +#: src/libslic3r/PrintConfig.cpp:1881 msgid "" "XY separation between an object and its support. If expressed as percentage " "(for example 50%), it will be calculated over external perimeter width." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1880 +#: src/libslic3r/PrintConfig.cpp:1891 msgid "Pattern angle" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1882 +#: src/libslic3r/PrintConfig.cpp:1893 msgid "" "Use this setting to rotate the support material pattern on the horizontal " "plane." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1892 src/libslic3r/PrintConfig.cpp:2644 +#: src/libslic3r/PrintConfig.cpp:1903 src/libslic3r/PrintConfig.cpp:2667 msgid "" "Only create support if it lies on a build plate. Don't create support on a " "print." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1898 +#: src/libslic3r/PrintConfig.cpp:1909 msgid "Contact Z distance" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1900 +#: src/libslic3r/PrintConfig.cpp:1911 msgid "" "The vertical distance between object and support material interface. Setting " "this to 0 will also prevent Slic3r from using bridge flow and speed for the " "first object layer." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1907 +#: src/libslic3r/PrintConfig.cpp:1918 msgid "0 (soluble)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1908 +#: src/libslic3r/PrintConfig.cpp:1919 msgid "0.2 (detachable)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1913 +#: src/libslic3r/PrintConfig.cpp:1924 msgid "Enforce support for the first" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1915 +#: src/libslic3r/PrintConfig.cpp:1926 msgid "" "Generate support material for the specified number of layers counting from " "bottom, regardless of whether normal support material is enabled or not and " @@ -7959,21 +8443,21 @@ msgid "" "of objects having a very thin or poor footprint on the build plate." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1920 +#: src/libslic3r/PrintConfig.cpp:1931 msgid "Enforce support for the first n layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1926 +#: src/libslic3r/PrintConfig.cpp:1937 msgid "Support material/raft/skirt extruder" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1928 +#: src/libslic3r/PrintConfig.cpp:1939 msgid "" "The extruder to use when printing support material, raft and skirt (1+, 0 to " "use the current extruder to minimize tool changes)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1937 +#: src/libslic3r/PrintConfig.cpp:1948 msgid "" "Set this to a non-zero value to set a manual extrusion width for support " "material. If left zero, default extrusion width will be used if set, " @@ -7981,89 +8465,89 @@ msgid "" "example 90%) it will be computed over layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1946 +#: src/libslic3r/PrintConfig.cpp:1957 msgid "Interface loops" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1948 +#: src/libslic3r/PrintConfig.cpp:1959 msgid "" "Cover the top contact layer of the supports with loops. Disabled by default." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1953 +#: src/libslic3r/PrintConfig.cpp:1964 msgid "Support material/raft interface extruder" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1955 +#: src/libslic3r/PrintConfig.cpp:1966 msgid "" "The extruder to use when printing support material interface (1+, 0 to use " "the current extruder to minimize tool changes). This affects raft too." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1962 +#: src/libslic3r/PrintConfig.cpp:1973 msgid "Interface layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1964 +#: src/libslic3r/PrintConfig.cpp:1975 msgid "" "Number of interface layers to insert between the object(s) and support " "material." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1971 +#: src/libslic3r/PrintConfig.cpp:1982 msgid "Interface pattern spacing" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1973 +#: src/libslic3r/PrintConfig.cpp:1984 msgid "Spacing between interface lines. Set zero to get a solid interface." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1982 +#: src/libslic3r/PrintConfig.cpp:1993 msgid "" "Speed for printing support material interface layers. If expressed as " "percentage (for example 50%) it will be calculated over support material " "speed." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1991 +#: src/libslic3r/PrintConfig.cpp:2002 msgid "Pattern" msgstr "" -#: src/libslic3r/PrintConfig.cpp:1993 +#: src/libslic3r/PrintConfig.cpp:2004 msgid "Pattern used to generate support material." msgstr "" -#: src/libslic3r/PrintConfig.cpp:1999 +#: src/libslic3r/PrintConfig.cpp:2010 msgid "Rectilinear grid" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2005 +#: src/libslic3r/PrintConfig.cpp:2016 msgid "Pattern spacing" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2007 +#: src/libslic3r/PrintConfig.cpp:2018 msgid "Spacing between support material lines." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2016 +#: src/libslic3r/PrintConfig.cpp:2027 msgid "Speed for printing support material." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2023 +#: src/libslic3r/PrintConfig.cpp:2034 msgid "Synchronize with object layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2025 +#: src/libslic3r/PrintConfig.cpp:2036 msgid "" "Synchronize support layers with the object print layers. This is useful with " "multi-material printers, where the extruder switch is expensive." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2031 +#: src/libslic3r/PrintConfig.cpp:2042 msgid "Overhang threshold" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2033 +#: src/libslic3r/PrintConfig.cpp:2044 msgid "" "Support material will not be generated for overhangs whose slope angle (90° " "= vertical) is above the given threshold. In other words, this value " @@ -8072,43 +8556,43 @@ msgid "" "detection (recommended)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2045 +#: src/libslic3r/PrintConfig.cpp:2056 msgid "With sheath around the support" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2047 +#: src/libslic3r/PrintConfig.cpp:2058 msgid "" "Add a sheath (a single perimeter line) around the base support. This makes " "the support more reliable, but also more difficult to remove." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2054 +#: src/libslic3r/PrintConfig.cpp:2065 msgid "" "Extruder temperature for layers after the first one. Set this to zero to " "disable temperature control commands in the output." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2062 +#: src/libslic3r/PrintConfig.cpp:2073 msgid "Detect thin walls" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2064 +#: src/libslic3r/PrintConfig.cpp:2075 msgid "" "Detect single-width walls (parts where two extrusions don't fit and we need " "to collapse them into a single trace)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2070 +#: src/libslic3r/PrintConfig.cpp:2081 msgid "Threads" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2071 +#: src/libslic3r/PrintConfig.cpp:2082 msgid "" "Threads are used to parallelize long-running tasks. Optimal threads number " "is slightly above the number of available cores/processors." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2083 +#: src/libslic3r/PrintConfig.cpp:2094 msgid "" "This custom code is inserted before every toolchange. Placeholder variables " "for all PrusaSlicer settings as well as {previous_extruder} and " @@ -8118,7 +8602,7 @@ msgid "" "behaviour both before and after the toolchange." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2096 +#: src/libslic3r/PrintConfig.cpp:2107 msgid "" "Set this to a non-zero value to set a manual extrusion width for infill for " "top surfaces. You may want to use thinner extrudates to fill all narrow " @@ -8127,7 +8611,7 @@ msgid "" "percentage (for example 90%) it will be computed over layer height." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2108 +#: src/libslic3r/PrintConfig.cpp:2119 msgid "" "Speed for printing top solid layers (it only applies to the uppermost " "external layers and not to their internal solid layers). You may want to " @@ -8136,43 +8620,54 @@ msgid "" "for auto." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2123 +#: src/libslic3r/PrintConfig.cpp:2134 msgid "Number of solid layers to generate on top surfaces." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2124 +#: src/libslic3r/PrintConfig.cpp:2135 msgid "Top solid layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2130 +#: src/libslic3r/PrintConfig.cpp:2143 +msgid "" +"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." +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2146 +msgid "Minimum top shell thickness" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2153 msgid "Speed for travel moves (jumps between distant extrusion points)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2138 +#: src/libslic3r/PrintConfig.cpp:2161 msgid "Use firmware retraction" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2139 +#: src/libslic3r/PrintConfig.cpp:2162 msgid "" "This experimental setting uses G10 and G11 commands to have the firmware " "handle the retraction. This is only supported in recent Marlin." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2145 +#: src/libslic3r/PrintConfig.cpp:2168 msgid "Use relative E distances" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2146 +#: src/libslic3r/PrintConfig.cpp:2169 msgid "" "If your firmware requires relative E values, check this, otherwise leave it " "unchecked. Most firmwares use absolute values." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2152 +#: src/libslic3r/PrintConfig.cpp:2175 msgid "Use volumetric E" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2153 +#: src/libslic3r/PrintConfig.cpp:2176 msgid "" "This experimental setting uses outputs the E values in cubic millimeters " "instead of linear millimeters. If your firmware doesn't already know " @@ -8182,127 +8677,127 @@ msgid "" "only supported in recent Marlin." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2163 +#: src/libslic3r/PrintConfig.cpp:2186 msgid "Enable variable layer height feature" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2164 +#: src/libslic3r/PrintConfig.cpp:2187 msgid "" "Some printers or printer setups may have difficulties printing with a " "variable layer height. Enabled by default." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2170 +#: src/libslic3r/PrintConfig.cpp:2193 msgid "Wipe while retracting" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2171 +#: src/libslic3r/PrintConfig.cpp:2194 msgid "" "This flag will move the nozzle while retracting to minimize the possible " "blob on leaky extruders." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2178 +#: src/libslic3r/PrintConfig.cpp:2201 msgid "" "Multi material printers may need to prime or purge extruders on tool " "changes. Extrude the excess material into the wipe tower." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2184 +#: src/libslic3r/PrintConfig.cpp:2207 msgid "Purging volumes - load/unload volumes" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2185 +#: src/libslic3r/PrintConfig.cpp:2208 msgid "" "This vector saves required volumes to change from/to each tool used on the " "wipe tower. These values are used to simplify creation of the full purging " "volumes below." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2191 +#: src/libslic3r/PrintConfig.cpp:2214 msgid "Purging volumes - matrix" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2192 +#: src/libslic3r/PrintConfig.cpp:2215 msgid "" "This matrix describes volumes (in cubic milimetres) required to purge the " "new filament on the wipe tower for any given pair of tools." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2201 +#: src/libslic3r/PrintConfig.cpp:2224 msgid "Position X" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2202 +#: src/libslic3r/PrintConfig.cpp:2225 msgid "X coordinate of the left front corner of a wipe tower" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2208 +#: src/libslic3r/PrintConfig.cpp:2231 msgid "Position Y" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2209 +#: src/libslic3r/PrintConfig.cpp:2232 msgid "Y coordinate of the left front corner of a wipe tower" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2216 +#: src/libslic3r/PrintConfig.cpp:2239 msgid "Width of a wipe tower" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2222 +#: src/libslic3r/PrintConfig.cpp:2245 msgid "Wipe tower rotation angle" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2223 +#: src/libslic3r/PrintConfig.cpp:2246 msgid "Wipe tower rotation angle with respect to x-axis." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2230 +#: src/libslic3r/PrintConfig.cpp:2253 msgid "Wipe into this object's infill" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2231 +#: src/libslic3r/PrintConfig.cpp:2254 msgid "" "Purging after toolchange will done inside this object's infills. This lowers " "the amount of waste but may result in longer print time due to additional " "travel moves." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2238 +#: src/libslic3r/PrintConfig.cpp:2261 msgid "Wipe into this object" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2239 +#: src/libslic3r/PrintConfig.cpp:2262 msgid "" "Object will be used to purge the nozzle after a toolchange to save material " "that would otherwise end up in the wipe tower and decrease print time. " "Colours of the objects will be mixed as a result." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2245 +#: src/libslic3r/PrintConfig.cpp:2268 msgid "Maximal bridging distance" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2246 +#: src/libslic3r/PrintConfig.cpp:2269 msgid "Maximal distance between supports on sparse infill sections." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2252 +#: src/libslic3r/PrintConfig.cpp:2275 msgid "XY Size Compensation" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2254 +#: src/libslic3r/PrintConfig.cpp:2277 msgid "" "The object will be grown/shrunk in the XY plane by the configured value " "(negative = inwards, positive = outwards). This might be useful for fine-" "tuning hole sizes." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2262 +#: src/libslic3r/PrintConfig.cpp:2285 msgid "Z offset" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2263 +#: src/libslic3r/PrintConfig.cpp:2286 msgid "" "This value will be added (or subtracted) from all the Z coordinates in the " "output G-code. It is used to compensate for bad Z endstop position: for " @@ -8310,389 +8805,389 @@ msgid "" "print bed, set this to -0.3 (or fix your endstop)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2330 +#: src/libslic3r/PrintConfig.cpp:2353 msgid "Display width" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2331 +#: src/libslic3r/PrintConfig.cpp:2354 msgid "Width of the display" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2336 +#: src/libslic3r/PrintConfig.cpp:2359 msgid "Display height" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2337 +#: src/libslic3r/PrintConfig.cpp:2360 msgid "Height of the display" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2342 +#: src/libslic3r/PrintConfig.cpp:2365 msgid "Number of pixels in" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2344 +#: src/libslic3r/PrintConfig.cpp:2367 msgid "Number of pixels in X" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2350 +#: src/libslic3r/PrintConfig.cpp:2373 msgid "Number of pixels in Y" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2355 +#: src/libslic3r/PrintConfig.cpp:2378 msgid "Display horizontal mirroring" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2356 +#: src/libslic3r/PrintConfig.cpp:2379 msgid "Mirror horizontally" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2357 +#: src/libslic3r/PrintConfig.cpp:2380 msgid "Enable horizontal mirroring of output images" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2362 +#: src/libslic3r/PrintConfig.cpp:2385 msgid "Display vertical mirroring" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2363 +#: src/libslic3r/PrintConfig.cpp:2386 msgid "Mirror vertically" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2364 +#: src/libslic3r/PrintConfig.cpp:2387 msgid "Enable vertical mirroring of output images" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2369 +#: src/libslic3r/PrintConfig.cpp:2392 msgid "Display orientation" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2370 +#: src/libslic3r/PrintConfig.cpp:2393 msgid "" "Set the actual LCD display orientation inside the SLA printer. Portrait mode " "will flip the meaning of display width and height parameters and the output " "images will be rotated by 90 degrees." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2376 +#: src/libslic3r/PrintConfig.cpp:2399 msgid "Landscape" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2377 +#: src/libslic3r/PrintConfig.cpp:2400 msgid "Portrait" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2382 +#: src/libslic3r/PrintConfig.cpp:2405 msgid "Fast" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2383 +#: src/libslic3r/PrintConfig.cpp:2406 msgid "Fast tilt" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2384 +#: src/libslic3r/PrintConfig.cpp:2407 msgid "Time of the fast tilt" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2391 +#: src/libslic3r/PrintConfig.cpp:2414 msgid "Slow" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2392 +#: src/libslic3r/PrintConfig.cpp:2415 msgid "Slow tilt" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2393 +#: src/libslic3r/PrintConfig.cpp:2416 msgid "Time of the slow tilt" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2400 +#: src/libslic3r/PrintConfig.cpp:2423 msgid "Area fill" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2401 +#: src/libslic3r/PrintConfig.cpp:2424 msgid "" "The percentage of the bed area. \n" "If the print area exceeds the specified value, \n" "then a slow tilt will be used, otherwise - a fast tilt" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2408 src/libslic3r/PrintConfig.cpp:2409 -#: src/libslic3r/PrintConfig.cpp:2410 +#: src/libslic3r/PrintConfig.cpp:2431 src/libslic3r/PrintConfig.cpp:2432 +#: src/libslic3r/PrintConfig.cpp:2433 msgid "Printer scaling correction" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2416 src/libslic3r/PrintConfig.cpp:2417 +#: src/libslic3r/PrintConfig.cpp:2439 src/libslic3r/PrintConfig.cpp:2440 msgid "Printer absolute correction" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2418 +#: src/libslic3r/PrintConfig.cpp:2441 msgid "" "Will inflate or deflate the sliced 2D polygons according to the sign of the " "correction." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2424 src/libslic3r/PrintConfig.cpp:2425 +#: src/libslic3r/PrintConfig.cpp:2447 src/libslic3r/PrintConfig.cpp:2448 msgid "Printer gamma correction" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2426 +#: src/libslic3r/PrintConfig.cpp:2449 msgid "" "This will apply a gamma correction to the rasterized 2D polygons. A gamma " "value of zero means thresholding with the threshold in the middle. This " "behaviour eliminates antialiasing without losing holes in polygons." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2438 src/libslic3r/PrintConfig.cpp:2439 +#: src/libslic3r/PrintConfig.cpp:2461 src/libslic3r/PrintConfig.cpp:2462 msgid "SLA material type" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2450 src/libslic3r/PrintConfig.cpp:2451 +#: src/libslic3r/PrintConfig.cpp:2473 src/libslic3r/PrintConfig.cpp:2474 msgid "Initial layer height" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2457 src/libslic3r/PrintConfig.cpp:2458 +#: src/libslic3r/PrintConfig.cpp:2480 src/libslic3r/PrintConfig.cpp:2481 msgid "Bottle volume" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2459 +#: src/libslic3r/PrintConfig.cpp:2482 msgid "ml" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2464 src/libslic3r/PrintConfig.cpp:2465 +#: src/libslic3r/PrintConfig.cpp:2487 src/libslic3r/PrintConfig.cpp:2488 msgid "Bottle weight" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2466 +#: src/libslic3r/PrintConfig.cpp:2489 msgid "kg" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2473 +#: src/libslic3r/PrintConfig.cpp:2496 msgid "g/ml" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2480 +#: src/libslic3r/PrintConfig.cpp:2503 msgid "money/bottle" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2485 +#: src/libslic3r/PrintConfig.cpp:2508 msgid "Faded layers" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2486 +#: src/libslic3r/PrintConfig.cpp:2509 msgid "" "Number of the layers needed for the exposure time fade from initial exposure " "time to the exposure time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2493 src/libslic3r/PrintConfig.cpp:2494 +#: src/libslic3r/PrintConfig.cpp:2516 src/libslic3r/PrintConfig.cpp:2517 msgid "Minimum exposure time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2501 src/libslic3r/PrintConfig.cpp:2502 +#: src/libslic3r/PrintConfig.cpp:2524 src/libslic3r/PrintConfig.cpp:2525 msgid "Maximum exposure time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2509 src/libslic3r/PrintConfig.cpp:2510 +#: src/libslic3r/PrintConfig.cpp:2532 src/libslic3r/PrintConfig.cpp:2533 msgid "Exposure time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2516 src/libslic3r/PrintConfig.cpp:2517 +#: src/libslic3r/PrintConfig.cpp:2539 src/libslic3r/PrintConfig.cpp:2540 msgid "Minimum initial exposure time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2524 src/libslic3r/PrintConfig.cpp:2525 +#: src/libslic3r/PrintConfig.cpp:2547 src/libslic3r/PrintConfig.cpp:2548 msgid "Maximum initial exposure time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2532 src/libslic3r/PrintConfig.cpp:2533 +#: src/libslic3r/PrintConfig.cpp:2555 src/libslic3r/PrintConfig.cpp:2556 msgid "Initial exposure time" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2539 src/libslic3r/PrintConfig.cpp:2540 +#: src/libslic3r/PrintConfig.cpp:2562 src/libslic3r/PrintConfig.cpp:2563 msgid "Correction for expansion" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2546 +#: src/libslic3r/PrintConfig.cpp:2569 msgid "SLA print material notes" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2547 +#: src/libslic3r/PrintConfig.cpp:2570 msgid "You can put your notes regarding the SLA print material here." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2559 src/libslic3r/PrintConfig.cpp:2570 +#: src/libslic3r/PrintConfig.cpp:2582 src/libslic3r/PrintConfig.cpp:2593 msgid "Default SLA material profile" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2581 +#: src/libslic3r/PrintConfig.cpp:2604 msgid "Generate supports" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2583 +#: src/libslic3r/PrintConfig.cpp:2606 msgid "Generate supports for the models" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2588 +#: src/libslic3r/PrintConfig.cpp:2611 msgid "Support head front diameter" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2590 +#: src/libslic3r/PrintConfig.cpp:2613 msgid "Diameter of the pointing side of the head" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2597 +#: src/libslic3r/PrintConfig.cpp:2620 msgid "Support head penetration" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2599 +#: src/libslic3r/PrintConfig.cpp:2622 msgid "How much the pinhead has to penetrate the model surface" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2606 +#: src/libslic3r/PrintConfig.cpp:2629 msgid "Support head width" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2608 +#: src/libslic3r/PrintConfig.cpp:2631 msgid "Width from the back sphere center to the front sphere center" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2616 +#: src/libslic3r/PrintConfig.cpp:2639 msgid "Support pillar diameter" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2618 +#: src/libslic3r/PrintConfig.cpp:2641 msgid "Diameter in mm of the support pillars" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2626 +#: src/libslic3r/PrintConfig.cpp:2649 msgid "Support pillar connection mode" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2627 +#: src/libslic3r/PrintConfig.cpp:2650 msgid "" "Controls the bridge type between two neighboring pillars. Can be zig-zag, " "cross (double zig-zag) or dynamic which will automatically switch between " "the first two depending on the distance of the two pillars." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2635 +#: src/libslic3r/PrintConfig.cpp:2658 msgid "Zig-Zag" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2636 +#: src/libslic3r/PrintConfig.cpp:2659 msgid "Cross" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2637 +#: src/libslic3r/PrintConfig.cpp:2660 msgid "Dynamic" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2649 +#: src/libslic3r/PrintConfig.cpp:2672 msgid "Pillar widening factor" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2651 +#: src/libslic3r/PrintConfig.cpp:2674 msgid "" "Merging bridges or pillars into another pillars can increase the radius. " "Zero means no increase, one means full increase." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2660 +#: src/libslic3r/PrintConfig.cpp:2683 msgid "Support base diameter" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2662 +#: src/libslic3r/PrintConfig.cpp:2685 msgid "Diameter in mm of the pillar base" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2670 +#: src/libslic3r/PrintConfig.cpp:2693 msgid "Support base height" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2672 +#: src/libslic3r/PrintConfig.cpp:2695 msgid "The height of the pillar base cone" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2679 +#: src/libslic3r/PrintConfig.cpp:2702 msgid "Support base safety distance" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2682 +#: src/libslic3r/PrintConfig.cpp:2705 msgid "" "The minimum distance of the pillar base from the model in mm. Makes sense in " "zero elevation mode where a gap according to this parameter is inserted " "between the model and the pad." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2692 +#: src/libslic3r/PrintConfig.cpp:2715 msgid "Critical angle" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2694 +#: src/libslic3r/PrintConfig.cpp:2717 msgid "The default angle for connecting support sticks and junctions." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2702 +#: src/libslic3r/PrintConfig.cpp:2725 msgid "Max bridge length" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2704 +#: src/libslic3r/PrintConfig.cpp:2727 msgid "The max length of a bridge" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2711 +#: src/libslic3r/PrintConfig.cpp:2734 msgid "Max pillar linking distance" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2713 +#: src/libslic3r/PrintConfig.cpp:2736 msgid "" "The max distance of two pillars to get linked with each other. A zero value " "will prohibit pillar cascading." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2721 +#: src/libslic3r/PrintConfig.cpp:2744 msgid "Object elevation" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2723 +#: src/libslic3r/PrintConfig.cpp:2746 msgid "" "How much the supports should lift up the supported object. If \"Pad around " "object\" is enabled, this value is ignored." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2734 +#: src/libslic3r/PrintConfig.cpp:2757 msgid "This is a relative measure of support points density." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2740 +#: src/libslic3r/PrintConfig.cpp:2763 msgid "Minimal distance of the support points" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2742 +#: src/libslic3r/PrintConfig.cpp:2765 msgid "No support points will be placed closer than this threshold." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2748 +#: src/libslic3r/PrintConfig.cpp:2771 msgid "Use pad" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2750 +#: src/libslic3r/PrintConfig.cpp:2773 msgid "Add a pad underneath the supported model" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2755 +#: src/libslic3r/PrintConfig.cpp:2778 msgid "Pad wall thickness" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2757 +#: src/libslic3r/PrintConfig.cpp:2780 msgid "The thickness of the pad and its optional cavity walls." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2765 +#: src/libslic3r/PrintConfig.cpp:2788 msgid "Pad wall height" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2766 +#: src/libslic3r/PrintConfig.cpp:2789 msgid "" "Defines the pad cavity depth. Set to zero to disable the cavity. Be careful " "when enabling this feature, as some resins may produce an extreme suction " @@ -8700,373 +9195,404 @@ msgid "" "difficult." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2779 +#: src/libslic3r/PrintConfig.cpp:2802 msgid "Pad brim size" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2780 +#: src/libslic3r/PrintConfig.cpp:2803 msgid "How far should the pad extend around the contained geometry" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2790 +#: src/libslic3r/PrintConfig.cpp:2813 msgid "Max merge distance" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2792 +#: src/libslic3r/PrintConfig.cpp:2815 msgid "" "Some objects can get along with a few smaller pads instead of a single big " "one. This parameter defines how far the center of two smaller pads should " "be. If theyare closer, they will get merged into one pad." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2812 +#: src/libslic3r/PrintConfig.cpp:2835 msgid "Pad wall slope" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2814 +#: src/libslic3r/PrintConfig.cpp:2837 msgid "" "The slope of the pad wall relative to the bed plane. 90 degrees means " "straight walls." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2823 +#: src/libslic3r/PrintConfig.cpp:2846 msgid "Pad around object" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2825 +#: src/libslic3r/PrintConfig.cpp:2848 msgid "Create pad around object and ignore the support elevation" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2830 +#: src/libslic3r/PrintConfig.cpp:2853 msgid "Pad around object everywhere" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2832 +#: src/libslic3r/PrintConfig.cpp:2855 msgid "Force pad around object everywhere" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2837 +#: src/libslic3r/PrintConfig.cpp:2860 msgid "Pad object gap" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2839 +#: src/libslic3r/PrintConfig.cpp:2862 msgid "" "The gap between the object bottom and the generated pad in zero elevation " "mode." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2848 +#: src/libslic3r/PrintConfig.cpp:2871 msgid "Pad object connector stride" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2850 +#: src/libslic3r/PrintConfig.cpp:2873 msgid "" "Distance between two connector sticks which connect the object and the " "generated pad." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2857 +#: src/libslic3r/PrintConfig.cpp:2880 msgid "Pad object connector width" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2859 +#: src/libslic3r/PrintConfig.cpp:2882 msgid "" "Width of the connector sticks which connect the object and the generated pad." msgstr "" -#: src/libslic3r/PrintConfig.cpp:2866 +#: src/libslic3r/PrintConfig.cpp:2889 msgid "Pad object connector penetration" msgstr "" -#: src/libslic3r/PrintConfig.cpp:2869 +#: src/libslic3r/PrintConfig.cpp:2892 msgid "How much should the tiny connectors penetrate into the model body." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3247 +#: src/libslic3r/PrintConfig.cpp:2899 +msgid "Enable hollowing" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2901 +msgid "Hollow out a model to have an empty interior" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2906 +msgid "Hollowing thickness" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2908 +msgid "Minimum wall thickness of a hollowed model." +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2916 +msgid "Hollowing accuracy" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2918 +msgid "" +"Performance vs accuracy of calculation. Lower values may produce unwanted " +"artifacts." +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:2925 +msgid "Hollowing closing distance" +msgstr "" + +#: src/libslic3r/PrintConfig.cpp:3305 msgid "Export OBJ" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3248 +#: src/libslic3r/PrintConfig.cpp:3306 msgid "Export the model(s) as OBJ." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3259 +#: src/libslic3r/PrintConfig.cpp:3317 msgid "Export SLA" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3260 +#: src/libslic3r/PrintConfig.cpp:3318 msgid "Slice the model and export SLA printing layers as PNG." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3265 +#: src/libslic3r/PrintConfig.cpp:3323 msgid "Export 3MF" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3266 +#: src/libslic3r/PrintConfig.cpp:3324 msgid "Export the model(s) as 3MF." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3270 +#: src/libslic3r/PrintConfig.cpp:3328 msgid "Export AMF" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3271 +#: src/libslic3r/PrintConfig.cpp:3329 msgid "Export the model(s) as AMF." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3275 +#: src/libslic3r/PrintConfig.cpp:3333 msgid "Export STL" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3276 +#: src/libslic3r/PrintConfig.cpp:3334 msgid "Export the model(s) as STL." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3281 +#: src/libslic3r/PrintConfig.cpp:3339 msgid "Slice the model and export toolpaths as G-code." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3286 +#: src/libslic3r/PrintConfig.cpp:3344 msgid "Slice" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3287 +#: src/libslic3r/PrintConfig.cpp:3345 msgid "" "Slice the model as FFF or SLA based on the printer_technology configuration " "value." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3292 +#: src/libslic3r/PrintConfig.cpp:3350 msgid "Help" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3293 +#: src/libslic3r/PrintConfig.cpp:3351 msgid "Show this help." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3298 +#: src/libslic3r/PrintConfig.cpp:3356 msgid "Help (FFF options)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3299 +#: src/libslic3r/PrintConfig.cpp:3357 msgid "Show the full list of print/G-code configuration options." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3303 +#: src/libslic3r/PrintConfig.cpp:3361 msgid "Help (SLA options)" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3304 +#: src/libslic3r/PrintConfig.cpp:3362 msgid "Show the full list of SLA print configuration options." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3308 +#: src/libslic3r/PrintConfig.cpp:3366 msgid "Output Model Info" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3309 +#: src/libslic3r/PrintConfig.cpp:3367 msgid "Write information about the model to the console." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3313 +#: src/libslic3r/PrintConfig.cpp:3371 msgid "Save config file" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3314 +#: src/libslic3r/PrintConfig.cpp:3372 msgid "Save configuration to the specified file." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3324 +#: src/libslic3r/PrintConfig.cpp:3382 msgid "Align XY" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3325 +#: src/libslic3r/PrintConfig.cpp:3383 msgid "Align the model to the given point." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3330 +#: src/libslic3r/PrintConfig.cpp:3388 msgid "Cut model at the given Z." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3351 +#: src/libslic3r/PrintConfig.cpp:3409 msgid "Center" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3352 +#: src/libslic3r/PrintConfig.cpp:3410 msgid "Center the print around the given center." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3356 +#: src/libslic3r/PrintConfig.cpp:3414 msgid "Don't arrange" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3357 +#: src/libslic3r/PrintConfig.cpp:3415 msgid "" "Do not rearrange the given models before merging and keep their original XY " "coordinates." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3360 +#: src/libslic3r/PrintConfig.cpp:3418 msgid "Duplicate" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3361 +#: src/libslic3r/PrintConfig.cpp:3419 msgid "Multiply copies by this factor." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3365 +#: src/libslic3r/PrintConfig.cpp:3423 msgid "Duplicate by grid" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3366 +#: src/libslic3r/PrintConfig.cpp:3424 msgid "Multiply copies by creating a grid." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3369 +#: src/libslic3r/PrintConfig.cpp:3427 msgid "Merge" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3370 +#: src/libslic3r/PrintConfig.cpp:3428 msgid "" "Arrange the supplied models in a plate and merge them in a single model in " "order to perform actions once." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3375 +#: src/libslic3r/PrintConfig.cpp:3433 msgid "" "Try to repair any non-manifold meshes (this option is implicitly added " "whenever we need to slice the model to perform the requested action)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3379 +#: src/libslic3r/PrintConfig.cpp:3437 msgid "Rotation angle around the Z axis in degrees." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3383 +#: src/libslic3r/PrintConfig.cpp:3441 msgid "Rotate around X" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3384 +#: src/libslic3r/PrintConfig.cpp:3442 msgid "Rotation angle around the X axis in degrees." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3388 +#: src/libslic3r/PrintConfig.cpp:3446 msgid "Rotate around Y" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3389 +#: src/libslic3r/PrintConfig.cpp:3447 msgid "Rotation angle around the Y axis in degrees." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3394 +#: src/libslic3r/PrintConfig.cpp:3452 msgid "Scaling factor or percentage." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3399 +#: src/libslic3r/PrintConfig.cpp:3457 msgid "" "Detect unconnected parts in the given model(s) and split them into separate " "objects." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3402 +#: src/libslic3r/PrintConfig.cpp:3460 msgid "Scale to Fit" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3403 +#: src/libslic3r/PrintConfig.cpp:3461 msgid "Scale to fit the given volume." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3412 +#: src/libslic3r/PrintConfig.cpp:3470 msgid "Ignore non-existent config files" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3413 +#: src/libslic3r/PrintConfig.cpp:3471 msgid "Do not fail if a file supplied to --load does not exist." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3416 +#: src/libslic3r/PrintConfig.cpp:3474 msgid "Load config file" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3417 +#: src/libslic3r/PrintConfig.cpp:3475 msgid "" "Load configuration from the specified file. It can be used more than once to " "load options from multiple files." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3420 +#: src/libslic3r/PrintConfig.cpp:3478 msgid "Output File" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3421 +#: src/libslic3r/PrintConfig.cpp:3479 msgid "" "The file where the output will be written (if not specified, it will be " "based on the input file)." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3431 +#: src/libslic3r/PrintConfig.cpp:3489 msgid "Data directory" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3432 +#: src/libslic3r/PrintConfig.cpp:3490 msgid "" "Load and store settings at the given directory. This is useful for " "maintaining different profiles or including configurations from a network " "storage." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3435 +#: src/libslic3r/PrintConfig.cpp:3493 msgid "Logging level" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3436 +#: src/libslic3r/PrintConfig.cpp:3494 msgid "" -"Messages with severity lower or eqal to the loglevel will be printed out. 0:" -"trace, 1:debug, 2:info, 3:warning, 4:error, 5:fatal" +"Sets logging sensitivity. 0:fatal, 1:error, 2:warning, 3:info, 4:debug, 5:" +"trace\n" +"For example. loglevel=2 logs fatal, error and warning level messages." msgstr "" -#: src/libslic3r/PrintConfig.cpp:3441 +#: src/libslic3r/PrintConfig.cpp:3500 msgid "Render with a software renderer" msgstr "" -#: src/libslic3r/PrintConfig.cpp:3442 +#: src/libslic3r/PrintConfig.cpp:3501 msgid "" "Render with a software renderer. The bundled MESA software renderer is " "loaded instead of the default OpenGL driver." msgstr "" -#: src/libslic3r/PrintObject.cpp:106 +#: src/libslic3r/PrintObject.cpp:108 msgid "Processing triangulated mesh" msgstr "" -#: src/libslic3r/PrintObject.cpp:150 +#: src/libslic3r/PrintObject.cpp:152 msgid "Generating perimeters" msgstr "" -#: src/libslic3r/PrintObject.cpp:260 +#: src/libslic3r/PrintObject.cpp:255 msgid "Preparing infill" msgstr "" -#: src/libslic3r/PrintObject.cpp:400 +#: src/libslic3r/PrintObject.cpp:395 msgid "Generating support material" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:362 +#: src/libslic3r/GCode/PreviewData.cpp:347 msgid "Height (mm)" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:364 +#: src/libslic3r/GCode/PreviewData.cpp:349 msgid "Width (mm)" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:366 +#: src/libslic3r/GCode/PreviewData.cpp:351 msgid "Speed (mm/s)" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:368 +#: src/libslic3r/GCode/PreviewData.cpp:353 msgid "Fan Speed (%)" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:370 +#: src/libslic3r/GCode/PreviewData.cpp:355 msgid "Volumetric flow rate (mm³/s)" msgstr "" diff --git a/resources/localization/cs/PrusaSlicer.mo b/resources/localization/cs/PrusaSlicer.mo index 55963eefb7..f006a9fd38 100644 Binary files a/resources/localization/cs/PrusaSlicer.mo and b/resources/localization/cs/PrusaSlicer.mo differ diff --git a/resources/localization/de/PrusaSlicer.mo b/resources/localization/de/PrusaSlicer.mo index 300f0ab6b8..475772ca1a 100644 Binary files a/resources/localization/de/PrusaSlicer.mo and b/resources/localization/de/PrusaSlicer.mo differ diff --git a/resources/localization/ja/PrusaSlicer.mo b/resources/localization/ja/PrusaSlicer.mo index 50f89660dc..c770e0b226 100644 Binary files a/resources/localization/ja/PrusaSlicer.mo and b/resources/localization/ja/PrusaSlicer.mo differ diff --git a/resources/localization/ko/PrusaSlicer.mo b/resources/localization/ko/PrusaSlicer.mo index 5ba55934e2..867fdc1e93 100644 Binary files a/resources/localization/ko/PrusaSlicer.mo and b/resources/localization/ko/PrusaSlicer.mo differ diff --git a/resources/localization/ko/PrusaSlicer_ko_KR.po b/resources/localization/ko/PrusaSlicer_ko_KR.po index 7bb8a8c6a7..71bc4488a3 100644 --- a/resources/localization/ko/PrusaSlicer_ko_KR.po +++ b/resources/localization/ko/PrusaSlicer_ko_KR.po @@ -1,3 +1,8 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# msgid "" msgstr "" "Project-Id-Version: \n" @@ -16,42 +21,42 @@ msgstr "" "X-Crowdin-Language: ko\n" "X-Crowdin-File: ko_KR.po\n" -#: src/slic3r/GUI/AboutDialog.cpp:39 src/slic3r/GUI/AboutDialog.cpp:289 +#: src/slic3r/GUI/AboutDialog.cpp:39 src/slic3r/GUI/AboutDialog.cpp:291 msgid "Portions copyright" msgstr "다른 저작권" -#: src/slic3r/GUI/AboutDialog.cpp:125 src/slic3r/GUI/AboutDialog.cpp:254 +#: src/slic3r/GUI/AboutDialog.cpp:127 src/slic3r/GUI/AboutDialog.cpp:256 msgid "Copyright" msgstr "저작권" #. TRN "Slic3r _is licensed under the_ License" -#: src/slic3r/GUI/AboutDialog.cpp:127 +#: src/slic3r/GUI/AboutDialog.cpp:129 msgid "" "License agreements of all following programs (libraries) are part of " "application license agreement" msgstr "" "다음의 모든 프로그램 (라이브러리)의 라이센스 계약은 응용 프로그램 라이센스 계" -"약의 일부입니다." +"약의 일부입니다" -#: src/slic3r/GUI/AboutDialog.cpp:197 +#: src/slic3r/GUI/AboutDialog.cpp:199 #, c-format msgid "About %s" msgstr "%s에 대하여" -#: src/slic3r/GUI/AboutDialog.cpp:229 src/slic3r/GUI/MainFrame.cpp:60 +#: src/slic3r/GUI/AboutDialog.cpp:231 src/slic3r/GUI/MainFrame.cpp:64 msgid "Version" msgstr "버전" #. TRN "Slic3r _is licensed under the_ License" -#: src/slic3r/GUI/AboutDialog.cpp:256 +#: src/slic3r/GUI/AboutDialog.cpp:258 msgid "is licensed under the" msgstr "라이선스는" -#: src/slic3r/GUI/AboutDialog.cpp:257 +#: src/slic3r/GUI/AboutDialog.cpp:259 msgid "GNU Affero General Public License, version 3" msgstr "GNU Affero 일반 공중 사용 허가서, 버전 3" -#: src/slic3r/GUI/AboutDialog.cpp:258 +#: src/slic3r/GUI/AboutDialog.cpp:260 msgid "" "PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap " "community." @@ -59,7 +64,7 @@ msgstr "" "프루사슬라이서는 알레산드로 라넬루치와 RepRap 커뮤니티 Slic3r를 기반으로합니" "다." -#: src/slic3r/GUI/AboutDialog.cpp:259 +#: src/slic3r/GUI/AboutDialog.cpp:261 msgid "" "Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, " "Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and " @@ -67,184 +72,211 @@ msgid "" msgstr "" "Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, " "Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and " -"numerous others. 한국어 번역 울산에테르." +"numerous others. 한국어 번역 울산에테르, 밤송이직박구리" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:92 +#: src/slic3r/GUI/AppConfig.cpp:118 +msgid "" +"Error parsing PrusaSlicer config file, it is probably corrupted. Try to " +"manually delete the file to recover from the error. Your user profiles will " +"not be affected." +msgstr "" +"PrusaSlicer 구성 파일을 구문 분석하는 오류, 아마 손상된 것입니다. 파일을 수동" +"으로 삭제하여 오류에 복구해 보십시오. 사용자 프로필은 영향을 받지 않습니다." + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:118 msgid "" "Copying of the temporary G-code to the output G-code failed. Maybe the SD " "card is write locked?" msgstr "" -"임시 G-코드를 출력할 SD카드에 복사하는 데 실패했습니다. SD카드의 락을 확인 하" +"임시 G-code를 출력할 SD카드에 복사하는 데 실패했습니다. SD카드의 락을 확인 하" "시오." -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:93 -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:406 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:120 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:470 msgid "Running post-processing scripts" msgstr "포스트 프로세싱 스크립트" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:95 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:122 msgid "G-code file exported to %1%" msgstr "%1%로 내보낸 G 코드 파일" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:99 -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:117 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:126 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:176 msgid "Slicing complete" msgstr "슬라이스 완료" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:113 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:172 msgid "Masked SLA file exported to %1%" msgstr "마스크 된 SLA 파일을 %1%로 내보냅니" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:408 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:214 +#, c-format +msgid "" +"%s has encountered an error. It was likely caused by running out of memory. " +"If you are sure you have enough RAM on your system, this may also be a bug " +"and we would be glad if you reported it." +msgstr "" +"%s에서 오류가 발생했습니다. 메모리 부족으로 인해 발생했을 수 있습니다. 시스템" +"에 충분한 RAM이 있다고 확신하는 경우 버그가 될 수 있으며 보고해 주길 바랍니" +"다." + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:472 msgid "Copying of the temporary G-code to the output G-code failed" msgstr "임시 G 코드를 출력 G 코드에 복사 하지 못했습니다" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:417 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:497 msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" msgstr "" "`%1%`. 로 업로드를 예약 합니다. 창-> 인쇄 호스트 업로드 대기열을 참조 하십시" "오" -#: src/slic3r/GUI/BedShapeDialog.cpp:65 +#: src/slic3r/GUI/BedShapeDialog.cpp:66 src/slic3r/GUI/GUI_ObjectList.cpp:2038 msgid "Shape" msgstr "모양" -#: src/slic3r/GUI/BedShapeDialog.cpp:72 +#: src/slic3r/GUI/BedShapeDialog.cpp:73 msgid "Rectangular" msgstr "직사각형" -#: src/slic3r/GUI/BedShapeDialog.cpp:76 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:391 src/slic3r/GUI/Plater.cpp:145 -#: src/slic3r/GUI/Tab.cpp:2469 +#: src/slic3r/GUI/BedShapeDialog.cpp:77 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:232 src/slic3r/GUI/Plater.cpp:162 +#: src/slic3r/GUI/Tab.cpp:2306 msgid "Size" msgstr "사이즈" -#: src/slic3r/GUI/BedShapeDialog.cpp:77 +#: src/slic3r/GUI/BedShapeDialog.cpp:78 msgid "Size in X and Y of the rectangular plate." -msgstr "사격형 플레이트 X 및 Y 크기." - -#: src/slic3r/GUI/BedShapeDialog.cpp:83 -msgid "Origin" -msgstr "원본" +msgstr "사각 플레이트 X 및 Y 크기." #: src/slic3r/GUI/BedShapeDialog.cpp:84 +msgid "Origin" +msgstr "원점" + +#: src/slic3r/GUI/BedShapeDialog.cpp:85 msgid "" "Distance of the 0,0 G-code coordinate from the front left corner of the " "rectangle." -msgstr "사각형의 전면 왼쪽된 모서리에서 0, 0 G-코드 좌표 거리입니다." +msgstr "사각 전면 왼쪽 모서리에서 원저(0, 0) G-code 좌표 거리입니다." -#: src/slic3r/GUI/BedShapeDialog.cpp:88 +#: src/slic3r/GUI/BedShapeDialog.cpp:89 msgid "Circular" msgstr "원형" -#: src/slic3r/GUI/BedShapeDialog.cpp:91 src/slic3r/GUI/ConfigWizard.cpp:118 -#: src/slic3r/GUI/ConfigWizard.cpp:571 src/slic3r/GUI/ConfigWizard.cpp:585 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:388 -#: src/slic3r/GUI/WipeTowerDialog.cpp:84 src/slic3r/GUI/wxExtensions.cpp:486 -#: src/libslic3r/PrintConfig.cpp:70 src/libslic3r/PrintConfig.cpp:77 -#: src/libslic3r/PrintConfig.cpp:86 src/libslic3r/PrintConfig.cpp:220 -#: src/libslic3r/PrintConfig.cpp:295 src/libslic3r/PrintConfig.cpp:303 -#: src/libslic3r/PrintConfig.cpp:353 src/libslic3r/PrintConfig.cpp:363 -#: src/libslic3r/PrintConfig.cpp:488 src/libslic3r/PrintConfig.cpp:499 -#: src/libslic3r/PrintConfig.cpp:517 src/libslic3r/PrintConfig.cpp:695 -#: src/libslic3r/PrintConfig.cpp:1215 src/libslic3r/PrintConfig.cpp:1276 -#: src/libslic3r/PrintConfig.cpp:1294 src/libslic3r/PrintConfig.cpp:1312 -#: src/libslic3r/PrintConfig.cpp:1364 src/libslic3r/PrintConfig.cpp:1374 -#: src/libslic3r/PrintConfig.cpp:1495 src/libslic3r/PrintConfig.cpp:1503 -#: src/libslic3r/PrintConfig.cpp:1544 src/libslic3r/PrintConfig.cpp:1552 -#: src/libslic3r/PrintConfig.cpp:1562 src/libslic3r/PrintConfig.cpp:1570 -#: src/libslic3r/PrintConfig.cpp:1578 src/libslic3r/PrintConfig.cpp:1661 -#: src/libslic3r/PrintConfig.cpp:1878 src/libslic3r/PrintConfig.cpp:1948 -#: src/libslic3r/PrintConfig.cpp:1982 src/libslic3r/PrintConfig.cpp:2176 -#: src/libslic3r/PrintConfig.cpp:2183 src/libslic3r/PrintConfig.cpp:2190 -#: src/libslic3r/PrintConfig.cpp:2220 src/libslic3r/PrintConfig.cpp:2230 -#: src/libslic3r/PrintConfig.cpp:2240 src/libslic3r/PrintConfig.cpp:2403 -#: src/libslic3r/PrintConfig.cpp:2478 src/libslic3r/PrintConfig.cpp:2487 -#: src/libslic3r/PrintConfig.cpp:2496 src/libslic3r/PrintConfig.cpp:2506 -#: src/libslic3r/PrintConfig.cpp:2550 src/libslic3r/PrintConfig.cpp:2560 -#: src/libslic3r/PrintConfig.cpp:2572 src/libslic3r/PrintConfig.cpp:2592 -#: src/libslic3r/PrintConfig.cpp:2602 src/libslic3r/PrintConfig.cpp:2613 -#: src/libslic3r/PrintConfig.cpp:2631 src/libslic3r/PrintConfig.cpp:2646 -#: src/libslic3r/PrintConfig.cpp:2660 src/libslic3r/PrintConfig.cpp:2673 -#: src/libslic3r/PrintConfig.cpp:2683 src/libslic3r/PrintConfig.cpp:2704 -#: src/libslic3r/PrintConfig.cpp:2715 src/libslic3r/PrintConfig.cpp:2725 -#: src/libslic3r/PrintConfig.cpp:2735 +#: src/slic3r/GUI/BedShapeDialog.cpp:92 src/slic3r/GUI/ConfigWizard.cpp:218 +#: src/slic3r/GUI/ConfigWizard.cpp:971 src/slic3r/GUI/ConfigWizard.cpp:985 +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:87 +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:135 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:333 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:94 +#: src/slic3r/GUI/WipeTowerDialog.cpp:85 src/libslic3r/PrintConfig.cpp:75 +#: src/libslic3r/PrintConfig.cpp:82 src/libslic3r/PrintConfig.cpp:91 +#: src/libslic3r/PrintConfig.cpp:178 src/libslic3r/PrintConfig.cpp:236 +#: src/libslic3r/PrintConfig.cpp:311 src/libslic3r/PrintConfig.cpp:319 +#: src/libslic3r/PrintConfig.cpp:369 src/libslic3r/PrintConfig.cpp:379 +#: src/libslic3r/PrintConfig.cpp:505 src/libslic3r/PrintConfig.cpp:516 +#: src/libslic3r/PrintConfig.cpp:534 src/libslic3r/PrintConfig.cpp:713 +#: src/libslic3r/PrintConfig.cpp:1240 src/libslic3r/PrintConfig.cpp:1301 +#: src/libslic3r/PrintConfig.cpp:1319 src/libslic3r/PrintConfig.cpp:1337 +#: src/libslic3r/PrintConfig.cpp:1393 src/libslic3r/PrintConfig.cpp:1403 +#: src/libslic3r/PrintConfig.cpp:1525 src/libslic3r/PrintConfig.cpp:1533 +#: src/libslic3r/PrintConfig.cpp:1574 src/libslic3r/PrintConfig.cpp:1582 +#: src/libslic3r/PrintConfig.cpp:1592 src/libslic3r/PrintConfig.cpp:1600 +#: src/libslic3r/PrintConfig.cpp:1608 src/libslic3r/PrintConfig.cpp:1691 +#: src/libslic3r/PrintConfig.cpp:1924 src/libslic3r/PrintConfig.cpp:1995 +#: src/libslic3r/PrintConfig.cpp:2029 src/libslic3r/PrintConfig.cpp:2157 +#: src/libslic3r/PrintConfig.cpp:2236 src/libslic3r/PrintConfig.cpp:2243 +#: src/libslic3r/PrintConfig.cpp:2250 src/libslic3r/PrintConfig.cpp:2280 +#: src/libslic3r/PrintConfig.cpp:2290 src/libslic3r/PrintConfig.cpp:2300 +#: src/libslic3r/PrintConfig.cpp:2485 src/libslic3r/PrintConfig.cpp:2624 +#: src/libslic3r/PrintConfig.cpp:2633 src/libslic3r/PrintConfig.cpp:2642 +#: src/libslic3r/PrintConfig.cpp:2652 src/libslic3r/PrintConfig.cpp:2696 +#: src/libslic3r/PrintConfig.cpp:2706 src/libslic3r/PrintConfig.cpp:2718 +#: src/libslic3r/PrintConfig.cpp:2738 src/libslic3r/PrintConfig.cpp:2748 +#: src/libslic3r/PrintConfig.cpp:2758 src/libslic3r/PrintConfig.cpp:2776 +#: src/libslic3r/PrintConfig.cpp:2791 src/libslic3r/PrintConfig.cpp:2805 +#: src/libslic3r/PrintConfig.cpp:2816 src/libslic3r/PrintConfig.cpp:2829 +#: src/libslic3r/PrintConfig.cpp:2874 src/libslic3r/PrintConfig.cpp:2884 +#: src/libslic3r/PrintConfig.cpp:2893 src/libslic3r/PrintConfig.cpp:2903 +#: src/libslic3r/PrintConfig.cpp:2919 msgid "mm" msgstr "mm" -#: src/slic3r/GUI/BedShapeDialog.cpp:92 src/libslic3r/PrintConfig.cpp:692 +#: src/slic3r/GUI/BedShapeDialog.cpp:93 src/libslic3r/PrintConfig.cpp:710 msgid "Diameter" msgstr "노즐 직경" -#: src/slic3r/GUI/BedShapeDialog.cpp:93 +#: src/slic3r/GUI/BedShapeDialog.cpp:94 msgid "" "Diameter of the print bed. It is assumed that origin (0,0) is located in the " "center." -msgstr "인쇄 침대의 직경. 원점 (0,0) 은 중심에 있다고 가정합니다." +msgstr "인쇄 배드의 직경. 원점 (0,0) 은 중재봉선에 있다고 가정합니다." -#: src/slic3r/GUI/BedShapeDialog.cpp:97 src/slic3r/GUI/GUI_Preview.cpp:246 -#: src/libslic3r/GCode/PreviewData.cpp:175 +#: src/slic3r/GUI/BedShapeDialog.cpp:98 src/slic3r/GUI/GUI_Preview.cpp:251 +#: src/libslic3r/ExtrusionEntity.cpp:322 msgid "Custom" msgstr "사용자 정의" -#: src/slic3r/GUI/BedShapeDialog.cpp:101 +#: src/slic3r/GUI/BedShapeDialog.cpp:102 msgid "Load shape from STL..." msgstr "STL파일 로드." -#: src/slic3r/GUI/BedShapeDialog.cpp:154 +#: src/slic3r/GUI/BedShapeDialog.cpp:155 msgid "Settings" msgstr "설정" -#: src/slic3r/GUI/BedShapeDialog.cpp:171 +#: src/slic3r/GUI/BedShapeDialog.cpp:172 msgid "Texture" msgstr "질감" -#: src/slic3r/GUI/BedShapeDialog.cpp:181 src/slic3r/GUI/BedShapeDialog.cpp:249 +#: src/slic3r/GUI/BedShapeDialog.cpp:182 src/slic3r/GUI/BedShapeDialog.cpp:261 msgid "Load..." msgstr "불러오기..." -#: src/slic3r/GUI/BedShapeDialog.cpp:189 src/slic3r/GUI/BedShapeDialog.cpp:257 -#: src/slic3r/GUI/Tab.cpp:3204 +#: src/slic3r/GUI/BedShapeDialog.cpp:190 src/slic3r/GUI/BedShapeDialog.cpp:269 +#: src/slic3r/GUI/Tab.cpp:3094 msgid "Remove" msgstr "제거" -#: src/slic3r/GUI/BedShapeDialog.cpp:239 +#: src/slic3r/GUI/BedShapeDialog.cpp:223 src/slic3r/GUI/BedShapeDialog.cpp:302 +msgid "Not found: " +msgstr "찾을 수 없음:" + +#: src/slic3r/GUI/BedShapeDialog.cpp:251 msgid "Model" msgstr "모델" -#: src/slic3r/GUI/BedShapeDialog.cpp:464 +#: src/slic3r/GUI/BedShapeDialog.cpp:487 msgid "Choose an STL file to import bed shape from:" -msgstr "다음 에서 침대 모양을 가져올 STL 파일을 선택합니다." +msgstr "가져올 베드 모양을(STL 파일) 선택합니다:" -#: src/slic3r/GUI/BedShapeDialog.cpp:471 src/slic3r/GUI/BedShapeDialog.cpp:520 -#: src/slic3r/GUI/BedShapeDialog.cpp:543 +#: src/slic3r/GUI/BedShapeDialog.cpp:494 src/slic3r/GUI/BedShapeDialog.cpp:543 +#: src/slic3r/GUI/BedShapeDialog.cpp:566 msgid "Invalid file format." msgstr "잘못된 파일 형식." -#: src/slic3r/GUI/BedShapeDialog.cpp:482 +#: src/slic3r/GUI/BedShapeDialog.cpp:505 msgid "Error! Invalid model" msgstr "오류! 잘못된 모델" -#: src/slic3r/GUI/BedShapeDialog.cpp:490 +#: src/slic3r/GUI/BedShapeDialog.cpp:513 msgid "The selected file contains no geometry." -msgstr "선택한 파일에는 형상이 없는 포함 되어 있습니다." +msgstr "선택한 파일에 없는 형상이 있습니다." -#: src/slic3r/GUI/BedShapeDialog.cpp:494 +#: src/slic3r/GUI/BedShapeDialog.cpp:517 msgid "" "The selected file contains several disjoint areas. This is not supported." msgstr "" -"선택한 파일 여러 분리 된 영역을 포함 되어 있습니다. 이것ㅇ 지원 되지 않습니" -"다." - -#: src/slic3r/GUI/BedShapeDialog.cpp:509 -msgid "Choose a file to import bed texture from (PNG/SVG):" -msgstr "(PNG /SVG)에서 침대 텍스처를 가져올 파일을 선택합니다." +"선택한 파일은 여러개의 분리 된 영역을 포함 되어 있어 지원 되지 않습니다." #: src/slic3r/GUI/BedShapeDialog.cpp:532 -msgid "Choose an STL file to import bed model from:" -msgstr "다음에서 침대 모델을 가져올 STL 파일을 선택합니다." +msgid "Choose a file to import bed texture from (PNG/SVG):" +msgstr "(PNG /SVG)에서 배드 텍스처를 가져올 파일을 선택합니다." -#: src/slic3r/GUI/BedShapeDialog.hpp:59 src/slic3r/GUI/ConfigWizard.cpp:530 +#: src/slic3r/GUI/BedShapeDialog.cpp:555 +msgid "Choose an STL file to import bed model from:" +msgstr "다음에서 베드 모델을 가져올 STL 파일을 선택합니다:" + +#: src/slic3r/GUI/BedShapeDialog.hpp:59 src/slic3r/GUI/ConfigWizard.cpp:930 msgid "Bed Shape" msgstr "배드 모양" @@ -288,8 +320,150 @@ msgstr "이 값은 시스템 값과 같습니다" msgid "" "Value was changed and is not equal to the system value or the last saved " "preset" +msgstr "값이 변경 되었고, 시스템 값 또는 마지막으로 저장된 설정값과 다릅니다." + +#: src/slic3r/GUI/ConfigManipulation.cpp:48 +msgid "" +"Zero layer height is not valid.\n" +"\n" +"The layer height will be reset to 0.01." msgstr "" -"값이 변경 되었고 시스템 값 또는 마지막으로 저장 된 사전 설정과 같지 않음" +"바닥 레이어 높이가 잘못되었습니다.\n" +"\n" +"레이어 높이가 0.01로 재설정됩니다." + +#: src/slic3r/GUI/ConfigManipulation.cpp:49 +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 src/slic3r/GUI/Tab.cpp:1039 +#: src/libslic3r/PrintConfig.cpp:71 +msgid "Layer height" +msgstr "레이어 높이" + +#: src/slic3r/GUI/ConfigManipulation.cpp:60 +msgid "" +"Zero first layer height is not valid.\n" +"\n" +"The first layer height will be reset to 0.01." +msgstr "" +"첫 번째 레이어 높이가 0이면 유효하지 않습니다.\n" +"\n" +"첫 번째 레이어 높이는 0.01로 재설정됩니다." + +#: src/slic3r/GUI/ConfigManipulation.cpp:61 src/libslic3r/PrintConfig.cpp:890 +msgid "First layer height" +msgstr "첫 레이어 높이" + +#: src/slic3r/GUI/ConfigManipulation.cpp:75 +#, no-c-format +msgid "" +"The Spiral Vase mode requires:\n" +"- one perimeter\n" +"- no top solid layers\n" +"- 0% fill density\n" +"- no support material\n" +"- inactive Ensure vertical shell thickness" +msgstr "" +"나선형 꽃병 모드는 다음을 필요로 합니다.\n" +"- 하나의 둘레\n" +"- 상단 솔리드 레이어 없음\n" +"- 0% 충진 밀도\n" +"- 서포트가 필요 없음\n" +"- 비활성 수직 쉘 두께 보장" + +#: src/slic3r/GUI/ConfigManipulation.cpp:82 +msgid "Shall I adjust those settings in order to enable Spiral Vase?" +msgstr "나선형 꽃병을 활성화하기 위해 이러한 설정을 조정해야 합니까?" + +#: src/slic3r/GUI/ConfigManipulation.cpp:83 +msgid "Spiral Vase" +msgstr "스파이럴 바이스" + +#: src/slic3r/GUI/ConfigManipulation.cpp:107 +msgid "" +"The Wipe Tower currently supports the non-soluble supports only\n" +"if they are printed with the current extruder without triggering a tool " +"change.\n" +"(both support_material_extruder and support_material_interface_extruder need " +"to be set to 0)." +msgstr "" +"와이프 타워는 현재 비수용성 지원만 지원합니다.\n" +"공구 교환을 트리거하지 않고 현재 압출기로 인쇄된 경우\n" +"(support_material_extruder support_material_interface_extruder 모두 0으로 설" +"정해야 합니다)." + +#: src/slic3r/GUI/ConfigManipulation.cpp:111 +msgid "Shall I adjust those settings in order to enable the Wipe Tower?" +msgstr "와이프 타워를 활성화하기 위해 이러한 설정을 조정해야 합니까?" + +#: src/slic3r/GUI/ConfigManipulation.cpp:112 +#: src/slic3r/GUI/ConfigManipulation.cpp:132 +msgid "Wipe Tower" +msgstr "와이프 타워(Wipe Tower)" + +#: src/slic3r/GUI/ConfigManipulation.cpp:128 +msgid "" +"For the Wipe Tower to work with the soluble supports, the support layers\n" +"need to be synchronized with the object layers." +msgstr "" +"와이프 타워가 가용성 지지체와 함께 작동 하려면 서포트 레이어를 객체(object) " +"레이어와 동기화 해야 합니다." + +#: src/slic3r/GUI/ConfigManipulation.cpp:131 +msgid "Shall I synchronize support layers in order to enable the Wipe Tower?" +msgstr "와이프 타워를 활성화하기 위해 지원 레이어를 동기화해야 합니까?" + +#: src/slic3r/GUI/ConfigManipulation.cpp:151 +msgid "" +"Supports work better, if the following feature is enabled:\n" +"- Detect bridging perimeters" +msgstr "" +"다음 기능이 활성화된 경우 더 나은 작업을 지원합니다.\n" +"- 브리징 경계를 감지" + +#: src/slic3r/GUI/ConfigManipulation.cpp:154 +msgid "Shall I adjust those settings for supports?" +msgstr "지원에 대한 설정을 조정해야 합니까?" + +#: src/slic3r/GUI/ConfigManipulation.cpp:155 +msgid "Support Generator" +msgstr "서포트 생성" + +#: src/slic3r/GUI/ConfigManipulation.cpp:200 +msgid "The %1% infill pattern is not supposed to work at 100%% density." +msgstr "%1% 채우기 패턴은 100% 밀도로 작동하도록 되어 있지 않습니다." + +#: src/slic3r/GUI/ConfigManipulation.cpp:202 +msgid "Shall I switch to rectilinear fill pattern?" +msgstr "직선 채우기 패턴으로 전환해야 합니까?" + +#: src/slic3r/GUI/ConfigManipulation.cpp:203 +#: src/slic3r/GUI/GUI_ObjectList.cpp:35 src/slic3r/GUI/GUI_ObjectList.cpp:94 +#: src/slic3r/GUI/GUI_ObjectList.cpp:612 src/slic3r/GUI/Plater.cpp:524 +#: src/slic3r/GUI/Tab.cpp:1081 src/slic3r/GUI/Tab.cpp:1082 +#: src/libslic3r/PrintConfig.cpp:193 src/libslic3r/PrintConfig.cpp:416 +#: src/libslic3r/PrintConfig.cpp:436 src/libslic3r/PrintConfig.cpp:777 +#: src/libslic3r/PrintConfig.cpp:791 src/libslic3r/PrintConfig.cpp:828 +#: src/libslic3r/PrintConfig.cpp:982 src/libslic3r/PrintConfig.cpp:992 +#: src/libslic3r/PrintConfig.cpp:1010 src/libslic3r/PrintConfig.cpp:1029 +#: src/libslic3r/PrintConfig.cpp:1048 src/libslic3r/PrintConfig.cpp:1738 +#: src/libslic3r/PrintConfig.cpp:1755 +msgid "Infill" +msgstr "인필(채움)" + +#: src/slic3r/GUI/ConfigManipulation.cpp:309 +msgid "Head penetration should not be greater than the head width." +msgstr "헤드 관통은 헤드 폭 보다 크지 않아야 합니다." + +#: src/slic3r/GUI/ConfigManipulation.cpp:311 +msgid "Invalid Head penetration" +msgstr "잘못된 헤드 관통" + +#: src/slic3r/GUI/ConfigManipulation.cpp:322 +msgid "Pinhead diameter should be smaller than the pillar diameter." +msgstr "핀헤드 지름은 기둥 지름 보다 작아야 합니다." + +#: src/slic3r/GUI/ConfigManipulation.cpp:324 +msgid "Invalid pinhead diameter" +msgstr "잘못된 핀 헤드 지름" #: src/slic3r/GUI/ConfigSnapshotDialog.cpp:18 msgid "Upgrade" @@ -311,152 +485,165 @@ msgstr "사용자" msgid "Unknown" msgstr "알 수 없음" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:39 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:43 msgid "Active" msgstr "활동중" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:45 -msgid "slic3r version" -msgstr "slic3r 버전" +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:50 +msgid "PrusaSlicer version" +msgstr "Prusa슬라이서 버전" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:46 src/slic3r/GUI/Preset.cpp:1307 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:51 src/slic3r/GUI/Preset.cpp:1452 msgid "print" -msgstr "프린트" +msgstr "출력" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:47 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:52 msgid "filaments" msgstr "필라멘트" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:48 src/slic3r/GUI/Preset.cpp:1311 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:53 src/slic3r/GUI/Preset.cpp:1456 msgid "printer" msgstr "프린터" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:52 src/slic3r/GUI/Tab.cpp:939 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:57 src/slic3r/GUI/Tab.cpp:961 msgid "vendor" msgstr "제조 회사" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:52 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:57 msgid "version" msgstr "버전" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:53 -msgid "min slic3r version" -msgstr "최소 slic3r 버전" - -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:55 -msgid "max slic3r version" -msgstr "최대 slic3r 버전" - #: src/slic3r/GUI/ConfigSnapshotDialog.cpp:58 +msgid "min PrusaSlicer version" +msgstr "이전 slic3r 버전" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:60 +msgid "max PrusaSlicer version" +msgstr "최신 slic3r 버전" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:63 msgid "model" msgstr "모델" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:58 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:63 msgid "variants" msgstr "변종" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:70 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:75 #, c-format msgid "Incompatible with this %s" msgstr "%s 과 호환되지 않습니다" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:73 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:78 msgid "Activate" msgstr "활성화" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:99 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:104 msgid "Configuration Snapshots" msgstr "구성 스냅숏" -#: src/slic3r/GUI/ConfigWizard.cpp:118 +#: src/slic3r/GUI/ConfigWizard.cpp:218 msgid "nozzle" msgstr "노즐" -#: src/slic3r/GUI/ConfigWizard.cpp:122 +#: src/slic3r/GUI/ConfigWizard.cpp:222 msgid "Alternate nozzles:" msgstr "대체 노즐:" -#: src/slic3r/GUI/ConfigWizard.cpp:188 +#: src/slic3r/GUI/ConfigWizard.cpp:289 msgid "All standard" msgstr "모두 표준설정" -#: src/slic3r/GUI/ConfigWizard.cpp:189 src/slic3r/GUI/Tab.cpp:3254 +#: src/slic3r/GUI/ConfigWizard.cpp:289 +msgid "Standard" +msgstr "표준" + +#: src/slic3r/GUI/ConfigWizard.cpp:290 src/slic3r/GUI/ConfigWizard.cpp:573 +#: src/slic3r/GUI/Tab.cpp:3144 msgid "All" msgstr "모두 선택" -#: src/slic3r/GUI/ConfigWizard.cpp:190 src/slic3r/GUI/Plater.cpp:470 -#: src/libslic3r/GCode/PreviewData.cpp:162 +#: src/slic3r/GUI/ConfigWizard.cpp:291 src/slic3r/GUI/ConfigWizard.cpp:574 +#: src/slic3r/GUI/Plater.cpp:496 src/slic3r/GUI/Plater.cpp:636 +#: src/libslic3r/ExtrusionEntity.cpp:309 msgid "None" msgstr "없음" -#: src/slic3r/GUI/ConfigWizard.cpp:296 +#: src/slic3r/GUI/ConfigWizard.cpp:427 #, c-format msgid "Welcome to the %s Configuration Assistant" msgstr "%s 구성 도우미에 오신 것을 환영 합니다" -#: src/slic3r/GUI/ConfigWizard.cpp:298 +#: src/slic3r/GUI/ConfigWizard.cpp:429 #, c-format msgid "Welcome to the %s Configuration Wizard" -msgstr "%s에 오신것을 환영 합니다." +msgstr "%s 구성 마법사에 오신 것을 환영 합니다" -#: src/slic3r/GUI/ConfigWizard.cpp:300 +#: src/slic3r/GUI/ConfigWizard.cpp:431 msgid "Welcome" msgstr "환영합니다" -#: src/slic3r/GUI/ConfigWizard.cpp:304 src/slic3r/GUI/GUI_App.cpp:747 -#, c-format -msgid "Run %s" -msgstr "%s 실행" - -#: src/slic3r/GUI/ConfigWizard.cpp:306 +#: src/slic3r/GUI/ConfigWizard.cpp:433 #, c-format msgid "" "Hello, welcome to %s! This %s helps you with the initial configuration; just " "a few settings and you will be ready to print." msgstr "" -"안녕하세요 ,%s에 오신 것을 환영 합니다! 이 %s는 초기 구성에 도움이 됩니다. 그" -"냥 몇 가지 설정 하 고 당신은 인쇄 할 준비가 될 것입니다." +"안녕하세요 ,%s에 오신 것을 환영 합니다! 이 %s는 초기 구성에 도움이 됩니다. " +"몇 가지 설정만으로 인쇄 준비가 될 것입니다." -#: src/slic3r/GUI/ConfigWizard.cpp:311 +#: src/slic3r/GUI/ConfigWizard.cpp:438 msgid "" "Remove user profiles - install from scratch (a snapshot will be taken " "beforehand)" -msgstr "사용자 프로필 제거-처음부터 설치 (스냅숏은 사전에 수행 됩니다)" +msgstr "사용자 프로필 제거 - 처음부터 설치 (스냅숏 값은 먼저 저장 됩니다.)" -#: src/slic3r/GUI/ConfigWizard.cpp:342 +#: src/slic3r/GUI/ConfigWizard.cpp:481 #, c-format msgid "%s Family" msgstr "%s의 가족들" -#: src/slic3r/GUI/ConfigWizard.cpp:379 +#: src/slic3r/GUI/ConfigWizard.cpp:565 +msgid "Vendor:" +msgstr "벤더:" + +#: src/slic3r/GUI/ConfigWizard.cpp:566 +msgid "Profile:" +msgstr "프로필:" + +#: src/slic3r/GUI/ConfigWizard.cpp:603 src/slic3r/GUI/ConfigWizard.cpp:631 +msgid "(All)" +msgstr "(모두)" + +#: src/slic3r/GUI/ConfigWizard.cpp:732 msgid "Custom Printer Setup" msgstr "사용자 지정 프린터 설정" -#: src/slic3r/GUI/ConfigWizard.cpp:379 +#: src/slic3r/GUI/ConfigWizard.cpp:732 msgid "Custom Printer" msgstr "사용자 정의 프린터" -#: src/slic3r/GUI/ConfigWizard.cpp:381 +#: src/slic3r/GUI/ConfigWizard.cpp:734 msgid "Define a custom printer profile" -msgstr "사용자 정의 프린터 프로필 정의" +msgstr "사용자 정의 프린터 프로필" -#: src/slic3r/GUI/ConfigWizard.cpp:383 +#: src/slic3r/GUI/ConfigWizard.cpp:736 msgid "Custom profile name:" -msgstr "사용자 지정 프로필 이름:" +msgstr "사용자 정의 프로필 명칭:" -#: src/slic3r/GUI/ConfigWizard.cpp:407 +#: src/slic3r/GUI/ConfigWizard.cpp:760 msgid "Automatic updates" msgstr "자동 업데이트" -#: src/slic3r/GUI/ConfigWizard.cpp:407 +#: src/slic3r/GUI/ConfigWizard.cpp:760 msgid "Updates" msgstr "업데이트" -#: src/slic3r/GUI/ConfigWizard.cpp:415 src/slic3r/GUI/Preferences.cpp:69 +#: src/slic3r/GUI/ConfigWizard.cpp:768 src/slic3r/GUI/Preferences.cpp:64 msgid "Check for application updates" msgstr "프로그램 업데이트 확인" -#: src/slic3r/GUI/ConfigWizard.cpp:419 +#: src/slic3r/GUI/ConfigWizard.cpp:772 #, c-format msgid "" "If enabled, %s checks for new application versions online. When a new " @@ -464,15 +651,15 @@ msgid "" "application startup (never during program usage). This is only a " "notification mechanisms, no automatic installation is done." msgstr "" -"활성화 된 경우 %s은 온라인의 새 버전을 확인합니다. 새 버전을 사용할 수있게되" -"면 다음 응용 프로그램 시작시 알림이 표시됩니다 (프로그램 사용 중에는 절대로 " -"사용하지 마십시오).이것은 알림 메커니즘 일뿐 자동 설치가 수행되지 않습니다." +"활성화 된 경우 %s은 온라인의 새 버전을 확인합니다. 새 버전을 사용할 수 있게 " +"되면, 다음 응용 프로그램 시작시 알림이 표시됩니다 (프로그램 사용 중에는 절대" +"로 사용하지 마십시오).이것은 단순한 알림 일뿐 자동으로 설치가 되지 않습니다." -#: src/slic3r/GUI/ConfigWizard.cpp:425 src/slic3r/GUI/Preferences.cpp:77 +#: src/slic3r/GUI/ConfigWizard.cpp:778 src/slic3r/GUI/Preferences.cpp:82 msgid "Update built-in Presets automatically" msgstr "기존의 설정 자동 업데이트" -#: src/slic3r/GUI/ConfigWizard.cpp:429 +#: src/slic3r/GUI/ConfigWizard.cpp:782 #, c-format msgid "" "If enabled, %s downloads updates of built-in system presets in the " @@ -480,74 +667,131 @@ msgid "" "When a new preset version becomes available it is offered at application " "startup." msgstr "" -"활성화 된 경우 %s은 백그라운드에서 내장 시스템 사전 설정의 업데이트를 다운로" -"드합니다. 이러한 업데이트는 별도의 임시 위치에 다운로드됩니다. 새로운 사전 설" -"정 버전을 사용할 수있게되면 응용 프로그램 시작시 제공됩니다." +"활성화 된 경우 %s은 백그라운드에서, 시스템 사전 설정을 다운로드합니다. 이러" +"한 업데이트는 별도의 임시 위치에 다운로드됩니다. 새로운 사전 설정 버전을 사용" +"할 수 있게되면 응용 프로그램 시작시 제공됩니다." -#: src/slic3r/GUI/ConfigWizard.cpp:432 +#: src/slic3r/GUI/ConfigWizard.cpp:785 msgid "" "Updates are never applied without user's consent and never overwrite user's " "customized settings." msgstr "" -"업데이트는 사용자의 동의없이 적용되지 않으며 사용자의 사용자 지정된 설정을 덮" -"어 쓰지 않습니다." +"업데이트는 사용자의 동의를 받아야 하고, 지정된 이전 설정을 덮어 쓰지 않습니" +"다." -#: src/slic3r/GUI/ConfigWizard.cpp:437 +#: src/slic3r/GUI/ConfigWizard.cpp:790 msgid "" "Additionally a backup snapshot of the whole configuration is created before " "an update is applied." -msgstr "또한 업데이트가 적용되기 전에 전체 구성의 백업 스냅 샷이 생성됩니다." +msgstr "" +"또한 업데이트가 적용되기 전에 전체 구성의 백업 구성(스냅샷)이 생성됩니다." -#: src/slic3r/GUI/ConfigWizard.cpp:444 +#: src/slic3r/GUI/ConfigWizard.cpp:798 src/slic3r/GUI/GUI_ObjectList.cpp:1655 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1658 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3932 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3935 src/slic3r/GUI/Plater.cpp:3211 +#: src/slic3r/GUI/Plater.cpp:3946 src/slic3r/GUI/Plater.cpp:3949 +#: src/slic3r/GUI/Plater.cpp:3980 src/slic3r/GUI/Plater.cpp:3983 +msgid "Reload from disk" +msgstr "디스크에서 다시 불러오기" + +#: src/slic3r/GUI/ConfigWizard.cpp:801 +msgid "" +"Export full pathnames of models and parts sources into 3mf and amf files" +msgstr "모델 및 부품 소스의 전체 경로 이름을 3mf 및 amf 파일로 내보내기" + +#: src/slic3r/GUI/ConfigWizard.cpp:805 +msgid "" +"If enabled, allows the Reload from disk command to automatically find and " +"load the files when invoked.\n" +"If not enabled, the Reload from disk command will ask to select each file " +"using an open file dialog." +msgstr "" +"활성화된 경우 디스크에서 다시 로드 명령을 사용하여 호출될 때 파일을 자동으로 " +"찾고 로드할 수 있습니다.\n" +"활성화되지 않으면 디스크에서 다시 로드 명령에서 열린 파일 대화 상자를 사용하" +"여 각 파일을 선택하라는 요청이 표시됩니다." + +#: src/slic3r/GUI/ConfigWizard.cpp:814 +msgid "View mode" +msgstr "방법 보기" + +#: src/slic3r/GUI/ConfigWizard.cpp:816 +msgid "" +"PrusaSlicer's user interfaces comes in three variants:\n" +"Simple, Advanced, and Expert.\n" +"The Simple mode shows only the most frequently used settings relevant for " +"regular 3D printing. The other two offer progressively more sophisticated " +"fine-tuning, they are suitable for advanced and expert users, respectively." +msgstr "" +"PrusaSlicer의 사용자 인터페이스는 세 가지 변형으로 제공됩니다.\n" +"간단하고 고급, 전문가.\n" +"단순 모드는 일반 3D 인쇄와 관련된 가장 자주 사용되는 설정만 표시합니다. 다른 " +"두 가지는 점진적으로 더 정교한 미세 조정을 제공하며, 각각 고급 및 전문가 사용" +"자에게 적합합니다." + +#: src/slic3r/GUI/ConfigWizard.cpp:821 +msgid "Simple mode" +msgstr "단순 모드" + +#: src/slic3r/GUI/ConfigWizard.cpp:822 +msgid "Advanced mode" +msgstr "고급 모드" + +#: src/slic3r/GUI/ConfigWizard.cpp:823 +msgid "Expert mode" +msgstr "전문가 모드" + +#: src/slic3r/GUI/ConfigWizard.cpp:857 msgid "Other Vendors" msgstr "다른 공급 업체" -#: src/slic3r/GUI/ConfigWizard.cpp:446 +#: src/slic3r/GUI/ConfigWizard.cpp:861 #, c-format -msgid "Pick another vendor supported by %s:" +msgid "Pick another vendor supported by %s" msgstr "%s가 지원하는 다른 공급 업체를 선택하십시오:" -#: src/slic3r/GUI/ConfigWizard.cpp:492 +#: src/slic3r/GUI/ConfigWizard.cpp:892 msgid "Firmware Type" -msgstr "펌웨어 타입" +msgstr "펌웨어 종류" -#: src/slic3r/GUI/ConfigWizard.cpp:492 src/slic3r/GUI/Tab.cpp:2100 +#: src/slic3r/GUI/ConfigWizard.cpp:892 src/slic3r/GUI/Tab.cpp:1931 msgid "Firmware" msgstr "펌웨어" -#: src/slic3r/GUI/ConfigWizard.cpp:496 +#: src/slic3r/GUI/ConfigWizard.cpp:896 msgid "Choose the type of firmware used by your printer." -msgstr "프린터에 패치할 펌웨어를 선택하세요." +msgstr "프린터에 업로드 할 펌웨어를 선택하세요." -#: src/slic3r/GUI/ConfigWizard.cpp:530 +#: src/slic3r/GUI/ConfigWizard.cpp:930 msgid "Bed Shape and Size" msgstr "배드 모양과 크기" -#: src/slic3r/GUI/ConfigWizard.cpp:533 +#: src/slic3r/GUI/ConfigWizard.cpp:933 msgid "Set the shape of your printer's bed." msgstr "프린터 배드모양을 설정하세요." -#: src/slic3r/GUI/ConfigWizard.cpp:553 +#: src/slic3r/GUI/ConfigWizard.cpp:953 msgid "Filament and Nozzle Diameters" msgstr "필라멘트와 노즐 크기" -#: src/slic3r/GUI/ConfigWizard.cpp:553 +#: src/slic3r/GUI/ConfigWizard.cpp:953 msgid "Print Diameters" msgstr "인쇄 직경" -#: src/slic3r/GUI/ConfigWizard.cpp:567 +#: src/slic3r/GUI/ConfigWizard.cpp:967 msgid "Enter the diameter of your printer's hot end nozzle." msgstr "핫 엔드 노즐 직경을 입력하십시오." -#: src/slic3r/GUI/ConfigWizard.cpp:570 +#: src/slic3r/GUI/ConfigWizard.cpp:970 msgid "Nozzle Diameter:" msgstr "노즐 직경:" -#: src/slic3r/GUI/ConfigWizard.cpp:580 +#: src/slic3r/GUI/ConfigWizard.cpp:980 msgid "Enter the diameter of your filament." msgstr "필라멘트의 직경을 입력하십시오." -#: src/slic3r/GUI/ConfigWizard.cpp:581 +#: src/slic3r/GUI/ConfigWizard.cpp:981 msgid "" "Good precision is required, so use a caliper and do multiple measurements " "along the filament, then compute the average." @@ -555,41 +799,41 @@ msgstr "" "정밀도가 필요하므로 캘리퍼를 사용하여 필라멘트를 따라 여러 번 측정 한 다음 평" "균을 계산하십시오." -#: src/slic3r/GUI/ConfigWizard.cpp:584 +#: src/slic3r/GUI/ConfigWizard.cpp:984 msgid "Filament Diameter:" msgstr "필라멘트 직경:" -#: src/slic3r/GUI/ConfigWizard.cpp:618 +#: src/slic3r/GUI/ConfigWizard.cpp:1018 msgid "Extruder and Bed Temperatures" msgstr "익스트루더와 배드 온도" -#: src/slic3r/GUI/ConfigWizard.cpp:618 +#: src/slic3r/GUI/ConfigWizard.cpp:1018 msgid "Temperatures" msgstr "온도" -#: src/slic3r/GUI/ConfigWizard.cpp:634 +#: src/slic3r/GUI/ConfigWizard.cpp:1034 msgid "Enter the temperature needed for extruding your filament." msgstr "필라멘트 압출에 필요한 온도를 입력하십시오." -#: src/slic3r/GUI/ConfigWizard.cpp:635 +#: src/slic3r/GUI/ConfigWizard.cpp:1035 msgid "A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS." msgstr "보통 PLA의 경우 160 ~ 230 ° C, ABS의 경우 215 ~ 250 ° C입니다." -#: src/slic3r/GUI/ConfigWizard.cpp:638 +#: src/slic3r/GUI/ConfigWizard.cpp:1038 msgid "Extrusion Temperature:" msgstr "출력 온도 :" -#: src/slic3r/GUI/ConfigWizard.cpp:639 src/slic3r/GUI/ConfigWizard.cpp:653 +#: src/slic3r/GUI/ConfigWizard.cpp:1039 src/slic3r/GUI/ConfigWizard.cpp:1053 msgid "°C" msgstr "°C" -#: src/slic3r/GUI/ConfigWizard.cpp:648 +#: src/slic3r/GUI/ConfigWizard.cpp:1048 msgid "" "Enter the bed temperature needed for getting your filament to stick to your " "heated bed." -msgstr "필라멘트가 핫배드에 접착하는데 필요한 배드온도를 입력하십시오." +msgstr "필라멘트가 핫배드에 접착하는데 필요한 온도를 입력하십시오." -#: src/slic3r/GUI/ConfigWizard.cpp:649 +#: src/slic3r/GUI/ConfigWizard.cpp:1049 msgid "" "A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have " "no heated bed." @@ -597,81 +841,451 @@ msgstr "" "보통은 PLA의 경우 60 ° C이고 ABS의 경우 110 ° C입니다. 핫배드가 없는 경우에" "는 0으로 두십시오." -#: src/slic3r/GUI/ConfigWizard.cpp:652 +#: src/slic3r/GUI/ConfigWizard.cpp:1052 msgid "Bed Temperature:" msgstr "배드 온도 :" -#: src/slic3r/GUI/ConfigWizard.cpp:1115 +#: src/slic3r/GUI/ConfigWizard.cpp:1474 src/slic3r/GUI/ConfigWizard.cpp:2014 +msgid "Filaments" +msgstr "필라멘트" + +#: src/slic3r/GUI/ConfigWizard.cpp:1474 src/slic3r/GUI/ConfigWizard.cpp:2016 +msgid "SLA Materials" +msgstr "SLA 재료" + +#: src/slic3r/GUI/ConfigWizard.cpp:1528 +msgid "FFF Technology Printers" +msgstr "FFF 기술 프린터" + +#: src/slic3r/GUI/ConfigWizard.cpp:1533 +msgid "SLA Technology Printers" +msgstr "SLA 기술 프린터" + +#: src/slic3r/GUI/ConfigWizard.cpp:1751 src/slic3r/GUI/DoubleSlider.cpp:1877 +#: src/slic3r/GUI/DoubleSlider.cpp:1898 src/slic3r/GUI/GUI.cpp:240 +msgid "Notice" +msgstr "공지" + +#: src/slic3r/GUI/ConfigWizard.cpp:1760 +msgid "You have to select at least one filament for selected printers" +msgstr "선택한 프린터에 대해 필라멘트를 하나 이상 선택해야 합니다." + +#: src/slic3r/GUI/ConfigWizard.cpp:1761 +msgid "Do you want to automatic select default filaments?" +msgstr "기본 필라멘트를 자동으로 선택하시겠습니까?" + +#: src/slic3r/GUI/ConfigWizard.cpp:1771 +msgid "You have to select at least one material for selected printers" +msgstr "선택한 프린터에 대해 하나 이상의 재질을 선택해야 합니다." + +#: src/slic3r/GUI/ConfigWizard.cpp:1772 +msgid "Do you want to automatic select default materials?" +msgstr "기본 재질을 자동으로 선택하시겠습니까?" + +#: src/slic3r/GUI/ConfigWizard.cpp:1979 msgid "Select all standard printers" msgstr "이 프로파일과 호환 가능한 프린터를 선택하세요" -#: src/slic3r/GUI/ConfigWizard.cpp:1118 +#: src/slic3r/GUI/ConfigWizard.cpp:1982 msgid "< &Back" msgstr "< &뒤로" -#: src/slic3r/GUI/ConfigWizard.cpp:1119 +#: src/slic3r/GUI/ConfigWizard.cpp:1983 msgid "&Next >" msgstr "&다음 >" -#: src/slic3r/GUI/ConfigWizard.cpp:1120 +#: src/slic3r/GUI/ConfigWizard.cpp:1984 msgid "&Finish" msgstr "&완료" -#: src/slic3r/GUI/ConfigWizard.cpp:1121 src/slic3r/GUI/FirmwareDialog.cpp:151 -#: src/slic3r/GUI/ProgressStatusBar.cpp:27 +#: src/slic3r/GUI/ConfigWizard.cpp:1985 src/slic3r/GUI/FirmwareDialog.cpp:151 +#: src/slic3r/GUI/ProgressStatusBar.cpp:26 msgid "Cancel" msgstr "취소" -#: src/slic3r/GUI/ConfigWizard.cpp:1135 +#: src/slic3r/GUI/ConfigWizard.cpp:1998 msgid "Prusa FFF Technology Printers" msgstr "Prusa FFF 방식 프린터" -#: src/slic3r/GUI/ConfigWizard.cpp:1138 +#: src/slic3r/GUI/ConfigWizard.cpp:2001 msgid "Prusa MSLA Technology Printers" msgstr "Prusa MSLA 방식 프린터" -#: src/slic3r/GUI/ConfigWizard.cpp:1207 +#: src/slic3r/GUI/ConfigWizard.cpp:2014 +msgid "Filament Profiles Selection" +msgstr "필라멘트 프로파일 선택" + +#: src/slic3r/GUI/ConfigWizard.cpp:2014 src/slic3r/GUI/GUI_ObjectList.cpp:3528 +msgid "Type:" +msgstr "형식:" + +#: src/slic3r/GUI/ConfigWizard.cpp:2016 +msgid "SLA Material Profiles Selection" +msgstr "SLA 재질 프로파일 선택" + +#: src/slic3r/GUI/ConfigWizard.cpp:2016 +msgid "Layer height:" +msgstr "레이어 높이:" + +#: src/slic3r/GUI/ConfigWizard.cpp:2112 msgid "Configuration Assistant" msgstr "구성 도우미" -#: src/slic3r/GUI/ConfigWizard.cpp:1208 +#: src/slic3r/GUI/ConfigWizard.cpp:2113 msgid "Configuration &Assistant" msgstr "구성 & 도우미" -#: src/slic3r/GUI/ConfigWizard.cpp:1210 +#: src/slic3r/GUI/ConfigWizard.cpp:2115 msgid "Configuration Wizard" msgstr "구성 마법사" -#: src/slic3r/GUI/ConfigWizard.cpp:1211 +#: src/slic3r/GUI/ConfigWizard.cpp:2116 msgid "Configuration &Wizard" msgstr "구성 & 마법사" -#: src/slic3r/GUI/Field.cpp:125 +#: src/slic3r/GUI/DoubleSlider.cpp:79 +msgid "Place bearings in slots and resume" +msgstr "슬롯에 베어링을 배치하고 다시 시작" + +#: src/slic3r/GUI/DoubleSlider.cpp:923 +msgid "One layer mode" +msgstr "하나의 레이어 모드" + +#: src/slic3r/GUI/DoubleSlider.cpp:925 +msgid "Discard all custom changes" +msgstr "모든 사용자 지정 변경 내용 삭제" + +#: src/slic3r/GUI/DoubleSlider.cpp:928 +msgid "For jump to print Z use left mouse button click OR (Shift+G)" +msgstr "Z를 인쇄하는 점프를 위해 왼쪽 마우스 버튼을 클릭 (Shift +G)" + +#: src/slic3r/GUI/DoubleSlider.cpp:929 +msgid "For set extruder sequence for whole print use right mouse button click" +msgstr "전체 인쇄 용 돌출부 시퀀스 설정용 마우스 버튼 클릭" + +#: src/slic3r/GUI/DoubleSlider.cpp:930 src/slic3r/GUI/DoubleSlider.cpp:1495 +msgid "Jump to print Z" +msgstr "Z 인쇄로 이동" + +#: src/slic3r/GUI/DoubleSlider.cpp:933 +msgid "For edit current color use right mouse button click on colored band" +msgstr "현재 색상 사용 오른쪽 마우스 버튼을 편집하려면 컬러 밴드를 클릭" + +#: src/slic3r/GUI/DoubleSlider.cpp:941 +msgid "Slider(print) mode" +msgstr "슬라이더(인쇄) 모드" + +#: src/slic3r/GUI/DoubleSlider.cpp:955 +msgid "For add change extruder use left mouse button click" +msgstr "추가 변경 압출기 사용 왼쪽 마우스 단추 단추를 클릭" + +#: src/slic3r/GUI/DoubleSlider.cpp:957 +msgid "" +"For add color change use left mouse button click if you want to use colors " +"from default color list, or Shift + left mouse button click if you want to " +"select a color" +msgstr "" +"색상 변경 추가하려면 기본 색상 목록에서 색상을 사용하려는 경우 왼쪽 마우스 버" +"튼을 클릭하거나 색상을 선택하려면 Shift + 왼쪽 마우스 버튼을 클릭하십시오." + +#: src/slic3r/GUI/DoubleSlider.cpp:960 +msgid "For add color change use left mouse button click" +msgstr "색상 변경 추가하려면 왼쪽 마우스 버튼을 클릭하십시오." + +#: src/slic3r/GUI/DoubleSlider.cpp:961 +msgid "OR pres \"+\" key" +msgstr "" + +#: src/slic3r/GUI/DoubleSlider.cpp:963 +msgid "For add another code use Ctrl + left mouse button click" +msgstr "다른 코드를 추가하려면 Ctrl + 왼쪽 마우스 버튼을 클릭" + +#: src/slic3r/GUI/DoubleSlider.cpp:964 +msgid "For add another code use right mouse button click" +msgstr "다른 코드를 추가하려면 오른쪽 마우스 버튼을 클릭하십시오." + +#: src/slic3r/GUI/DoubleSlider.cpp:973 +msgid "Color change (\"%1%\")" +msgstr "색상 변경(\"%1%\")" + +#: src/slic3r/GUI/DoubleSlider.cpp:974 +msgid "Color change (\"%1%\") for Extruder %2%" +msgstr "압출기 %2%의 색상 변경(\"%1%\")" + +#: src/slic3r/GUI/DoubleSlider.cpp:977 +msgid "Pause print (\"%1%\")" +msgstr "일시 중지 인쇄(\"%1%\")" + +#: src/slic3r/GUI/DoubleSlider.cpp:979 +msgid "Extruder(tool) is changed to Extruder \"%1%\"" +msgstr "압출기(도구)가 압출기 \"%1%\"로 변경됩니다." + +#: src/slic3r/GUI/DoubleSlider.cpp:980 +msgid "\"%1%\"" +msgstr "\"%1%\"" + +#: src/slic3r/GUI/DoubleSlider.cpp:986 +msgid "Note" +msgstr "메모" + +#: src/slic3r/GUI/DoubleSlider.cpp:988 +msgid "" +"G-code of this tick has a conflict with slider(print) mode.\n" +"Any its editing will cause a changes of DoubleSlider data." +msgstr "" +"이 틱의 G 코드는 슬라이더(인쇄) 모드와 충돌합니다.\n" +"편집하면 두개의 슬라이더 데이터가 변경됩니다." + +#: src/slic3r/GUI/DoubleSlider.cpp:991 +msgid "" +"There is a color change for extruder that wouldn't be used till the end of " +"printing.\n" +"This code wouldn't be processed during GCode generation." +msgstr "" +"인쇄가 끝날 때까지 사용되지 않는 압출기의 색상 변경이 있습니다.\n" +"이 코드는 GCode 생성 중에 처리되지 않습니다." + +#: src/slic3r/GUI/DoubleSlider.cpp:994 +msgid "" +"There is a extruder change to the same extruder.\n" +"This code wouldn't be processed during GCode generation." +msgstr "" +"동일한 압출기로 돌출기 변경이 있습니다.\n" +"이 코드는 GCode 생성 중에 처리되지 않습니다." + +#: src/slic3r/GUI/DoubleSlider.cpp:997 +msgid "" +"There is a color change for extruder that has not been used before.\n" +"Check your choice to avoid redundant color changes." +msgstr "" +"이전에 사용되지 않은 압출기의 색상 변경이 있습니다.\n" +"중복 색상 변경을 방지하려면 선택 사항을 확인하십시오." + +#: src/slic3r/GUI/DoubleSlider.cpp:1002 +msgid "For Delete tick use left mouse button click OR pres \"-\" key" +msgstr "삭제 진드기 사용 왼쪽 마우스 버튼 클릭 또는 \"-\" 키를 pres" + +#: src/slic3r/GUI/DoubleSlider.cpp:1004 +msgid "For Edit tick use Ctrl + Left mouse button click" +msgstr "편집 진드기 사용 Ctrl + 왼쪽 마우스 버튼을 클릭" + +#: src/slic3r/GUI/DoubleSlider.cpp:1005 +msgid "For Edit tick use right mouse button click" +msgstr "편집 사용 오른쪽 마우스 버튼을 클릭" + +#: src/slic3r/GUI/DoubleSlider.cpp:1099 src/slic3r/GUI/DoubleSlider.cpp:1135 +#: src/slic3r/GUI/GLCanvas3D.cpp:982 src/slic3r/GUI/Tab.cpp:2302 +#: src/libslic3r/GCode/PreviewData.cpp:445 +#, c-format +msgid "Extruder %d" +msgstr "익스트루더 %d" + +#: src/slic3r/GUI/DoubleSlider.cpp:1100 +msgid "active" +msgstr "활성" + +#: src/slic3r/GUI/DoubleSlider.cpp:1109 +msgid "Switch code to Change extruder" +msgstr "코드 전환- 압출기 변경" + +#: src/slic3r/GUI/DoubleSlider.cpp:1109 src/slic3r/GUI/GUI_ObjectList.cpp:1666 +msgid "Change extruder" +msgstr "압출기(익스트루더) 변경" + +#: src/slic3r/GUI/DoubleSlider.cpp:1110 +msgid "Change extruder (N/A)" +msgstr "돌출자 변경(N/A)" + +#: src/slic3r/GUI/DoubleSlider.cpp:1112 +msgid "Use another extruder" +msgstr "다른 압출기 사용" + +#: src/slic3r/GUI/DoubleSlider.cpp:1136 +msgid "used" +msgstr "사용됨" + +#: src/slic3r/GUI/DoubleSlider.cpp:1144 +msgid "Switch code to Color change (%1%) for:" +msgstr "코드 에서 색상 변경(%1%) 에 대 한:" + +#: src/slic3r/GUI/DoubleSlider.cpp:1145 +msgid "Add color change (%1%) for:" +msgstr "색상 변경 추가(%1%) 에 대 한:" + +#: src/slic3r/GUI/DoubleSlider.cpp:1443 +msgid "Add color change" +msgstr "색상 변경 추가" + +#: src/slic3r/GUI/DoubleSlider.cpp:1453 +msgid "Add pause print" +msgstr "일시 정지 인쇄 추가" + +#: src/slic3r/GUI/DoubleSlider.cpp:1456 +msgid "Add custom G-code" +msgstr "사용자 지정 G 코드 추가" + +#: src/slic3r/GUI/DoubleSlider.cpp:1474 +msgid "Edit color" +msgstr "색상 편집" + +#: src/slic3r/GUI/DoubleSlider.cpp:1475 +msgid "Edit pause print message" +msgstr "일시 정지 인쇄 메시지 편집" + +#: src/slic3r/GUI/DoubleSlider.cpp:1476 +msgid "Edit custom G-code" +msgstr "사용자 지정 G 코드 편집" + +#: src/slic3r/GUI/DoubleSlider.cpp:1482 +msgid "Delete color change" +msgstr "색상 변경 삭제" + +#: src/slic3r/GUI/DoubleSlider.cpp:1483 +msgid "Delete tool change" +msgstr "도구 변경 삭제" + +#: src/slic3r/GUI/DoubleSlider.cpp:1484 +msgid "Delete pause print" +msgstr "일시 정지 인쇄 삭제" + +#: src/slic3r/GUI/DoubleSlider.cpp:1485 +msgid "Delete custom G-code" +msgstr "사용자 지정 G 코드 삭제" + +#: src/slic3r/GUI/DoubleSlider.cpp:1498 +msgid "Set extruder sequence for whole print" +msgstr "전체 인쇄를 위한 압출기 시퀀스 설정" + +#: src/slic3r/GUI/DoubleSlider.cpp:1584 +msgid "Enter custom G-code used on current layer" +msgstr "현재 레이어에 사용되는 사용자 지정 G 코드를 입력합니다." + +#: src/slic3r/GUI/DoubleSlider.cpp:1585 +msgid "Custom Gcode on current layer (%1% mm)." +msgstr "현재 레이어에 사용자 지정 Gcode(%1% mm)." + +#: src/slic3r/GUI/DoubleSlider.cpp:1600 +msgid "Enter short message shown on Printer display during pause print" +msgstr "" +"일시 정지시 인쇄 중에 프린터 디스플레이에 표시된 짧은 메시지를 입력합니다." + +#: src/slic3r/GUI/DoubleSlider.cpp:1601 +msgid "Message for pause print on current layer (%1% mm)." +msgstr "현재 레이어에서 일시 중지 인쇄를 위한 메시지(%1% mm)." + +#: src/slic3r/GUI/DoubleSlider.cpp:1616 +msgid "Enter print z value to jump to" +msgstr "인쇄 z 값을 입력하여" + +#: src/slic3r/GUI/DoubleSlider.cpp:1617 +msgid "Jump to print z" +msgstr "Z 인쇄로 이동" + +#: src/slic3r/GUI/DoubleSlider.cpp:1871 +msgid "" +"The last color change data was saved for a single extruder printer profile." +msgstr "" +"단일 압출기 프린터 프로파일에 대해 마지막 색상 변경 데이터가 저장되었습니다." + +#: src/slic3r/GUI/DoubleSlider.cpp:1872 +msgid "" +"The last color change data was saved for a multiple extruder printer profile." +msgstr "" +"여러 압출기 프린터 프로파일에 대해 마지막 색상 변경 데이터가 저장되었습니다." + +#: src/slic3r/GUI/DoubleSlider.cpp:1874 +msgid "Your current changes will cause a deletion of all saved color changes." +msgstr "현재 변경으로 인해 저장된 모든 색상 변경 내용이 삭제됩니다." + +#: src/slic3r/GUI/DoubleSlider.cpp:1875 src/slic3r/GUI/DoubleSlider.cpp:1896 +msgid "Are you sure you want to continue?" +msgstr "정말 계속하기를 원하십니까?" + +#: src/slic3r/GUI/DoubleSlider.cpp:1888 +msgid "The last color change data was saved for a multi extruder printing." +msgstr "다중 압출기 인쇄를 위해 마지막 색상 변경 데이터가 저장되었습니다." + +#: src/slic3r/GUI/DoubleSlider.cpp:1889 +msgid "" +"Select YES if you want to delete all saved tool changes, \n" +"\tNO if you want all tool changes switch to color changes, \n" +"\tor CANCEL for do nothing" +msgstr "" +"저장된 도구 변경 내용을 모두 삭제하려면 YES를 선택합니다. \n" +"\t아니오 모든 도구 변경 이 색상 변경으로 전환하려면 \n" +"\t또는 아무것도 하지 않는 취소" + +#: src/slic3r/GUI/DoubleSlider.cpp:1892 +msgid "Do you want to delete all saved tool changes?" +msgstr "저장된 도구 변경 내용을 모두 삭제하시겠습니까?" + +#: src/slic3r/GUI/DoubleSlider.cpp:1894 +msgid "" +"The last color change data was saved for a multi extruder printing with tool " +"changes for whole print." +msgstr "" +"마지막 색상 변경 데이터는 전체 인쇄에 대한 도구 변경이 있는 다중 압출기 인쇄" +"를 위해 저장되었습니다." + +#: src/slic3r/GUI/DoubleSlider.cpp:1895 +msgid "Your current changes will cause a deletion of all saved tool changes." +msgstr "현재 변경 으로 인해 저장된 모든 도구 변경 내용이 삭제됩니다." + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:23 +msgid "Set extruder sequence" +msgstr "압출기 시퀀스 설정" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:39 +msgid "Set extruder change for every" +msgstr "모든 압출기 변경 설정" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:52 +#: src/libslic3r/PrintConfig.cpp:352 src/libslic3r/PrintConfig.cpp:995 +#: src/libslic3r/PrintConfig.cpp:1514 src/libslic3r/PrintConfig.cpp:1699 +#: src/libslic3r/PrintConfig.cpp:1760 src/libslic3r/PrintConfig.cpp:1940 +#: src/libslic3r/PrintConfig.cpp:1986 +msgid "layers" +msgstr "레이어" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:136 +msgid "Set extruder(tool) sequence" +msgstr "돌출기(도구) 시퀀스 설정" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:182 +msgid "Remove extruder from sequence" +msgstr "시퀀스에서 압출기 제거" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:192 +msgid "Add extruder to sequence" +msgstr "시퀀스에 압출기 추가" + +#: src/slic3r/GUI/Field.cpp:131 msgid "default value" msgstr "기본값" -#: src/slic3r/GUI/Field.cpp:128 +#: src/slic3r/GUI/Field.cpp:134 msgid "parameter name" msgstr "매개 변수 명칭" -#: src/slic3r/GUI/Field.cpp:139 +#: src/slic3r/GUI/Field.cpp:145 src/slic3r/GUI/OptionsGroup.cpp:570 msgid "N/A" msgstr "N/A" -#: src/slic3r/GUI/Field.cpp:158 +#: src/slic3r/GUI/Field.cpp:170 #, c-format msgid "%s doesn't support percentage" msgstr "%s 이(가) 백분율을 지원하지 않음" -#: src/slic3r/GUI/Field.cpp:174 src/slic3r/GUI/Field.cpp:197 +#: src/slic3r/GUI/Field.cpp:190 src/slic3r/GUI/Field.cpp:221 +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:376 msgid "Invalid numeric input." msgstr "숫자 입력이 잘못 되었습니다." -#: src/slic3r/GUI/Field.cpp:179 +#: src/slic3r/GUI/Field.cpp:199 msgid "Input value is out of range" -msgstr "Input value is out of range" +msgstr "입력 값이 범위를 벗어났습니다." -#: src/slic3r/GUI/Field.cpp:206 +#: src/slic3r/GUI/Field.cpp:235 #, c-format msgid "" "Do you mean %s%% instead of %s %s?\n" @@ -680,9 +1294,9 @@ msgid "" msgstr "" "%s %s 대신 %s%%을 하려고 합니까?\n" "이 값을 %s%%, 로 변경하려면 YES를 선택하십시오. \n" -"또는 %s %s 이(가) 올바른 값인지 확인하는 경우 NO를 선택하세요. " +"또는 %s %s 이(가) 올바른 값인지 확인하는 경우 NO를 선택하세요." -#: src/slic3r/GUI/Field.cpp:209 +#: src/slic3r/GUI/Field.cpp:238 msgid "Parameter validation" msgstr "매개 변수 유효성 검사" @@ -731,8 +1345,7 @@ msgstr "" #, c-format msgid "" "Multiple %s devices found. Please only connect one at a time for flashing." -msgstr "" -"여러 %s 장치를 찾았습니다. 깜박이는 경우에는 한 번에 하나씩만 연결 하십시오." +msgstr "여러 %s 장치를 찾았습니다. 깜박이면 한 번에 하나씩만 연결하십시오." #: src/slic3r/GUI/FirmwareDialog.cpp:436 #, c-format @@ -741,8 +1354,8 @@ msgid "" "If the device is connected, please press the Reset button next to the USB " "connector ..." msgstr "" -"%s 장치를 찾을 하지 않았습니다.\n" -"장치가 연결 된 경우 USB 커넥터 옆에 있는 리셋 단추를 누르십시오." +"%s 장치를 찾을 수 없습니다.\n" +"장치가 연결되어 있는 경우 USB 커넥터 옆에 있는 리셋 버튼을 누르십시오..." #: src/slic3r/GUI/FirmwareDialog.cpp:548 #, c-format @@ -767,8 +1380,8 @@ msgstr "펌웨어 업로드" msgid "Firmware image:" msgstr "펌웨어 이미지:" -#: src/slic3r/GUI/FirmwareDialog.cpp:805 src/slic3r/GUI/Tab.cpp:1824 -#: src/slic3r/GUI/Tab.cpp:1880 +#: src/slic3r/GUI/FirmwareDialog.cpp:805 src/slic3r/GUI/Tab.cpp:1649 +#: src/slic3r/GUI/Tab.cpp:1705 msgid "Browse" msgstr "검색" @@ -801,11 +1414,12 @@ msgid "Advanced: Output log" msgstr "고급: 출력 로그" #: src/slic3r/GUI/FirmwareDialog.cpp:852 +#: src/slic3r/GUI/Mouse3DController.cpp:387 #: src/slic3r/GUI/PrintHostDialogs.cpp:161 msgid "Close" msgstr "닫기" -#: src/slic3r/GUI/FirmwareDialog.cpp:903 +#: src/slic3r/GUI/FirmwareDialog.cpp:902 msgid "" "Are you sure you want to cancel firmware flashing?\n" "This could leave your printer in an unusable state!" @@ -813,147 +1427,317 @@ msgstr "" "새펌웨어 적용을 취소하시겠습니까?\n" "프린터가 사용할 수 없는 상태가 될 수 있습니다!" -#: src/slic3r/GUI/FirmwareDialog.cpp:904 +#: src/slic3r/GUI/FirmwareDialog.cpp:903 msgid "Confirmation" msgstr "확인" -#: src/slic3r/GUI/FirmwareDialog.cpp:907 +#: src/slic3r/GUI/FirmwareDialog.cpp:906 msgid "Cancelling..." msgstr "취소 중...." -#: src/slic3r/GUI/GLCanvas3D.cpp:526 -msgid "Layers heights" -msgstr "레이어 높이" +#: src/slic3r/GUI/GLCanvas3D.cpp:239 src/slic3r/GUI/GLCanvas3D.cpp:4622 +msgid "Variable layer height" +msgstr "가변 레이어 높이" -#: src/slic3r/GUI/GLCanvas3D.cpp:623 +#: src/slic3r/GUI/GLCanvas3D.cpp:242 +msgid "Left mouse button:" +msgstr "왼쪽 마우스 단추:" + +#: src/slic3r/GUI/GLCanvas3D.cpp:245 +msgid "Add detail" +msgstr "디테일 추가" + +#: src/slic3r/GUI/GLCanvas3D.cpp:248 +msgid "Right mouse button:" +msgstr "오른쪽 마우스 버튼:" + +#: src/slic3r/GUI/GLCanvas3D.cpp:251 +msgid "Remove detail" +msgstr "디테일 제거" + +#: src/slic3r/GUI/GLCanvas3D.cpp:254 +msgid "Shift + Left mouse button:" +msgstr "시프트 + 왼쪽 마우스 버튼:" + +#: src/slic3r/GUI/GLCanvas3D.cpp:257 +msgid "Reset to base" +msgstr "기본으로 재설정" + +#: src/slic3r/GUI/GLCanvas3D.cpp:260 +msgid "Shift + Right mouse button:" +msgstr "시프트 + 오른쪽 마우스 버튼:" + +#: src/slic3r/GUI/GLCanvas3D.cpp:263 +msgid "Smoothing" +msgstr "부드럽게" + +#: src/slic3r/GUI/GLCanvas3D.cpp:266 +msgid "Mouse wheel:" +msgstr "마우스 휠: " + +#: src/slic3r/GUI/GLCanvas3D.cpp:269 +msgid "Increase/decrease edit area" +msgstr "편집 영역 증가/감소" + +#: src/slic3r/GUI/GLCanvas3D.cpp:272 +msgid "Adaptive" +msgstr "어뎁티브" + +#: src/slic3r/GUI/GLCanvas3D.cpp:278 +msgid "Quality / Speed" +msgstr "품질 / 속도" + +#: src/slic3r/GUI/GLCanvas3D.cpp:282 +msgid "Higher print quality versus higher print speed." +msgstr "인쇄 품질이 높고 인쇄 속도가 빨라질 수 있습니다." + +#: src/slic3r/GUI/GLCanvas3D.cpp:293 +msgid "Smooth" +msgstr "부드럽게" + +#: src/slic3r/GUI/GLCanvas3D.cpp:299 src/libslic3r/PrintConfig.cpp:511 +msgid "Radius" +msgstr " 반지름" + +#: src/slic3r/GUI/GLCanvas3D.cpp:309 +msgid "Keep min" +msgstr "최소 유지" + +#: src/slic3r/GUI/GLCanvas3D.cpp:318 +msgid "Reset" +msgstr "초기화" + +#: src/slic3r/GUI/GLCanvas3D.cpp:604 +msgid "Variable layer height - Manual edit" +msgstr "가변 레이어 높이 - 수동 편집" + +#: src/slic3r/GUI/GLCanvas3D.cpp:690 msgid "An object outside the print area was detected" -msgstr "인쇄 영역 밖에 있는 개체가 감지 되었습니다" +msgstr "인쇄 영역 밖에 있는 객체(object)가 감지 되었습니다" -#: src/slic3r/GUI/GLCanvas3D.cpp:624 +#: src/slic3r/GUI/GLCanvas3D.cpp:691 msgid "A toolpath outside the print area was detected" msgstr "인쇄 영역 밖에 있는 공구 경로가 감지 되었습니다" -#: src/slic3r/GUI/GLCanvas3D.cpp:625 +#: src/slic3r/GUI/GLCanvas3D.cpp:692 msgid "SLA supports outside the print area were detected" -msgstr "인쇄 영역 외부의 SLA 지원 감지 됨" +msgstr "인쇄 영역 외부의 SLA 서포트가 감지 됨" -#: src/slic3r/GUI/GLCanvas3D.cpp:626 -msgid "Some objects are not visible when editing supports" -msgstr "편집 지원 시 일부 객체가 표시 되지 않음" +#: src/slic3r/GUI/GLCanvas3D.cpp:693 +msgid "Some objects are not visible" +msgstr "일부 개체가 표시되지 않습니다" -#: src/slic3r/GUI/GLCanvas3D.cpp:628 +#: src/slic3r/GUI/GLCanvas3D.cpp:695 msgid "" "An object outside the print area was detected\n" "Resolve the current problem to continue slicing" msgstr "" -"인쇄 영역 밖에 있는 개체가 감지 되었습니다.\n" -"현재 문제를 해결 하여 슬라이싱을 계속 합니다" +"인쇄 영역 밖에 있는 객체(object)가 감지 되었습니다.\n" +"현재 문제를 해결하고 슬라이싱을 계속 합니다" -#: src/slic3r/GUI/GLCanvas3D.cpp:1711 +#: src/slic3r/GUI/GLCanvas3D.cpp:909 src/slic3r/GUI/GLCanvas3D.cpp:938 +msgid "Default print color" +msgstr "기본 인쇄 색상" + +#: src/slic3r/GUI/GLCanvas3D.cpp:939 src/slic3r/GUI/GLCanvas3D.cpp:948 +#: src/slic3r/GUI/GLCanvas3D.cpp:987 +msgid "Pause print or custom G-code" +msgstr "인쇄 또는 사용자 지정 G-코드 일시 중지" + +#: src/slic3r/GUI/GLCanvas3D.cpp:960 +#, c-format +msgid "up to %.2f mm" +msgstr "최대%.2f mm" + +#: src/slic3r/GUI/GLCanvas3D.cpp:964 +#, c-format +msgid "above %.2f mm" +msgstr "above %.2f mm" + +#: src/slic3r/GUI/GLCanvas3D.cpp:968 +#, c-format +msgid "%.2f - %.2f mm" +msgstr "%.2f - %.2f mm" + +#: src/slic3r/GUI/GLCanvas3D.cpp:995 +#, c-format +msgid "Color change for Extruder %d at %.2f mm" +msgstr "압출기 %d의 색상 변화 %.2f mm" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1306 +msgid "Seq." +msgstr "순서" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1705 +msgid "Variable layer height - Reset" +msgstr "가변 레이어 높이 - 재설정" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1713 +msgid "Variable layer height - Adaptive" +msgstr "가변 레이어 높이 - 어뎁티브" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1721 +msgid "Variable layer height - Smooth all" +msgstr "가변 레이어 높이 - 모두 부드럽게" + +#: src/slic3r/GUI/GLCanvas3D.cpp:2075 msgid "Mirror Object" -msgstr "오브젝트 미러" +msgstr "객체(object) 반전" -#: src/slic3r/GUI/GLCanvas3D.cpp:2872 +#: src/slic3r/GUI/GLCanvas3D.cpp:2945 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:571 +msgid "Gizmo-Move" +msgstr "개체(Gizmo) 이동" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3025 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:573 +msgid "Gizmo-Rotate" +msgstr "개체(Gizmo) 회전" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3538 msgid "Move Object" -msgstr "오브젝트 이동" +msgstr "객체(object) 이동" -#: src/slic3r/GUI/GLCanvas3D.cpp:3389 src/slic3r/GUI/GLCanvas3D.cpp:3609 -#: src/slic3r/GUI/MainFrame.cpp:559 -msgid "Undo" -msgstr "실행 취소" +#: src/slic3r/GUI/GLCanvas3D.cpp:4085 +msgid "Undo History" +msgstr "실행취소 기록" -#: src/slic3r/GUI/GLCanvas3D.cpp:3389 src/slic3r/GUI/GLCanvas3D.cpp:3639 -#: src/slic3r/GUI/MainFrame.cpp:562 -msgid "Redo" -msgstr "다시 실행" +#: src/slic3r/GUI/GLCanvas3D.cpp:4085 +msgid "Redo History" +msgstr "다시 실행 히스토리" -#: src/slic3r/GUI/GLCanvas3D.cpp:3395 +#: src/slic3r/GUI/GLCanvas3D.cpp:4103 #, c-format -msgid "%s Stack" -msgstr "%s 스택" +msgid "Undo %1$d Action" +msgid_plural "Undo %1$d Actions" +msgstr[0] "실행 취소 %1$d 작업" -#: src/slic3r/GUI/GLCanvas3D.cpp:3413 +#: src/slic3r/GUI/GLCanvas3D.cpp:4103 #, c-format -msgid "%s %d Action" -msgstr "%s %d 액션" +msgid "Redo %1$d Action" +msgid_plural "Redo %1$d Actions" +msgstr[0] "작업 %1$d 다시 실행" -#: src/slic3r/GUI/GLCanvas3D.cpp:3460 +#: src/slic3r/GUI/GLCanvas3D.cpp:4516 msgid "Add..." msgstr "추가..." -#: src/slic3r/GUI/GLCanvas3D.cpp:3468 src/slic3r/GUI/GUI_ObjectList.cpp:1434 -#: src/slic3r/GUI/Plater.cpp:3467 src/slic3r/GUI/Plater.cpp:3486 -#: src/slic3r/GUI/Tab.cpp:3204 +#: src/slic3r/GUI/GLCanvas3D.cpp:4524 src/slic3r/GUI/GUI_ObjectList.cpp:1697 +#: src/slic3r/GUI/Plater.cpp:3942 src/slic3r/GUI/Plater.cpp:3969 +#: src/slic3r/GUI/Tab.cpp:3094 msgid "Delete" msgstr "지우기 " -#: src/slic3r/GUI/GLCanvas3D.cpp:3477 src/slic3r/GUI/Plater.cpp:4075 +#: src/slic3r/GUI/GLCanvas3D.cpp:4533 src/slic3r/GUI/Plater.cpp:4693 msgid "Delete all" msgstr "전부 지우기" -#: src/slic3r/GUI/GLCanvas3D.cpp:3486 src/slic3r/GUI/KBShortcutsDialog.cpp:134 -#: src/slic3r/GUI/Plater.cpp:2636 +#: src/slic3r/GUI/GLCanvas3D.cpp:4542 src/slic3r/GUI/KBShortcutsDialog.cpp:137 +#: src/slic3r/GUI/Plater.cpp:2740 msgid "Arrange" msgstr "정렬" -#: src/slic3r/GUI/GLCanvas3D.cpp:3486 +#: src/slic3r/GUI/GLCanvas3D.cpp:4542 src/slic3r/GUI/KBShortcutsDialog.cpp:138 msgid "Arrange selection" msgstr "선택 정렬" -#: src/slic3r/GUI/GLCanvas3D.cpp:3498 +#: src/slic3r/GUI/GLCanvas3D.cpp:4554 msgid "Copy" msgstr "복사" -#: src/slic3r/GUI/GLCanvas3D.cpp:3507 +#: src/slic3r/GUI/GLCanvas3D.cpp:4563 msgid "Paste" msgstr "붙여넣기" -#: src/slic3r/GUI/GLCanvas3D.cpp:3519 +#: src/slic3r/GUI/GLCanvas3D.cpp:4575 src/slic3r/GUI/Plater.cpp:3799 +#: src/slic3r/GUI/Plater.cpp:3811 src/slic3r/GUI/Plater.cpp:3956 msgid "Add instance" msgstr "복제본 추가" -#: src/slic3r/GUI/GLCanvas3D.cpp:3530 +#: src/slic3r/GUI/GLCanvas3D.cpp:4586 src/slic3r/GUI/Plater.cpp:3958 msgid "Remove instance" msgstr "복제본 제거" -#: src/slic3r/GUI/GLCanvas3D.cpp:3543 +#: src/slic3r/GUI/GLCanvas3D.cpp:4599 msgid "Split to objects" -msgstr "객체로 분할" +msgstr "객체(object)별 분할" -#: src/slic3r/GUI/GLCanvas3D.cpp:3553 src/slic3r/GUI/GUI_ObjectList.cpp:1280 +#: src/slic3r/GUI/GLCanvas3D.cpp:4609 src/slic3r/GUI/GUI_ObjectList.cpp:1485 msgid "Split to parts" -msgstr "파트로 분할" +msgstr "부품(Part)별 분할" -#: src/slic3r/GUI/GLCanvas3D.cpp:3566 -msgid "Layers editing" -msgstr "레이어층을 편집" +#: src/slic3r/GUI/GLCanvas3D.cpp:4673 src/slic3r/GUI/MainFrame.cpp:581 +msgid "Undo" +msgstr "되돌리기" -#: src/slic3r/GUI/GLCanvas3D.cpp:5623 +#: src/slic3r/GUI/GLCanvas3D.cpp:4673 src/slic3r/GUI/GLCanvas3D.cpp:4706 +msgid "Click right mouse button to open History" +msgstr "마우스 오른쪽 버튼을 클릭하여 기록을 엽니다." + +#: src/slic3r/GUI/GLCanvas3D.cpp:4690 +msgid "Next Undo action: %1%" +msgstr "다음 작업 실행 취소 : %1%" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4706 src/slic3r/GUI/MainFrame.cpp:584 +msgid "Redo" +msgstr "다시실행" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4722 +msgid "Next Redo action: %1%" +msgstr "다음 작업 다시 실행: %1%" + +#: src/slic3r/GUI/GLCanvas3D.cpp:6659 msgid "Selection-Add from rectangle" -msgstr "사각형에서 선택-추가" +msgstr "선택-사각형에서 추가" -#: src/slic3r/GUI/GLCanvas3D.cpp:5642 +#: src/slic3r/GUI/GLCanvas3D.cpp:6678 msgid "Selection-Remove from rectangle" msgstr "선택- 사각형에서 제거" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:40 -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:144 src/libslic3r/PrintConfig.cpp:3176 +#: src/slic3r/GUI/GLCanvas3DManager.cpp:284 +#, c-format +msgid "" +"PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n" +"while OpenGL version %s, render %s, vendor %s was detected." +msgstr "" +"PrusaSlicer 제대로 실행하려면 OpenGL 2.0 가능한 그래픽 드라이버가 필요합니" +"다. \n" +"OpenGL 버전 %s, 렌더링 %s 동안, 공급 업체 %s가 감지되었습니다." + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:287 +msgid "You may need to update your graphics card driver." +msgstr "그래픽 카드 드라이버를 업데이트해야 할 수 있습니다." + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:290 +msgid "" +"As a workaround, you may run PrusaSlicer with a software rendered 3D " +"graphics by running prusa-slicer.exe with the --sw_renderer parameter." +msgstr "" +"해결 방법으로 prusaSlicer를 \"sw_renderer 매개 변수\"로 prusa-slicer.exe를 실" +"행하여 3D 그래픽을 렌더링한 소프트웨어로 실행할 수 있습니다." + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:292 +msgid "Unsupported OpenGL version" +msgstr "지원되지 않는 OpenGL 버전" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:42 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:144 src/libslic3r/PrintConfig.cpp:3397 msgid "Cut" msgstr "자르기" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:149 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:168 msgid "Keep upper part" msgstr "상위 부분 유지" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:150 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:169 msgid "Keep lower part" msgstr "낮은 부분 유지" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:151 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:170 msgid "Rotate lower part upwards" msgstr "아래쪽 부분을 위쪽으로 회전" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:154 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:175 msgid "Perform cut" msgstr "절단 실행" @@ -961,906 +1745,1069 @@ msgstr "절단 실행" msgid "Place on face" msgstr "면 배치 " +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:41 +msgid "Hollow this object" +msgstr "이 개체를 비우기" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:42 +msgid "Preview hollowed and drilled model" +msgstr "미리 보기 중이 비어 있고 드릴된 모델" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:43 +msgid "Offset" +msgstr "오프셋" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:44 +msgid "Quality" +msgstr "품질" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:45 +msgid "Closing distance" +msgstr "닫힘 거리" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:46 +msgid "Hole diameter" +msgstr "구멍 직경" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:47 +msgid "Hole depth" +msgstr "구멍 깊이" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:48 +msgid "Remove selected holes" +msgstr "선택한 구멍 제거" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:49 +msgid "Remove all holes" +msgstr "모든 구멍 제거" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:50 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:58 +msgid "Clipping of view" +msgstr "갈무리된것 보기" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:51 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:59 +msgid "Reset direction" +msgstr "방향 재설정" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:52 +msgid "Show supports" +msgstr "서포트 표시" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:413 +msgid "Add drainage hole" +msgstr "배수 구멍 추가" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:530 +msgid "Delete drainage hole" +msgstr "배수 구멍 삭제" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:786 +msgid "Hollowing parameter change" +msgstr "공동화 매개변수 변경" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:858 +msgid "Change drainage hole diameter" +msgstr "배수 구멍 직경 변경" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:948 +msgid "Hollowing and drilling" +msgstr "공동화 및 드릴링" + +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:1027 +msgid "Move drainage hole" +msgstr "구멍 이동" + #: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:48 msgid "Move" msgstr "이동" -#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:177 -msgid "Position (mm)" -msgstr "위치 (mm)" - -#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:177 -msgid "Displacement (mm)" -msgstr "변위 (mm)" - #: src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp:449 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:466 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:485 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:503 -#: src/libslic3r/PrintConfig.cpp:3225 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:480 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:499 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:517 +#: src/libslic3r/PrintConfig.cpp:3446 msgid "Rotate" msgstr "회전" -#: src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp:482 -msgid "Rotation (deg)" -msgstr "회전 (°)" - #: src/slic3r/GUI/Gizmos/GLGizmoScale.cpp:47 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:390 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:486 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:504 -#: src/libslic3r/PrintConfig.cpp:3240 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:230 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:500 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:518 +#: src/libslic3r/PrintConfig.cpp:3461 msgid "Scale" msgstr "크기" -#: src/slic3r/GUI/Gizmos/GLGizmoScale.cpp:292 -msgid "Scale (%)" -msgstr "스케일 (%)" - -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:44 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:48 msgid "Head diameter" msgstr "헤드 지름" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:45 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:49 msgid "Lock supports under new islands" -msgstr "새 고립 영역에서 잠금 지원" +msgstr "새영역에서 서포트 잠금 지원" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:46 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1427 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:50 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1350 msgid "Remove selected points" -msgstr "선택한 점 제거" +msgstr "선택한 지점 제거" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:47 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:51 msgid "Remove all points" -msgstr "모든 점 제거" +msgstr "모든 지점 제거" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:48 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1430 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:52 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1353 msgid "Apply changes" msgstr "변경 내용을 적용" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:49 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1431 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:53 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1354 msgid "Discard changes" msgstr "변경사항을 취소" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:50 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:54 msgid "Minimal points distance" -msgstr "최소 포인트 거리" +msgstr "최소한의 지점 거리" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:51 -#: src/libslic3r/PrintConfig.cpp:2620 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:55 +#: src/libslic3r/PrintConfig.cpp:2765 msgid "Support points density" -msgstr "지원 포인트 밀도" +msgstr "서포트 지점 밀도" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:52 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1433 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:56 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1356 msgid "Auto-generate points" -msgstr "점 자동 생성" +msgstr "지점 자동 생성" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:53 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:57 msgid "Manual editing" msgstr "수동 편집" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:54 -msgid "Clipping of view" -msgstr "클랩핑된것 보기" - -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:55 -msgid "Reset direction" -msgstr "방향 재설정" - -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:531 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:479 msgid "Add support point" msgstr "서포트 지점 추가" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:720 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:615 msgid "Delete support point" msgstr "서포트 지점 삭제" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:925 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:804 msgid "Change point head diameter" -msgstr "변경 점 헤드 지름" +msgstr "변경된 해드의 끝 점 지름" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:991 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:872 msgid "Support parameter change" msgstr "서포트 매개 변수 변경" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1099 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:978 msgid "SLA Support Points" msgstr "SLA 지원 포인트" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1138 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:999 +msgid "SLA gizmo turned on" +msgstr "SLA 개체(gizmo)이동 켜기" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1024 msgid "Do you want to save your manually edited support points?" -msgstr "수동으로 편집한 서포트 지점을 저장 하 시겠습니까?" +msgstr "수동으로 편집한 서포트 지점을 저장 하시 겠습니까?" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1139 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1025 msgid "Save changes?" -msgstr "변경 사항을 저장 하 시겠습니까?" +msgstr "변경 사항을 저장 하시겠습니까?" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1183 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1037 +msgid "SLA gizmo turned off" +msgstr "SLA 개체(gizmo) 이동 끄기" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1076 msgid "Move support point" -msgstr "서포트 점 이동" +msgstr "서포트 지점 이동" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1282 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1175 msgid "Support points edit" -msgstr "서포트 포인트 편집" +msgstr "서포트 지점 편집" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1333 -msgid "" -"Autogeneration will erase all manually edited points.\n" -"\n" -"Are you sure you want to do it?\n" -msgstr "" -"자동 생성은 수동으로 편집한 모든 점을 지웁니다.\n" -"\n" -"그렇게 하시겠습니까?\n" +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1247 +msgid "Autogeneration will erase all manually edited points." +msgstr "자동 생성은 수동으로 편집한 모든 점을 지웁히 지웁습니다." -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1335 src/slic3r/GUI/GUI.cpp:289 -#: src/slic3r/GUI/WipeTowerDialog.cpp:44 src/slic3r/GUI/WipeTowerDialog.cpp:328 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1248 +msgid "Are you sure you want to do it?" +msgstr "당신은 그것을 하시겠습니까?" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1249 src/slic3r/GUI/GUI.cpp:246 +#: src/slic3r/GUI/Tab.cpp:3054 src/slic3r/GUI/WipeTowerDialog.cpp:45 +#: src/slic3r/GUI/WipeTowerDialog.cpp:366 msgid "Warning" msgstr "위험" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1338 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1252 msgid "Autogenerate support points" msgstr "서포트 자동 생성" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1390 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1313 msgid "SLA gizmo keyboard shortcuts" -msgstr "SLA 장치바로 가기" +msgstr "SLA 장치 바로 가기" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1401 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1324 msgid "Note: some shortcuts work in (non)editing mode only." msgstr "참고: 일부 단축키는 (비)편집 모드 에서만 작동 합니다." -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1419 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1422 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1423 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1342 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1345 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1346 msgid "Left click" msgstr "왼쪽 클릭" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1419 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1342 msgid "Add point" -msgstr "점 추가" +msgstr "지점 추가" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1420 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1343 msgid "Right click" msgstr "오른쪽 클릭" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1420 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1343 msgid "Remove point" msgstr "복제본 제거" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1421 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1424 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1425 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1344 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1347 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1348 msgid "Drag" msgstr "드래그" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1421 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1344 msgid "Move point" -msgstr "점 이동" +msgstr "지점 이동" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1422 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1345 msgid "Add point to selection" -msgstr "선택 영역에 점 추가" +msgstr "선택 영역에 지점 추가" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1423 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1346 msgid "Remove point from selection" -msgstr "선택 영역에서 점 제거" +msgstr "선택 영역에서 지점 제거" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1424 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1347 msgid "Select by rectangle" msgstr "직사각형으로 선택" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1425 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1348 msgid "Deselect by rectangle" msgstr "사각형으로 선택 해제" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1426 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1349 msgid "Select all points" -msgstr "모든 점 선택" +msgstr "모든 지점들 선택" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1428 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1351 msgid "Mouse wheel" msgstr "마우스 휠" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1428 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1351 msgid "Move clipping plane" -msgstr "클립핑 평면 이동" +msgstr "갈무리된 평면 이동" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1429 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1352 msgid "Reset clipping plane" -msgstr "클립핑 평면 재설정" +msgstr "갈무리된 평면 재설정" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1432 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1355 msgid "Switch to editing mode" msgstr "편집 모드로 전환" -#: src/slic3r/GUI/GUI.cpp:141 src/slic3r/GUI/Tab.cpp:3063 -msgid "It's impossible to print multi-part object(s) with SLA technology." -msgstr "SLA 방식을 사용 하여 멀티파트를 인쇄할 수는 없습니다." +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:498 +msgid "Gizmo-Place on Face" +msgstr "개체(Gizmo)를 배드위로" -#: src/slic3r/GUI/GUI.cpp:142 -msgid "Please check and fix your object list." -msgstr "개체 목록을 확인 하고 수정 하십시오." +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:572 +msgid "Gizmo-Scale" +msgstr "개체(Gizmo) 배율" -#: src/slic3r/GUI/GUI.cpp:143 src/slic3r/GUI/Plater.cpp:2213 -#: src/slic3r/GUI/Tab.cpp:3065 -msgid "Attention!" -msgstr "주목!" +#: src/slic3r/GUI/GUI_App.cpp:138 +#, c-format +msgid "" +"%s has encountered an error. It was likely caused by running out of memory. " +"If you are sure you have enough RAM on your system, this may also be a bug " +"and we would be glad if you reported it.\n" +"\n" +"The application will now terminate." +msgstr "" +"%s에서 오류가 발생했습니다. 메모리 부족으로 인해 발생했을 수 있습니다. 시스템" +"에 충분한 RAM이 있다고 확신하는 경우 버그가 될 수 있으며 보고하면 기쁠 것입니" +"다.\n" +"\n" +"이제 응용 프로그램이 종료됩니다." -#: src/slic3r/GUI/GUI.cpp:283 -msgid "Notice" -msgstr "공지" +#: src/slic3r/GUI/GUI_App.cpp:141 +msgid "Fatal error" +msgstr "치명적인 오류" -#: src/slic3r/GUI/GUI_App.cpp:435 +#: src/slic3r/GUI/GUI_App.cpp:442 msgid "Changing of an application language" msgstr "응용 프로그램 언어 변경" -#: src/slic3r/GUI/GUI_App.cpp:443 src/slic3r/GUI/GUI_App.cpp:452 +#: src/slic3r/GUI/GUI_App.cpp:450 src/slic3r/GUI/GUI_App.cpp:459 msgid "Recreating" msgstr "재현" -#: src/slic3r/GUI/GUI_App.cpp:456 +#: src/slic3r/GUI/GUI_App.cpp:466 msgid "Loading of current presets" -msgstr "현재 프리셋 불러오기" +msgstr "현재 기본 설정을 불러오기" -#: src/slic3r/GUI/GUI_App.cpp:464 +#: src/slic3r/GUI/GUI_App.cpp:474 msgid "Loading of a mode view" -msgstr "모드 보기 로드" +msgstr "보기 모드를 불러오기" -#: src/slic3r/GUI/GUI_App.cpp:544 +#: src/slic3r/GUI/GUI_App.cpp:555 msgid "Choose one file (3MF/AMF):" msgstr "파일(3MF/AMF) 선택:" -#: src/slic3r/GUI/GUI_App.cpp:556 +#: src/slic3r/GUI/GUI_App.cpp:567 msgid "Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):" msgstr "파일을 선택하세요 (STL/OBJ/AMF/3MF/PRUSA):" -#: src/slic3r/GUI/GUI_App.cpp:598 +#: src/slic3r/GUI/GUI_App.cpp:629 msgid "Select the language" msgstr "언어를 선택" -#: src/slic3r/GUI/GUI_App.cpp:599 +#: src/slic3r/GUI/GUI_App.cpp:629 msgid "Language" msgstr "언어" -#: src/slic3r/GUI/GUI_App.cpp:750 -msgid "&Configuration Snapshots" -msgstr "구성 스냅숏" +#: src/slic3r/GUI/GUI_App.cpp:797 +#, c-format +msgid "Run %s" +msgstr "%s 실행" -#: src/slic3r/GUI/GUI_App.cpp:750 +#: src/slic3r/GUI/GUI_App.cpp:800 +msgid "&Configuration Snapshots" +msgstr "구성 스냅샷" + +#: src/slic3r/GUI/GUI_App.cpp:800 msgid "Inspect / activate configuration snapshots" msgstr "구성 스냅 샷 검사 / 활성화" -#: src/slic3r/GUI/GUI_App.cpp:751 +#: src/slic3r/GUI/GUI_App.cpp:801 msgid "Take Configuration &Snapshot" msgstr "구성 스냅 샷 가져 오기" -#: src/slic3r/GUI/GUI_App.cpp:751 +#: src/slic3r/GUI/GUI_App.cpp:801 msgid "Capture a configuration snapshot" msgstr "구성 스냅 샷 캡처" -#: src/slic3r/GUI/GUI_App.cpp:754 +#: src/slic3r/GUI/GUI_App.cpp:802 +msgid "Check for updates" +msgstr "업데이트 확인" + +#: src/slic3r/GUI/GUI_App.cpp:802 +msgid "Check for configuration updates" +msgstr "구성 업데이트 확인" + +#: src/slic3r/GUI/GUI_App.cpp:804 msgid "&Preferences" msgstr "환경 설정" -#: src/slic3r/GUI/GUI_App.cpp:760 +#: src/slic3r/GUI/GUI_App.cpp:810 msgid "Application preferences" msgstr "응용 프로그램 환경 설정" -#: src/slic3r/GUI/GUI_App.cpp:763 src/slic3r/GUI/wxExtensions.cpp:2882 +#: src/slic3r/GUI/GUI_App.cpp:813 src/slic3r/GUI/wxExtensions.cpp:730 msgid "Simple" msgstr "단순" -#: src/slic3r/GUI/GUI_App.cpp:763 +#: src/slic3r/GUI/GUI_App.cpp:813 msgid "Simple View Mode" -msgstr "단순 보기 모드" +msgstr "기본 보기 모드" -#: src/slic3r/GUI/GUI_App.cpp:764 src/slic3r/GUI/GUI_ObjectList.cpp:93 -#: src/slic3r/GUI/GUI_ObjectList.cpp:567 src/slic3r/GUI/Tab.cpp:1037 -#: src/slic3r/GUI/Tab.cpp:1052 src/slic3r/GUI/Tab.cpp:1150 -#: src/slic3r/GUI/Tab.cpp:1153 src/slic3r/GUI/Tab.cpp:1649 -#: src/slic3r/GUI/Tab.cpp:2120 src/slic3r/GUI/Tab.cpp:3699 -#: src/slic3r/GUI/wxExtensions.cpp:2883 src/libslic3r/PrintConfig.cpp:83 -#: src/libslic3r/PrintConfig.cpp:197 src/libslic3r/PrintConfig.cpp:360 -#: src/libslic3r/PrintConfig.cpp:1013 src/libslic3r/PrintConfig.cpp:2226 +#: src/slic3r/GUI/GUI_App.cpp:814 src/slic3r/GUI/GUI_ObjectList.cpp:102 +#: src/slic3r/GUI/GUI_ObjectList.cpp:620 src/slic3r/GUI/Tab.cpp:1077 +#: src/slic3r/GUI/Tab.cpp:1092 src/slic3r/GUI/Tab.cpp:1191 +#: src/slic3r/GUI/Tab.cpp:1194 src/slic3r/GUI/Tab.cpp:1464 +#: src/slic3r/GUI/Tab.cpp:1951 src/slic3r/GUI/Tab.cpp:3633 +#: src/slic3r/GUI/wxExtensions.cpp:731 src/libslic3r/PrintConfig.cpp:88 +#: src/libslic3r/PrintConfig.cpp:213 src/libslic3r/PrintConfig.cpp:376 +#: src/libslic3r/PrintConfig.cpp:1038 src/libslic3r/PrintConfig.cpp:2286 msgid "Advanced" msgstr "고급" -#: src/slic3r/GUI/GUI_App.cpp:764 +#: src/slic3r/GUI/GUI_App.cpp:814 msgid "Advanced View Mode" msgstr "고급 보기 모드" -#: src/slic3r/GUI/GUI_App.cpp:765 src/slic3r/GUI/wxExtensions.cpp:2884 +#: src/slic3r/GUI/GUI_App.cpp:815 src/slic3r/GUI/wxExtensions.cpp:732 msgid "Expert" msgstr "전문가" -#: src/slic3r/GUI/GUI_App.cpp:765 +#: src/slic3r/GUI/GUI_App.cpp:815 msgid "Expert View Mode" msgstr "전문가 보기 모드" -#: src/slic3r/GUI/GUI_App.cpp:770 +#: src/slic3r/GUI/GUI_App.cpp:820 msgid "Mode" msgstr "모드" -#: src/slic3r/GUI/GUI_App.cpp:770 +#: src/slic3r/GUI/GUI_App.cpp:820 #, c-format msgid "%s View Mode" msgstr "%s 보기 모드" -#: src/slic3r/GUI/GUI_App.cpp:772 +#: src/slic3r/GUI/GUI_App.cpp:822 msgid "Change Application &Language" msgstr "응용 프로그램 언어 번경" -#: src/slic3r/GUI/GUI_App.cpp:774 +#: src/slic3r/GUI/GUI_App.cpp:824 msgid "Flash printer &firmware" msgstr "프린터 펌웨어 플래시" -#: src/slic3r/GUI/GUI_App.cpp:774 +#: src/slic3r/GUI/GUI_App.cpp:824 msgid "Upload a firmware image into an Arduino based printer" msgstr "아두이노 기반의 프린터 이미지 업로드" -#: src/slic3r/GUI/GUI_App.cpp:786 +#: src/slic3r/GUI/GUI_App.cpp:839 msgid "Taking configuration snapshot" msgstr "구성 스냅 샷 만들기" -#: src/slic3r/GUI/GUI_App.cpp:786 +#: src/slic3r/GUI/GUI_App.cpp:839 msgid "Snapshot name" msgstr "스냅 샷 이름" -#: src/slic3r/GUI/GUI_App.cpp:829 +#: src/slic3r/GUI/GUI_App.cpp:882 msgid "" "Switching the language will trigger application restart.\n" "You will lose content of the plater." msgstr "" -"언어를 전환 하면 응용 프로그램 재시작 합니다.플레이트 위 오브젝트는 모두 지워" -"집니다." +"언어를 전환 하면 응용 프로그램 재시작 합니다. 플레이트 위 객체(object)는 모" +"두 지워집니다." -#: src/slic3r/GUI/GUI_App.cpp:831 +#: src/slic3r/GUI/GUI_App.cpp:884 msgid "Do you want to proceed?" msgstr "계속 하시겠습니까?" -#: src/slic3r/GUI/GUI_App.cpp:832 +#: src/slic3r/GUI/GUI_App.cpp:885 msgid "Language selection" msgstr "국가에 맞는 언어를 선택" -#: src/slic3r/GUI/GUI_App.cpp:855 +#: src/slic3r/GUI/GUI_App.cpp:908 msgid "&Configuration" msgstr "&구성" -#: src/slic3r/GUI/GUI_App.cpp:877 +#: src/slic3r/GUI/GUI_App.cpp:932 msgid "The presets on the following tabs were modified" msgstr "다음 탭의 사전 설정이 수정 되었습니다" -#: src/slic3r/GUI/GUI_App.cpp:877 src/slic3r/GUI/Tab.cpp:3051 +#: src/slic3r/GUI/GUI_App.cpp:932 src/slic3r/GUI/Tab.cpp:2916 msgid "Discard changes and continue anyway?" msgstr "수정된 사항을 취소하고 계속하겠습니까?" -#: src/slic3r/GUI/GUI_App.cpp:880 +#: src/slic3r/GUI/GUI_App.cpp:935 msgid "Unsaved Presets" msgstr "저장되지 않은 기존설정" -#: src/slic3r/GUI/GUI_ObjectList.cpp:30 src/slic3r/GUI/GUI_ObjectList.cpp:84 -#: src/slic3r/GUI/GUI_ObjectList.cpp:558 src/libslic3r/PrintConfig.cpp:67 -#: src/libslic3r/PrintConfig.cpp:160 src/libslic3r/PrintConfig.cpp:392 -#: src/libslic3r/PrintConfig.cpp:453 src/libslic3r/PrintConfig.cpp:461 -#: src/libslic3r/PrintConfig.cpp:867 src/libslic3r/PrintConfig.cpp:1051 -#: src/libslic3r/PrintConfig.cpp:1354 src/libslic3r/PrintConfig.cpp:1420 -#: src/libslic3r/PrintConfig.cpp:1601 src/libslic3r/PrintConfig.cpp:2037 -#: src/libslic3r/PrintConfig.cpp:2095 +#: src/slic3r/GUI/GUI_App.cpp:1084 src/slic3r/GUI/Tab.cpp:2928 +msgid "It's impossible to print multi-part object(s) with SLA technology." +msgstr "SLA 방식을 사용 하여 다중 객체(object)를 인쇄할 수는 없습니다." + +#: src/slic3r/GUI/GUI_App.cpp:1085 +msgid "Please check and fix your object list." +msgstr "객체(object) 목록을 확인 하고 수정 하십시오." + +#: src/slic3r/GUI/GUI_App.cpp:1086 src/slic3r/GUI/Plater.cpp:2299 +#: src/slic3r/GUI/Tab.cpp:2930 +msgid "Attention!" +msgstr "주목!" + +#: src/slic3r/GUI/GUI_App.cpp:1103 +msgid "Select a gcode file:" +msgstr "gcode 파일 선택:" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 +msgid "Start at height" +msgstr "높이에서 시작" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 +msgid "Stop at height" +msgstr "높이에서 정지" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:153 +msgid "Remove layer range" +msgstr "레이어 범위 제거" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:162 +msgid "Add layer range" +msgstr "레이어 범위 추가" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:34 src/slic3r/GUI/GUI_ObjectList.cpp:93 +#: src/slic3r/GUI/GUI_ObjectList.cpp:611 src/libslic3r/PrintConfig.cpp:72 +#: src/libslic3r/PrintConfig.cpp:165 src/libslic3r/PrintConfig.cpp:174 +#: src/libslic3r/PrintConfig.cpp:408 src/libslic3r/PrintConfig.cpp:470 +#: src/libslic3r/PrintConfig.cpp:478 src/libslic3r/PrintConfig.cpp:891 +#: src/libslic3r/PrintConfig.cpp:1076 src/libslic3r/PrintConfig.cpp:1383 +#: src/libslic3r/PrintConfig.cpp:1450 src/libslic3r/PrintConfig.cpp:1631 +#: src/libslic3r/PrintConfig.cpp:2084 src/libslic3r/PrintConfig.cpp:2143 +#: src/libslic3r/PrintConfig.cpp:2152 msgid "Layers and Perimeters" -msgstr "레이어 및 경계선" +msgstr "레이어 및 둘레" -#: src/slic3r/GUI/GUI_ObjectList.cpp:31 src/slic3r/GUI/GUI_ObjectList.cpp:85 -#: src/slic3r/GUI/GUI_ObjectList.cpp:559 src/slic3r/GUI/Plater.cpp:498 -#: src/slic3r/GUI/Tab.cpp:1041 src/slic3r/GUI/Tab.cpp:1042 -#: src/slic3r/GUI/Tab.cpp:1394 src/libslic3r/PrintConfig.cpp:177 -#: src/libslic3r/PrintConfig.cpp:400 src/libslic3r/PrintConfig.cpp:420 -#: src/libslic3r/PrintConfig.cpp:754 src/libslic3r/PrintConfig.cpp:768 -#: src/libslic3r/PrintConfig.cpp:805 src/libslic3r/PrintConfig.cpp:958 -#: src/libslic3r/PrintConfig.cpp:968 src/libslic3r/PrintConfig.cpp:986 -#: src/libslic3r/PrintConfig.cpp:1004 src/libslic3r/PrintConfig.cpp:1023 -#: src/libslic3r/PrintConfig.cpp:1708 src/libslic3r/PrintConfig.cpp:1725 -msgid "Infill" -msgstr "인필(채움)" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:32 src/slic3r/GUI/GUI_ObjectList.cpp:86 -#: src/slic3r/GUI/GUI_ObjectList.cpp:560 src/slic3r/GUI/GUI_Preview.cpp:243 -#: src/slic3r/GUI/Tab.cpp:1070 src/slic3r/GUI/Tab.cpp:1071 -#: src/libslic3r/PrintConfig.cpp:344 src/libslic3r/PrintConfig.cpp:1481 -#: src/libslic3r/PrintConfig.cpp:1830 src/libslic3r/PrintConfig.cpp:1836 -#: src/libslic3r/PrintConfig.cpp:1844 src/libslic3r/PrintConfig.cpp:1856 -#: src/libslic3r/PrintConfig.cpp:1866 src/libslic3r/PrintConfig.cpp:1874 -#: src/libslic3r/PrintConfig.cpp:1889 src/libslic3r/PrintConfig.cpp:1910 -#: src/libslic3r/PrintConfig.cpp:1921 src/libslic3r/PrintConfig.cpp:1937 -#: src/libslic3r/PrintConfig.cpp:1946 src/libslic3r/PrintConfig.cpp:1955 -#: src/libslic3r/PrintConfig.cpp:1966 src/libslic3r/PrintConfig.cpp:1980 -#: src/libslic3r/PrintConfig.cpp:1988 src/libslic3r/PrintConfig.cpp:1989 -#: src/libslic3r/PrintConfig.cpp:1998 src/libslic3r/PrintConfig.cpp:2006 -#: src/libslic3r/PrintConfig.cpp:2020 src/libslic3r/GCode/PreviewData.cpp:172 +#: src/slic3r/GUI/GUI_ObjectList.cpp:36 src/slic3r/GUI/GUI_ObjectList.cpp:95 +#: src/slic3r/GUI/GUI_ObjectList.cpp:613 src/slic3r/GUI/GUI_Preview.cpp:248 +#: src/slic3r/GUI/Tab.cpp:1110 src/slic3r/GUI/Tab.cpp:1111 +#: src/libslic3r/ExtrusionEntity.cpp:319 src/libslic3r/PrintConfig.cpp:360 +#: src/libslic3r/PrintConfig.cpp:1511 src/libslic3r/PrintConfig.cpp:1876 +#: src/libslic3r/PrintConfig.cpp:1882 src/libslic3r/PrintConfig.cpp:1890 +#: src/libslic3r/PrintConfig.cpp:1902 src/libslic3r/PrintConfig.cpp:1912 +#: src/libslic3r/PrintConfig.cpp:1920 src/libslic3r/PrintConfig.cpp:1935 +#: src/libslic3r/PrintConfig.cpp:1956 src/libslic3r/PrintConfig.cpp:1968 +#: src/libslic3r/PrintConfig.cpp:1984 src/libslic3r/PrintConfig.cpp:1993 +#: src/libslic3r/PrintConfig.cpp:2002 src/libslic3r/PrintConfig.cpp:2013 +#: src/libslic3r/PrintConfig.cpp:2027 src/libslic3r/PrintConfig.cpp:2035 +#: src/libslic3r/PrintConfig.cpp:2036 src/libslic3r/PrintConfig.cpp:2045 +#: src/libslic3r/PrintConfig.cpp:2053 src/libslic3r/PrintConfig.cpp:2067 msgid "Support material" msgstr "서포트 재료(Support material)" -#: src/slic3r/GUI/GUI_ObjectList.cpp:35 src/slic3r/GUI/GUI_ObjectList.cpp:90 -#: src/slic3r/GUI/GUI_ObjectList.cpp:564 src/libslic3r/PrintConfig.cpp:2202 -#: src/libslic3r/PrintConfig.cpp:2210 +#: src/slic3r/GUI/GUI_ObjectList.cpp:39 src/slic3r/GUI/GUI_ObjectList.cpp:99 +#: src/slic3r/GUI/GUI_ObjectList.cpp:617 src/libslic3r/PrintConfig.cpp:2262 +#: src/libslic3r/PrintConfig.cpp:2270 msgid "Wipe options" msgstr "지우기 옵션" -#: src/slic3r/GUI/GUI_ObjectList.cpp:41 +#: src/slic3r/GUI/GUI_ObjectList.cpp:45 msgid "Pad and Support" msgstr "패드 및 서포트" -#: src/slic3r/GUI/GUI_ObjectList.cpp:47 +#: src/slic3r/GUI/GUI_ObjectList.cpp:51 msgid "Add part" -msgstr "파트 추가" +msgstr "부품(Part) 추가" -#: src/slic3r/GUI/GUI_ObjectList.cpp:48 +#: src/slic3r/GUI/GUI_ObjectList.cpp:52 msgid "Add modifier" msgstr "편집영역(modifier) 추가" -#: src/slic3r/GUI/GUI_ObjectList.cpp:49 +#: src/slic3r/GUI/GUI_ObjectList.cpp:53 msgid "Add support enforcer" msgstr "서포트 지원(enforcer)영역 추가" -#: src/slic3r/GUI/GUI_ObjectList.cpp:50 +#: src/slic3r/GUI/GUI_ObjectList.cpp:54 msgid "Add support blocker" msgstr "서포트 금지영역(blocker) 추가" -#: src/slic3r/GUI/GUI_ObjectList.cpp:87 src/slic3r/GUI/GUI_ObjectList.cpp:561 -#: src/slic3r/GUI/GUI_Preview.cpp:222 src/slic3r/GUI/Tab.cpp:1095 -#: src/libslic3r/PrintConfig.cpp:209 src/libslic3r/PrintConfig.cpp:441 -#: src/libslic3r/PrintConfig.cpp:896 src/libslic3r/PrintConfig.cpp:1024 -#: src/libslic3r/PrintConfig.cpp:1410 src/libslic3r/PrintConfig.cpp:1647 -#: src/libslic3r/PrintConfig.cpp:1696 src/libslic3r/PrintConfig.cpp:1747 -#: src/libslic3r/PrintConfig.cpp:2080 +#: src/slic3r/GUI/GUI_ObjectList.cpp:96 src/slic3r/GUI/GUI_ObjectList.cpp:614 +#: src/slic3r/GUI/GUI_Preview.cpp:226 src/slic3r/GUI/Tab.cpp:1135 +#: src/libslic3r/PrintConfig.cpp:225 src/libslic3r/PrintConfig.cpp:458 +#: src/libslic3r/PrintConfig.cpp:920 src/libslic3r/PrintConfig.cpp:1049 +#: src/libslic3r/PrintConfig.cpp:1440 src/libslic3r/PrintConfig.cpp:1677 +#: src/libslic3r/PrintConfig.cpp:1726 src/libslic3r/PrintConfig.cpp:1778 +#: src/libslic3r/PrintConfig.cpp:2128 msgid "Speed" msgstr "속도" -#: src/slic3r/GUI/GUI_ObjectList.cpp:88 src/slic3r/GUI/GUI_ObjectList.cpp:562 -#: src/slic3r/GUI/Tab.cpp:1130 src/slic3r/GUI/Tab.cpp:1997 -#: src/libslic3r/PrintConfig.cpp:471 src/libslic3r/PrintConfig.cpp:979 -#: src/libslic3r/PrintConfig.cpp:1389 src/libslic3r/PrintConfig.cpp:1717 -#: src/libslic3r/PrintConfig.cpp:1902 src/libslic3r/PrintConfig.cpp:1928 +#: src/slic3r/GUI/GUI_ObjectList.cpp:97 src/slic3r/GUI/GUI_ObjectList.cpp:615 +#: src/slic3r/GUI/Tab.cpp:1170 src/slic3r/GUI/Tab.cpp:1822 +#: src/libslic3r/PrintConfig.cpp:488 src/libslic3r/PrintConfig.cpp:1003 +#: src/libslic3r/PrintConfig.cpp:1418 src/libslic3r/PrintConfig.cpp:1747 +#: src/libslic3r/PrintConfig.cpp:1948 src/libslic3r/PrintConfig.cpp:1975 msgid "Extruders" msgstr "익스트루더" -#: src/slic3r/GUI/GUI_ObjectList.cpp:89 src/slic3r/GUI/GUI_ObjectList.cpp:563 -#: src/libslic3r/PrintConfig.cpp:431 src/libslic3r/PrintConfig.cpp:538 -#: src/libslic3r/PrintConfig.cpp:855 src/libslic3r/PrintConfig.cpp:987 -#: src/libslic3r/PrintConfig.cpp:1398 src/libslic3r/PrintConfig.cpp:1737 -#: src/libslic3r/PrintConfig.cpp:1911 src/libslic3r/PrintConfig.cpp:2069 +#: src/slic3r/GUI/GUI_ObjectList.cpp:98 src/slic3r/GUI/GUI_ObjectList.cpp:616 +#: src/libslic3r/PrintConfig.cpp:447 src/libslic3r/PrintConfig.cpp:555 +#: src/libslic3r/PrintConfig.cpp:878 src/libslic3r/PrintConfig.cpp:1011 +#: src/libslic3r/PrintConfig.cpp:1427 src/libslic3r/PrintConfig.cpp:1767 +#: src/libslic3r/PrintConfig.cpp:1957 src/libslic3r/PrintConfig.cpp:2116 msgid "Extrusion Width" msgstr "압출 폭" -#: src/slic3r/GUI/GUI_ObjectList.cpp:95 src/slic3r/GUI/GUI_ObjectList.cpp:569 -#: src/slic3r/GUI/Plater.cpp:466 src/slic3r/GUI/Tab.cpp:3655 -#: src/slic3r/GUI/Tab.cpp:3656 src/libslic3r/PrintConfig.cpp:2469 -#: src/libslic3r/PrintConfig.cpp:2476 src/libslic3r/PrintConfig.cpp:2485 -#: src/libslic3r/PrintConfig.cpp:2494 src/libslic3r/PrintConfig.cpp:2504 -#: src/libslic3r/PrintConfig.cpp:2530 src/libslic3r/PrintConfig.cpp:2537 -#: src/libslic3r/PrintConfig.cpp:2548 src/libslic3r/PrintConfig.cpp:2558 -#: src/libslic3r/PrintConfig.cpp:2567 src/libslic3r/PrintConfig.cpp:2580 -#: src/libslic3r/PrintConfig.cpp:2590 src/libslic3r/PrintConfig.cpp:2599 -#: src/libslic3r/PrintConfig.cpp:2609 src/libslic3r/PrintConfig.cpp:2621 -#: src/libslic3r/PrintConfig.cpp:2629 +#: src/slic3r/GUI/GUI_ObjectList.cpp:104 src/slic3r/GUI/GUI_ObjectList.cpp:622 +#: src/slic3r/GUI/Plater.cpp:492 src/slic3r/GUI/Tab.cpp:3576 +#: src/slic3r/GUI/Tab.cpp:3577 src/libslic3r/PrintConfig.cpp:2615 +#: src/libslic3r/PrintConfig.cpp:2622 src/libslic3r/PrintConfig.cpp:2631 +#: src/libslic3r/PrintConfig.cpp:2640 src/libslic3r/PrintConfig.cpp:2650 +#: src/libslic3r/PrintConfig.cpp:2676 src/libslic3r/PrintConfig.cpp:2683 +#: src/libslic3r/PrintConfig.cpp:2694 src/libslic3r/PrintConfig.cpp:2704 +#: src/libslic3r/PrintConfig.cpp:2713 src/libslic3r/PrintConfig.cpp:2726 +#: src/libslic3r/PrintConfig.cpp:2736 src/libslic3r/PrintConfig.cpp:2745 +#: src/libslic3r/PrintConfig.cpp:2755 src/libslic3r/PrintConfig.cpp:2766 +#: src/libslic3r/PrintConfig.cpp:2774 msgid "Supports" msgstr "서포트" -#: src/slic3r/GUI/GUI_ObjectList.cpp:96 src/slic3r/GUI/GUI_ObjectList.cpp:570 -#: src/slic3r/GUI/Tab.cpp:3684 src/slic3r/GUI/Tab.cpp:3685 -#: src/libslic3r/PrintConfig.cpp:2637 src/libslic3r/PrintConfig.cpp:2644 -#: src/libslic3r/PrintConfig.cpp:2658 src/libslic3r/PrintConfig.cpp:2668 -#: src/libslic3r/PrintConfig.cpp:2681 src/libslic3r/PrintConfig.cpp:2690 -#: src/libslic3r/PrintConfig.cpp:2701 src/libslic3r/PrintConfig.cpp:2712 -#: src/libslic3r/PrintConfig.cpp:2722 src/libslic3r/PrintConfig.cpp:2732 +#: src/slic3r/GUI/GUI_ObjectList.cpp:105 src/slic3r/GUI/GUI_ObjectList.cpp:623 +#: src/slic3r/GUI/Plater.cpp:632 src/slic3r/GUI/Tab.cpp:3608 +#: src/slic3r/GUI/Tab.cpp:3609 src/libslic3r/PrintConfig.cpp:2782 +#: src/libslic3r/PrintConfig.cpp:2789 src/libslic3r/PrintConfig.cpp:2803 +#: src/libslic3r/PrintConfig.cpp:2814 src/libslic3r/PrintConfig.cpp:2824 +#: src/libslic3r/PrintConfig.cpp:2846 src/libslic3r/PrintConfig.cpp:2857 +#: src/libslic3r/PrintConfig.cpp:2864 src/libslic3r/PrintConfig.cpp:2871 +#: src/libslic3r/PrintConfig.cpp:2882 src/libslic3r/PrintConfig.cpp:2891 +#: src/libslic3r/PrintConfig.cpp:2900 msgid "Pad" msgstr "패드" -#: src/slic3r/GUI/GUI_ObjectList.cpp:217 +#: src/slic3r/GUI/GUI_ObjectList.cpp:106 src/slic3r/GUI/Tab.cpp:3626 +#: src/slic3r/GUI/Tab.cpp:3627 src/libslic3r/SLA/Hollowing.cpp:46 +#: src/libslic3r/SLA/Hollowing.cpp:58 src/libslic3r/SLA/Hollowing.cpp:67 +#: src/libslic3r/SLA/Hollowing.cpp:76 src/libslic3r/PrintConfig.cpp:2910 +#: src/libslic3r/PrintConfig.cpp:2917 src/libslic3r/PrintConfig.cpp:2927 +#: src/libslic3r/PrintConfig.cpp:2936 +msgid "Hollowing" +msgstr "속이 빈 공동(Hollowing)" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:268 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:153 msgid "Name" msgstr "이름" -#: src/slic3r/GUI/GUI_ObjectList.cpp:271 -#, c-format -msgid "Auto-repaired (%d errors):\n" -msgstr "오류자동수정 (%d errors)\n" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:278 -msgid "degenerate facets" -msgstr "더러운 면" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:279 -msgid "edges fixed" -msgstr "모서리 고정" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:280 -msgid "facets removed" -msgstr "면 제거" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:281 -msgid "facets added" -msgstr "면 추가됨" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:282 -msgid "facets reversed" -msgstr "면 반전" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:283 -msgid "backwards edges" -msgstr "뒤쪽 가장자리" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:291 -msgid "Right button click the icon to fix STL through Netfabb" -msgstr "아이콘을 클릭 하여 Netfabb에서 STL을 수정 합니다" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:325 -msgid "Right button click the icon to change the object settings" -msgstr "아이콘을 클릭 하여 개체 설정을 변경 합니다" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:375 src/slic3r/GUI/GUI_ObjectList.cpp:396 -#: src/slic3r/GUI/GUI_ObjectList.cpp:408 src/slic3r/GUI/GUI_ObjectList.cpp:3508 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3518 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3550 src/slic3r/GUI/wxExtensions.cpp:576 -#: src/slic3r/GUI/wxExtensions.cpp:633 src/slic3r/GUI/wxExtensions.cpp:658 -#: src/slic3r/GUI/wxExtensions.cpp:794 -msgid "default" -msgstr "기본값" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:380 src/slic3r/GUI/Tab.cpp:1613 -#: src/libslic3r/PrintConfig.cpp:470 +#: src/slic3r/GUI/GUI_ObjectList.cpp:276 src/slic3r/GUI/Tab.cpp:1428 +#: src/slic3r/GUI/wxExtensions.cpp:589 src/libslic3r/PrintConfig.cpp:487 msgid "Extruder" msgstr "익스트루더(Extruder)" -#: src/slic3r/GUI/GUI_ObjectList.cpp:493 +#: src/slic3r/GUI/GUI_ObjectList.cpp:280 src/slic3r/GUI/GUI_ObjectList.cpp:392 +msgid "Editing" +msgstr "편집" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:337 +#, c-format +msgid "Auto-repaired (%d errors):" +msgstr "오류자동수정 (%d errors):" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:344 +msgid "degenerate facets" +msgstr "더러운 면" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:345 +msgid "edges fixed" +msgstr "모서리 고정" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:346 +msgid "facets removed" +msgstr "면 제거" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:347 +msgid "facets added" +msgstr "면 추가됨" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:348 +msgid "facets reversed" +msgstr "면 반전" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:349 +msgid "backwards edges" +msgstr "뒤쪽 가장자리" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:357 +msgid "Right button click the icon to fix STL through Netfabb" +msgstr "아이콘을 클릭 하여 Netfabb에서 STL을 수정 합니다" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:394 +msgid "Right button click the icon to change the object settings" +msgstr "아이콘을 클릭 하여 객체(object) 설정을 변경 합니다" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:396 +msgid "Click the icon to change the object settings" +msgstr "아이콘을 클릭 하여 오브젝트 설정을 변경 합니다" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:400 +msgid "Right button click the icon to change the object printable property" +msgstr "오른쪽 버튼이 아이콘을 클릭하여 객체 인쇄 가능한 속성을 변경합니다." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:402 +msgid "Click the icon to change the object printable property" +msgstr "아이콘을 클릭하여 객체 인쇄 가능 속성을 변경합니다." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:455 src/slic3r/GUI/GUI_ObjectList.cpp:467 +#: src/slic3r/GUI/GUI_ObjectList.cpp:915 src/slic3r/GUI/GUI_ObjectList.cpp:3947 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3957 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3992 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:200 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:257 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:282 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:490 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:1725 +msgid "default" +msgstr "기본값" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:534 +msgid "Change Extruder" +msgstr "압출기(익스트루더) 변경" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:549 msgid "Rename Object" -msgstr "개체 이름 바꾸기" +msgstr "객체(object) 이름 바꾸기" -#: src/slic3r/GUI/GUI_ObjectList.cpp:493 +#: src/slic3r/GUI/GUI_ObjectList.cpp:549 msgid "Rename Sub-object" -msgstr "하위 오브젝트 이름 바꾸기" +msgstr "하위 객체(object) 이름 바꾸기" -#: src/slic3r/GUI/GUI_ObjectList.cpp:934 src/slic3r/GUI/GUI_ObjectList.cpp:3346 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1089 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3756 msgid "Instances to Separated Objects" -msgstr "분리된 객체에 대한 복제본" +msgstr "분리된 객체(object)에 대한 복제본" -#: src/slic3r/GUI/GUI_ObjectList.cpp:952 -msgid "Remove Volume(s)" -msgstr "볼륨 제거" +#: src/slic3r/GUI/GUI_ObjectList.cpp:1104 +msgid "Volumes in Object reordered" +msgstr "개체의 볼륨이 재정렬되었습니다." -#: src/slic3r/GUI/GUI_ObjectList.cpp:1007 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1316 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1322 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1556 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1104 +msgid "Object reordered" +msgstr "개체 순서가 다시 지정되었습니다." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1180 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1528 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1534 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1828 #, c-format msgid "Quick Add Settings (%s)" msgstr "빠른 추가 설정 (%s)" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1077 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1263 msgid "Select showing settings" -msgstr "설정 표시를 선택 합니다" +msgstr "표시된 설정을 선택 합니다" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1126 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1312 msgid "Add Settings for Layers" -msgstr "도면층에 대한 설정 추가" +msgstr "레이어 설정 추가" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1127 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1313 msgid "Add Settings for Sub-object" -msgstr "하위 개체에 대한 설정 추가" +msgstr "하위 객체(object)에 대한 설정 추가" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1128 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1314 msgid "Add Settings for Object" -msgstr "개체에 대한 설정 추가" +msgstr "객체(object)에 대한 설정 추가" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1189 -msgid "Add Settings Bundle for Layers" -msgstr "레이어에 대한 설정 번들 추가" +#: src/slic3r/GUI/GUI_ObjectList.cpp:1384 +msgid "Add Settings Bundle for Height range" +msgstr "높이 범위에 대한 설정 번들 추가" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1190 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1385 msgid "Add Settings Bundle for Sub-object" -msgstr "하위 오브젝트에 대한 설정 번들 추가" +msgstr "하위 객체(object)에 대한 번들 설정을 추가" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1191 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1386 msgid "Add Settings Bundle for Object" -msgstr "개체에 대한 설정 번들 추가" +msgstr "객체(object)에 대한 번들 설정을 추가" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1230 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1425 msgid "Load" msgstr "불러오기" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1235 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1260 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1263 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1430 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1462 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1466 msgid "Box" msgstr "박스" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1235 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1430 msgid "Cylinder" msgstr "원통" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1235 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1430 msgid "Sphere" msgstr "영역" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1235 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1430 msgid "Slab" msgstr "슬랩" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1287 -msgid "Edit Layers" -msgstr "레이어 편집" +#: src/slic3r/GUI/GUI_ObjectList.cpp:1498 +msgid "Height range Modifier" +msgstr "높이 범위에 대한 설정" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1295 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1507 msgid "Add settings" msgstr "다음 설정" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1362 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1582 msgid "Change type" msgstr "타입 변경" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1369 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1510 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1589 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1601 msgid "Set as a Separated Object" -msgstr "분리 된 객체로 설정" +msgstr "분리 된 객체(object)로 설정" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1375 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1601 +msgid "Set as a Separated Objects" +msgstr "분리된 객체(object)로 설정" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1611 +msgid "Printable" +msgstr "인쇄가능" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1626 msgid "Rename" msgstr "이름 변경" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1386 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1637 msgid "Fix through the Netfabb" -msgstr "네트워크를 통해 수정" +msgstr "Netfabb를 통해 수정" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1396 src/slic3r/GUI/Plater.cpp:3496 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1647 src/slic3r/GUI/Plater.cpp:3987 msgid "Export as STL" msgstr "STL로 내보내기" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1403 -msgid "Change extruder" -msgstr "압출기(익스트루더) 변경" +#: src/slic3r/GUI/GUI_ObjectList.cpp:1655 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1658 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3932 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3935 src/slic3r/GUI/Plater.cpp:3946 +#: src/slic3r/GUI/Plater.cpp:3949 +msgid "Reload the selected volumes from disk" +msgstr "디스크에서 선택한 볼륨 다시 로드" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1422 src/libslic3r/PrintConfig.cpp:309 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1685 src/libslic3r/PrintConfig.cpp:325 msgid "Default" msgstr "기본값" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1428 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1691 msgid "Select new extruder for the object/part" -msgstr "객체/부품에 대한 새 압출(익스트루더) 기 선택" +msgstr "객체(object)/부품(Part)에 대한 새 압출(익스트루더) 기 선택" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1440 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1703 msgid "Scale to print volume" msgstr "인쇄 볼륨에 따라 배율 조정" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1440 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1703 msgid "Scale the selected object to fit the print volume" -msgstr "인쇄 볼륨에 맞게 선택한 오브젝트의 배율 조정" +msgstr "인쇄 볼륨에 맞게 선택한 객체(object)의 배율 조정" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1510 -msgid "Set as a Separated Objects" -msgstr "분리 된 객체로 설정" +#: src/slic3r/GUI/GUI_ObjectList.cpp:1772 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2030 +msgid "Add Shape" +msgstr "모양 추가" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1585 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1858 msgid "Load Part" -msgstr "하중 부품" +msgstr "부품(Part)을 불러 오기" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1617 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1897 msgid "Error!" msgstr "에러!" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1662 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1972 msgid "Add Generic Subobject" -msgstr "일반 하위 개체 추가" +msgstr "기본이 되는 하위 객체(object) 추가" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1669 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2001 msgid "Generic" msgstr "일반" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1770 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1872 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2119 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2221 msgid "Last instance of an object cannot be deleted." -msgstr "개체의 마지막 복제본를 삭제할 수 없습니다." +msgstr "객체(object)의 마지막 복제본를 삭제할 수 없습니다." -#: src/slic3r/GUI/GUI_ObjectList.cpp:1782 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2131 msgid "Delete Settings" msgstr "설정 삭제" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1806 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2155 msgid "Delete All Instances from Object" -msgstr "개체에서 모든 복제본 삭제" +msgstr "객체(object)에서 모든 복제본 삭제" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1822 -msgid "Delete Layers Range" -msgstr "레이어 범위 삭제" +#: src/slic3r/GUI/GUI_ObjectList.cpp:2171 +msgid "Delete Height Range" +msgstr "높이 범위 삭제" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1853 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2202 msgid "From Object List You can't delete the last solid part from object." -msgstr "개체 목록에서 개체에서 마지막 솔리드 부품을 삭제할 수 없습니다." +msgstr "객체(object) 리스트에서 마지막 부품(Part)을 삭제할 수 없습니다." -#: src/slic3r/GUI/GUI_ObjectList.cpp:1857 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2206 msgid "Delete Subobject" -msgstr "하위 개체 삭제" +msgstr "하위 객체(object) 삭제" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1876 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2225 msgid "Delete Instance" msgstr "복제본 삭제" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1900 src/slic3r/GUI/Plater.cpp:2793 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2249 src/slic3r/GUI/Plater.cpp:2964 msgid "" "The selected object couldn't be split because it contains only one part." msgstr "" -"선택한 오브젝트는 파트 하나만 포함되어 있기 때문에 분할 할 수 없습니다." +"선택한 객체(object)는 부품(Part) 하나만 포함되어 있기 때문에 분할 할 수 없습" +"니다." -#: src/slic3r/GUI/GUI_ObjectList.cpp:1904 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2253 msgid "Split to Parts" -msgstr "파트로 분할" +msgstr "부품(Part)으로 분할" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1950 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2305 msgid "Add Layers" msgstr "레이어 추가" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2075 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2431 msgid "Group manipulation" msgstr "그룹 조작" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2087 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2443 msgid "Object manipulation" -msgstr "개체 조작" +msgstr "객체(object) 조작" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2100 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2456 msgid "Object Settings to modify" -msgstr "수정할 개체 설정" +msgstr "수정할 객체(object) 설정" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2104 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2460 msgid "Part Settings to modify" -msgstr "수정할 부품 설정" +msgstr "수정할 부품(Part) 설정" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2109 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2465 msgid "Layer range Settings to modify" msgstr "레이어 범위 설정 수정" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2115 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2471 msgid "Part manipulation" -msgstr "파트 조작" +msgstr "부품(Part) 조작" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2121 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2477 msgid "Instance manipulation" msgstr "복제본 제거" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2128 -msgid "Layers Editing" -msgstr "레이어층을 편집" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:2128 -msgid "Layer Editing" -msgstr "레이어 편집" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:2303 -msgid "Delete Selected Item" -msgstr "선택한 항목 삭제" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:2415 -msgid "Delete Selected" -msgstr "선택된 것을 삭제" +#: src/slic3r/GUI/GUI_ObjectList.cpp:2484 +msgid "Height ranges" +msgstr "높이 범위" #: src/slic3r/GUI/GUI_ObjectList.cpp:2484 -#: src/slic3r/GUI/GUI_ObjectList.cpp:2513 -#: src/slic3r/GUI/GUI_ObjectList.cpp:2531 -msgid "Add New Layers Range" -msgstr "새 레이어 범위 추가" +msgid "Settings for height range" +msgstr "높이 범위에 대한 설정" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2590 -msgid "Edit Layers Range" -msgstr "레이어 범위 편집" +#: src/slic3r/GUI/GUI_ObjectList.cpp:2670 +msgid "Delete Selected Item" +msgstr "선택한 항목(item) 삭제" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2867 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2807 +msgid "Delete Selected" +msgstr "선택된 것 삭제" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2873 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2902 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2920 +msgid "Add Height Range" +msgstr "높이 범위 추가" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2980 +msgid "Edit Height Range" +msgstr "높이 범위 편집" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3266 msgid "Selection-Remove from list" msgstr "목록에서 선택-제거" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2875 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3274 msgid "Selection-Add from list" msgstr "목록에서 선택-추가" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2993 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3392 msgid "Object or Instance" -msgstr "개체 또는 복제본" +msgstr "객체(object) 또는 복제본" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2994 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3127 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3393 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3526 msgid "Part" -msgstr "부품(Part)" +msgstr "부품(Part)(Part)" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2994 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3393 msgid "Layer" msgstr "레이어" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2996 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3395 msgid "Unsupported selection" msgstr "지원 되지 않는 선택" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2997 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3396 #, c-format msgid "You started your selection with %s Item." -msgstr "%s 항목으로 선택을 시작 했습니다." +msgstr "%s 선택된 항목으로 시작합니다." -#: src/slic3r/GUI/GUI_ObjectList.cpp:2998 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3397 #, c-format msgid "In this mode you can select only other %s Items%s" msgstr "이 모드에서는 %s의 다른 %s 항목만 선택할 수 있습니다" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3001 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3400 msgid "of a current Object" -msgstr "현재 개체의" +msgstr "현재 객체(object)의" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3006 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3081 src/slic3r/GUI/Plater.cpp:126 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3405 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3480 src/slic3r/GUI/Plater.cpp:143 msgid "Info" msgstr "정보" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3122 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3521 msgid "You can't change a type of the last solid part of the object." -msgstr "객체의 마지막 솔리드 부품 유형은 변경할 수 없습니다." +msgstr "객체(object)의 마지막 부품(Part) 유형은 변경할 수 없습니다." -#: src/slic3r/GUI/GUI_ObjectList.cpp:3127 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3526 msgid "Modifier" msgstr "편집 영역" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3127 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3526 msgid "Support Enforcer" msgstr "서포트 지원 영역" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3127 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3526 msgid "Support Blocker" msgstr "서포트 금지 영역" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3129 -msgid "Type:" -msgstr "형식:" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:3129 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3528 msgid "Select type of part" -msgstr "부품 유형 선택" +msgstr "부품(Part) 유형 선택" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3134 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3533 msgid "Change Part Type" -msgstr "부품 유형 변경" +msgstr "부품(Part) 유형 변경" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3368 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3778 msgid "Enter new name" msgstr "새 이름 입력" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3368 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3778 msgid "Renaming" msgstr "이름 바꾸기" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3384 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3480 src/slic3r/GUI/Tab.cpp:3536 -#: src/slic3r/GUI/Tab.cpp:3540 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3794 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3901 src/slic3r/GUI/Tab.cpp:3426 +#: src/slic3r/GUI/Tab.cpp:3430 msgid "The supplied name is not valid;" msgstr "제공된 이름이 유효하지 않습니다;" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3385 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3481 src/slic3r/GUI/Tab.cpp:3537 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3795 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3902 src/slic3r/GUI/Tab.cpp:3427 msgid "the following characters are not allowed:" msgstr "다음 문자는 허용되지 않습니다:" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3498 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3927 msgid "Set extruder for selected items" msgstr "선택한 항목에 대한 압출기(익스트루더) 설정" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3499 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3928 msgid "Select extruder number for selected objects and/or parts" -msgstr "선택한 객체 및 부품에 대한 압출기(익스트루더) 번호 선택" +msgstr "선택한 객체(object) 및 부품(Part)에 대한 압출기(익스트루더) 번호 선택" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3512 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3951 msgid "Select extruder number:" msgstr "압출기(익스트루더) 번호 선택:" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3513 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3952 msgid "This extruder will be set for selected items" msgstr "선택한 항목에 대한 압출기(익스트루더) 설정" +#: src/slic3r/GUI/GUI_ObjectList.cpp:3977 +msgid "Change Extruders" +msgstr "돌출기 변경" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:4074 src/slic3r/GUI/Selection.cpp:1474 +msgid "Set Printable" +msgstr "인쇄 가능 설정" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:4074 src/slic3r/GUI/Selection.cpp:1474 +msgid "Set Unprintable" +msgstr "인쇄할 수 없는 설정" + #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:62 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:105 msgid "World coordinates" @@ -1875,78 +2822,76 @@ msgstr "로컬 좌표" msgid "Select coordinate space, in which the transformation will be performed." msgstr "변환이 수행될 좌표 공간을 선택 합니다." -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:125 -msgid "Object Manipulation" -msgstr "개체 조작" - -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:176 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:155 src/libslic3r/GCode.cpp:638 msgid "Object name" -msgstr "개체 이름" +msgstr "객체(object) 이름" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:212 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:215 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:457 +msgid "Position" +msgstr "위치" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:216 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:458 +#: src/slic3r/GUI/Mouse3DController.cpp:321 +#: src/slic3r/GUI/Mouse3DController.cpp:344 +msgid "Rotation" +msgstr "회전" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:263 #, c-format msgid "Toggle %c axis mirroring" msgstr "전환 %c 축 미러링" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:245 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:297 msgid "Set Mirror" msgstr "미러 설정" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:285 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:290 -msgid "Reset scale" -msgstr "스케일 재설정" +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:337 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:349 +msgid "Drop to bed" +msgstr "배드를 아래로 내리기" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:303 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:363 msgid "Reset rotation" msgstr "회전 재설정" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:328 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:385 msgid "Reset Rotation" msgstr "회전 재설정" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:340 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:355 -msgid "Drop to bed" -msgstr "잠자리에 들기" +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:397 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:399 +msgid "Reset scale" +msgstr "크기 재설정" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:388 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:443 -msgid "Position" -msgstr "위치" - -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:389 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:444 -msgid "Rotation" -msgstr "회전" - -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:445 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:459 msgid "Scale factors" msgstr "축척 계수" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:502 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:516 msgid "Translate" msgstr "번역" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:554 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:578 msgid "" -"You cann't use non-uniform scaling mode for multiple objects/parts selection" +"You cannot use non-uniform scaling mode for multiple objects/parts selection" msgstr "" "여러 객체/부품 선택에 는 균일하지 않은 배율 조정 모드를 사용할 수 없습니다." -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:715 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:750 msgid "Set Position" msgstr "위치 설정" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:746 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:781 msgid "Set Orientation" msgstr "방향 설정" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:811 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:846 msgid "Set Scale" msgstr "축척 설정" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:895 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:875 msgid "" "The currently manipulated object is tilted (rotation angles are not " "multiples of 90°).\n" @@ -1954,969 +2899,1042 @@ msgid "" "coordinate system,\n" "once the rotation is embedded into the object coordinates." msgstr "" -"현재 조작 된 오브젝트가 기울어져 있습니다 (회전 각도가 90 °의 배수가 아님).\n" -"기울어진 오브젝트의 비균일 배율 조정은 표준 좌표계에서만 가능 합니다.\n" -"회전이 오브젝트 좌표로 삽입되면." +"현재 조작 된 객체(object)가 기울어져 있습니다 (회전 각도가 90°의 배수가 아" +"님).\n" +"기울어진 객체(object)의 배율 조정은 기본 좌표에서만 가능 합니다." -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:898 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:878 msgid "" "This operation is irreversible.\n" "Do you want to proceed?" msgstr "" "이 작업은 되돌릴수 없습니다.\n" -"계속 하 시겠습니까?" +"계속 하시겠습니까?" -#: src/slic3r/GUI/GUI_ObjectSettings.cpp:58 +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:59 msgid "Additional Settings" msgstr "추가적인 세팅" -#: src/slic3r/GUI/GUI_ObjectSettings.cpp:94 +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:95 msgid "Remove parameter" msgstr "매개 변수 제거" -#: src/slic3r/GUI/GUI_ObjectSettings.cpp:100 +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:101 #, c-format msgid "Delete Option %s" msgstr "삭제 %s 옵션" -#: src/slic3r/GUI/GUI_ObjectSettings.cpp:144 +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:152 #, c-format msgid "Change Option %s" msgstr "옵션 %s 변경" -#: src/slic3r/GUI/GUI_Preview.cpp:216 +#: src/slic3r/GUI/GUI_Preview.cpp:220 msgid "View" msgstr "보기" -#: src/slic3r/GUI/GUI_Preview.cpp:219 src/slic3r/GUI/GUI_Preview.cpp:554 -#: src/libslic3r/GCode/PreviewData.cpp:394 +#: src/slic3r/GUI/GUI_Preview.cpp:223 src/slic3r/GUI/GUI_Preview.cpp:577 +#: src/libslic3r/GCode/PreviewData.cpp:345 msgid "Feature type" msgstr "특색 유형" -#: src/slic3r/GUI/GUI_Preview.cpp:220 src/libslic3r/PrintConfig.cpp:483 +#: src/slic3r/GUI/GUI_Preview.cpp:224 src/libslic3r/PrintConfig.cpp:500 msgid "Height" msgstr "높이" -#: src/slic3r/GUI/GUI_Preview.cpp:221 src/libslic3r/PrintConfig.cpp:2188 +#: src/slic3r/GUI/GUI_Preview.cpp:225 src/libslic3r/PrintConfig.cpp:2248 msgid "Width" msgstr "폭" -#: src/slic3r/GUI/GUI_Preview.cpp:223 +#: src/slic3r/GUI/GUI_Preview.cpp:227 src/slic3r/GUI/Tab.cpp:1451 +msgid "Fan speed" +msgstr "팬 속도" + +#: src/slic3r/GUI/GUI_Preview.cpp:228 msgid "Volumetric flow rate" msgstr "용적의 유량값" -#: src/slic3r/GUI/GUI_Preview.cpp:224 src/slic3r/GUI/GUI_Preview.cpp:328 -#: src/slic3r/GUI/GUI_Preview.cpp:506 src/slic3r/GUI/GUI_Preview.cpp:553 -#: src/slic3r/GUI/GUI_Preview.cpp:749 src/libslic3r/GCode/PreviewData.cpp:404 +#: src/slic3r/GUI/GUI_Preview.cpp:229 src/slic3r/GUI/GUI_Preview.cpp:337 +#: src/slic3r/GUI/GUI_Preview.cpp:521 src/slic3r/GUI/GUI_Preview.cpp:576 +#: src/slic3r/GUI/GUI_Preview.cpp:831 src/libslic3r/GCode/PreviewData.cpp:357 msgid "Tool" msgstr "도구" -#: src/slic3r/GUI/GUI_Preview.cpp:225 src/slic3r/GUI/GUI_Preview.cpp:551 -#: src/libslic3r/GCode/PreviewData.cpp:406 +#: src/slic3r/GUI/GUI_Preview.cpp:230 src/slic3r/GUI/GUI_Preview.cpp:574 +#: src/libslic3r/GCode/PreviewData.cpp:359 msgid "Color Print" msgstr "컬러 프린트" -#: src/slic3r/GUI/GUI_Preview.cpp:228 +#: src/slic3r/GUI/GUI_Preview.cpp:233 msgid "Show" msgstr "보다" -#: src/slic3r/GUI/GUI_Preview.cpp:231 src/slic3r/GUI/GUI_Preview.cpp:232 +#: src/slic3r/GUI/GUI_Preview.cpp:236 src/slic3r/GUI/GUI_Preview.cpp:237 msgid "Feature types" msgstr "특색 유형" -#: src/slic3r/GUI/GUI_Preview.cpp:234 src/libslic3r/GCode/PreviewData.cpp:163 +#: src/slic3r/GUI/GUI_Preview.cpp:239 src/libslic3r/ExtrusionEntity.cpp:310 msgid "Perimeter" msgstr "가장자리" -#: src/slic3r/GUI/GUI_Preview.cpp:235 src/libslic3r/GCode/PreviewData.cpp:164 +#: src/slic3r/GUI/GUI_Preview.cpp:240 src/libslic3r/ExtrusionEntity.cpp:311 msgid "External perimeter" msgstr "외부 가장자리" -#: src/slic3r/GUI/GUI_Preview.cpp:236 src/libslic3r/GCode/PreviewData.cpp:165 +#: src/slic3r/GUI/GUI_Preview.cpp:241 src/libslic3r/ExtrusionEntity.cpp:312 msgid "Overhang perimeter" msgstr "오버행(Overhang) 둘레" -#: src/slic3r/GUI/GUI_Preview.cpp:237 src/libslic3r/GCode/PreviewData.cpp:166 +#: src/slic3r/GUI/GUI_Preview.cpp:242 src/libslic3r/ExtrusionEntity.cpp:313 msgid "Internal infill" msgstr "내부 채움" -#: src/slic3r/GUI/GUI_Preview.cpp:238 src/libslic3r/PrintConfig.cpp:1736 -#: src/libslic3r/PrintConfig.cpp:1746 src/libslic3r/GCode/PreviewData.cpp:167 +#: src/slic3r/GUI/GUI_Preview.cpp:243 src/libslic3r/ExtrusionEntity.cpp:314 +#: src/libslic3r/PrintConfig.cpp:1766 src/libslic3r/PrintConfig.cpp:1777 msgid "Solid infill" msgstr "솔리드 인필" -#: src/slic3r/GUI/GUI_Preview.cpp:239 src/libslic3r/PrintConfig.cpp:2068 -#: src/libslic3r/PrintConfig.cpp:2079 src/libslic3r/GCode/PreviewData.cpp:168 +#: src/slic3r/GUI/GUI_Preview.cpp:244 src/libslic3r/ExtrusionEntity.cpp:315 +#: src/libslic3r/PrintConfig.cpp:2115 src/libslic3r/PrintConfig.cpp:2127 msgid "Top solid infill" msgstr "가장 윗부분 채움" -#: src/slic3r/GUI/GUI_Preview.cpp:240 src/libslic3r/GCode/PreviewData.cpp:169 +#: src/slic3r/GUI/GUI_Preview.cpp:245 src/libslic3r/ExtrusionEntity.cpp:316 msgid "Bridge infill" msgstr "브릿지 채움" -#: src/slic3r/GUI/GUI_Preview.cpp:241 src/libslic3r/PrintConfig.cpp:895 -#: src/libslic3r/GCode/PreviewData.cpp:170 +#: src/slic3r/GUI/GUI_Preview.cpp:246 src/libslic3r/ExtrusionEntity.cpp:317 +#: src/libslic3r/PrintConfig.cpp:919 msgid "Gap fill" msgstr "공백 채움" -#: src/slic3r/GUI/GUI_Preview.cpp:242 src/slic3r/GUI/Tab.cpp:1061 -#: src/libslic3r/GCode/PreviewData.cpp:171 +#: src/slic3r/GUI/GUI_Preview.cpp:247 src/slic3r/GUI/Tab.cpp:1101 +#: src/libslic3r/ExtrusionEntity.cpp:318 msgid "Skirt" msgstr "스커트" -#: src/slic3r/GUI/GUI_Preview.cpp:244 src/libslic3r/PrintConfig.cpp:1954 -#: src/libslic3r/GCode/PreviewData.cpp:173 +#: src/slic3r/GUI/GUI_Preview.cpp:249 src/libslic3r/ExtrusionEntity.cpp:320 +#: src/libslic3r/PrintConfig.cpp:2001 msgid "Support material interface" -msgstr "서포트 재료 인터페이스" +msgstr "서포트 접점" -#: src/slic3r/GUI/GUI_Preview.cpp:245 src/slic3r/GUI/Tab.cpp:1141 -#: src/libslic3r/GCode/PreviewData.cpp:174 +#: src/slic3r/GUI/GUI_Preview.cpp:250 src/slic3r/GUI/Tab.cpp:1181 +#: src/libslic3r/ExtrusionEntity.cpp:321 msgid "Wipe tower" msgstr "와이프 타워(Wipe tower)" -#: src/slic3r/GUI/GUI_Preview.cpp:250 src/libslic3r/PrintConfig.cpp:2102 +#: src/slic3r/GUI/GUI_Preview.cpp:255 src/libslic3r/PrintConfig.cpp:2162 msgid "Travel" msgstr "이송" -#: src/slic3r/GUI/GUI_Preview.cpp:251 +#: src/slic3r/GUI/GUI_Preview.cpp:256 msgid "Retractions" msgstr "리트랙션" -#: src/slic3r/GUI/GUI_Preview.cpp:252 +#: src/slic3r/GUI/GUI_Preview.cpp:257 msgid "Unretractions" msgstr "리트랙션 취소" -#: src/slic3r/GUI/GUI_Preview.cpp:253 +#: src/slic3r/GUI/GUI_Preview.cpp:258 msgid "Shells" -msgstr "쉘" +msgstr "쉘(Shells)" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:13 src/slic3r/GUI/MainFrame.cpp:672 +#: src/slic3r/GUI/GUI_Preview.cpp:259 +msgid "Legend" +msgstr "범례" + +#: src/slic3r/GUI/Job.hpp:123 +msgid "ERROR: not enough resources to execute a new job." +msgstr "오류: 새 작업을 실행하기에 충분한 리소스가 없습니다." + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:14 src/slic3r/GUI/MainFrame.cpp:710 msgid "Keyboard Shortcuts" -msgstr "키보드 바로 가기" +msgstr "키보드 단축기" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:104 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:107 msgid "Open project STL/OBJ/AMF/3MF with config, delete bed" -msgstr "구성으로 프로젝트 STL/OBJ/AMF/3MF 열기, 배드 삭제" - -#: src/slic3r/GUI/KBShortcutsDialog.cpp:105 -msgid "Import STL/OBJ/AMF/3MF without config, keep bed" -msgstr "구성 없이 STL/OBJ/AMF/3MF 가져오기, 배드 유지" - -#: src/slic3r/GUI/KBShortcutsDialog.cpp:106 -msgid "Load Config from .ini/amf/3mf/gcode" -msgstr ".Ini/amf/3mf/gcode에서 구성 로드" - -#: src/slic3r/GUI/KBShortcutsDialog.cpp:107 src/slic3r/GUI/Plater.cpp:822 -#: src/slic3r/GUI/Plater.cpp:4687 src/libslic3r/PrintConfig.cpp:3127 -msgid "Export G-code" -msgstr "G-코드 내보내기" +msgstr "프로젝트 구성 열기(STL/OBJ/AMF/3MF), 배드 삭제" #: src/slic3r/GUI/KBShortcutsDialog.cpp:108 +msgid "Import STL/OBJ/AMF/3MF without config, keep bed" +msgstr "구성 없이 가져오기(STL/OBJ/AMF/3MF), 배드 유지" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:109 +msgid "Load Config from .ini/amf/3mf/gcode" +msgstr ".Ini/amf/3mf/gcode에서 구성 가져오기" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:110 src/slic3r/GUI/Plater.cpp:888 +#: src/slic3r/GUI/Plater.cpp:5544 src/libslic3r/PrintConfig.cpp:3348 +msgid "Export G-code" +msgstr "G-code 내보내기" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:111 msgid "Save project (3MF)" msgstr "프로젝트 저장 (3MF)" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:109 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:112 msgid "Load Config from .ini/amf/3mf/gcode and merge" -msgstr ".Ini/amf/3mf/gcode 및 병합에서 구성 로드" +msgstr ".Ini/amf/3mf/gcode 및 병합에서 구성 가져오기" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:110 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:113 msgid "(Re)slice" msgstr "(Re)슬라이스" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:113 -msgid "Select Plater Tab" -msgstr "선택 및 플래이트 탭" - -#: src/slic3r/GUI/KBShortcutsDialog.cpp:115 -msgid "Select Print Settings Tab" -msgstr "인쇄 설정 탭을 선택 합니다" - #: src/slic3r/GUI/KBShortcutsDialog.cpp:116 -msgid "Select Filament Settings Tab" -msgstr "필라멘트 설정 탭 선택" - -#: src/slic3r/GUI/KBShortcutsDialog.cpp:117 -msgid "Select Printer Settings Tab" -msgstr "프린터 설정 탭을 선택 합니다" +msgid "Select Plater Tab" +msgstr "선택 및 플래이터 탭" #: src/slic3r/GUI/KBShortcutsDialog.cpp:118 +msgid "Select Print Settings Tab" +msgstr "인쇄 설정을 선택 합니다" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:119 +msgid "Select Filament Settings Tab" +msgstr "필라멘트 설정을 선택" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:120 +msgid "Select Printer Settings Tab" +msgstr "프린터 설정을 선택 합니다" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:121 msgid "Switch to 3D" msgstr "3D로 전환" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:119 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:122 msgid "Switch to Preview" msgstr "미리 보기로 전환" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:120 src/slic3r/GUI/Preferences.cpp:10 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:123 src/slic3r/GUI/Preferences.cpp:10 msgid "Preferences" msgstr "기본 설정" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:121 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:124 #: src/slic3r/GUI/PrintHostDialogs.cpp:136 msgid "Print host upload queue" -msgstr "호스트 업로드 대기열 인쇄" +msgstr "프린터 호스트 업로드 대기" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:122 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:125 msgid "Camera view" msgstr "카메라 뷰" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:123 -msgid "Add Instance of the selected object" -msgstr "선택한 개체의 복제본 추가" - -#: src/slic3r/GUI/KBShortcutsDialog.cpp:124 -msgid "Remove Instance of the selected object" -msgstr "선택한 개체의 복제본 제거" - -#: src/slic3r/GUI/KBShortcutsDialog.cpp:125 -msgid "Show keyboard shortcuts list" -msgstr "바로 가기 키 목록 표시" - #: src/slic3r/GUI/KBShortcutsDialog.cpp:126 -msgid "Press to select multiple object or move multiple object with mouse" -msgstr "여러 개체를 선택 하거나 마우스로 여러 개체를 이동 하려면 누릅니다" +msgid "Add Instance of the selected object" +msgstr "선택한 객체(object)의 복제본 추가" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:127 +msgid "Remove Instance of the selected object" +msgstr "선택한 객체(object)의 복제본 제거" #: src/slic3r/GUI/KBShortcutsDialog.cpp:128 +msgid "Show keyboard shortcuts list" +msgstr "단축 키 목록 표시" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:129 +msgid "Press to select multiple object or move multiple object with mouse" +msgstr "" +"여러 객체(object)를 선택 하거나 마우스로 여러 객체(object)를 이동 하려면 누릅" +"니다" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:131 msgid "Main Shortcuts" msgstr "주요 단축키" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:135 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:139 msgid "Select All objects" -msgstr "모든 객체 선택" +msgstr "모든 객체(object) 선택" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:136 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:140 msgid "Delete selected" msgstr "선택 삭제" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:137 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:141 msgid "Delete All" msgstr "전부 지움" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:138 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:142 msgid "Copy to clipboard" msgstr "클립보드로 복사" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:139 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:143 msgid "Paste from clipboard" msgstr "클립보드에서 붙여넣기" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:140 -msgid "Gizmo move" -msgstr "객체 이동" - -#: src/slic3r/GUI/KBShortcutsDialog.cpp:141 -msgid "Gizmo scale" -msgstr "객체 배율" - -#: src/slic3r/GUI/KBShortcutsDialog.cpp:142 -msgid "Gizmo rotate" -msgstr "객체 회전" - -#: src/slic3r/GUI/KBShortcutsDialog.cpp:143 -msgid "Gizmo cut" -msgstr "기즈모 자르기" - #: src/slic3r/GUI/KBShortcutsDialog.cpp:144 -msgid "Gizmo Place face on bed" -msgstr "기즈모를 배드위에서" +msgid "Gizmo move" +msgstr "개체(Gizmo) 이동" #: src/slic3r/GUI/KBShortcutsDialog.cpp:145 -msgid "Gizmo SLA support points" -msgstr "객체 SLA 지원 포인트" +msgid "Gizmo scale" +msgstr "개체(Gizmo) 배율" #: src/slic3r/GUI/KBShortcutsDialog.cpp:146 -#, c-format +msgid "Gizmo rotate" +msgstr "개체(Gizmo) 회전" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:147 +msgid "Gizmo cut" +msgstr "개체(Gizmo) 자르기" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:148 +msgid "Gizmo Place face on bed" +msgstr "개체(Gizmo)를 배드위로" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:149 +msgid "Gizmo SLA support points" +msgstr "SLA 개체(Gizmo) 서포트 지점들" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:150 +#, no-c-format msgid "" "Press to activate selection rectangle\n" "or to snap by 5% in Gizmo scale\n" "or to snap by 1mm in Gizmo move" msgstr "" "활성화된 사각형을 선택합니다.\n" -"5% in 객체 크기를 스냅에 맞춰 조절합니다.\n" -"1mm 씩 객체를 스냅에 맞추 이동합니다." +"5% in 객체(object) 크기를 스냅에 맞춰 조절합니다.\n" +"1mm 씩 객체(object)를 스냅에 맞추 이동합니다." -#: src/slic3r/GUI/KBShortcutsDialog.cpp:147 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:151 msgid "" "Press to scale selection to fit print volume\n" "in Gizmo scale" -msgstr "" -"인쇄 볼륨에 맞게 선택 크기를 조정하려면 누릅니다.\n" -"기즈모 스케일" +msgstr "개체(Gizmo)크기를 인쇄 볼륨에 맞게 조정하려면 누릅니다." -#: src/slic3r/GUI/KBShortcutsDialog.cpp:148 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:152 msgid "" "Press to activate deselection rectangle\n" "or to scale or rotate selected objects\n" "around their own center" msgstr "" -"자신의 중심 주변\n" -"선택한 개체의 크기를 조정 하거나\n" +"중재봉선 주변으로. 선택한 개체의 크기를 조정 하거나 \n" "회전 하려면 누릅니다" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:149 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:153 msgid "Press to activate one direction scaling in Gizmo scale" -msgstr "기즈모 크기 조절을 활성화 합니다." +msgstr "개체(Gizmo) 크기 조절을 활성화 합니다." -#: src/slic3r/GUI/KBShortcutsDialog.cpp:150 -msgid "Change camera type" -msgstr "카메라 유형 변경" +#: src/slic3r/GUI/KBShortcutsDialog.cpp:154 +msgid "Change camera type (perspective, orthographic)" +msgstr "카메라 유형 변경(원근, 직교)" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:151 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:155 msgid "Zoom to Bed" msgstr "배드 확대" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:152 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:156 msgid "Zoom to all objects in scene, if none selected" -msgstr "장면의 모든 오브젝트로 확대/축소 (선택 하지 않은 경우)" +msgstr "모든 객체(object)를 확대/축소 (선택 하지 않은 경우)" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:153 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:157 msgid "Zoom to selected object" -msgstr "선택한 개체로 확대/축소" +msgstr "선택한 객체(object)를 확대/축소" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:154 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:158 msgid "Zoom in" msgstr "확대" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:155 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:159 msgid "Zoom out" -msgstr "줌 아웃" - -#: src/slic3r/GUI/KBShortcutsDialog.cpp:156 -msgid "Unselect gizmo / Clear selection" -msgstr "기즈모 선택을 취소 하거나 지우기" - -#: src/slic3r/GUI/KBShortcutsDialog.cpp:158 -msgid "Toggle picking pass texture rendering on/off" -msgstr "선택 패스 텍스처 렌더링 켜기/끄기 전환" +msgstr "축소" #: src/slic3r/GUI/KBShortcutsDialog.cpp:161 +msgid "Show/Hide object/instance labels" +msgstr "개체/인스턴스 레이블 표시/숨기기" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:163 +msgid "Show/Hide 3Dconnexion devices settings dialog" +msgstr "3Dconnexion 장치 설정 대화 상자 표시/숨기기" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:164 +msgid "Unselect gizmo / Clear selection" +msgstr "개체(Gizmo) 선택을 취소 하거나 지우기" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:170 msgid "Plater Shortcuts" msgstr "플레이터 단축기" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:176 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:187 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 msgid "Arrow Up" msgstr "위쪽 화살표" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:176 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:178 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:187 msgid "Upper Layer" msgstr "상위 레이어" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:177 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:188 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:186 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:198 msgid "Arrow Down" msgstr "아래쪽 화살표" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:177 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:179 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:186 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:188 msgid "Lower Layer" msgstr "하위 레이어" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:181 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:189 +msgid "Show/Hide (L)egend" +msgstr "표시/숨기기(L)egend" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:191 msgid "Preview Shortcuts" msgstr "미리보기 단축기" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:187 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 msgid "Move current slider thumb Up" -msgstr "현재 마우스 휠 슬라이더를 위로 이동" +msgstr "현재 마우스 휠을 위로 이동" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:188 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:198 msgid "Move current slider thumb Down" -msgstr "현재 마우스 휠 슬라이더를 아래로 이동" +msgstr "현재 마우스 휠을 아래로 이동" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:189 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:199 msgid "Arrow Left" msgstr "왼쪽 화살표" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:189 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:199 msgid "Set upper thumb to current slider thumb" msgstr "마우스 휠을 위로 움직여 설정" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:190 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:200 msgid "Arrow Right" msgstr "오른쪽 화살표" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:190 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:200 msgid "Set lower thumb to current slider thumb" msgstr "마우스 휠을 아래로 움직여 설정" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:191 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:201 msgid "Add color change marker for current layer" -msgstr "현재 레이어의 색상 변경 마커 추가" +msgstr "현재 레이어의 색상을 변경할 마커 추가" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:192 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:202 msgid "Delete color change marker for current layer" -msgstr "현재 레이어의 색상 변경 마커 삭제" +msgstr "현재 레이어의 색상을 변경할 마커 삭제" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:194 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:204 msgid "Layers Slider Shortcuts" msgstr "레이어 슬라이더 단축키" -#: src/slic3r/GUI/MainFrame.cpp:62 +#: src/slic3r/GUI/MainFrame.cpp:66 msgid "" " - Remember to check for updates at http://github.com/prusa3d/PrusaSlicer/" "releases" msgstr "" -" -http://github.com/prusa3d/slic3r/releases에서 업데이트를 확인하는 것을 잊" -"지 마십시오" +" -http://github.com/prusa3d/slic3r/releases에서 업데이트 확인하는 것을 잊지 " +"마십시오" -#: src/slic3r/GUI/MainFrame.cpp:157 +#: src/slic3r/GUI/MainFrame.cpp:174 msgid "based on Slic3r" msgstr "Slic3r 기반" -#: src/slic3r/GUI/MainFrame.cpp:187 +#: src/slic3r/GUI/MainFrame.cpp:204 msgid "Plater" msgstr "플레이터" -#: src/slic3r/GUI/MainFrame.cpp:393 +#: src/slic3r/GUI/MainFrame.cpp:405 msgid "&New Project" msgstr "&새로운 프로젝트" -#: src/slic3r/GUI/MainFrame.cpp:393 +#: src/slic3r/GUI/MainFrame.cpp:405 msgid "Start a new project" msgstr "새로운 프로젝트 시작" -#: src/slic3r/GUI/MainFrame.cpp:396 +#: src/slic3r/GUI/MainFrame.cpp:408 msgid "&Open Project" msgstr "&프로젝트 열기" -#: src/slic3r/GUI/MainFrame.cpp:396 +#: src/slic3r/GUI/MainFrame.cpp:408 msgid "Open a project file" msgstr "프로젝트 파일 열기" -#: src/slic3r/GUI/MainFrame.cpp:401 +#: src/slic3r/GUI/MainFrame.cpp:413 msgid "Recent projects" msgstr "최근 프로젝트" -#: src/slic3r/GUI/MainFrame.cpp:410 +#: src/slic3r/GUI/MainFrame.cpp:422 msgid "The selected project is no more available" msgstr "선택한 프로젝트를 더 이상 사용할 수 없습니다." -#: src/slic3r/GUI/MainFrame.cpp:410 src/slic3r/GUI/MainFrame.cpp:747 +#: src/slic3r/GUI/MainFrame.cpp:422 src/slic3r/GUI/MainFrame.cpp:787 #: src/slic3r/GUI/PrintHostDialogs.cpp:231 msgid "Error" msgstr "에러" -#: src/slic3r/GUI/MainFrame.cpp:434 +#: src/slic3r/GUI/MainFrame.cpp:446 msgid "&Save Project" msgstr "프로젝트 저장" -#: src/slic3r/GUI/MainFrame.cpp:434 +#: src/slic3r/GUI/MainFrame.cpp:446 msgid "Save current project file" msgstr "현재 프로젝트 파일 저장" -#: src/slic3r/GUI/MainFrame.cpp:438 src/slic3r/GUI/MainFrame.cpp:440 +#: src/slic3r/GUI/MainFrame.cpp:450 src/slic3r/GUI/MainFrame.cpp:452 msgid "Save Project &as" msgstr "프로젝트 저장" -#: src/slic3r/GUI/MainFrame.cpp:438 src/slic3r/GUI/MainFrame.cpp:440 +#: src/slic3r/GUI/MainFrame.cpp:450 src/slic3r/GUI/MainFrame.cpp:452 msgid "Save current project file as" msgstr "현재 프로젝트 파일을 다른 이름으로 저장" -#: src/slic3r/GUI/MainFrame.cpp:448 +#: src/slic3r/GUI/MainFrame.cpp:460 msgid "Import STL/OBJ/AM&F/3MF" msgstr "가져오기 STL/OBJ/AM&F/3MF" -#: src/slic3r/GUI/MainFrame.cpp:448 +#: src/slic3r/GUI/MainFrame.cpp:460 msgid "Load a model" msgstr "모델 로드" -#: src/slic3r/GUI/MainFrame.cpp:452 +#: src/slic3r/GUI/MainFrame.cpp:464 msgid "Import &Config" msgstr "&구성 가져오기" -#: src/slic3r/GUI/MainFrame.cpp:452 +#: src/slic3r/GUI/MainFrame.cpp:464 msgid "Load exported configuration file" msgstr "내 보낸 구성 파일로드" -#: src/slic3r/GUI/MainFrame.cpp:454 +#: src/slic3r/GUI/MainFrame.cpp:467 msgid "Import Config from &project" msgstr "프로젝트에서 설정 가져오기" -#: src/slic3r/GUI/MainFrame.cpp:454 +#: src/slic3r/GUI/MainFrame.cpp:467 msgid "Load configuration from project file" msgstr "프로젝트 파일에서 구성 로드" -#: src/slic3r/GUI/MainFrame.cpp:457 +#: src/slic3r/GUI/MainFrame.cpp:471 msgid "Import Config &Bundle" msgstr "번들 &설정 가져오기" -#: src/slic3r/GUI/MainFrame.cpp:457 +#: src/slic3r/GUI/MainFrame.cpp:471 msgid "Load presets from a bundle" -msgstr "번들에서 미리 설정로드" +msgstr "미리 설정 번들값 가져오기" -#: src/slic3r/GUI/MainFrame.cpp:459 +#: src/slic3r/GUI/MainFrame.cpp:474 msgid "&Import" msgstr "&가져오기" -#: src/slic3r/GUI/MainFrame.cpp:462 src/slic3r/GUI/MainFrame.cpp:708 +#: src/slic3r/GUI/MainFrame.cpp:477 src/slic3r/GUI/MainFrame.cpp:751 msgid "Export &G-code" -msgstr "G-코드 내보내기" +msgstr "G-code 내보내기" -#: src/slic3r/GUI/MainFrame.cpp:462 +#: src/slic3r/GUI/MainFrame.cpp:477 msgid "Export current plate as G-code" msgstr "현재 플레이터를 G 코드로 내보내기" -#: src/slic3r/GUI/MainFrame.cpp:466 src/slic3r/GUI/MainFrame.cpp:709 +#: src/slic3r/GUI/MainFrame.cpp:481 src/slic3r/GUI/MainFrame.cpp:752 msgid "S&end G-code" msgstr "S&엔드 G- 코드" -#: src/slic3r/GUI/MainFrame.cpp:466 +#: src/slic3r/GUI/MainFrame.cpp:481 msgid "Send to print current plate as G-code" msgstr "현재 플레이트를 G 코드로 인쇄하기 위해 보내기" -#: src/slic3r/GUI/MainFrame.cpp:471 +#: src/slic3r/GUI/MainFrame.cpp:486 msgid "Export plate as &STL" msgstr "STL로 내보내기" -#: src/slic3r/GUI/MainFrame.cpp:471 +#: src/slic3r/GUI/MainFrame.cpp:486 msgid "Export current plate as STL" msgstr "현재 플레이터를 STL로 내보내기" -#: src/slic3r/GUI/MainFrame.cpp:474 -msgid "Export plate as STL including supports" -msgstr "서포트를 포함하여 STL파일로 내보내기" +#: src/slic3r/GUI/MainFrame.cpp:489 +msgid "Export plate as STL &including supports" +msgstr "서포트를 포함하여 플레이터를 STL로 내보내기" -#: src/slic3r/GUI/MainFrame.cpp:474 +#: src/slic3r/GUI/MainFrame.cpp:489 msgid "Export current plate as STL including supports" msgstr "서포트를 포함 하여 현재 플레이터를 STL로 내보내기" -#: src/slic3r/GUI/MainFrame.cpp:477 +#: src/slic3r/GUI/MainFrame.cpp:492 msgid "Export plate as &AMF" msgstr "AMF로 내보내기" -#: src/slic3r/GUI/MainFrame.cpp:477 +#: src/slic3r/GUI/MainFrame.cpp:492 msgid "Export current plate as AMF" -msgstr "현재 플레이터를AMF로 내보내기" +msgstr "현재 플레이터를 AMF로 내보내기" -#: src/slic3r/GUI/MainFrame.cpp:481 +#: src/slic3r/GUI/MainFrame.cpp:496 +msgid "Export &toolpaths as OBJ" +msgstr "OBJ로 내보내기 및 공구 경로" + +#: src/slic3r/GUI/MainFrame.cpp:496 +msgid "Export toolpaths as OBJ" +msgstr "도구 경로를 OBJ로 내보내기" + +#: src/slic3r/GUI/MainFrame.cpp:500 msgid "Export &Config" msgstr "&구성 내보내기" -#: src/slic3r/GUI/MainFrame.cpp:481 +#: src/slic3r/GUI/MainFrame.cpp:500 msgid "Export current configuration to file" msgstr "현재 구성을 파일로 내보내기" -#: src/slic3r/GUI/MainFrame.cpp:483 +#: src/slic3r/GUI/MainFrame.cpp:503 msgid "Export Config &Bundle" msgstr "번들 & 내보내기 설정" -#: src/slic3r/GUI/MainFrame.cpp:483 +#: src/slic3r/GUI/MainFrame.cpp:503 msgid "Export all presets to file" msgstr "모든 이전 설정을 파일로 내보내기" -#: src/slic3r/GUI/MainFrame.cpp:485 +#: src/slic3r/GUI/MainFrame.cpp:506 msgid "&Export" msgstr "&내보내기" -#: src/slic3r/GUI/MainFrame.cpp:491 +#: src/slic3r/GUI/MainFrame.cpp:512 msgid "Quick Slice" msgstr "빠른 슬라이스" -#: src/slic3r/GUI/MainFrame.cpp:491 +#: src/slic3r/GUI/MainFrame.cpp:512 msgid "Slice a file into a G-code" msgstr "파일을 G 코드로 분할" -#: src/slic3r/GUI/MainFrame.cpp:497 +#: src/slic3r/GUI/MainFrame.cpp:518 msgid "Quick Slice and Save As" msgstr "빠른 슬라이스 및 다른 이름으로 저장" -#: src/slic3r/GUI/MainFrame.cpp:497 +#: src/slic3r/GUI/MainFrame.cpp:518 msgid "Slice a file into a G-code, save as" -msgstr "파일을 G 코드로 분할하고 다음으로 저장" +msgstr "파일을 G 코드로 분할하고 다른 이름으로 저장" -#: src/slic3r/GUI/MainFrame.cpp:503 +#: src/slic3r/GUI/MainFrame.cpp:524 msgid "Repeat Last Quick Slice" msgstr "마지막으로 빠른 슬라이스 반복" -#: src/slic3r/GUI/MainFrame.cpp:503 +#: src/slic3r/GUI/MainFrame.cpp:524 msgid "Repeat last quick slice" msgstr "마지막으로 빠른 슬라이스 반복" -#: src/slic3r/GUI/MainFrame.cpp:511 +#: src/slic3r/GUI/MainFrame.cpp:532 msgid "(Re)Slice No&w" msgstr "지금(다시)자르기" -#: src/slic3r/GUI/MainFrame.cpp:511 +#: src/slic3r/GUI/MainFrame.cpp:532 msgid "Start new slicing process" msgstr "새로운 슬라이싱 작업 시작" -#: src/slic3r/GUI/MainFrame.cpp:515 +#: src/slic3r/GUI/MainFrame.cpp:536 msgid "&Repair STL file" msgstr "STL 파일 복구" -#: src/slic3r/GUI/MainFrame.cpp:515 +#: src/slic3r/GUI/MainFrame.cpp:536 msgid "Automatically repair an STL file" msgstr "STL 파일을 자동으로 복구합니다" -#: src/slic3r/GUI/MainFrame.cpp:518 +#: src/slic3r/GUI/MainFrame.cpp:540 msgid "&Quit" msgstr "&종료" -#: src/slic3r/GUI/MainFrame.cpp:518 +#: src/slic3r/GUI/MainFrame.cpp:540 #, c-format msgid "Quit %s" msgstr "%s 종료" -#: src/slic3r/GUI/MainFrame.cpp:543 +#: src/slic3r/GUI/MainFrame.cpp:565 msgid "&Select all" msgstr "&모두 선택 " -#: src/slic3r/GUI/MainFrame.cpp:544 +#: src/slic3r/GUI/MainFrame.cpp:566 msgid "Selects all objects" -msgstr "모든 개체를 선택 합니다" +msgstr "모든 객체(object)를 선택 합니다" -#: src/slic3r/GUI/MainFrame.cpp:546 +#: src/slic3r/GUI/MainFrame.cpp:568 msgid "D&eselect all" -msgstr "선택 취소 D&select+" +msgstr "모든 선택 취소 D&select" -#: src/slic3r/GUI/MainFrame.cpp:547 +#: src/slic3r/GUI/MainFrame.cpp:569 msgid "Deselects all objects" -msgstr "모든 객체 선택 취소" +msgstr "모든 객체(object) 선택 취소" -#: src/slic3r/GUI/MainFrame.cpp:550 +#: src/slic3r/GUI/MainFrame.cpp:572 msgid "&Delete selected" msgstr "&선택 삭제 " -#: src/slic3r/GUI/MainFrame.cpp:551 +#: src/slic3r/GUI/MainFrame.cpp:573 msgid "Deletes the current selection" msgstr "현재 선택 영역을 삭제 합니다" -#: src/slic3r/GUI/MainFrame.cpp:553 +#: src/slic3r/GUI/MainFrame.cpp:575 msgid "Delete &all" msgstr "전부 지움 " -#: src/slic3r/GUI/MainFrame.cpp:554 +#: src/slic3r/GUI/MainFrame.cpp:576 msgid "Deletes all objects" -msgstr "모든 객체를 삭제 합니다" +msgstr "모든 객체(object)를 삭제 합니다" -#: src/slic3r/GUI/MainFrame.cpp:558 +#: src/slic3r/GUI/MainFrame.cpp:580 msgid "&Undo" msgstr "&되돌리기" -#: src/slic3r/GUI/MainFrame.cpp:561 +#: src/slic3r/GUI/MainFrame.cpp:583 msgid "&Redo" msgstr "&앞으로" -#: src/slic3r/GUI/MainFrame.cpp:566 +#: src/slic3r/GUI/MainFrame.cpp:588 msgid "&Copy" msgstr "&복사 " -#: src/slic3r/GUI/MainFrame.cpp:567 +#: src/slic3r/GUI/MainFrame.cpp:589 msgid "Copy selection to clipboard" msgstr "선택영역을 클립보드로 복사합니다" -#: src/slic3r/GUI/MainFrame.cpp:569 +#: src/slic3r/GUI/MainFrame.cpp:591 msgid "&Paste" msgstr "&붙이기 " -#: src/slic3r/GUI/MainFrame.cpp:570 +#: src/slic3r/GUI/MainFrame.cpp:592 msgid "Paste clipboard" msgstr "클립보드 붙여넣기" -#: src/slic3r/GUI/MainFrame.cpp:579 +#: src/slic3r/GUI/MainFrame.cpp:596 +msgid "Re&load from disk" +msgstr "디스크에서 다시 로드" + +#: src/slic3r/GUI/MainFrame.cpp:597 +msgid "Reload the plater from disk" +msgstr "디스크에서 플래터 다시 로드" + +#: src/slic3r/GUI/MainFrame.cpp:606 msgid "&Plater Tab" msgstr "&선택 및 플래이터 탭" -#: src/slic3r/GUI/MainFrame.cpp:579 +#: src/slic3r/GUI/MainFrame.cpp:606 msgid "Show the plater" msgstr "플레이터를 보기" -#: src/slic3r/GUI/MainFrame.cpp:586 +#: src/slic3r/GUI/MainFrame.cpp:614 msgid "P&rint Settings Tab" msgstr "프린트 설정 탭" -#: src/slic3r/GUI/MainFrame.cpp:586 +#: src/slic3r/GUI/MainFrame.cpp:614 msgid "Show the print settings" msgstr "인쇄 설정 표시" -#: src/slic3r/GUI/MainFrame.cpp:588 src/slic3r/GUI/MainFrame.cpp:711 +#: src/slic3r/GUI/MainFrame.cpp:617 src/slic3r/GUI/MainFrame.cpp:754 msgid "&Filament Settings Tab" msgstr "&필라멘트 설정 탭" -#: src/slic3r/GUI/MainFrame.cpp:588 +#: src/slic3r/GUI/MainFrame.cpp:617 msgid "Show the filament settings" msgstr "필라멘트 설정보기" -#: src/slic3r/GUI/MainFrame.cpp:591 +#: src/slic3r/GUI/MainFrame.cpp:621 msgid "Print&er Settings Tab" msgstr "설정 인쇄 탭" -#: src/slic3r/GUI/MainFrame.cpp:591 +#: src/slic3r/GUI/MainFrame.cpp:621 msgid "Show the printer settings" msgstr "간단한 설정보기" -#: src/slic3r/GUI/MainFrame.cpp:595 +#: src/slic3r/GUI/MainFrame.cpp:626 msgid "3&D" msgstr "3&D" -#: src/slic3r/GUI/MainFrame.cpp:595 +#: src/slic3r/GUI/MainFrame.cpp:626 msgid "Show the 3D editing view" msgstr "3D 편집용 보기 표시" -#: src/slic3r/GUI/MainFrame.cpp:598 +#: src/slic3r/GUI/MainFrame.cpp:629 msgid "Pre&view" msgstr "미리 보기" -#: src/slic3r/GUI/MainFrame.cpp:598 +#: src/slic3r/GUI/MainFrame.cpp:629 msgid "Show the 3D slices preview" msgstr "3D 슬라이스 미리 보기 표시" -#: src/slic3r/GUI/MainFrame.cpp:617 +#: src/slic3r/GUI/MainFrame.cpp:648 msgid "Print &Host Upload Queue" -msgstr "호스트 업로드 대기열 인쇄" +msgstr "프린터 호스트 업로드 대기" -#: src/slic3r/GUI/MainFrame.cpp:617 +#: src/slic3r/GUI/MainFrame.cpp:648 msgid "Display the Print Host Upload Queue window" msgstr "호스트 업로드 대기열 인쇄 창 표시" -#: src/slic3r/GUI/MainFrame.cpp:626 +#: src/slic3r/GUI/MainFrame.cpp:658 msgid "Iso" msgstr "기본 " -#: src/slic3r/GUI/MainFrame.cpp:626 +#: src/slic3r/GUI/MainFrame.cpp:658 msgid "Iso View" msgstr "표준 보기" #. TRN To be shown in the main menu View->Top #. TRN To be shown in Print Settings "Top solid layers" -#: src/slic3r/GUI/MainFrame.cpp:630 src/libslic3r/PrintConfig.cpp:2094 +#: src/slic3r/GUI/MainFrame.cpp:662 src/libslic3r/PrintConfig.cpp:2142 +#: src/libslic3r/PrintConfig.cpp:2151 msgid "Top" msgstr "윗부분 " -#: src/slic3r/GUI/MainFrame.cpp:630 +#: src/slic3r/GUI/MainFrame.cpp:662 msgid "Top View" msgstr "위에서 보기" #. TRN To be shown in the main menu View->Bottom #. TRN To be shown in Print Settings "Bottom solid layers" -#: src/slic3r/GUI/MainFrame.cpp:633 src/libslic3r/PrintConfig.cpp:159 +#. TRN To be shown in Print Settings "Top solid layers" +#: src/slic3r/GUI/MainFrame.cpp:665 src/libslic3r/PrintConfig.cpp:164 +#: src/libslic3r/PrintConfig.cpp:173 msgid "Bottom" msgstr "바닥 " -#: src/slic3r/GUI/MainFrame.cpp:633 +#: src/slic3r/GUI/MainFrame.cpp:665 msgid "Bottom View" msgstr "바닥 보기" -#: src/slic3r/GUI/MainFrame.cpp:635 +#: src/slic3r/GUI/MainFrame.cpp:667 msgid "Front" msgstr "앞 " -#: src/slic3r/GUI/MainFrame.cpp:635 +#: src/slic3r/GUI/MainFrame.cpp:667 msgid "Front View" msgstr "앞면 보기 " -#: src/slic3r/GUI/MainFrame.cpp:637 src/libslic3r/PrintConfig.cpp:1611 +#: src/slic3r/GUI/MainFrame.cpp:669 src/libslic3r/PrintConfig.cpp:1641 msgid "Rear" msgstr "뒷면 " -#: src/slic3r/GUI/MainFrame.cpp:637 +#: src/slic3r/GUI/MainFrame.cpp:669 msgid "Rear View" msgstr "뒷면 보기" -#: src/slic3r/GUI/MainFrame.cpp:639 +#: src/slic3r/GUI/MainFrame.cpp:671 msgid "Left" msgstr "왼쪽 " -#: src/slic3r/GUI/MainFrame.cpp:639 +#: src/slic3r/GUI/MainFrame.cpp:671 msgid "Left View" msgstr "왼쪽 보기" -#: src/slic3r/GUI/MainFrame.cpp:641 +#: src/slic3r/GUI/MainFrame.cpp:673 msgid "Right" msgstr "오른쪽 " -#: src/slic3r/GUI/MainFrame.cpp:641 +#: src/slic3r/GUI/MainFrame.cpp:673 msgid "Right View" msgstr "오른쪽 보기" -#: src/slic3r/GUI/MainFrame.cpp:648 +#: src/slic3r/GUI/MainFrame.cpp:677 +msgid "Show &labels" +msgstr "레이블 & 표시 " + +#: src/slic3r/GUI/MainFrame.cpp:677 +msgid "Show object/instance labels in 3D scene" +msgstr "3D 장면에서 오브젝트/인스턴스 레이블 표시" + +#: src/slic3r/GUI/MainFrame.cpp:686 msgid "Prusa 3D &Drivers" msgstr "푸르사 3D 드라이버" -#: src/slic3r/GUI/MainFrame.cpp:648 +#: src/slic3r/GUI/MainFrame.cpp:686 msgid "Open the Prusa3D drivers download page in your browser" msgstr "브라우저에서 Prusa3D 드라이버 다운로드 페이지를 엽니다" -#: src/slic3r/GUI/MainFrame.cpp:650 +#: src/slic3r/GUI/MainFrame.cpp:688 msgid "Software &Releases" msgstr "소프트웨어 &자료" -#: src/slic3r/GUI/MainFrame.cpp:650 +#: src/slic3r/GUI/MainFrame.cpp:688 msgid "Open the software releases page in your browser" msgstr "브라우저에서 소프트웨어 정보 페이지 열기" -#: src/slic3r/GUI/MainFrame.cpp:656 +#: src/slic3r/GUI/MainFrame.cpp:694 #, c-format msgid "%s &Website" msgstr "%s &웹사이트" -#: src/slic3r/GUI/MainFrame.cpp:657 +#: src/slic3r/GUI/MainFrame.cpp:695 #, c-format msgid "Open the %s website in your browser" msgstr "%s 웹사이트를 브라우저에서 열기" -#: src/slic3r/GUI/MainFrame.cpp:663 +#: src/slic3r/GUI/MainFrame.cpp:701 msgid "System &Info" msgstr "시스템 정보" -#: src/slic3r/GUI/MainFrame.cpp:663 +#: src/slic3r/GUI/MainFrame.cpp:701 msgid "Show system information" msgstr "시스템 정보 표시" -#: src/slic3r/GUI/MainFrame.cpp:665 +#: src/slic3r/GUI/MainFrame.cpp:703 msgid "Show &Configuration Folder" msgstr "폴더 표시 및 구성" -#: src/slic3r/GUI/MainFrame.cpp:665 +#: src/slic3r/GUI/MainFrame.cpp:703 msgid "Show user configuration folder (datadir)" -msgstr "사용자 구성 폴더 표시 (datadir)" +msgstr "사용자 구성 폴더를 표시 (datadir)" -#: src/slic3r/GUI/MainFrame.cpp:667 +#: src/slic3r/GUI/MainFrame.cpp:705 msgid "Report an I&ssue" msgstr "문제를 보고" -#: src/slic3r/GUI/MainFrame.cpp:667 +#: src/slic3r/GUI/MainFrame.cpp:705 #, c-format msgid "Report an issue on %s" msgstr "%s에 문제 보고" -#: src/slic3r/GUI/MainFrame.cpp:669 +#: src/slic3r/GUI/MainFrame.cpp:707 #, c-format msgid "&About %s" msgstr "%s 에 대하여" -#: src/slic3r/GUI/MainFrame.cpp:669 +#: src/slic3r/GUI/MainFrame.cpp:707 msgid "Show about dialog" msgstr "다이얼로그 표시" -#: src/slic3r/GUI/MainFrame.cpp:672 +#: src/slic3r/GUI/MainFrame.cpp:710 msgid "Show the list of the keyboard shortcuts" msgstr "키보드 단축키 목록 표시" -#: src/slic3r/GUI/MainFrame.cpp:680 +#: src/slic3r/GUI/MainFrame.cpp:723 msgid "&File" msgstr "&파일" -#: src/slic3r/GUI/MainFrame.cpp:681 +#: src/slic3r/GUI/MainFrame.cpp:724 msgid "&Edit" msgstr "&수정" -#: src/slic3r/GUI/MainFrame.cpp:682 +#: src/slic3r/GUI/MainFrame.cpp:725 msgid "&Window" msgstr "&윈도우" -#: src/slic3r/GUI/MainFrame.cpp:683 +#: src/slic3r/GUI/MainFrame.cpp:726 msgid "&View" msgstr "&시점" -#: src/slic3r/GUI/MainFrame.cpp:686 +#: src/slic3r/GUI/MainFrame.cpp:729 msgid "&Help" msgstr "&도움말" -#: src/slic3r/GUI/MainFrame.cpp:708 +#: src/slic3r/GUI/MainFrame.cpp:751 msgid "E&xport" msgstr "보내기" -#: src/slic3r/GUI/MainFrame.cpp:709 +#: src/slic3r/GUI/MainFrame.cpp:752 msgid "S&end to print" msgstr "끝내고 프린트" -#: src/slic3r/GUI/MainFrame.cpp:711 +#: src/slic3r/GUI/MainFrame.cpp:754 msgid "Mate&rial Settings Tab" msgstr "재료(메터리리알) 설정 탭" -#: src/slic3r/GUI/MainFrame.cpp:732 +#: src/slic3r/GUI/MainFrame.cpp:775 msgid "Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):" msgstr "슬라이스 할 파일을 선택하십시오 (STL / OBJ / AMF / 3MF / PRUSA):" -#: src/slic3r/GUI/MainFrame.cpp:746 +#: src/slic3r/GUI/MainFrame.cpp:786 msgid "No previously sliced file." msgstr "이전에 분리 된 파일이 없습니다." -#: src/slic3r/GUI/MainFrame.cpp:752 +#: src/slic3r/GUI/MainFrame.cpp:792 msgid "Previously sliced file (" msgstr "이전에 분리 된 파일 (" -#: src/slic3r/GUI/MainFrame.cpp:752 +#: src/slic3r/GUI/MainFrame.cpp:792 msgid ") not found." msgstr ")을 찾을 수 없습니다." -#: src/slic3r/GUI/MainFrame.cpp:753 +#: src/slic3r/GUI/MainFrame.cpp:793 msgid "File Not Found" msgstr "파일을 찾을수 없다" -#: src/slic3r/GUI/MainFrame.cpp:788 +#: src/slic3r/GUI/MainFrame.cpp:828 #, c-format msgid "Save %s file as:" -msgstr "%s 파일을 다음과 같이 저장 합니다" +msgstr "%s 파일을 저장 합니다:" -#: src/slic3r/GUI/MainFrame.cpp:788 +#: src/slic3r/GUI/MainFrame.cpp:828 msgid "SVG" msgstr "Svg" -#: src/slic3r/GUI/MainFrame.cpp:788 +#: src/slic3r/GUI/MainFrame.cpp:828 msgid "G-code" msgstr "G 코드" -#: src/slic3r/GUI/MainFrame.cpp:803 +#: src/slic3r/GUI/MainFrame.cpp:840 msgid "Save zip file as:" msgstr "압축(zip)파일 다른이름 저장:" -#: src/slic3r/GUI/MainFrame.cpp:815 src/slic3r/GUI/Plater.cpp:2933 -#: src/slic3r/GUI/Plater.cpp:4418 src/slic3r/GUI/Tab.cpp:1170 -#: src/slic3r/GUI/Tab.cpp:3700 +#: src/slic3r/GUI/MainFrame.cpp:849 src/slic3r/GUI/Plater.cpp:3107 +#: src/slic3r/GUI/Plater.cpp:5126 src/slic3r/GUI/Tab.cpp:1211 +#: src/slic3r/GUI/Tab.cpp:3634 msgid "Slicing" msgstr "슬라이싱" #. TRN "Processing input_file_basename" -#: src/slic3r/GUI/MainFrame.cpp:817 +#: src/slic3r/GUI/MainFrame.cpp:851 #, c-format msgid "Processing %s" msgstr "처리 %s" -#: src/slic3r/GUI/MainFrame.cpp:840 +#: src/slic3r/GUI/MainFrame.cpp:874 msgid " was successfully sliced." msgstr " 성공적으로 슬라이스." -#: src/slic3r/GUI/MainFrame.cpp:842 +#: src/slic3r/GUI/MainFrame.cpp:876 msgid "Slicing Done!" msgstr "슬라이스 완료!" -#: src/slic3r/GUI/MainFrame.cpp:857 +#: src/slic3r/GUI/MainFrame.cpp:891 msgid "Select the STL file to repair:" msgstr "복구 할 STL 파일을 선택하십시오:" -#: src/slic3r/GUI/MainFrame.cpp:870 +#: src/slic3r/GUI/MainFrame.cpp:901 msgid "Save OBJ file (less prone to coordinate errors than STL) as:" msgstr "OBJ 파일을 저장하십시오 (STL보다 오류를 덜 조정할 가능성이 적음):" -#: src/slic3r/GUI/MainFrame.cpp:885 +#: src/slic3r/GUI/MainFrame.cpp:913 msgid "Your file was repaired." msgstr "파일이 복구되었습니다." -#: src/slic3r/GUI/MainFrame.cpp:885 src/libslic3r/PrintConfig.cpp:3221 +#: src/slic3r/GUI/MainFrame.cpp:913 src/libslic3r/PrintConfig.cpp:3442 msgid "Repair" msgstr "수정" -#: src/slic3r/GUI/MainFrame.cpp:899 +#: src/slic3r/GUI/MainFrame.cpp:927 msgid "Save configuration as:" msgstr "구성을 저장 :" -#: src/slic3r/GUI/MainFrame.cpp:919 src/slic3r/GUI/MainFrame.cpp:983 +#: src/slic3r/GUI/MainFrame.cpp:946 src/slic3r/GUI/MainFrame.cpp:1008 msgid "Select configuration to load:" -msgstr "로드 할 구성 선택 :" +msgstr "불러올 구성 선택 :" -#: src/slic3r/GUI/MainFrame.cpp:956 +#: src/slic3r/GUI/MainFrame.cpp:982 msgid "Save presets bundle as:" -msgstr "이전 설정 번들을 다음과 같이 저장 :" +msgstr "이전 번들 설정을 다음과 같이 저장 :" -#: src/slic3r/GUI/MainFrame.cpp:1007 +#: src/slic3r/GUI/MainFrame.cpp:1029 #, c-format msgid "%d presets successfully imported." msgstr "% d 사전 설정을 가져 왔습니다." +#: src/slic3r/GUI/Mouse3DController.cpp:291 +msgid "3Dconnexion settings" +msgstr "3Dconnexion 설정" + +#: src/slic3r/GUI/Mouse3DController.cpp:306 +msgid "Device:" +msgstr "장치:" + +#: src/slic3r/GUI/Mouse3DController.cpp:313 +msgid "Speed:" +msgstr "스피드:" + +#: src/slic3r/GUI/Mouse3DController.cpp:317 +#: src/slic3r/GUI/Mouse3DController.cpp:337 +#: src/slic3r/GUI/Mouse3DController.cpp:339 +msgid "Translation" +msgstr "번역" + +#: src/slic3r/GUI/Mouse3DController.cpp:326 +#: src/slic3r/GUI/Mouse3DController.cpp:337 +msgid "Zoom" +msgstr "확대" + +#: src/slic3r/GUI/Mouse3DController.cpp:332 +msgid "Deadzone:" +msgstr "데드존:" + #: src/slic3r/GUI/MsgDialog.cpp:73 #, c-format msgid "%s error" @@ -2927,137 +3945,184 @@ msgstr "%s 오류" msgid "%s has encountered an error" msgstr "%s에 오류가 발생 했습니다" -#: src/slic3r/GUI/Plater.cpp:146 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:56 +msgid "Instances" +msgstr "복제본" + +#: src/slic3r/GUI/ObjectDataViewModel.cpp:60 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:216 +#, c-format +msgid "Instance %d" +msgstr "복제본 %d" + +#: src/slic3r/GUI/ObjectDataViewModel.cpp:67 src/slic3r/GUI/Tab.cpp:3482 +#: src/slic3r/GUI/Tab.cpp:3572 +msgid "Layers" +msgstr "레이어" + +#: src/slic3r/GUI/ObjectDataViewModel.cpp:94 +msgid "Range" +msgstr "범위" + +#: src/slic3r/GUI/OptionsGroup.cpp:249 +msgctxt "Layers" +msgid "Top" +msgstr "윗부분 " + +#: src/slic3r/GUI/OptionsGroup.cpp:249 +msgctxt "Layers" +msgid "Bottom" +msgstr "바닥 " + +#: src/slic3r/GUI/Plater.cpp:163 msgid "Volume" msgstr "크기" -#: src/slic3r/GUI/Plater.cpp:147 +#: src/slic3r/GUI/Plater.cpp:164 msgid "Facets" msgstr "측면" -#: src/slic3r/GUI/Plater.cpp:148 +#: src/slic3r/GUI/Plater.cpp:165 msgid "Materials" msgstr "재료" -#: src/slic3r/GUI/Plater.cpp:151 +#: src/slic3r/GUI/Plater.cpp:168 msgid "Manifold" msgstr "많은" -#: src/slic3r/GUI/Plater.cpp:201 +#: src/slic3r/GUI/Plater.cpp:218 msgid "Sliced Info" msgstr "슬라이스된 정보" -#: src/slic3r/GUI/Plater.cpp:220 src/slic3r/GUI/Plater.cpp:1135 +#: src/slic3r/GUI/Plater.cpp:237 src/slic3r/GUI/Plater.cpp:1226 msgid "Used Filament (m)" msgstr "사용자 필라멘트 (m)" -#: src/slic3r/GUI/Plater.cpp:221 +#: src/slic3r/GUI/Plater.cpp:238 msgid "Used Filament (mm³)" msgstr "사용자 필라멘트 (mm³)" -#: src/slic3r/GUI/Plater.cpp:222 +#: src/slic3r/GUI/Plater.cpp:239 msgid "Used Filament (g)" msgstr "사용자 필라멘트 (g)" -#: src/slic3r/GUI/Plater.cpp:223 +#: src/slic3r/GUI/Plater.cpp:240 msgid "Used Material (unit)" msgstr "사용 재료 (단위)" -#: src/slic3r/GUI/Plater.cpp:224 src/slic3r/GUI/Plater.cpp:1150 -#: src/libslic3r/PrintConfig.cpp:742 -msgid "Cost" -msgstr "비용" +#: src/slic3r/GUI/Plater.cpp:241 +msgid "Cost (money)" +msgstr "비용 (돈)" -#: src/slic3r/GUI/Plater.cpp:225 src/slic3r/GUI/Plater.cpp:1122 -#: src/slic3r/GUI/Plater.cpp:1164 +#: src/slic3r/GUI/Plater.cpp:242 src/slic3r/GUI/Plater.cpp:1213 +#: src/slic3r/GUI/Plater.cpp:1255 msgid "Estimated printing time" msgstr "예상 인쇄 시간" -#: src/slic3r/GUI/Plater.cpp:226 +#: src/slic3r/GUI/Plater.cpp:243 msgid "Number of tool changes" msgstr "공구(tool) 변경 수" -#: src/slic3r/GUI/Plater.cpp:317 +#: src/slic3r/GUI/Plater.cpp:340 msgid "Click to edit preset" msgstr "사전 설정을 편집 하려면 클릭 하십시오" -#: src/slic3r/GUI/Plater.cpp:469 +#: src/slic3r/GUI/Plater.cpp:495 msgid "Select what kind of support do you need" msgstr "서포트의 종류를 선택하세요" -#: src/slic3r/GUI/Plater.cpp:471 src/libslic3r/PrintConfig.cpp:1865 -#: src/libslic3r/PrintConfig.cpp:2529 +#: src/slic3r/GUI/Plater.cpp:497 src/libslic3r/PrintConfig.cpp:1911 +#: src/libslic3r/PrintConfig.cpp:2675 msgid "Support on build plate only" msgstr "출력물만 서포트를 지지" -#: src/slic3r/GUI/Plater.cpp:472 src/slic3r/GUI/Plater.cpp:587 +#: src/slic3r/GUI/Plater.cpp:498 src/slic3r/GUI/Plater.cpp:621 msgid "For support enforcers only" msgstr "서포트 지원영역 전용" -#: src/slic3r/GUI/Plater.cpp:473 +#: src/slic3r/GUI/Plater.cpp:499 msgid "Everywhere" msgstr "모든곳" -#: src/slic3r/GUI/Plater.cpp:505 src/slic3r/GUI/Tab.cpp:1067 +#: src/slic3r/GUI/Plater.cpp:531 src/slic3r/GUI/Tab.cpp:1107 msgid "Brim" msgstr "브림" -#: src/slic3r/GUI/Plater.cpp:507 +#: src/slic3r/GUI/Plater.cpp:533 msgid "" "This flag enables the brim that will be printed around each object on the " "first layer." -msgstr "" -"이 플래그는 첫 번째 레이어의 각 개체 주위에 인쇄 될 브림을 활성화합니다." +msgstr "첫 번째 레이어의 각 객체(object) 주위에 인쇄 될 브림을 활성화합니다." -#: src/slic3r/GUI/Plater.cpp:515 +#: src/slic3r/GUI/Plater.cpp:541 msgid "Purging volumes" -msgstr "볼륨 삭제" +msgstr "퍼징 조절" -#: src/slic3r/GUI/Plater.cpp:766 +#: src/slic3r/GUI/Plater.cpp:635 +msgid "Select what kind of pad do you need" +msgstr "필요한 패드 종류를 선택하십시오." + +#: src/slic3r/GUI/Plater.cpp:637 +msgid "Below object" +msgstr "개체 아래" + +#: src/slic3r/GUI/Plater.cpp:638 +msgid "Around object" +msgstr "개체 주위" + +#: src/slic3r/GUI/Plater.cpp:812 msgid "Print settings" msgstr "프린트 설정" -#: src/slic3r/GUI/Plater.cpp:767 src/slic3r/GUI/Tab.cpp:1604 -#: src/slic3r/GUI/Tab.cpp:1605 +#: src/slic3r/GUI/Plater.cpp:813 src/slic3r/GUI/Tab.cpp:1419 +#: src/slic3r/GUI/Tab.cpp:1420 msgid "Filament" msgstr "필라멘트" -#: src/slic3r/GUI/Plater.cpp:768 +#: src/slic3r/GUI/Plater.cpp:814 msgid "SLA print settings" msgstr "SLA 인쇄 설정" -#: src/slic3r/GUI/Plater.cpp:769 src/slic3r/GUI/Preset.cpp:1310 +#: src/slic3r/GUI/Plater.cpp:815 src/slic3r/GUI/Preset.cpp:1455 msgid "SLA material" msgstr "SLA 재료" -#: src/slic3r/GUI/Plater.cpp:770 +#: src/slic3r/GUI/Plater.cpp:816 msgid "Printer" msgstr "프린터" -#: src/slic3r/GUI/Plater.cpp:820 src/slic3r/GUI/Plater.cpp:4688 +#: src/slic3r/GUI/Plater.cpp:875 src/slic3r/GUI/Plater.cpp:5545 msgid "Send to printer" msgstr "프린터로 보내기" -#: src/slic3r/GUI/Plater.cpp:823 src/slic3r/GUI/Plater.cpp:2933 -#: src/slic3r/GUI/Plater.cpp:4421 -msgid "Slice now" -msgstr "지금 자르기" +#: src/slic3r/GUI/Plater.cpp:876 +msgid "Remove device" +msgstr "장치 제거" -#: src/slic3r/GUI/Plater.cpp:963 +#: src/slic3r/GUI/Plater.cpp:877 +msgid "Export to SD card/ USB thumb drive" +msgstr "SD 카드/USB 썸 드라이브로 내보내기" + +#: src/slic3r/GUI/Plater.cpp:889 src/slic3r/GUI/Plater.cpp:3107 +#: src/slic3r/GUI/Plater.cpp:5129 +msgid "Slice now" +msgstr "바로 슬라이스" + +#: src/slic3r/GUI/Plater.cpp:1039 msgid "Hold Shift to Slice & Export G-code" msgstr "Shift 키를 누른 채 G 코드 내보내기" -#: src/slic3r/GUI/Plater.cpp:1068 +#: src/slic3r/GUI/Plater.cpp:1149 #, c-format msgid "%d (%d shells)" msgstr "% d (% d 쉘)" -#: src/slic3r/GUI/Plater.cpp:1073 +#: src/slic3r/GUI/Plater.cpp:1154 #, c-format msgid "Auto-repaired (%d errors)" msgstr "오류자동수정 (%d errors)" -#: src/slic3r/GUI/Plater.cpp:1076 +#: src/slic3r/GUI/Plater.cpp:1157 #, c-format msgid "" "%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d " @@ -3066,442 +4131,483 @@ msgstr "" "%d 면 고정, %d 모서리 고정, %d 면 제거, %d 면 추가, %d 면 반전, %d 후방 모서" "리" -#: src/slic3r/GUI/Plater.cpp:1086 +#: src/slic3r/GUI/Plater.cpp:1167 msgid "Yes" msgstr "예" -#: src/slic3r/GUI/Plater.cpp:1109 +#: src/slic3r/GUI/Plater.cpp:1188 msgid "Used Material (ml)" msgstr "사용 재료 (ml)" -#: src/slic3r/GUI/Plater.cpp:1112 +#: src/slic3r/GUI/Plater.cpp:1191 msgid "object(s)" -msgstr "개체(들)" +msgstr "객체(object)" -#: src/slic3r/GUI/Plater.cpp:1112 +#: src/slic3r/GUI/Plater.cpp:1191 msgid "supports and pad" msgstr "지지대 및 패드" -#: src/slic3r/GUI/Plater.cpp:1137 src/slic3r/GUI/Plater.cpp:1152 +#: src/slic3r/GUI/Plater.cpp:1228 src/slic3r/GUI/Plater.cpp:1243 msgid "objects" -msgstr "개체" +msgstr "객체들(objects)" -#: src/slic3r/GUI/Plater.cpp:1137 src/slic3r/GUI/Plater.cpp:1152 +#: src/slic3r/GUI/Plater.cpp:1228 src/slic3r/GUI/Plater.cpp:1243 msgid "wipe tower" msgstr "와이프 타워(Wipe tower)" -#: src/slic3r/GUI/Plater.cpp:1167 +#: src/slic3r/GUI/Plater.cpp:1241 src/libslic3r/PrintConfig.cpp:761 +#: src/libslic3r/PrintConfig.cpp:2511 src/libslic3r/PrintConfig.cpp:2512 +msgid "Cost" +msgstr "비용" + +#: src/slic3r/GUI/Plater.cpp:1258 msgid "normal mode" msgstr "일반 모드" -#: src/slic3r/GUI/Plater.cpp:1171 src/slic3r/GUI/Plater.cpp:1180 -msgid "Color " -msgstr "색" +#: src/slic3r/GUI/Plater.cpp:1262 src/slic3r/GUI/Plater.cpp:1271 +#: src/libslic3r/PrintConfig.cpp:583 +msgid "Color" +msgstr "색상" -#: src/slic3r/GUI/Plater.cpp:1176 +#: src/slic3r/GUI/Plater.cpp:1267 msgid "stealth mode" msgstr "스텔스 모드" -#: src/slic3r/GUI/Plater.cpp:1271 +#: src/slic3r/GUI/Plater.cpp:1375 msgid "Load File" msgstr "파일 로드" -#: src/slic3r/GUI/Plater.cpp:1275 +#: src/slic3r/GUI/Plater.cpp:1379 msgid "Load Files" msgstr "파일 로드" -#: src/slic3r/GUI/Plater.cpp:1503 -msgid "ERROR: not enough resources to execute a new job." -msgstr "오류: 새 작업을 실행하기에 충분한 리소스가 아닙니다." - -#: src/slic3r/GUI/Plater.cpp:2056 +#: src/slic3r/GUI/Plater.cpp:2137 msgid "New Project" msgstr "새로운 프로젝트" -#: src/slic3r/GUI/Plater.cpp:2173 +#: src/slic3r/GUI/Plater.cpp:2259 msgid "Loading" msgstr "로딩" -#: src/slic3r/GUI/Plater.cpp:2183 +#: src/slic3r/GUI/Plater.cpp:2269 #, c-format -msgid "Processing input file %s\n" -msgstr "입력 파일 처리 %s\n" +msgid "Processing input file %s" +msgstr "입력 파일 처리 %s" -#: src/slic3r/GUI/Plater.cpp:2211 -msgid "" -"You can't to load SLA project if there is at least one multi-part object on " -"the bed" +#: src/slic3r/GUI/Plater.cpp:2297 +msgid "You cannot load SLA project with a multi-part object on the bed" msgstr "" -"침대에 다중 부품 개체가 하나 이상 있는 경우 SLA 프로젝트를 로드할 수 없습니" -"다." +"배드에 여러 부분으로 구성된 객체가 있는 SLA 프로젝트를 로드할 수 없습니다." -#: src/slic3r/GUI/Plater.cpp:2212 src/slic3r/GUI/Tab.cpp:3064 +#: src/slic3r/GUI/Plater.cpp:2298 src/slic3r/GUI/Tab.cpp:2929 msgid "Please check your object list before preset changing." -msgstr "사전 설정을 변경 하기 전에 개체 목록을 확인 하십시오." +msgstr "사전 설정을 변경 하기 전에 객체(object) 목록을 확인 하십시오." -#: src/slic3r/GUI/Plater.cpp:2255 +#: src/slic3r/GUI/Plater.cpp:2343 msgid "" -"This file contains several objects positioned at multiple heights. Instead " -"of considering them as multiple objects, should I consider\n" -"this file as a single object having multiple parts?\n" +"This file contains several objects positioned at multiple heights.\n" +"Instead of considering them as multiple objects, should I consider\n" +"this file as a single object having multiple parts?" msgstr "" -"이 파일에는 여러 높이에 위치한 여러 객체가 들어 있습니다. 여러 객체로 간주하" +"이 파일에는 여러 높이마다 객체(object)가 있습니다. 여러 객체(object)로 간주하" "는 대신,\n" -"이 파일은 여러 부분을 갖는 단일 객체로 보입니까?\n" +"이 파일은 여러 부품을 갖는 단일 객체(object)로 보입니까?" -#: src/slic3r/GUI/Plater.cpp:2258 src/slic3r/GUI/Plater.cpp:2310 +#: src/slic3r/GUI/Plater.cpp:2346 src/slic3r/GUI/Plater.cpp:2399 msgid "Multi-part object detected" -msgstr "다중 부품 객체가 감지" +msgstr "다중 부품(Part) 객체(object)가 감지" -#: src/slic3r/GUI/Plater.cpp:2265 +#: src/slic3r/GUI/Plater.cpp:2353 msgid "" "This file cannot be loaded in a simple mode. Do you want to switch to an " -"advanced mode?\n" +"advanced mode?" msgstr "" -"이 파일은 단순 모드에서 로드할 수 없습니다. 고급 모드로 전환 하시겠습니까?\n" +"이 파일은 기본 모드에서 로드할 수 없습니다. 고급 모드로 전환 하시겠습니까?" -#: src/slic3r/GUI/Plater.cpp:2266 +#: src/slic3r/GUI/Plater.cpp:2354 msgid "Detected advanced data" msgstr "감지된 고급 데이터" -#: src/slic3r/GUI/Plater.cpp:2287 +#: src/slic3r/GUI/Plater.cpp:2376 #, c-format msgid "" "You can't to add the object(s) from %s because of one or some of them " "is(are) multi-part" -msgstr "멀티파트 하나 또는 그 중 일부 때문에 %s에서 개체를 추가 할 수 없습니다" +msgstr "" +"다중 부품(Part) 하나 또는 그 중 일부 때문에 %s에서 객체(object)를 추가 할 수 " +"없습니다" -#: src/slic3r/GUI/Plater.cpp:2307 +#: src/slic3r/GUI/Plater.cpp:2396 msgid "" "Multiple objects were loaded for a multi-material printer.\n" "Instead of considering them as multiple objects, should I consider\n" -"these files to represent a single object having multiple parts?\n" +"these files to represent a single object having multiple parts?" msgstr "" -"다중 재료 프린터에 대해 여러 객체가로드되었습니다.\n" -"여러 객체로 간주하는 대신,\n" -"이 파일들은 여러 부분을 갖는 단일 객체를 나타낼 수 있습니까?\n" +"다중 재료 프린터에 대해 여러 객체(object)가로드되었습니다.\n" +"여러 객체(object)로 간주하는 대신,\n" +"이 파일들은 여러 부분을 갖는 단일 객체(object)를 나타낼 수 있습니까?" -#: src/slic3r/GUI/Plater.cpp:2323 +#: src/slic3r/GUI/Plater.cpp:2412 msgid "Loaded" msgstr "로드(loaded)" -#: src/slic3r/GUI/Plater.cpp:2418 +#: src/slic3r/GUI/Plater.cpp:2514 msgid "" "Your object appears to be too large, so it was automatically scaled down to " "fit your print bed." -msgstr "개체가 너무 커서 인쇄물에 맞게 자동으로 축소되었습니다." +msgstr "객체(object)가 너무 커서 인쇄물에 맞게 자동으로 축소되었습니다." -#: src/slic3r/GUI/Plater.cpp:2419 +#: src/slic3r/GUI/Plater.cpp:2515 msgid "Object too large?" -msgstr "개체가 너무 큽니까?" +msgstr "객체(object)가 너무 큽니까?" -#: src/slic3r/GUI/Plater.cpp:2476 +#: src/slic3r/GUI/Plater.cpp:2577 msgid "Export STL file:" msgstr "STL 파일 내보내기:" -#: src/slic3r/GUI/Plater.cpp:2483 +#: src/slic3r/GUI/Plater.cpp:2584 msgid "Export AMF file:" msgstr "AMF 파일 내보내기:" -#: src/slic3r/GUI/Plater.cpp:2489 +#: src/slic3r/GUI/Plater.cpp:2590 msgid "Save file as:" msgstr "다른 이름으로 파일 저장:" -#: src/slic3r/GUI/Plater.cpp:2592 -msgid "Delete Object" -msgstr "오브젝트 지우기" +#: src/slic3r/GUI/Plater.cpp:2596 +msgid "Export OBJ file:" +msgstr "OBJ 파일 내보내기:" -#: src/slic3r/GUI/Plater.cpp:2603 +#: src/slic3r/GUI/Plater.cpp:2698 +msgid "Delete Object" +msgstr "객체(object) 지우기" + +#: src/slic3r/GUI/Plater.cpp:2709 msgid "Reset Project" msgstr "프로젝트 재설정" -#: src/slic3r/GUI/Plater.cpp:2630 src/slic3r/GUI/Plater.cpp:3517 -msgid "Mirror" -msgstr "반전(Mirror)" +#: src/slic3r/GUI/Plater.cpp:2746 +msgid "Hollow" +msgstr "속비우기" -#: src/slic3r/GUI/Plater.cpp:2643 +#: src/slic3r/GUI/Plater.cpp:2753 msgid "Optimize Rotation" msgstr "회전 최적화" -#: src/slic3r/GUI/Plater.cpp:2689 +#: src/slic3r/GUI/Plater.cpp:2799 msgid "Arranging" msgstr "정렬" -#: src/slic3r/GUI/Plater.cpp:2712 +#: src/slic3r/GUI/Plater.cpp:2821 msgid "Could not arrange model objects! Some geometries may be invalid." msgstr "" -"모델 개체를 정렬할 수 없습니다. 일부 형상은 유효 하지 않을 수 있습니다." +"모델 객체(object)를 정렬할 수 없습니다. 일부 형상은 유효 하지 않을 수 있습니" +"다." -#: src/slic3r/GUI/Plater.cpp:2718 +#: src/slic3r/GUI/Plater.cpp:2827 msgid "Arranging canceled." msgstr "취소 된 정렬" -#: src/slic3r/GUI/Plater.cpp:2719 +#: src/slic3r/GUI/Plater.cpp:2828 msgid "Arranging done." msgstr "정렬 완료." -#: src/slic3r/GUI/Plater.cpp:2735 +#: src/slic3r/GUI/Plater.cpp:2844 msgid "Searching for optimal orientation" msgstr "최적의 방향 검색" -#: src/slic3r/GUI/Plater.cpp:2768 +#: src/slic3r/GUI/Plater.cpp:2877 msgid "Orientation search canceled." -msgstr "오리엔테이션 검색이 취소 됨" +msgstr "방향 검색이 취소되었습니다." -#: src/slic3r/GUI/Plater.cpp:2769 +#: src/slic3r/GUI/Plater.cpp:2878 msgid "Orientation found." msgstr "방향을 찾았습니다." -#: src/slic3r/GUI/Plater.cpp:2785 +#: src/slic3r/GUI/Plater.cpp:2908 +msgid "Indexing hollowed object" +msgstr "빈 개체 인덱싱" + +#: src/slic3r/GUI/Plater.cpp:2912 +msgid "Hollowing cancelled." +msgstr "공동화 취소." + +#: src/slic3r/GUI/Plater.cpp:2913 +msgid "Hollowing done." +msgstr "속을 비움니다." + +#: src/slic3r/GUI/Plater.cpp:2915 +msgid "Hollowing failed." +msgstr "공동화를 실패했습니다." + +#: src/slic3r/GUI/Plater.cpp:2956 msgid "" "The selected object can't be split because it contains more than one volume/" "material." msgstr "" -"선택한 객체는 둘 이상의 볼륨 / 재료가 포함되어 있기 때문에 분할 할 수 없습니" -"다." +"선택한 객체(object)는 둘 이상의 부품/재료가 포함되어 있기 때문에 분할 할 수 " +"없습니다." -#: src/slic3r/GUI/Plater.cpp:2796 +#: src/slic3r/GUI/Plater.cpp:2967 msgid "Split to Objects" -msgstr "객체로 분할" +msgstr "객체(object)로 분할" -#: src/slic3r/GUI/Plater.cpp:2918 +#: src/slic3r/GUI/Plater.cpp:3092 msgid "Invalid data" msgstr "잘못 된 데이터" -#: src/slic3r/GUI/Plater.cpp:2927 +#: src/slic3r/GUI/Plater.cpp:3101 msgid "Ready to slice" msgstr "슬라이스 준비" -#: src/slic3r/GUI/Plater.cpp:2965 src/slic3r/GUI/PrintHostDialogs.cpp:232 +#: src/slic3r/GUI/Plater.cpp:3139 src/slic3r/GUI/PrintHostDialogs.cpp:232 msgid "Cancelling" msgstr "취소 중" -#: src/slic3r/GUI/Plater.cpp:2982 +#: src/slic3r/GUI/Plater.cpp:3156 msgid "Another export job is currently running." msgstr "다른 내보내기 작업이 현재 실행 중입니다." -#: src/slic3r/GUI/Plater.cpp:3036 src/slic3r/GUI/Plater.cpp:3493 -msgid "Reload from Disk" -msgstr "디스크에서 다시 불러오기" +#: src/slic3r/GUI/Plater.cpp:3274 +msgid "Please select the file to reload" +msgstr "다시 로드할 파일을 선택하십시오." -#: src/slic3r/GUI/Plater.cpp:3072 +#: src/slic3r/GUI/Plater.cpp:3309 +msgid "It is not allowed to change the file to reload" +msgstr "다시 로드할 파일을 변경할 수 없습니다." + +#: src/slic3r/GUI/Plater.cpp:3309 +msgid "Do you want to retry" +msgstr "다시 시도하시겠습니까?" + +#: src/slic3r/GUI/Plater.cpp:3330 +msgid "Reload from: " +msgstr "다음에서 다시 로드합니다." + +#: src/slic3r/GUI/Plater.cpp:3438 +msgid "Unable to reload:" +msgstr "다시 로드할 수 없음:" + +#: src/slic3r/GUI/Plater.cpp:3443 +msgid "Error during reload" +msgstr "다시 로드하는 동안 오류" + +#: src/slic3r/GUI/Plater.cpp:3463 +msgid "Reload all from disk" +msgstr "디스크에서 모두 다시 로드" + +#: src/slic3r/GUI/Plater.cpp:3484 msgid "Fix Throught NetFabb" msgstr "NetFabb으로 수정" -#: src/slic3r/GUI/Plater.cpp:3254 +#: src/slic3r/GUI/Plater.cpp:3675 msgid "Export failed" msgstr "내보내기 실패" -#: src/slic3r/GUI/Plater.cpp:3259 src/slic3r/GUI/PrintHostDialogs.cpp:233 +#: src/slic3r/GUI/Plater.cpp:3680 src/slic3r/GUI/PrintHostDialogs.cpp:233 msgid "Cancelled" msgstr "취소됨" -#: src/slic3r/GUI/Plater.cpp:3347 src/slic3r/GUI/Plater.cpp:3359 -#: src/slic3r/GUI/Plater.cpp:3473 -msgid "Increase copies" -msgstr "복사본 늘리기" - -#: src/slic3r/GUI/Plater.cpp:3467 src/slic3r/GUI/Plater.cpp:3486 +#: src/slic3r/GUI/Plater.cpp:3942 src/slic3r/GUI/Plater.cpp:3969 msgid "Remove the selected object" -msgstr "선택한 객체 제거" +msgstr "선택한 객체(object) 제거" -#: src/slic3r/GUI/Plater.cpp:3473 -msgid "Place one more copy of the selected object" -msgstr "선택한 객체를 하나 더 복사합니다" +#: src/slic3r/GUI/Plater.cpp:3956 +msgid "Add one more instance of the selected object" +msgstr "선택한 개체의 인스턴스를 하나 더 추가합니다." -#: src/slic3r/GUI/Plater.cpp:3475 -msgid "Decrease copies" -msgstr "복사본 감소" +#: src/slic3r/GUI/Plater.cpp:3958 +msgid "Remove one instance of the selected object" +msgstr "선택한 개체의 인스턴스 하나 제거" -#: src/slic3r/GUI/Plater.cpp:3475 -msgid "Remove one copy of the selected object" -msgstr "선택한 객체 복사본 하나 삭제" +#: src/slic3r/GUI/Plater.cpp:3960 +msgid "Set number of instances" +msgstr "복제할 수량 설정" -#: src/slic3r/GUI/Plater.cpp:3477 -msgid "Set number of copies" -msgstr "복사될 수량 설정" +#: src/slic3r/GUI/Plater.cpp:3960 +msgid "Change the number of instances of the selected object" +msgstr "선택한 객체의 인스턴스 수 변경" -#: src/slic3r/GUI/Plater.cpp:3477 -msgid "Change the number of copies of the selected object" -msgstr "선택한 개체의 복사본 수 변경" +#: src/slic3r/GUI/Plater.cpp:3980 src/slic3r/GUI/Plater.cpp:3983 +msgid "Reload the selected object from disk" +msgstr "디스크에서 선택한 개체 다시 로드" -#: src/slic3r/GUI/Plater.cpp:3493 -msgid "Reload the selected file from Disk" -msgstr "디스크에서 다시 불러오기" - -#: src/slic3r/GUI/Plater.cpp:3496 +#: src/slic3r/GUI/Plater.cpp:3987 msgid "Export the selected object as STL file" -msgstr "선택한 객체를 STL 파일로 내보내기" +msgstr "선택한 객체(object)를 STL 파일로 내보내기" -#: src/slic3r/GUI/Plater.cpp:3510 +#: src/slic3r/GUI/Plater.cpp:4016 msgid "Along X axis" msgstr "X 축을 따라" -#: src/slic3r/GUI/Plater.cpp:3510 +#: src/slic3r/GUI/Plater.cpp:4016 msgid "Mirror the selected object along the X axis" -msgstr "선택한 객체를 X 축을 따라 반전합니다" +msgstr "선택한 객체(object)를 X 축을 따라 반전합니다" -#: src/slic3r/GUI/Plater.cpp:3512 +#: src/slic3r/GUI/Plater.cpp:4018 msgid "Along Y axis" msgstr "Y 축을 따라" -#: src/slic3r/GUI/Plater.cpp:3512 +#: src/slic3r/GUI/Plater.cpp:4018 msgid "Mirror the selected object along the Y axis" -msgstr "선택한 객체를 Y 축을 따라 반전합니다" +msgstr "선택한 객체(object)를 Y 축을 따라 반전합니다" -#: src/slic3r/GUI/Plater.cpp:3514 +#: src/slic3r/GUI/Plater.cpp:4020 msgid "Along Z axis" msgstr "Z 축 따라" -#: src/slic3r/GUI/Plater.cpp:3514 +#: src/slic3r/GUI/Plater.cpp:4020 msgid "Mirror the selected object along the Z axis" -msgstr "선택한 객체를 Z 축을 따라 반전합니다" +msgstr "선택한 객체(object)를 Z 축을 따라 반전합니다" -#: src/slic3r/GUI/Plater.cpp:3517 +#: src/slic3r/GUI/Plater.cpp:4023 +msgid "Mirror" +msgstr "반전(Mirror)" + +#: src/slic3r/GUI/Plater.cpp:4023 msgid "Mirror the selected object" msgstr "반전할 객제를 선택" -#: src/slic3r/GUI/Plater.cpp:3529 +#: src/slic3r/GUI/Plater.cpp:4035 msgid "To objects" -msgstr "개체에" +msgstr "객체(object)에" -#: src/slic3r/GUI/Plater.cpp:3529 src/slic3r/GUI/Plater.cpp:3549 +#: src/slic3r/GUI/Plater.cpp:4035 src/slic3r/GUI/Plater.cpp:4055 msgid "Split the selected object into individual objects" -msgstr "선택한 개체를 개별 개체로 분할 합니다." +msgstr "선택한 객체(object)를 개별 객체(object)로 분할 합니다." -#: src/slic3r/GUI/Plater.cpp:3531 +#: src/slic3r/GUI/Plater.cpp:4037 msgid "To parts" -msgstr "부품에" +msgstr "부품(Part)에" -#: src/slic3r/GUI/Plater.cpp:3531 src/slic3r/GUI/Plater.cpp:3563 +#: src/slic3r/GUI/Plater.cpp:4037 src/slic3r/GUI/Plater.cpp:4069 msgid "Split the selected object into individual sub-parts" -msgstr "선택한 오브젝트를 개별 하위 파트로 분할" +msgstr "선택한 객체(object)를 개별 하위 부품(Part)으로 분할" -#: src/slic3r/GUI/Plater.cpp:3534 src/slic3r/GUI/Plater.cpp:3549 -#: src/slic3r/GUI/Plater.cpp:3563 src/libslic3r/PrintConfig.cpp:3245 +#: src/slic3r/GUI/Plater.cpp:4040 src/slic3r/GUI/Plater.cpp:4055 +#: src/slic3r/GUI/Plater.cpp:4069 src/libslic3r/PrintConfig.cpp:3466 msgid "Split" msgstr "쪼개기" -#: src/slic3r/GUI/Plater.cpp:3534 +#: src/slic3r/GUI/Plater.cpp:4040 msgid "Split the selected object" -msgstr "선택한 개체 분할" +msgstr "선택한 객체(object) 분할" -#: src/slic3r/GUI/Plater.cpp:3555 +#: src/slic3r/GUI/Plater.cpp:4061 msgid "Optimize orientation" msgstr "방향 최적화" -#: src/slic3r/GUI/Plater.cpp:3555 +#: src/slic3r/GUI/Plater.cpp:4061 msgid "Optimize the rotation of the object for better print results." -msgstr "더 나은 인쇄 결과를 위해 개체의 회전을 최적화합니다." +msgstr "더 나은 인쇄 결과를 위해 객체(object)의 회전을 최적화합니다." -#: src/slic3r/GUI/Plater.cpp:3595 +#: src/slic3r/GUI/Plater.cpp:4120 msgid "3D editor view" msgstr "3D 편집화면 보기" -#: src/slic3r/GUI/Plater.cpp:3603 src/slic3r/GUI/Tab.cpp:2534 +#: src/slic3r/GUI/Plater.cpp:4128 src/slic3r/GUI/Tab.cpp:2372 msgid "Preview" -msgstr "프리뷰" +msgstr "미리보기" -#: src/slic3r/GUI/Plater.cpp:3831 +#: src/slic3r/GUI/Plater.cpp:4422 msgid "" "%1% printer was active at the time the target Undo / Redo snapshot was " "taken. Switching to %1% printer requires reloading of %1% presets." msgstr "" -"%1% 프린터가 대상 재생 취소/다시 작업 스냅샷을 생성할 때 활성화되었습니다. " -"%1% 프린터로 전환하려면 %1% 사전 설정을 다시 로드해야 합니다." +"%1% 프린터가 대상을 '되돌리기/취소하기' 작업 구성을 생성할 때 활성화되었습니" +"다. %1% 프린터로 전환하려면 %1% 사전 설정을 다시 불러와야 합니다." -#: src/slic3r/GUI/Plater.cpp:3992 +#: src/slic3r/GUI/Plater.cpp:4597 msgid "Load Project" -msgstr "프로젝트 로드" +msgstr "프로젝트 불러오기" -#: src/slic3r/GUI/Plater.cpp:4016 +#: src/slic3r/GUI/Plater.cpp:4625 msgid "Import Object" -msgstr "개체 가져오기" +msgstr "객체(object) 가져오기" -#: src/slic3r/GUI/Plater.cpp:4020 +#: src/slic3r/GUI/Plater.cpp:4629 msgid "Import Objects" -msgstr "객체 가져오기" +msgstr "객체(object) 가져오기" -#: src/slic3r/GUI/Plater.cpp:4075 -msgid "All objects will be removed, continue ?" -msgstr "모든 개체가 제거 됩니다, 계속합니까?" +#: src/slic3r/GUI/Plater.cpp:4693 +msgid "All objects will be removed, continue?" +msgstr "모든 객체(object)가 제거 됩니다, 계속합니까?" -#: src/slic3r/GUI/Plater.cpp:4083 +#: src/slic3r/GUI/Plater.cpp:4701 msgid "Delete Selected Objects" -msgstr "선택한 객체 삭제" +msgstr "선택한 객체(object) 삭제" -#: src/slic3r/GUI/Plater.cpp:4091 +#: src/slic3r/GUI/Plater.cpp:4709 msgid "Increase Instances" msgstr "복제본 늘리기" -#: src/slic3r/GUI/Plater.cpp:4127 +#: src/slic3r/GUI/Plater.cpp:4744 msgid "Decrease Instances" msgstr "복제본 감소" -#: src/slic3r/GUI/Plater.cpp:4163 +#: src/slic3r/GUI/Plater.cpp:4780 #, c-format msgid "Set numbers of copies to %d" msgstr "복사본 수를 %d로 설정" -#: src/slic3r/GUI/Plater.cpp:4193 +#: src/slic3r/GUI/Plater.cpp:4810 msgid "Cut by Plane" msgstr "평면으로 절단" -#: src/slic3r/GUI/Plater.cpp:4225 +#: src/slic3r/GUI/Plater.cpp:4863 msgid "Save G-code file as:" msgstr "G-code 파일 다른 이름 저장:" -#: src/slic3r/GUI/Plater.cpp:4225 +#: src/slic3r/GUI/Plater.cpp:4863 msgid "Save SL1 file as:" msgstr "SL1 파일 다른이름 저장:" -#: src/slic3r/GUI/Plater.cpp:4337 +#: src/slic3r/GUI/Plater.cpp:4990 #, c-format msgid "STL file exported to %s" msgstr "내보낸 STL 파일 %s" -#: src/slic3r/GUI/Plater.cpp:4353 +#: src/slic3r/GUI/Plater.cpp:5011 #, c-format msgid "AMF file exported to %s" msgstr "내보낸 AMF 파일 %s" -#: src/slic3r/GUI/Plater.cpp:4356 +#: src/slic3r/GUI/Plater.cpp:5014 #, c-format msgid "Error exporting AMF file %s" msgstr "AMF 파일 내보내기 오류 %s" -#: src/slic3r/GUI/Plater.cpp:4382 +#: src/slic3r/GUI/Plater.cpp:5057 #, c-format msgid "3MF file exported to %s" msgstr "3MF 파일을 내보냈습니다 %s" -#: src/slic3r/GUI/Plater.cpp:4387 +#: src/slic3r/GUI/Plater.cpp:5062 #, c-format msgid "Error exporting 3MF file %s" msgstr "3MF 파일 내보내기 오류 %s" -#: src/slic3r/GUI/Plater.cpp:4687 +#: src/slic3r/GUI/Plater.cpp:5544 msgid "Export" msgstr "내보내기" -#: src/slic3r/GUI/Plater.cpp:4688 +#: src/slic3r/GUI/Plater.cpp:5545 msgid "Send G-code" -msgstr "G 코드 보내기" +msgstr "G-code 보내기" -#: src/slic3r/GUI/Plater.cpp:4772 +#: src/slic3r/GUI/Plater.cpp:5629 msgid "Paste From Clipboard" msgstr "클립보드에서 붙여넣기" -#: src/slic3r/GUI/Preferences.cpp:22 src/slic3r/GUI/Tab.cpp:1955 -#: src/slic3r/GUI/Tab.cpp:2193 +#: src/slic3r/GUI/Preferences.cpp:22 src/slic3r/GUI/Tab.cpp:1780 +#: src/slic3r/GUI/Tab.cpp:2024 msgid "General" msgstr "일반" -#: src/slic3r/GUI/Preferences.cpp:44 +#: src/slic3r/GUI/Preferences.cpp:39 msgid "Remember output directory" msgstr "출력 디렉토리 기억하기" -#: src/slic3r/GUI/Preferences.cpp:46 +#: src/slic3r/GUI/Preferences.cpp:41 msgid "" "If this is enabled, Slic3r will prompt the last output directory instead of " "the one containing the input files." @@ -3509,29 +4615,30 @@ msgstr "" "이 옵션을 사용하면 Slic3r은 입력 파일이 들어있는 디렉터리 대신 마지막 출력 디" "렉터리를 묻습니다." -#: src/slic3r/GUI/Preferences.cpp:52 +#: src/slic3r/GUI/Preferences.cpp:47 msgid "Auto-center parts" -msgstr "부품을 자동으로 중심에" +msgstr "부품(Part)을 자동으로 중재봉선에" -#: src/slic3r/GUI/Preferences.cpp:54 +#: src/slic3r/GUI/Preferences.cpp:49 msgid "" "If this is enabled, Slic3r will auto-center objects around the print bed " "center." -msgstr "이 옵션을 사용하면 Slic3r가 개체를 인쇄판 중앙에 자동으로 배치합니다." +msgstr "" +"이 옵션을 사용하면 Slic3r가 객체(object)를 인쇄판 중앙에 자동으로 배치합니다." -#: src/slic3r/GUI/Preferences.cpp:60 +#: src/slic3r/GUI/Preferences.cpp:55 msgid "Background processing" msgstr "백그라운드 프로세싱" -#: src/slic3r/GUI/Preferences.cpp:62 +#: src/slic3r/GUI/Preferences.cpp:57 msgid "" "If this is enabled, Slic3r will pre-process objects as soon as they're " "loaded in order to save time when exporting G-code." msgstr "" -"이 사용 하는 경우 Slic3r는 전처리 개체 최대한 빨리 그들이 시간을 절약 하기 위" -"해 로드 G-코드를 내보낼 때." +"이 사용 하는 경우 Slic3r는 최대한 빨리 시간을 절약 하기 위해 로드된 G-code를 " +"내보낸다." -#: src/slic3r/GUI/Preferences.cpp:71 +#: src/slic3r/GUI/Preferences.cpp:66 msgid "" "If enabled, PrusaSlicer will check for the new versions of itself online. " "When a new version becomes available a notification is displayed at the next " @@ -3542,46 +4649,58 @@ msgstr "" "되면 다음 응용 프로그램 시작시 (프로그램 사용 중이 아님) 알림이 표시 됩니다. " "이는 알림 메커니즘일뿐이며 자동 설치는 수행되지 않습니다." -#: src/slic3r/GUI/Preferences.cpp:79 +#: src/slic3r/GUI/Preferences.cpp:73 +msgid "Export sources full pathnames to 3mf and amf" +msgstr "소스 전체 경로 이름을 3mf 및 amf로 내보내기" + +#: src/slic3r/GUI/Preferences.cpp:75 +msgid "" +"If enabled, allows the Reload from disk command to automatically find and " +"load the files when invoked." +msgstr "" +"활성화된 경우 디스크에서 다시 로드 명령을 사용하여 호출될 때 파일을 자동으로 " +"찾고 로드할 수 있습니다." + +#: src/slic3r/GUI/Preferences.cpp:84 msgid "" "If enabled, Slic3r downloads updates of built-in system presets in the " "background. These updates are downloaded into a separate temporary location. " "When a new preset version becomes available it is offered at application " "startup." msgstr "" -"활성화 된 경우 Slic3r은 백그라운드에서 내장 시스템 사전 설정의 업데이트를 다" -"운로드합니다. 이러한 업데이트는 별도의 임시 위치에 다운로드됩니다. 새로운 사" -"전 설정 버전을 사용할 수있게되면 응용 프로그램 시작시 제공됩니다." +"활성화 된 경우 Slic3r은 백그라운드에서 내장된 시스템 설정의 업데이트를 다운로" +"드합니다. 이러한 업데이트는 별도의 임시 위치에 다운로드됩니다. 새로운 '사전 " +"설정' 버전을 사용할 수 있게되면 응용 프로그램 시작시 제공됩니다." -#: src/slic3r/GUI/Preferences.cpp:84 +#: src/slic3r/GUI/Preferences.cpp:89 msgid "Suppress \" - default - \" presets" msgstr "\"- 기본 -\"사전 설정 숨기기" -#: src/slic3r/GUI/Preferences.cpp:86 +#: src/slic3r/GUI/Preferences.cpp:91 msgid "" "Suppress \" - default - \" presets in the Print / Filament / Printer " "selections once there are any other valid presets available." msgstr "" -"사용 가능한 다른 유효한 사전 설정이 있으면 인쇄 / 필라멘트 / 프린터 선택에서 " -"\"- 기본 -\"사전 설정을 억제하십시오." +"사용 가능한 다른 유효한 '사전 설정'이 있으면 인쇄 / 필라멘트 / 프린터 선택에" +"서 \"- 기본 -\"'사전 설정'을 억제하십시오." -#: src/slic3r/GUI/Preferences.cpp:92 +#: src/slic3r/GUI/Preferences.cpp:97 msgid "Show incompatible print and filament presets" msgstr "호환 되지 않는 인쇄 및 필라멘트 설정" -#: src/slic3r/GUI/Preferences.cpp:94 +#: src/slic3r/GUI/Preferences.cpp:99 msgid "" "When checked, the print and filament presets are shown in the preset editor " "even if they are marked as incompatible with the active printer" msgstr "" -"이 옵션을 선택하면 활성 프린터와 호환되지 않는 것으로 표시된 경우에도 인쇄 " -"및 필라멘트 사전 설정이 사전 설정 편집기에 표시됩니다" +"이 옵션을 선택하면 프린터와 호환되지 않는 것으로 표시된 경우에도 인쇄 및 필라" +"멘트 '사전 설정'이 '사전 설정' 편집기에 표시됩니다" -#: src/slic3r/GUI/Preferences.cpp:101 +#: src/slic3r/GUI/Preferences.cpp:106 msgid "Use Retina resolution for the 3D scene" msgstr "3D 장면에 레티나 해상도 사용" -#: src/slic3r/GUI/Preferences.cpp:103 +#: src/slic3r/GUI/Preferences.cpp:108 msgid "" "If enabled, the 3D scene will be rendered in Retina resolution. If you are " "experiencing 3D performance problems, disabling this option may help." @@ -3589,191 +4708,240 @@ msgstr "" "활성화 된 경우 3D 장면은 레티나 해상도로 렌더링 됩니다. 3D 성능 문제가 발생하" "는 경우, 옵션을 사용하지 않도록 설정 하면 도움이 될 수 있습니다." -#: src/slic3r/GUI/Preferences.cpp:110 -msgid "Use perspective camera" -msgstr "원근 카메라 사용" +#: src/slic3r/GUI/Preferences.cpp:115 +msgid "Camera" +msgstr "카메라" -#: src/slic3r/GUI/Preferences.cpp:112 +#: src/slic3r/GUI/Preferences.cpp:121 +msgid "Use perspective camera" +msgstr "원근 보기 사용" + +#: src/slic3r/GUI/Preferences.cpp:123 msgid "" "If enabled, use perspective camera. If not enabled, use orthographic camera." msgstr "" -"이 옵션을 사용하면 원근 카메라를 사용합니다. 활성화되지 않은 경우 직교 카메라" +"이 옵션을 사용하면 원근 보기모드를 사용합니다. 활성화되지 않은 경우 일반 보기" "를 사용합니다." -#: src/slic3r/GUI/Preferences.cpp:117 +#: src/slic3r/GUI/Preferences.cpp:129 +msgid "Use free camera" +msgstr "무료 카메라 사용" + +#: src/slic3r/GUI/Preferences.cpp:131 +msgid "If enabled, use free camera. If not enabled, use constrained camera." +msgstr "" +"활성화된 경우 무료 카메라를 사용하십시오. 활성화되지 않은 경우 구속된 카메라" +"를 사용합니다." + +#: src/slic3r/GUI/Preferences.cpp:137 +msgid "GUI" +msgstr "GUI" + +#: src/slic3r/GUI/Preferences.cpp:147 msgid "Use custom size for toolbar icons" msgstr "도구 모음 아이콘에 사용자 지정 크기 사용" -#: src/slic3r/GUI/Preferences.cpp:119 +#: src/slic3r/GUI/Preferences.cpp:149 msgid "If enabled, you can change size of toolbar icons manually." msgstr "활성화된 경우 도구 모음 아이콘의 크기를 수동으로 변경할 수 있습니다." -#: src/slic3r/GUI/Preferences.cpp:144 +#: src/slic3r/GUI/Preferences.cpp:176 #, c-format msgid "You need to restart %s to make the changes effective." msgstr "변경 내용을 적용 하려면 %s를 다시 시작 해야 합니다." -#: src/slic3r/GUI/Preferences.cpp:192 +#: src/slic3r/GUI/Preferences.cpp:226 msgid "Icon size in a respect to the default size" msgstr "기본 크기에 대한 아이콘 크기" -#: src/slic3r/GUI/Preferences.cpp:207 +#: src/slic3r/GUI/Preferences.cpp:241 msgid "Select toolbar icon size in respect to the default one." msgstr "기본 도구 모음에 대해 도구 모음 아이콘 크기를 선택합니다." -#: src/slic3r/GUI/Preset.cpp:212 +#: src/slic3r/GUI/Preset.cpp:247 msgid "modified" msgstr "수정된곳" -#: src/slic3r/GUI/Preset.cpp:963 src/slic3r/GUI/Preset.cpp:1003 -#: src/slic3r/GUI/Preset.cpp:1068 src/slic3r/GUI/Preset.cpp:1100 -#: src/slic3r/GUI/PresetBundle.cpp:1480 src/slic3r/GUI/PresetBundle.cpp:1545 +#: src/slic3r/GUI/Preset.cpp:1059 src/slic3r/GUI/Preset.cpp:1106 +#: src/slic3r/GUI/Preset.cpp:1182 src/slic3r/GUI/Preset.cpp:1216 +#: src/slic3r/GUI/PresetBundle.cpp:1576 src/slic3r/GUI/PresetBundle.cpp:1665 msgid "System presets" msgstr "시스템 기본설정" -#: src/slic3r/GUI/Preset.cpp:1007 src/slic3r/GUI/Preset.cpp:1104 -#: src/slic3r/GUI/PresetBundle.cpp:1550 +#: src/slic3r/GUI/Preset.cpp:1110 src/slic3r/GUI/Preset.cpp:1220 +#: src/slic3r/GUI/PresetBundle.cpp:1670 msgid "User presets" msgstr "사용자 사전설정" -#: src/slic3r/GUI/Preset.cpp:1036 src/slic3r/GUI/Tab.cpp:241 +#: src/slic3r/GUI/Preset.cpp:1141 src/slic3r/GUI/Tab.cpp:243 msgid "Add a new printer" msgstr "새 프린터 추가" -#: src/slic3r/GUI/Preset.cpp:1308 +#: src/slic3r/GUI/Preset.cpp:1143 +msgid "Add/Remove materials" +msgstr "재질 추가/제거" + +#: src/slic3r/GUI/Preset.cpp:1453 msgid "filament" msgstr "필라멘트" -#: src/slic3r/GUI/Preset.cpp:1309 +#: src/slic3r/GUI/Preset.cpp:1454 msgid "SLA print" msgstr "SLA 인쇄" -#: src/slic3r/GUI/PresetHints.cpp:28 +#: src/slic3r/GUI/PresetBundle.cpp:1681 +msgid "Add/Remove filaments" +msgstr "필라멘트 추가/제거" + +#: src/slic3r/GUI/PresetHints.cpp:29 msgid "" "If estimated layer time is below ~%1%s, fan will run at %2%%% and print " "speed will be reduced so that no less than %3%s are spent on that layer " "(however, speed will never be reduced below %4%mm/s)." msgstr "" -"예상 레이어 시간이 ~%1%초 미만이면 팬이 %2%%%에서 실행되고 인쇄 속도가 감소되" -"어 해당 레이어에 %3%초 이상 소비됩니다 (단, 속도는 %4%mm/s 이하로 감소하지 않" -"습니다) ." +"예상 레이어 시간이 ~%1%s 미만이면 팬이 %2%%% 에서 실행되고 인쇄 속도가 감소하" +"여 해당 레이어에 %3%s 이상이 소비되지 않습니다 (단, 속도는 아래로 감소하지 않" +"습니다 %4%mm/s)." -#: src/slic3r/GUI/PresetHints.cpp:35 +#: src/slic3r/GUI/PresetHints.cpp:36 msgid "" -"\n" "If estimated layer time is greater, but still below ~%1%s, fan will run at a " "proportionally decreasing speed between %2%%% and %3%%%." msgstr "" -"\n" -"예상 레이어 시간이 더 길지만 ~%1%초 미만인 경우 팬은 %2%%%와 %3%%%사이 비례, " -"감소하는 속도로 실행됩니다." +"예상 레이어 시간이 더 크지만 여전히 ~%1%s 미만이면, 팬은 %2%%% ~ %3%%% 사이에" +"서 비례적으로 감소하는 속도로 실행될 것이다." -#: src/slic3r/GUI/PresetHints.cpp:39 -msgid "" -"\n" -"During the other layers, fan" -msgstr "" -"\n" -"다른 레이어의, 팬설정은 " +#: src/slic3r/GUI/PresetHints.cpp:40 +msgid "During the other layers, fan" +msgstr "다른 레이어의, 팬설정" -#: src/slic3r/GUI/PresetHints.cpp:41 +#: src/slic3r/GUI/PresetHints.cpp:42 msgid "Fan" msgstr "팬(Fan)" -#: src/slic3r/GUI/PresetHints.cpp:47 +#: src/slic3r/GUI/PresetHints.cpp:48 msgid "will always run at %1%%%" msgstr "항상 다음처럼 실행 %1%%%" -#: src/slic3r/GUI/PresetHints.cpp:50 +#: src/slic3r/GUI/PresetHints.cpp:51 msgid "except for the first %1% layers." -msgstr "첫 번째 %d 레이어를 제외하고" +msgstr "첫 번째 %1% 레이어를 제외하고" -#: src/slic3r/GUI/PresetHints.cpp:52 +#: src/slic3r/GUI/PresetHints.cpp:53 msgid "except for the first layer." msgstr "첫 번째 레이어를 제외하고" -#: src/slic3r/GUI/PresetHints.cpp:54 +#: src/slic3r/GUI/PresetHints.cpp:55 msgid "will be turned off." msgstr "off 됩니다." -#: src/slic3r/GUI/PresetHints.cpp:155 +#: src/slic3r/GUI/PresetHints.cpp:156 msgid "external perimeters" msgstr "외부 둘레" -#: src/slic3r/GUI/PresetHints.cpp:164 +#: src/slic3r/GUI/PresetHints.cpp:165 msgid "perimeters" msgstr "둘레" -#: src/slic3r/GUI/PresetHints.cpp:173 +#: src/slic3r/GUI/PresetHints.cpp:174 msgid "infill" msgstr "채움(infill)" -#: src/slic3r/GUI/PresetHints.cpp:183 +#: src/slic3r/GUI/PresetHints.cpp:184 msgid "solid infill" msgstr "외부(solid)부분 채움" -#: src/slic3r/GUI/PresetHints.cpp:191 +#: src/slic3r/GUI/PresetHints.cpp:192 msgid "top solid infill" msgstr "가장 윗부분 채움" -#: src/slic3r/GUI/PresetHints.cpp:202 +#: src/slic3r/GUI/PresetHints.cpp:203 msgid "support" msgstr "서포트" -#: src/slic3r/GUI/PresetHints.cpp:212 +#: src/slic3r/GUI/PresetHints.cpp:213 msgid "support interface" -msgstr "서포트 인터페이스" +msgstr "서포트 접점" -#: src/slic3r/GUI/PresetHints.cpp:218 +#: src/slic3r/GUI/PresetHints.cpp:219 msgid "First layer volumetric" msgstr "첫번째 레이어 용적은" -#: src/slic3r/GUI/PresetHints.cpp:218 +#: src/slic3r/GUI/PresetHints.cpp:219 msgid "Bridging volumetric" msgstr "브리징(Bridging) 용적" -#: src/slic3r/GUI/PresetHints.cpp:218 +#: src/slic3r/GUI/PresetHints.cpp:219 msgid "Volumetric" msgstr "용적" -#: src/slic3r/GUI/PresetHints.cpp:219 +#: src/slic3r/GUI/PresetHints.cpp:220 msgid "flow rate is maximized" msgstr "의 유속(flow)이 최대화된다. " -#: src/slic3r/GUI/PresetHints.cpp:222 +#: src/slic3r/GUI/PresetHints.cpp:223 msgid "by the print profile maximum" msgstr "인쇄 프로파일 최대 값" -#: src/slic3r/GUI/PresetHints.cpp:223 +#: src/slic3r/GUI/PresetHints.cpp:224 msgid "when printing" msgstr "인쇄 할 때" -#: src/slic3r/GUI/PresetHints.cpp:224 +#: src/slic3r/GUI/PresetHints.cpp:225 msgid "with a volumetric rate" msgstr "의 용적 비율로 " -#: src/slic3r/GUI/PresetHints.cpp:228 +#: src/slic3r/GUI/PresetHints.cpp:229 #, c-format msgid "%3.2f mm³/s at filament speed %3.2f mm/s." msgstr "%3.2f mm³/s 필라멘트 속도는 %3.2f mm/s이다." -#: src/slic3r/GUI/PresetHints.cpp:246 +#: src/slic3r/GUI/PresetHints.cpp:247 msgid "" "Recommended object thin wall thickness: Not available due to invalid layer " "height." msgstr "" "권장 객체(object)의 벽(wall) 두께: 잘못된 레이어 높이 때문에 사용할 수 없음." -#: src/slic3r/GUI/PresetHints.cpp:262 +#: src/slic3r/GUI/PresetHints.cpp:263 #, c-format msgid "Recommended object thin wall thickness for layer height %.2f and" -msgstr "객체 레이어 높이 %.2f 에 권장하는 두깨는 " +msgstr "객체(object) 레이어 높이 %.2f 에 권장하는 두깨는 " -#: src/slic3r/GUI/PresetHints.cpp:268 +#: src/slic3r/GUI/PresetHints.cpp:269 #, c-format msgid "%d lines: %.2f mm" msgstr "%d 라인:%.2f mm" +#: src/slic3r/GUI/PresetHints.cpp:300 +msgid "" +"Top / bottom shell thickness hint: Not available due to invalid layer height." +msgstr "상단/하단 쉘 두께 힌트: 잘못된 레이어 높이로 인해 사용할 수 없습니다." + +#: src/slic3r/GUI/PresetHints.cpp:313 +msgid "Top shell is %1% mm thick for layer height %2% mm." +msgstr "상단 쉘은 층 높이 %2% mm에 대한 두께 %1% mm입니다." + +#: src/slic3r/GUI/PresetHints.cpp:316 +msgid "Minimum top shell thickness is %1% mm." +msgstr "최소 상단 쉘 두께는 %1% mm입니다." + +#: src/slic3r/GUI/PresetHints.cpp:319 +msgid "Top is open." +msgstr "상단이 열려 있습니다." + +#: src/slic3r/GUI/PresetHints.cpp:332 +msgid "Bottom shell is %1% mm thick for layer height %2% mm." +msgstr "바닥 층 높이 %2% mm에 대한 두께 %1% mm입니다." + +#: src/slic3r/GUI/PresetHints.cpp:335 +msgid "Minimum bottom shell thickness is %1% mm." +msgstr "최소 바닥 쉘 두께는 %1% mm입니다." + +#: src/slic3r/GUI/PresetHints.cpp:338 +msgid "Bottom is open." +msgstr "바닥이 열려 있습니다." + #: src/slic3r/GUI/PrintHostDialogs.cpp:33 msgid "Send G-Code to printer host" msgstr "프린터 호스트로 G 코드 보내기" @@ -3847,11 +5015,13 @@ msgstr "전혀 충돌 없음" msgid "Time" msgstr "시간" -#: src/slic3r/GUI/RammingChart.cpp:76 src/slic3r/GUI/WipeTowerDialog.cpp:82 -#: src/libslic3r/PrintConfig.cpp:627 src/libslic3r/PrintConfig.cpp:671 -#: src/libslic3r/PrintConfig.cpp:686 src/libslic3r/PrintConfig.cpp:2349 -#: src/libslic3r/PrintConfig.cpp:2358 src/libslic3r/PrintConfig.cpp:2418 -#: src/libslic3r/PrintConfig.cpp:2425 +#: src/slic3r/GUI/RammingChart.cpp:76 src/slic3r/GUI/WipeTowerDialog.cpp:83 +#: src/libslic3r/PrintConfig.cpp:645 src/libslic3r/PrintConfig.cpp:689 +#: src/libslic3r/PrintConfig.cpp:704 src/libslic3r/PrintConfig.cpp:2418 +#: src/libslic3r/PrintConfig.cpp:2427 src/libslic3r/PrintConfig.cpp:2528 +#: src/libslic3r/PrintConfig.cpp:2536 src/libslic3r/PrintConfig.cpp:2544 +#: src/libslic3r/PrintConfig.cpp:2551 src/libslic3r/PrintConfig.cpp:2559 +#: src/libslic3r/PrintConfig.cpp:2567 msgid "s" msgstr "s" @@ -3859,11 +5029,55 @@ msgstr "s" msgid "Volumetric speed" msgstr "용적(Volumetric) 스피트" -#: src/slic3r/GUI/RammingChart.cpp:81 src/libslic3r/PrintConfig.cpp:584 -#: src/libslic3r/PrintConfig.cpp:1234 +#: src/slic3r/GUI/RammingChart.cpp:81 src/libslic3r/PrintConfig.cpp:602 +#: src/libslic3r/PrintConfig.cpp:1259 msgid "mm³/s" msgstr "밀리미터 ³/s" +#: src/slic3r/GUI/Selection.cpp:146 +msgid "Selection-Add" +msgstr "선택 추가" + +#: src/slic3r/GUI/Selection.cpp:187 +msgid "Selection-Remove" +msgstr "선택-제거" + +#: src/slic3r/GUI/Selection.cpp:219 +msgid "Selection-Add Object" +msgstr "선택-추가 객체" + +#: src/slic3r/GUI/Selection.cpp:238 +msgid "Selection-Remove Object" +msgstr "선택-제거 개체" + +#: src/slic3r/GUI/Selection.cpp:256 +msgid "Selection-Add Instance" +msgstr "선택-인스턴스 추가" + +#: src/slic3r/GUI/Selection.cpp:275 +msgid "Selection-Remove Instance" +msgstr "선택-제거 인스턴스" + +#: src/slic3r/GUI/Selection.cpp:376 +msgid "Selection-Add All" +msgstr "선택-모두 추가" + +#: src/slic3r/GUI/Selection.cpp:402 +msgid "Selection-Remove All" +msgstr "선택-모두 제거" + +#: src/slic3r/GUI/Selection.cpp:939 +msgid "Scale To Fit" +msgstr "크기에 맞게 조정" + +#: src/slic3r/GUI/Selection.cpp:1475 +msgid "Set Printable Instance" +msgstr "인쇄 가능한 인스턴스 설정" + +#: src/slic3r/GUI/Selection.cpp:1475 +msgid "Set Unprintable Instance" +msgstr "인쇄할 수 없는 인스턴스 설정" + #: src/slic3r/GUI/SysInfoDialog.cpp:78 msgid "System Information" msgstr "시스템 정보" @@ -3872,7 +5086,7 @@ msgstr "시스템 정보" msgid "Copy to Clipboard" msgstr "클립보드에 복사" -#: src/slic3r/GUI/Tab.cpp:52 src/libslic3r/PrintConfig.cpp:239 +#: src/slic3r/GUI/Tab.cpp:52 src/libslic3r/PrintConfig.cpp:255 msgid "Compatible printers" msgstr "호환 가능한 프린터들" @@ -3880,7 +5094,7 @@ msgstr "호환 가능한 프린터들" msgid "Select the printers this profile is compatible with." msgstr "이 프로파일과 호환 가능한 프린터를 선택하세요." -#: src/slic3r/GUI/Tab.cpp:58 src/libslic3r/PrintConfig.cpp:254 +#: src/slic3r/GUI/Tab.cpp:58 src/libslic3r/PrintConfig.cpp:270 msgid "Compatible print profiles" msgstr "호환되는 인쇄 프로 파일" @@ -3889,16 +5103,16 @@ msgid "Select the print profiles this profile is compatible with." msgstr "이 프로필이 호환되는 인쇄 프로필을 선택 합니다." #. TRN "Save current Settings" -#: src/slic3r/GUI/Tab.cpp:133 +#: src/slic3r/GUI/Tab.cpp:135 #, c-format msgid "Save current %s" msgstr "현재 %s 저장" -#: src/slic3r/GUI/Tab.cpp:134 +#: src/slic3r/GUI/Tab.cpp:136 msgid "Delete this preset" msgstr "이전 설정 삭제" -#: src/slic3r/GUI/Tab.cpp:139 +#: src/slic3r/GUI/Tab.cpp:141 msgid "" "Hover the cursor over buttons to find more information \n" "or click this button." @@ -3906,380 +5120,279 @@ msgstr "" "버튼 위로 커서를 가져 가서 자세한 정보를 찾습니다.\n" "또는이 버튼을 클릭하십시오." -#: src/slic3r/GUI/Tab.cpp:921 +#: src/slic3r/GUI/Tab.cpp:943 msgid "This is a default preset." msgstr "기본 설정입니다." -#: src/slic3r/GUI/Tab.cpp:923 +#: src/slic3r/GUI/Tab.cpp:945 msgid "This is a system preset." msgstr "시스템 설정입니다." -#: src/slic3r/GUI/Tab.cpp:925 +#: src/slic3r/GUI/Tab.cpp:947 msgid "Current preset is inherited from the default preset." msgstr "현재 사전 설정은 기본 사전 설정에서 상속됩니다." -#: src/slic3r/GUI/Tab.cpp:928 +#: src/slic3r/GUI/Tab.cpp:950 #, c-format msgid "" "Current preset is inherited from:\n" "\t%s" msgstr "전의 %s 설정에서 가져 옵니다 " -#: src/slic3r/GUI/Tab.cpp:932 +#: src/slic3r/GUI/Tab.cpp:954 msgid "It can't be deleted or modified." msgstr "삭제하거나 수정할 수 없습니다." -#: src/slic3r/GUI/Tab.cpp:933 +#: src/slic3r/GUI/Tab.cpp:955 msgid "" "Any modifications should be saved as a new preset inherited from this one." msgstr "모든 수정 사항은 이 항목에서 받은 기본 설정으로 저장해야합니다." -#: src/slic3r/GUI/Tab.cpp:934 +#: src/slic3r/GUI/Tab.cpp:956 msgid "To do that please specify a new name for the preset." msgstr "그렇게하려면 기본 설정의 새 이름을 지정하십시오." -#: src/slic3r/GUI/Tab.cpp:938 +#: src/slic3r/GUI/Tab.cpp:960 msgid "Additional information:" msgstr "추가 정보:" -#: src/slic3r/GUI/Tab.cpp:944 +#: src/slic3r/GUI/Tab.cpp:966 msgid "printer model" msgstr "프린터 모델" -#: src/slic3r/GUI/Tab.cpp:952 +#: src/slic3r/GUI/Tab.cpp:974 msgid "default print profile" msgstr "기본 인쇄 프로파일" -#: src/slic3r/GUI/Tab.cpp:955 +#: src/slic3r/GUI/Tab.cpp:977 msgid "default filament profile" msgstr "기본 필라멘트 프로파일" -#: src/slic3r/GUI/Tab.cpp:969 +#: src/slic3r/GUI/Tab.cpp:991 msgid "default SLA material profile" msgstr "기본 SLA 재질 프로 파일" -#: src/slic3r/GUI/Tab.cpp:973 +#: src/slic3r/GUI/Tab.cpp:995 msgid "default SLA print profile" msgstr "기본 SLA 인쇄 프로필" -#: src/slic3r/GUI/Tab.cpp:1008 src/slic3r/GUI/Tab.cpp:3649 +#: src/slic3r/GUI/Tab.cpp:1003 +msgid "full profile name" +msgstr "전체 프로필 이름" + +#: src/slic3r/GUI/Tab.cpp:1004 +msgid "symbolic profile name" +msgstr "기호 프로파일 이름" + +#: src/slic3r/GUI/Tab.cpp:1038 src/slic3r/GUI/Tab.cpp:3570 msgid "Layers and perimeters" -msgstr "레이어 및 경계선" +msgstr "레이어 및 둘레" -#: src/slic3r/GUI/Tab.cpp:1009 src/slic3r/GUI/Tab.cpp:1257 -#: src/libslic3r/PrintConfig.cpp:66 -msgid "Layer height" -msgstr "레이어 높이" - -#: src/slic3r/GUI/Tab.cpp:1013 +#: src/slic3r/GUI/Tab.cpp:1043 msgid "Vertical shells" msgstr "쉘 높이" -#: src/slic3r/GUI/Tab.cpp:1024 +#: src/slic3r/GUI/Tab.cpp:1054 msgid "Horizontal shells" msgstr "쉘 너비" -#: src/slic3r/GUI/Tab.cpp:1025 src/libslic3r/PrintConfig.cpp:1759 +#: src/slic3r/GUI/Tab.cpp:1055 src/libslic3r/PrintConfig.cpp:1790 msgid "Solid layers" msgstr "솔리드 레이어" -#: src/slic3r/GUI/Tab.cpp:1030 +#: src/slic3r/GUI/Tab.cpp:1059 +msgid "Minimum shell thickness" +msgstr "최소 쉘 두께" + +#: src/slic3r/GUI/Tab.cpp:1070 msgid "Quality (slower slicing)" msgstr "품질(슬라이싱이 느려짐)" -#: src/slic3r/GUI/Tab.cpp:1048 +#: src/slic3r/GUI/Tab.cpp:1088 msgid "Reducing printing time" msgstr "출력 시간 단축" -#: src/slic3r/GUI/Tab.cpp:1060 +#: src/slic3r/GUI/Tab.cpp:1100 msgid "Skirt and brim" msgstr "스커트와 브림" -#: src/slic3r/GUI/Tab.cpp:1077 +#: src/slic3r/GUI/Tab.cpp:1117 msgid "Raft" msgstr "라프트" -#: src/slic3r/GUI/Tab.cpp:1081 +#: src/slic3r/GUI/Tab.cpp:1121 msgid "Options for support material and raft" msgstr "서포트와 라프트 재료를 선택" -#: src/slic3r/GUI/Tab.cpp:1096 +#: src/slic3r/GUI/Tab.cpp:1136 msgid "Speed for print moves" msgstr "출력중 이동 속도" -#: src/slic3r/GUI/Tab.cpp:1108 +#: src/slic3r/GUI/Tab.cpp:1148 msgid "Speed for non-print moves" msgstr "미출력시 이동속도" -#: src/slic3r/GUI/Tab.cpp:1111 +#: src/slic3r/GUI/Tab.cpp:1151 msgid "Modifiers" msgstr "수정" -#: src/slic3r/GUI/Tab.cpp:1114 +#: src/slic3r/GUI/Tab.cpp:1154 msgid "Acceleration control (advanced)" msgstr "가속 제어(고급)" -#: src/slic3r/GUI/Tab.cpp:1121 +#: src/slic3r/GUI/Tab.cpp:1161 msgid "Autospeed (advanced)" msgstr "오토스피트(고급)" -#: src/slic3r/GUI/Tab.cpp:1129 +#: src/slic3r/GUI/Tab.cpp:1169 msgid "Multiple Extruders" msgstr "다중 익스트루더" -#: src/slic3r/GUI/Tab.cpp:1137 +#: src/slic3r/GUI/Tab.cpp:1177 msgid "Ooze prevention" msgstr "오즈 방지(Ooze prevention)" -#: src/slic3r/GUI/Tab.cpp:1154 +#: src/slic3r/GUI/Tab.cpp:1195 msgid "Extrusion width" msgstr "악출 폭(Extrusion width)" -#: src/slic3r/GUI/Tab.cpp:1164 +#: src/slic3r/GUI/Tab.cpp:1205 msgid "Overlap" msgstr "겹침(Overlap)" -#: src/slic3r/GUI/Tab.cpp:1167 +#: src/slic3r/GUI/Tab.cpp:1208 msgid "Flow" msgstr "유량(Flow)" -#: src/slic3r/GUI/Tab.cpp:1176 +#: src/slic3r/GUI/Tab.cpp:1217 msgid "Other" msgstr "그 외" -#: src/slic3r/GUI/Tab.cpp:1179 src/slic3r/GUI/Tab.cpp:3703 +#: src/slic3r/GUI/Tab.cpp:1220 src/slic3r/GUI/Tab.cpp:3637 msgid "Output options" msgstr "출력 옵션" -#: src/slic3r/GUI/Tab.cpp:1180 +#: src/slic3r/GUI/Tab.cpp:1221 msgid "Sequential printing" msgstr "연속 인쇄" -#: src/slic3r/GUI/Tab.cpp:1182 +#: src/slic3r/GUI/Tab.cpp:1223 msgid "Extruder clearance (mm)" msgstr "익스트루더 간격(mm)" -#: src/slic3r/GUI/Tab.cpp:1191 src/slic3r/GUI/Tab.cpp:3704 +#: src/slic3r/GUI/Tab.cpp:1232 src/slic3r/GUI/Tab.cpp:3638 msgid "Output file" msgstr "출력 파일" -#: src/slic3r/GUI/Tab.cpp:1198 src/libslic3r/PrintConfig.cpp:1432 +#: src/slic3r/GUI/Tab.cpp:1239 src/libslic3r/PrintConfig.cpp:1462 msgid "Post-processing scripts" msgstr "사후 처리 스크립트" -#: src/slic3r/GUI/Tab.cpp:1204 src/slic3r/GUI/Tab.cpp:1205 -#: src/slic3r/GUI/Tab.cpp:1716 src/slic3r/GUI/Tab.cpp:1717 -#: src/slic3r/GUI/Tab.cpp:2165 src/slic3r/GUI/Tab.cpp:2166 -#: src/slic3r/GUI/Tab.cpp:2273 src/slic3r/GUI/Tab.cpp:2274 -#: src/slic3r/GUI/Tab.cpp:3586 src/slic3r/GUI/Tab.cpp:3587 +#: src/slic3r/GUI/Tab.cpp:1245 src/slic3r/GUI/Tab.cpp:1246 +#: src/slic3r/GUI/Tab.cpp:1531 src/slic3r/GUI/Tab.cpp:1532 +#: src/slic3r/GUI/Tab.cpp:1996 src/slic3r/GUI/Tab.cpp:1997 +#: src/slic3r/GUI/Tab.cpp:2110 src/slic3r/GUI/Tab.cpp:2111 +#: src/slic3r/GUI/Tab.cpp:3507 src/slic3r/GUI/Tab.cpp:3508 msgid "Notes" msgstr "메모" -#: src/slic3r/GUI/Tab.cpp:1211 src/slic3r/GUI/Tab.cpp:1724 -#: src/slic3r/GUI/Tab.cpp:2172 src/slic3r/GUI/Tab.cpp:2280 -#: src/slic3r/GUI/Tab.cpp:3594 src/slic3r/GUI/Tab.cpp:3709 +#: src/slic3r/GUI/Tab.cpp:1252 src/slic3r/GUI/Tab.cpp:1539 +#: src/slic3r/GUI/Tab.cpp:2003 src/slic3r/GUI/Tab.cpp:2117 +#: src/slic3r/GUI/Tab.cpp:3515 src/slic3r/GUI/Tab.cpp:3643 msgid "Dependencies" msgstr "속한 그룹" -#: src/slic3r/GUI/Tab.cpp:1212 src/slic3r/GUI/Tab.cpp:1725 -#: src/slic3r/GUI/Tab.cpp:2173 src/slic3r/GUI/Tab.cpp:2281 -#: src/slic3r/GUI/Tab.cpp:3595 src/slic3r/GUI/Tab.cpp:3710 +#: src/slic3r/GUI/Tab.cpp:1253 src/slic3r/GUI/Tab.cpp:1540 +#: src/slic3r/GUI/Tab.cpp:2004 src/slic3r/GUI/Tab.cpp:2118 +#: src/slic3r/GUI/Tab.cpp:3516 src/slic3r/GUI/Tab.cpp:3644 msgid "Profile dependencies" msgstr "프로파일 속한곳" -#: src/slic3r/GUI/Tab.cpp:1256 -msgid "" -"Zero layer height is not valid.\n" -"\n" -"The layer height will be reset to 0.01." -msgstr "" -"바닥 레이어 높이가 잘못되었습니다.\n" -"\n" -"레이어 높이가 0.01로 재설정됩니다." - -#: src/slic3r/GUI/Tab.cpp:1268 -msgid "" -"Zero first layer height is not valid.\n" -"\n" -"The first layer height will be reset to 0.01." -msgstr "" -"첫 번째 레이어 높이가 0이면 유효하지 않습니다.\n" -"\n" -"첫 번째 레이어 높이는 0.01로 재설정됩니다." - -#: src/slic3r/GUI/Tab.cpp:1269 src/libslic3r/PrintConfig.cpp:866 -msgid "First layer height" -msgstr "첫 레이어 높이" - -#: src/slic3r/GUI/Tab.cpp:1283 -#, c-format -msgid "" -"The Spiral Vase mode requires:\n" -"- one perimeter\n" -"- no top solid layers\n" -"- 0% fill density\n" -"- no support material\n" -"- no ensure_vertical_shell_thickness\n" -"\n" -"Shall I adjust those settings in order to enable Spiral Vase?" -msgstr "" -"스파이럴 바이스 모드에는 다음이 필요합니다.\n" -"- one 둘레\n" -"- 탑 솔리드 레이어 없음\n" -"- 0% fill density\n" -"- 서포트 재료 없음\n" -"- 수직 벽 두깨를 보장하지 않음\n" -"\n" -"스파이럴 바이스를 사용하려면 이러한 설정을 조정해야합니까?" - -#: src/slic3r/GUI/Tab.cpp:1290 -msgid "Spiral Vase" -msgstr "스파이럴 바이스" - -#: src/slic3r/GUI/Tab.cpp:1311 -msgid "" -"The Wipe Tower currently supports the non-soluble supports only\n" -"if they are printed with the current extruder without triggering a tool " -"change.\n" -"(both support_material_extruder and support_material_interface_extruder need " -"to be set to 0).\n" -"\n" -"Shall I adjust those settings in order to enable the Wipe Tower?" -msgstr "" -"와이퍼 타워는 현재 비 가용성 서포트 만 지원합니다.\n" -"공구 교환을 트리거하지 않고 현재 압출기로 인쇄 한 경우.\n" -"(support_material_extruder 및 support_material_interface_extruder를 모두 0으" -"로 설정해야 함).\n" -"\n" -"와이퍼 타워를 사용하려면 이러한 설정을 조정해야합니까?" - -#: src/slic3r/GUI/Tab.cpp:1315 src/slic3r/GUI/Tab.cpp:1332 -msgid "Wipe Tower" -msgstr "와이프 타워(Wipe Tower)" - -#: src/slic3r/GUI/Tab.cpp:1329 -msgid "" -"For the Wipe Tower to work with the soluble supports, the support layers\n" -"need to be synchronized with the object layers.\n" -"\n" -"Shall I synchronize support layers in order to enable the Wipe Tower?" -msgstr "" -"와이퍼 타워가 가용성 서포트와 함께 작용하기 위해, 서포트 레이어\n" -"객체 레이어와 동기화되어야합니다.\n" -"\n" -"와이퍼 타워를 사용하려면 서포트 레이어를 동기화해야합니까?" - -#: src/slic3r/GUI/Tab.cpp:1347 -msgid "" -"Supports work better, if the following feature is enabled:\n" -"- Detect bridging perimeters\n" -"\n" -"Shall I adjust those settings for supports?" -msgstr "" -"다음 기능을 사용하는 경우 더 나은 작업을 지원합니다.\n" -"- 브리지 경계 검출\n" -"\n" -"서포트에 대한 설정을 조정해야합니까?" - -#: src/slic3r/GUI/Tab.cpp:1350 -msgid "Support Generator" -msgstr "서포트 생성" - -#: src/slic3r/GUI/Tab.cpp:1392 -msgid "" -"The %1% infill pattern is not supposed to work at 100%% density.\n" -"\n" -"Shall I switch to rectilinear fill pattern?" -msgstr "" -"%1% 채우기 패턴은 100%의 밀도로 작동 하지 않습니다.\n" -"\n" -"직선 채우기 패턴으로 전환 해야 합니까?" - -#: src/slic3r/GUI/Tab.cpp:1502 src/slic3r/GUI/Tab.cpp:1557 +#: src/slic3r/GUI/Tab.cpp:1317 src/slic3r/GUI/Tab.cpp:1372 msgid "Filament Overrides" msgstr "필라멘트 재정의" -#: src/slic3r/GUI/Tab.cpp:1503 src/slic3r/GUI/Tab.cpp:1562 -#: src/slic3r/GUI/Tab.cpp:2514 +#: src/slic3r/GUI/Tab.cpp:1318 src/slic3r/GUI/Tab.cpp:1377 +#: src/slic3r/GUI/Tab.cpp:2352 msgid "Retraction" msgstr "리트렉션" -#: src/slic3r/GUI/Tab.cpp:1612 src/libslic3r/PrintConfig.cpp:2030 +#: src/slic3r/GUI/Tab.cpp:1427 src/libslic3r/PrintConfig.cpp:2077 msgid "Temperature" msgstr "온도" -#: src/slic3r/GUI/Tab.cpp:1618 +#: src/slic3r/GUI/Tab.cpp:1433 msgid "Bed" msgstr "배드(Bed)" -#: src/slic3r/GUI/Tab.cpp:1623 +#: src/slic3r/GUI/Tab.cpp:1438 msgid "Cooling" msgstr "냉각(Cooling)" -#: src/slic3r/GUI/Tab.cpp:1624 src/libslic3r/PrintConfig.cpp:1335 -#: src/libslic3r/PrintConfig.cpp:2150 +#: src/slic3r/GUI/Tab.cpp:1439 src/libslic3r/PrintConfig.cpp:1364 +#: src/libslic3r/PrintConfig.cpp:2210 msgid "Enable" msgstr "사용" -#: src/slic3r/GUI/Tab.cpp:1635 +#: src/slic3r/GUI/Tab.cpp:1450 msgid "Fan settings" msgstr "팬 설정" -#: src/slic3r/GUI/Tab.cpp:1636 -msgid "Fan speed" -msgstr "팬 속도" - -#: src/slic3r/GUI/Tab.cpp:1644 +#: src/slic3r/GUI/Tab.cpp:1459 msgid "Cooling thresholds" msgstr "냉각 임계 값" -#: src/slic3r/GUI/Tab.cpp:1650 +#: src/slic3r/GUI/Tab.cpp:1465 msgid "Filament properties" msgstr "필라멘트 특성" -#: src/slic3r/GUI/Tab.cpp:1654 +#: src/slic3r/GUI/Tab.cpp:1469 msgid "Print speed override" msgstr "인쇄 속도 중단" -#: src/slic3r/GUI/Tab.cpp:1664 +#: src/slic3r/GUI/Tab.cpp:1479 msgid "Wipe tower parameters" msgstr "타워 매개변수 지우기" -#: src/slic3r/GUI/Tab.cpp:1667 +#: src/slic3r/GUI/Tab.cpp:1482 msgid "Toolchange parameters with single extruder MM printers" -msgstr "싱글 익스트루더 멀티 터리알 프린터를 사용한 공구 교환 매개 변수" +msgstr "MMU 프린터의 툴체인지 매개 변수" -#: src/slic3r/GUI/Tab.cpp:1681 +#: src/slic3r/GUI/Tab.cpp:1496 msgid "Ramming settings" msgstr "래밍 설정" -#: src/slic3r/GUI/Tab.cpp:1703 src/slic3r/GUI/Tab.cpp:2128 +#: src/slic3r/GUI/Tab.cpp:1518 src/slic3r/GUI/Tab.cpp:1959 msgid "Custom G-code" msgstr "수동 G코드" -#: src/slic3r/GUI/Tab.cpp:1704 src/slic3r/GUI/Tab.cpp:2129 -#: src/libslic3r/PrintConfig.cpp:1785 src/libslic3r/PrintConfig.cpp:1800 +#: src/slic3r/GUI/Tab.cpp:1519 src/slic3r/GUI/Tab.cpp:1960 +#: src/libslic3r/PrintConfig.cpp:1823 src/libslic3r/PrintConfig.cpp:1838 msgid "Start G-code" msgstr "스타트 G코드" -#: src/slic3r/GUI/Tab.cpp:1710 src/slic3r/GUI/Tab.cpp:2135 -#: src/libslic3r/PrintConfig.cpp:369 src/libslic3r/PrintConfig.cpp:379 +#: src/slic3r/GUI/Tab.cpp:1525 src/slic3r/GUI/Tab.cpp:1966 +#: src/libslic3r/PrintConfig.cpp:385 src/libslic3r/PrintConfig.cpp:395 msgid "End G-code" msgstr "엔드 G코드" -#: src/slic3r/GUI/Tab.cpp:1843 src/slic3r/GUI/Tab.cpp:2068 +#: src/slic3r/GUI/Tab.cpp:1582 +msgid "Volumetric flow hints not available" +msgstr "체적 흐름 힌트를 사용할 수 없음" + +#: src/slic3r/GUI/Tab.cpp:1668 src/slic3r/GUI/Tab.cpp:1899 msgid "Test" msgstr "시험(test)" -#: src/slic3r/GUI/Tab.cpp:1853 +#: src/slic3r/GUI/Tab.cpp:1678 msgid "Could not get a valid Printer Host reference" msgstr "유효한 프린터 호스트 참조를 가져올 수 없습니다" -#: src/slic3r/GUI/Tab.cpp:1859 src/slic3r/GUI/Tab.cpp:2081 +#: src/slic3r/GUI/Tab.cpp:1684 src/slic3r/GUI/Tab.cpp:1912 msgid "Success!" msgstr "성공!" -#: src/slic3r/GUI/Tab.cpp:1874 +#: src/slic3r/GUI/Tab.cpp:1699 msgid "" "HTTPS CA file is optional. It is only needed if you use HTTPS with a self-" "signed certificate." @@ -4287,15 +5400,15 @@ msgstr "" "HTTPS CA 파일은 선택 사항입니다. 자체 서명 된 인증서로 HTTPS를 사용하는 경우" "에만 필요합니다." -#: src/slic3r/GUI/Tab.cpp:1887 +#: src/slic3r/GUI/Tab.cpp:1712 msgid "Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*" msgstr "인증서 파일 (* .crt, * .pem) | * .crt; * .pem | 모든 파일 | *. *" -#: src/slic3r/GUI/Tab.cpp:1888 +#: src/slic3r/GUI/Tab.cpp:1713 msgid "Open CA certificate file" msgstr "CA 인증서 파일 열기" -#: src/slic3r/GUI/Tab.cpp:1916 +#: src/slic3r/GUI/Tab.cpp:1741 #, c-format msgid "" "HTTPS CA File:\n" @@ -4305,176 +5418,183 @@ msgid "" "Store / Keychain." msgstr "" "HTTPS CA 파일:\n" -"\t이 시스템에서 %s는 시스템 인증서 저장소나 키체인의 HTTPS 인증서를 사용 합니" -"다.\n" -"\t사용자 지정 CA 파일을 사용 하려면 CA 파일을 인증서 저장소/키체인에 가져오십" -"시오." +" \t이 시스템에서 %s는 시스템 인증서 저장소나 키체인의 HTTPS 인증서를 사용 " +"합니다.\n" +" \t사용자 지정 CA 파일을 사용 하려면 CA 파일을 인증서 저장소/키체인에 가져" +"오십시오." -#: src/slic3r/GUI/Tab.cpp:1956 src/slic3r/GUI/Tab.cpp:2194 +#: src/slic3r/GUI/Tab.cpp:1781 src/slic3r/GUI/Tab.cpp:2025 msgid "Size and coordinates" msgstr "크기와 좌표" -#: src/slic3r/GUI/Tab.cpp:1961 src/slic3r/GUI/Tab.cpp:2199 -#: src/slic3r/GUI/Tab.cpp:3256 +#: src/slic3r/GUI/Tab.cpp:1786 src/slic3r/GUI/Tab.cpp:2030 +#: src/slic3r/GUI/Tab.cpp:3146 msgid "Set" msgstr "설정" -#: src/slic3r/GUI/Tab.cpp:1993 +#: src/slic3r/GUI/Tab.cpp:1818 msgid "Capabilities" msgstr "기능" -#: src/slic3r/GUI/Tab.cpp:1998 +#: src/slic3r/GUI/Tab.cpp:1823 msgid "Number of extruders of the printer." -msgstr "프린터 익스트루더 숫자." +msgstr "프린터 익스트루더 갯수." -#: src/slic3r/GUI/Tab.cpp:2023 +#: src/slic3r/GUI/Tab.cpp:1851 msgid "" "Single Extruder Multi Material is selected, \n" "and all extruders must have the same diameter.\n" "Do you want to change the diameter for all extruders to first extruder " "nozzle diameter value?" msgstr "" -"단일 압출기 멀티 재질이 선택되고, \n" +"단일 압출기 다중 재질이 선택되고, \n" "모든 압출기는 동일한 직경을 가져야 합니다.\n" "모든 압출기의 지름을 첫 번째 압출기 노즐 값으로 변경하시겠습니까?" -#: src/slic3r/GUI/Tab.cpp:2026 src/slic3r/GUI/Tab.cpp:2484 -#: src/libslic3r/PrintConfig.cpp:1310 +#: src/slic3r/GUI/Tab.cpp:1854 src/slic3r/GUI/Tab.cpp:2322 +#: src/libslic3r/PrintConfig.cpp:1335 msgid "Nozzle diameter" msgstr "노즐 직경" -#: src/slic3r/GUI/Tab.cpp:2053 +#: src/slic3r/GUI/Tab.cpp:1884 msgid "USB/Serial connection" msgstr "USB/시리얼 연결" -#: src/slic3r/GUI/Tab.cpp:2054 src/libslic3r/PrintConfig.cpp:1640 +#: src/slic3r/GUI/Tab.cpp:1885 src/libslic3r/PrintConfig.cpp:1670 msgid "Serial port" msgstr "시리얼 포트" -#: src/slic3r/GUI/Tab.cpp:2059 +#: src/slic3r/GUI/Tab.cpp:1890 msgid "Rescan serial ports" msgstr "시리얼포트 재검색" -#: src/slic3r/GUI/Tab.cpp:2081 +#: src/slic3r/GUI/Tab.cpp:1912 msgid "Connection to printer works correctly." msgstr "프린터 연결이 올바르게 작동합니다." -#: src/slic3r/GUI/Tab.cpp:2084 +#: src/slic3r/GUI/Tab.cpp:1915 msgid "Connection failed." msgstr "연결 실패." -#: src/slic3r/GUI/Tab.cpp:2097 src/slic3r/GUI/Tab.cpp:2268 +#: src/slic3r/GUI/Tab.cpp:1928 src/slic3r/GUI/Tab.cpp:2105 msgid "Print Host upload" msgstr "호스트 업로드 인쇄" -#: src/slic3r/GUI/Tab.cpp:2141 src/libslic3r/PrintConfig.cpp:138 +#: src/slic3r/GUI/Tab.cpp:1972 src/libslic3r/PrintConfig.cpp:143 msgid "Before layer change G-code" msgstr "레이어 변경 전 G 코드" -#: src/slic3r/GUI/Tab.cpp:2147 src/libslic3r/PrintConfig.cpp:1056 +#: src/slic3r/GUI/Tab.cpp:1978 src/libslic3r/PrintConfig.cpp:1081 msgid "After layer change G-code" msgstr "레이어 변경 후 G 코드" -#: src/slic3r/GUI/Tab.cpp:2153 src/libslic3r/PrintConfig.cpp:2056 +#: src/slic3r/GUI/Tab.cpp:1984 src/libslic3r/PrintConfig.cpp:2103 msgid "Tool change G-code" msgstr "툴 채인지 G 코드" -#: src/slic3r/GUI/Tab.cpp:2159 +#: src/slic3r/GUI/Tab.cpp:1990 msgid "Between objects G-code (for sequential printing)" -msgstr "객체 간 G 코드 (순차 인쇄용)" +msgstr "객체(object) 간 G 코드 (순차 인쇄용)" -#: src/slic3r/GUI/Tab.cpp:2231 +#: src/slic3r/GUI/Tab.cpp:2062 msgid "Display" msgstr "표시" -#: src/slic3r/GUI/Tab.cpp:2246 +#: src/slic3r/GUI/Tab.cpp:2077 msgid "Tilt" msgstr "기울이기" -#: src/slic3r/GUI/Tab.cpp:2247 +#: src/slic3r/GUI/Tab.cpp:2078 msgid "Tilt time" msgstr "기울이기 시간" -#: src/slic3r/GUI/Tab.cpp:2253 src/slic3r/GUI/Tab.cpp:3568 +#: src/slic3r/GUI/Tab.cpp:2084 src/slic3r/GUI/Tab.cpp:3489 msgid "Corrections" msgstr "수정" -#: src/slic3r/GUI/Tab.cpp:2333 src/slic3r/GUI/Tab.cpp:2418 -#: src/libslic3r/PrintConfig.cpp:1106 src/libslic3r/PrintConfig.cpp:1124 -#: src/libslic3r/PrintConfig.cpp:1142 src/libslic3r/PrintConfig.cpp:1159 -#: src/libslic3r/PrintConfig.cpp:1170 src/libslic3r/PrintConfig.cpp:1181 -#: src/libslic3r/PrintConfig.cpp:1192 +#: src/slic3r/GUI/Tab.cpp:2099 src/slic3r/GUI/Tab.cpp:3485 +msgid "Exposure" +msgstr "노출" + +#: src/slic3r/GUI/Tab.cpp:2170 src/slic3r/GUI/Tab.cpp:2255 +#: src/libslic3r/PrintConfig.cpp:1131 src/libslic3r/PrintConfig.cpp:1149 +#: src/libslic3r/PrintConfig.cpp:1167 src/libslic3r/PrintConfig.cpp:1184 +#: src/libslic3r/PrintConfig.cpp:1195 src/libslic3r/PrintConfig.cpp:1206 +#: src/libslic3r/PrintConfig.cpp:1217 msgid "Machine limits" msgstr "머신 한계설정" -#: src/slic3r/GUI/Tab.cpp:2347 +#: src/slic3r/GUI/Tab.cpp:2184 msgid "Values in this column are for Normal mode" msgstr "이 열의 값은 일반 모드입니다" -#: src/slic3r/GUI/Tab.cpp:2348 +#: src/slic3r/GUI/Tab.cpp:2185 msgid "Normal" msgstr "보통" -#: src/slic3r/GUI/Tab.cpp:2353 +#: src/slic3r/GUI/Tab.cpp:2190 msgid "Values in this column are for Stealth mode" msgstr "이 열의 값은 무음 모드 용입니다" -#: src/slic3r/GUI/Tab.cpp:2354 +#: src/slic3r/GUI/Tab.cpp:2191 msgid "Stealth" msgstr "스텔스" -#: src/slic3r/GUI/Tab.cpp:2362 +#: src/slic3r/GUI/Tab.cpp:2199 msgid "Maximum feedrates" msgstr "최대 이송속도" -#: src/slic3r/GUI/Tab.cpp:2367 +#: src/slic3r/GUI/Tab.cpp:2204 msgid "Maximum accelerations" msgstr "최고 가속도" -#: src/slic3r/GUI/Tab.cpp:2374 +#: src/slic3r/GUI/Tab.cpp:2211 msgid "Jerk limits" msgstr "저크(Jerk)값 한계" -#: src/slic3r/GUI/Tab.cpp:2379 +#: src/slic3r/GUI/Tab.cpp:2216 msgid "Minimum feedrates" msgstr "최대 이송속도" -#: src/slic3r/GUI/Tab.cpp:2443 src/slic3r/GUI/Tab.cpp:2451 +#: src/slic3r/GUI/Tab.cpp:2280 src/slic3r/GUI/Tab.cpp:2288 msgid "Single extruder MM setup" msgstr "싱글 익스트루더 MM 설정" -#: src/slic3r/GUI/Tab.cpp:2452 +#: src/slic3r/GUI/Tab.cpp:2289 msgid "Single extruder multimaterial parameters" msgstr "싱글 익스트루더 멀티메터리알 파라미터" -#: src/slic3r/GUI/Tab.cpp:2465 src/libslic3r/GCode/PreviewData.cpp:477 -#, c-format -msgid "Extruder %d" -msgstr "익스트루더 %d" +#: src/slic3r/GUI/Tab.cpp:2320 +msgid "" +"This is a single extruder multimaterial printer, diameters of all extruders " +"will be set to the new value. Do you want to proceed?" +msgstr "" +"이것은 단일 압출기 다중 재질 프린터이며 모든 압출기의 지름이 새 값으로 설정됩" +"니다. 계속하시겠습니까?" -#: src/slic3r/GUI/Tab.cpp:2483 -msgid "Do you want to change the diameter for all extruders?" -msgstr "모든 압출기의 지름을 변경하시겠습니까?" - -#: src/slic3r/GUI/Tab.cpp:2506 +#: src/slic3r/GUI/Tab.cpp:2344 msgid "Layer height limits" msgstr "레이어 높이 한계치" -#: src/slic3r/GUI/Tab.cpp:2511 +#: src/slic3r/GUI/Tab.cpp:2349 msgid "Position (for multi-extruder printers)" -msgstr "위치 (멀티 익스트루더 프린터 포함)" +msgstr "위치 (다중 익스트루더 프린터 포함)" -#: src/slic3r/GUI/Tab.cpp:2517 +#: src/slic3r/GUI/Tab.cpp:2355 msgid "Only lift Z" msgstr "Z축 올림" -#: src/slic3r/GUI/Tab.cpp:2530 +#: src/slic3r/GUI/Tab.cpp:2368 msgid "" "Retraction when tool is disabled (advanced settings for multi-extruder " "setups)" -msgstr "도구 비활성화시 리트렉션 (멀티 익스트루더 고급 설정)" +msgstr "도구 비활성화시 리트렉션 (다중 익스트루더 고급 설정)" -#: src/slic3r/GUI/Tab.cpp:2693 +#: src/slic3r/GUI/Tab.cpp:2376 +msgid "Reset to Filament Color" +msgstr "필라멘트 색상으로 재설정" + +#: src/slic3r/GUI/Tab.cpp:2557 msgid "" "The Wipe option is not available when using the Firmware Retraction mode.\n" "\n" @@ -4484,92 +5604,101 @@ msgstr "" "\n" "펌웨어 리트렉션 하려면 비활성화해야합니까?" -#: src/slic3r/GUI/Tab.cpp:2695 +#: src/slic3r/GUI/Tab.cpp:2559 msgid "Firmware Retraction" msgstr "펌웨어 레트렉션" -#: src/slic3r/GUI/Tab.cpp:3024 +#: src/slic3r/GUI/Tab.cpp:2889 #, c-format msgid "Default preset (%s)" msgstr "시스템 기본값 (%s)" -#: src/slic3r/GUI/Tab.cpp:3025 +#: src/slic3r/GUI/Tab.cpp:2890 #, c-format msgid "Preset (%s)" msgstr "프리셋 ( %s)" -#: src/slic3r/GUI/Tab.cpp:3042 +#: src/slic3r/GUI/Tab.cpp:2907 msgid "has the following unsaved changes:" msgstr "저장되지 않은 수정사항:" -#: src/slic3r/GUI/Tab.cpp:3045 +#: src/slic3r/GUI/Tab.cpp:2910 msgid "is not compatible with printer" msgstr "프린터와 호완 되지 않습니다" -#: src/slic3r/GUI/Tab.cpp:3046 +#: src/slic3r/GUI/Tab.cpp:2911 msgid "is not compatible with print profile" msgstr "인쇄 프로필과 호환 되지 않음" -#: src/slic3r/GUI/Tab.cpp:3048 +#: src/slic3r/GUI/Tab.cpp:2913 msgid "and it has the following unsaved changes:" msgstr "저장되지 않은 변경점은 다음과 같습니다:" -#: src/slic3r/GUI/Tab.cpp:3052 +#: src/slic3r/GUI/Tab.cpp:2917 msgid "Unsaved Changes" msgstr "미 저장된 변경점" -#: src/slic3r/GUI/Tab.cpp:3143 +#: src/slic3r/GUI/Tab.cpp:3015 +msgctxt "PresetName" msgid "%1% - Copy" msgstr "%1%-복사" -#: src/slic3r/GUI/Tab.cpp:3166 +#: src/slic3r/GUI/Tab.cpp:3038 msgid "The supplied name is empty. It can't be saved." msgstr "파일 이름이 비어 있습니다. 저장할 수 없습니다." -#: src/slic3r/GUI/Tab.cpp:3171 +#: src/slic3r/GUI/Tab.cpp:3043 msgid "Cannot overwrite a system profile." msgstr "시스템 프로파일을 겹쳐 쓸 수 없습니다." -#: src/slic3r/GUI/Tab.cpp:3175 +#: src/slic3r/GUI/Tab.cpp:3047 msgid "Cannot overwrite an external profile." msgstr "외부 프로필을 덮어 쓸 수 없습니다." -#: src/slic3r/GUI/Tab.cpp:3201 +#: src/slic3r/GUI/Tab.cpp:3052 +msgid "Preset with name \"%1%\" already exists." +msgstr "이름 \"%1%\"가 있는 사전 설정이 이미 있습니다." + +#: src/slic3r/GUI/Tab.cpp:3053 +msgid "Replace?" +msgstr "교체?" + +#: src/slic3r/GUI/Tab.cpp:3091 msgid "remove" msgstr "제거(remove)" -#: src/slic3r/GUI/Tab.cpp:3201 +#: src/slic3r/GUI/Tab.cpp:3091 msgid "delete" msgstr "지우기" #. TRN remove/delete -#: src/slic3r/GUI/Tab.cpp:3203 +#: src/slic3r/GUI/Tab.cpp:3093 msgid "Are you sure you want to %1% the selected preset?" msgstr "선택한 사전 설정의 %1%를 선택 하시겠습니까?" #. TRN Remove/Delete -#: src/slic3r/GUI/Tab.cpp:3206 +#: src/slic3r/GUI/Tab.cpp:3096 msgid "%1% Preset" msgstr "%1% 기본설정" -#: src/slic3r/GUI/Tab.cpp:3332 +#: src/slic3r/GUI/Tab.cpp:3222 msgid "LOCKED LOCK" msgstr "잠긴 잠금" #. TRN Description for "LOCKED LOCK" -#: src/slic3r/GUI/Tab.cpp:3334 +#: src/slic3r/GUI/Tab.cpp:3224 msgid "" "indicates that the settings are the same as the system (or default) values " "for the current option group" msgstr "" "설정이 현재 옵션 그룹의 시스템(또는 기본값) 값과 동일하다는 것을 나타냅니다." -#: src/slic3r/GUI/Tab.cpp:3336 +#: src/slic3r/GUI/Tab.cpp:3226 msgid "UNLOCKED LOCK" msgstr "잠금 해제 잠금" #. TRN Description for "UNLOCKED LOCK" -#: src/slic3r/GUI/Tab.cpp:3338 +#: src/slic3r/GUI/Tab.cpp:3228 msgid "" "indicates that some settings were changed and are not equal to the system " "(or default) values for the current option group.\n" @@ -4581,12 +5710,12 @@ msgstr "" "잠금 해제 잠금 아이콘을 클릭하여 현재 옵션 그룹에 대한 모든 설정을 시스템(또" "는 기본값) 값으로 재설정합니다." -#: src/slic3r/GUI/Tab.cpp:3343 +#: src/slic3r/GUI/Tab.cpp:3233 msgid "WHITE BULLET" msgstr "흰색 글머리 기호" #. TRN Description for "WHITE BULLET" -#: src/slic3r/GUI/Tab.cpp:3345 +#: src/slic3r/GUI/Tab.cpp:3235 msgid "" "for the left button: \tindicates a non-system (or non-default) preset,\n" "for the right button: \tindicates that the settings hasn't been modified." @@ -4594,12 +5723,12 @@ msgstr "" "왼쪽 단추의 경우: 비시스템(또는 기본이 아닌) 사전 설정을 나타냅니다.\n" "오른쪽 버튼: 설정이 수정되지 않았음을 나타냅니다." -#: src/slic3r/GUI/Tab.cpp:3348 +#: src/slic3r/GUI/Tab.cpp:3238 msgid "BACK ARROW" msgstr "돌아가기 화살표" #. TRN Description for "BACK ARROW" -#: src/slic3r/GUI/Tab.cpp:3350 +#: src/slic3r/GUI/Tab.cpp:3240 msgid "" "indicates that the settings were changed and are not equal to the last saved " "preset for the current option group.\n" @@ -4611,7 +5740,7 @@ msgstr "" "현재 옵션 그룹의 모든 설정을 시스템 값으로 재설정하려면 자물쇠 잠금 아이콘을 " "클릭하십시오." -#: src/slic3r/GUI/Tab.cpp:3360 +#: src/slic3r/GUI/Tab.cpp:3250 msgid "" "LOCKED LOCK icon indicates that the settings are the same as the system (or " "default) values for the current option group" @@ -4619,7 +5748,7 @@ msgstr "" "LOCKED LOCK 아이콘은 설정이 현재 옵션 그룹의 시스템(또는 기본값) 값과 동일하" "다는 것을 나타냅니다." -#: src/slic3r/GUI/Tab.cpp:3362 +#: src/slic3r/GUI/Tab.cpp:3252 msgid "" "UNLOCKED LOCK icon indicates that some settings were changed and are not " "equal to the system (or default) values for the current option group.\n" @@ -4631,11 +5760,11 @@ msgstr "" "현재 옵션 그룹에 대한 모든 설정을 시스템(또는 기본값) 값으로 재설정하려면 클" "릭합니다." -#: src/slic3r/GUI/Tab.cpp:3365 +#: src/slic3r/GUI/Tab.cpp:3255 msgid "WHITE BULLET icon indicates a non system (or non default) preset." msgstr "WHITE BULLET 아이콘은 시스템 사전 설정이 아닌 것을 나타냅니다." -#: src/slic3r/GUI/Tab.cpp:3368 +#: src/slic3r/GUI/Tab.cpp:3258 msgid "" "WHITE BULLET icon indicates that the settings are the same as in the last " "saved preset for the current option group." @@ -4643,7 +5772,7 @@ msgstr "" "WHITE BULLET 기호 아이콘은 설정이 현재 옵션 그룹에 대해 마지막으로 저장 된 사" "전 설정과 동일 하다는 것을 나타냅니다." -#: src/slic3r/GUI/Tab.cpp:3370 +#: src/slic3r/GUI/Tab.cpp:3260 msgid "" "BACK ARROW icon indicates that the settings were changed and are not equal " "to the last saved preset for the current option group.\n" @@ -4655,7 +5784,7 @@ msgstr "" "마지막 현재 옵션 그룹에 대 한 모든 설정 다시 설정을 클릭 하 여 사전 설정을 저" "장." -#: src/slic3r/GUI/Tab.cpp:3376 +#: src/slic3r/GUI/Tab.cpp:3266 msgid "" "LOCKED LOCK icon indicates that the value is the same as the system (or " "default) value." @@ -4663,7 +5792,7 @@ msgstr "" "LOCKED LOCK 아이콘은 값이 시스템(또는 기본값) 값과 동일하다는 것을 나타냅니" "다." -#: src/slic3r/GUI/Tab.cpp:3377 +#: src/slic3r/GUI/Tab.cpp:3267 msgid "" "UNLOCKED LOCK icon indicates that the value was changed and is not equal to " "the system (or default) value.\n" @@ -4673,7 +5802,7 @@ msgstr "" "을 나타냅니다.\n" "현재 값을 시스템(또는 기본값) 값으로 재설정하려면 클릭합니다." -#: src/slic3r/GUI/Tab.cpp:3383 +#: src/slic3r/GUI/Tab.cpp:3273 msgid "" "WHITE BULLET icon indicates that the value is the same as in the last saved " "preset." @@ -4681,7 +5810,7 @@ msgstr "" "WHITE BULLET 기호 아이콘은 마지막으로 저장 한 사전 설정과 동일한 값을 나타냅" "니다." -#: src/slic3r/GUI/Tab.cpp:3384 +#: src/slic3r/GUI/Tab.cpp:3274 msgid "" "BACK ARROW icon indicates that the value was changed and is not equal to the " "last saved preset.\n" @@ -4692,81 +5821,56 @@ msgstr "" "현재 값을 마지막으로 저장된 사전 설정으로 재설정하려면 클릭합니다." #. TRN Preset -#: src/slic3r/GUI/Tab.cpp:3497 +#: src/slic3r/GUI/Tab.cpp:3387 #, c-format msgid "Save %s as:" msgstr "Save %s as:" -#: src/slic3r/GUI/Tab.cpp:3541 +#: src/slic3r/GUI/Tab.cpp:3431 msgid "the following suffix is not allowed:" msgstr "다음 접미사는 허용되지 않습니다:" -#: src/slic3r/GUI/Tab.cpp:3545 +#: src/slic3r/GUI/Tab.cpp:3435 msgid "The supplied name is not available." msgstr "제공된 이름을 사용할 수 없습니다." -#: src/slic3r/GUI/Tab.cpp:3558 +#: src/slic3r/GUI/Tab.cpp:3448 src/slic3r/GUI/Tab.cpp:3450 msgid "Material" msgstr "재료" -#: src/slic3r/GUI/Tab.cpp:3560 src/slic3r/GUI/Tab.cpp:3651 -#: src/slic3r/GUI/wxExtensions.cpp:454 -msgid "Layers" -msgstr "레이어" - -#: src/slic3r/GUI/Tab.cpp:3564 -msgid "Exposure" -msgstr "노출" - -#: src/slic3r/GUI/Tab.cpp:3659 +#: src/slic3r/GUI/Tab.cpp:3580 msgid "Support head" msgstr "서포트 헤드" -#: src/slic3r/GUI/Tab.cpp:3664 +#: src/slic3r/GUI/Tab.cpp:3585 msgid "Support pillar" msgstr "서포트 기둥" -#: src/slic3r/GUI/Tab.cpp:3675 +#: src/slic3r/GUI/Tab.cpp:3599 msgid "Connection of the support sticks and junctions" msgstr "서포트 기둥 및 접합부 연결" -#: src/slic3r/GUI/Tab.cpp:3680 +#: src/slic3r/GUI/Tab.cpp:3604 msgid "Automatic generation" msgstr "자동 생성" -#: src/slic3r/GUI/Tab.cpp:3747 -msgid "Head penetration should not be greater than the head width." -msgstr "헤드 관통은 헤드 폭 보다 크지 않아야 합니다." - -#: src/slic3r/GUI/Tab.cpp:3751 -msgid "Invalid Head penetration" -msgstr "잘못된 헤드 관통" - -#: src/slic3r/GUI/Tab.cpp:3767 -msgid "Pinhead diameter should be smaller than the pillar diameter." -msgstr "핀헤드 지름은 기둥 지름 보다 작아야 합니다." - -#: src/slic3r/GUI/Tab.cpp:3771 -msgid "Invalid pinhead diameter" -msgstr "잘못된 핀 헤드 지름" - -#: src/slic3r/GUI/Tab.hpp:324 src/slic3r/GUI/Tab.hpp:422 +#: src/slic3r/GUI/Tab.hpp:327 src/slic3r/GUI/Tab.hpp:429 msgid "Print Settings" msgstr "출력 설정" -#: src/slic3r/GUI/Tab.hpp:348 +#: src/slic3r/GUI/Tab.hpp:354 msgid "Filament Settings" msgstr "필라멘트 설정" -#: src/slic3r/GUI/Tab.hpp:383 +#: src/slic3r/GUI/Tab.hpp:390 msgid "Printer Settings" msgstr "프린터 설정" -#: src/slic3r/GUI/Tab.hpp:407 +#: src/slic3r/GUI/Tab.hpp:414 msgid "Material Settings" msgstr "재질 설정" -#: src/slic3r/GUI/Tab.hpp:434 +#: src/slic3r/GUI/Tab.hpp:441 msgid "Save preset" msgstr "사전 설정 저장" @@ -4779,39 +5883,40 @@ msgstr "사용가능한 업데이트" msgid "New version of %s is available" msgstr "%s의 새 버전을 사용할 수 있습니다" -#: src/slic3r/GUI/UpdateDialogs.cpp:45 +#: src/slic3r/GUI/UpdateDialogs.cpp:43 msgid "Current version:" msgstr "현재 버전:" -#: src/slic3r/GUI/UpdateDialogs.cpp:47 +#: src/slic3r/GUI/UpdateDialogs.cpp:45 msgid "New version:" msgstr "새로운 버전:" -#: src/slic3r/GUI/UpdateDialogs.cpp:55 +#: src/slic3r/GUI/UpdateDialogs.cpp:53 msgid "Changelog && Download" msgstr "변경 로그 및 다운로드" -#: src/slic3r/GUI/UpdateDialogs.cpp:62 src/slic3r/GUI/UpdateDialogs.cpp:125 +#: src/slic3r/GUI/UpdateDialogs.cpp:60 src/slic3r/GUI/UpdateDialogs.cpp:125 +#: src/slic3r/GUI/UpdateDialogs.cpp:183 msgid "Open changelog page" msgstr "변경 로그 페이지 열기" -#: src/slic3r/GUI/UpdateDialogs.cpp:67 +#: src/slic3r/GUI/UpdateDialogs.cpp:65 msgid "Open download page" msgstr "다운로드 페이지 열기" -#: src/slic3r/GUI/UpdateDialogs.cpp:73 +#: src/slic3r/GUI/UpdateDialogs.cpp:71 msgid "Don't notify about new releases any more" msgstr "새로운 수정사항에 대해 더 이상 알림 안 함" -#: src/slic3r/GUI/UpdateDialogs.cpp:91 src/slic3r/GUI/UpdateDialogs.cpp:205 +#: src/slic3r/GUI/UpdateDialogs.cpp:89 src/slic3r/GUI/UpdateDialogs.cpp:266 msgid "Configuration update" msgstr "구성 업데이트" -#: src/slic3r/GUI/UpdateDialogs.cpp:91 +#: src/slic3r/GUI/UpdateDialogs.cpp:89 msgid "Configuration update is available" msgstr "구성 업데이트를 사용할 수 있음" -#: src/slic3r/GUI/UpdateDialogs.cpp:94 +#: src/slic3r/GUI/UpdateDialogs.cpp:92 msgid "" "Would you like to install it?\n" "\n" @@ -4827,21 +5932,47 @@ msgstr "" "\n" "업데이트 된 구성 번들 :" -#: src/slic3r/GUI/UpdateDialogs.cpp:115 +#: src/slic3r/GUI/UpdateDialogs.cpp:113 src/slic3r/GUI/UpdateDialogs.cpp:173 msgid "Comment:" -msgstr "\"댓글\"" +msgstr "코멘트:" -#: src/slic3r/GUI/UpdateDialogs.cpp:149 +#: src/slic3r/GUI/UpdateDialogs.cpp:148 src/slic3r/GUI/UpdateDialogs.cpp:210 #, c-format msgid "%s incompatibility" msgstr "%s 비 호환성" -#: src/slic3r/GUI/UpdateDialogs.cpp:150 +#: src/slic3r/GUI/UpdateDialogs.cpp:148 +msgid "Configuration update is necessary to install" +msgstr "설치하려면 구성 업데이트가 필요합니다." + +#: src/slic3r/GUI/UpdateDialogs.cpp:151 +#, c-format +msgid "" +"%s will now start updates. Otherwise it won't be able to start.\n" +"\n" +"Note that a full configuration snapshot will be created first. It can then " +"be restored at any time should there be a problem with the new version.\n" +"\n" +"Updated configuration bundles:" +msgstr "" +"%s는 이제 업데이트를 시작합니다. 그렇지 않으면 시작할 수 없습니다.\n" +"\n" +"전체 구성 스냅숏이 먼저 만들어집니다. 그런 다음 새 버전에 문제가 있으면 언제" +"든지 복원 할 수 있습니다.\n" +"\n" +"업데이트된 구성 번들:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:191 src/slic3r/GUI/UpdateDialogs.cpp:246 +#, c-format +msgid "Exit %s" +msgstr "%s 종료" + +#: src/slic3r/GUI/UpdateDialogs.cpp:211 #, c-format msgid "%s configuration is incompatible" msgstr "%s 과 호환되지 않습니다" -#: src/slic3r/GUI/UpdateDialogs.cpp:155 +#: src/slic3r/GUI/UpdateDialogs.cpp:216 #, c-format msgid "" "This version of %s is not compatible with currently installed configuration " @@ -4851,34 +5982,29 @@ msgid "" "\n" "You may either exit %s and try again with a newer version, or you may re-run " "the initial configuration. Doing so will create a backup snapshot of the " -"existing configuration before installing files compatible with this %s.\n" +"existing configuration before installing files compatible with this %s." msgstr "" "%s의 이 버전은 현재 설치 된 구성 번들과 호환 되지 않습니다.\n" "이것은 새로운 것을 사용한 후 이전 %s를 실행 한 결과로 발생 했을 것입니다.\n" "\n" " %s를 종료하고 최신 버전으로 다시 시도 하거나 초기 구성을 다시 실행할 수 있습" "니다. 이렇게 하면 %s와 호환 되는 파일을 설치하기 전에 기존 구성의 백업 스냅샷" -"이 생성 됩니다.\n" +"이 생성 됩니다." -#: src/slic3r/GUI/UpdateDialogs.cpp:164 +#: src/slic3r/GUI/UpdateDialogs.cpp:225 #, c-format msgid "This %s version: %s" msgstr "이 %s 버전: %s" -#: src/slic3r/GUI/UpdateDialogs.cpp:169 +#: src/slic3r/GUI/UpdateDialogs.cpp:230 msgid "Incompatible bundles:" msgstr "호환되지 않는 번들 :" -#: src/slic3r/GUI/UpdateDialogs.cpp:185 -#, c-format -msgid "Exit %s" -msgstr "%s Exit" - -#: src/slic3r/GUI/UpdateDialogs.cpp:188 +#: src/slic3r/GUI/UpdateDialogs.cpp:249 msgid "Re-configure" msgstr "재구성" -#: src/slic3r/GUI/UpdateDialogs.cpp:209 +#: src/slic3r/GUI/UpdateDialogs.cpp:270 #, c-format msgid "" "%s now uses an updated configuration structure.\n" @@ -4904,15 +6030,28 @@ msgstr "" "다음의 %s를 계속 진행하여 새 프리셋을 설정하고 자동 프리셋 업데이트를 사용할" "지 여부를 선택하십시오." -#: src/slic3r/GUI/UpdateDialogs.cpp:225 +#: src/slic3r/GUI/UpdateDialogs.cpp:286 msgid "For more information please visit our wiki page:" msgstr "자세한 정보는 위키 페이지를 참조하십시오 :" -#: src/slic3r/GUI/WipeTowerDialog.cpp:14 +#: src/slic3r/GUI/UpdateDialogs.cpp:303 +msgid "Configuration updates" +msgstr "구성 업데이트" + +#: src/slic3r/GUI/UpdateDialogs.cpp:303 +msgid "No updates aviable" +msgstr "실행 가능한 업데이트없음" + +#: src/slic3r/GUI/UpdateDialogs.cpp:308 +#, c-format +msgid "%s has no configuration updates aviable." +msgstr "%s에는 구성 업데이트가 실행 가능한 업데이트가 없습니다." + +#: src/slic3r/GUI/WipeTowerDialog.cpp:15 msgid "Ramming customization" msgstr "사용자 정의 다지기(Ramming)" -#: src/slic3r/GUI/WipeTowerDialog.cpp:40 +#: src/slic3r/GUI/WipeTowerDialog.cpp:41 msgid "" "Ramming denotes the rapid extrusion just before a tool change in a single-" "extruder MM printer. Its purpose is to properly shape the end of the " @@ -4924,7 +6063,7 @@ msgid "" "This is an expert-level setting, incorrect adjustment will likely lead to " "jams, extruder wheel grinding into filament etc." msgstr "" -"래밍은 단일 압출기 MM 프린터에서 공구 교환 직전의 신속한 압출을 나타냅니다. " +"래밍은 단일 압출기 MMU 프린터에서 공구 교환 직전의 신속한 압출을 나타냅니다. " "그 목적은 언로드 된 필라멘트의 끝 부분을 적절히 형성하여 새로운 필라멘트의 삽" "입을 방지하고 나중에 다시 삽입 할 수 있도록하기위한 것입니다. 이 단계는 중요" "하며 다른 재료는 좋은 모양을 얻기 위해 다른 압출 속도를 요구할 수 있습니다. " @@ -4933,49 +6072,49 @@ msgstr "" "전문가 수준의 설정이므로 잘못된 조정으로 인해 용지 걸림, 압출기 휠이 필라멘" "트 등에 연삭 될 수 있습니다." -#: src/slic3r/GUI/WipeTowerDialog.cpp:82 +#: src/slic3r/GUI/WipeTowerDialog.cpp:83 msgid "Total ramming time" msgstr "총 래밍 시간" -#: src/slic3r/GUI/WipeTowerDialog.cpp:84 +#: src/slic3r/GUI/WipeTowerDialog.cpp:85 msgid "Total rammed volume" msgstr "총 레미드 양" -#: src/slic3r/GUI/WipeTowerDialog.cpp:88 +#: src/slic3r/GUI/WipeTowerDialog.cpp:89 msgid "Ramming line width" msgstr "래밍 선 너비" -#: src/slic3r/GUI/WipeTowerDialog.cpp:90 +#: src/slic3r/GUI/WipeTowerDialog.cpp:91 msgid "Ramming line spacing" msgstr "래밍 선 간격" -#: src/slic3r/GUI/WipeTowerDialog.cpp:141 +#: src/slic3r/GUI/WipeTowerDialog.cpp:142 msgid "Wipe tower - Purging volume adjustment" msgstr "와이프 타워 - 버려진 필라멘트 조절" -#: src/slic3r/GUI/WipeTowerDialog.cpp:225 +#: src/slic3r/GUI/WipeTowerDialog.cpp:254 msgid "" "Here you can adjust required purging volume (mm³) for any given pair of " "tools." msgstr "여기서 주어진 도구 쌍에 필요한 정화 용량 (mm³)을 조정할 수 있습니다." -#: src/slic3r/GUI/WipeTowerDialog.cpp:226 +#: src/slic3r/GUI/WipeTowerDialog.cpp:255 msgid "Extruder changed to" msgstr "익스트루더 번경" -#: src/slic3r/GUI/WipeTowerDialog.cpp:234 +#: src/slic3r/GUI/WipeTowerDialog.cpp:263 msgid "unloaded" msgstr "언로드(unloaded)" -#: src/slic3r/GUI/WipeTowerDialog.cpp:235 +#: src/slic3r/GUI/WipeTowerDialog.cpp:264 msgid "loaded" msgstr "로드(loaded)" -#: src/slic3r/GUI/WipeTowerDialog.cpp:240 +#: src/slic3r/GUI/WipeTowerDialog.cpp:276 msgid "Tool #" msgstr "도구(Tool) #" -#: src/slic3r/GUI/WipeTowerDialog.cpp:247 +#: src/slic3r/GUI/WipeTowerDialog.cpp:285 msgid "" "Total purging volume is calculated by summing two values below, depending on " "which tools are loaded/unloaded." @@ -4983,15 +6122,15 @@ msgstr "" "총 정화 량은 어느 공구가로드 / 언로드되는지에 따라 아래의 두 값을 합산하여 계" "산됩니다." -#: src/slic3r/GUI/WipeTowerDialog.cpp:248 +#: src/slic3r/GUI/WipeTowerDialog.cpp:286 msgid "Volume to purge (mm³) when the filament is being" msgstr "제거할 필라멘트 양 (mm³)" -#: src/slic3r/GUI/WipeTowerDialog.cpp:262 +#: src/slic3r/GUI/WipeTowerDialog.cpp:300 msgid "From" msgstr "From" -#: src/slic3r/GUI/WipeTowerDialog.cpp:327 +#: src/slic3r/GUI/WipeTowerDialog.cpp:365 msgid "" "Switching to simple settings will discard changes done in the advanced " "mode!\n" @@ -5002,108 +6141,62 @@ msgstr "" "\n" "계속하시겠습니까?" -#: src/slic3r/GUI/WipeTowerDialog.cpp:339 +#: src/slic3r/GUI/WipeTowerDialog.cpp:377 msgid "Show simplified settings" msgstr "간단한 설정보기" -#: src/slic3r/GUI/WipeTowerDialog.cpp:339 +#: src/slic3r/GUI/WipeTowerDialog.cpp:377 msgid "Show advanced settings" msgstr "고급 설정보기" -#: src/slic3r/GUI/wxExtensions.cpp:443 -msgid "Instances" -msgstr "복제본" - -#: src/slic3r/GUI/wxExtensions.cpp:447 src/slic3r/GUI/wxExtensions.cpp:592 -#, c-format -msgid "Instance %d" -msgstr "복제본 %d" - -#: src/slic3r/GUI/wxExtensions.cpp:486 -msgid "Range" -msgstr "범위" - -#: src/slic3r/GUI/wxExtensions.cpp:2570 -msgid "One layer mode" -msgstr "하나의 레이어 모드" - -#: src/slic3r/GUI/wxExtensions.cpp:2571 -msgid "Add/Del color change" -msgstr "현재 레이어의 색상 변경 마커 추가" - -#: src/slic3r/GUI/wxExtensions.cpp:2572 -msgid "Discard all color changes" -msgstr "모든 색상 변경 무시" - -#: src/slic3r/GUI/wxExtensions.cpp:2832 +#: src/slic3r/GUI/wxExtensions.cpp:680 #, c-format msgid "Switch to the %s mode" msgstr "%s 모드로 전환" -#: src/slic3r/GUI/wxExtensions.cpp:2833 +#: src/slic3r/GUI/wxExtensions.cpp:681 #, c-format msgid "Current mode is %s" msgstr "현재 모드는 %s입니다" -#: src/slic3r/Utils/Duet.cpp:51 -msgid "Connection to Duet works correctly." -msgstr "듀엣보드에 대한 연결이 올바르게 작동 합니다." - -#: src/slic3r/Utils/Duet.cpp:56 -msgid "Could not connect to Duet" -msgstr "듀엣보드에 연결할 수 없습니다" - -#: src/slic3r/Utils/Duet.cpp:84 src/slic3r/Utils/Duet.cpp:154 -msgid "Unknown error occured" -msgstr "알 수 없는 오류가 발생 했습니다" - -#: src/slic3r/Utils/Duet.cpp:148 -msgid "Wrong password" -msgstr "잘못된 암호" - -#: src/slic3r/Utils/Duet.cpp:151 -msgid "Could not get resources to create a new connection" -msgstr "새 연결을 만들 리소스를 가져올수 없습니다" - -#: src/slic3r/Utils/OctoPrint.cpp:69 +#: src/slic3r/Utils/AstroBox.cpp:68 src/slic3r/Utils/OctoPrint.cpp:68 #, c-format msgid "Mismatched type of print host: %s" msgstr "일치 하지않는 인쇄 호스트 유형: %s" -#: src/slic3r/Utils/OctoPrint.cpp:84 -msgid "Connection to OctoPrint works correctly." -msgstr "OctoPrint에 연결하면 올바르게 작동합니다." +#: src/slic3r/Utils/AstroBox.cpp:83 +msgid "Connection to AstroBox works correctly." +msgstr "아스트로박스에 대한 연결이 올바르게 작동합니다." -#: src/slic3r/Utils/OctoPrint.cpp:90 -msgid "Could not connect to OctoPrint" -msgstr "OctoPrint에 연결할 수 없습니다" +#: src/slic3r/Utils/AstroBox.cpp:89 +msgid "Could not connect to AstroBox" +msgstr "아스트로박스에 연결할 수 없습니다." -#: src/slic3r/Utils/OctoPrint.cpp:90 -msgid "Note: OctoPrint version at least 1.1.0 is required." -msgstr "참고 : OctoPrint 버전 1.1.0 이상이 필요합니다." +#: src/slic3r/Utils/AstroBox.cpp:89 +msgid "Note: AstroBox version at least 1.1.0 is required." +msgstr "참고: 아스트로박스 버전은 최소 1.1.0이 필요합니다." -#: src/slic3r/Utils/OctoPrint.cpp:195 -msgid "Connection to Prusa SL1 works correctly." -msgstr "Prusa SL1에 대한 연결이 제대로 작동 합니다." +#: src/slic3r/Utils/Duet.cpp:49 +msgid "Connection to Duet works correctly." +msgstr "듀엣보드에 대한 연결이 올바르게 작동 합니다." -#: src/slic3r/Utils/OctoPrint.cpp:200 -msgid "Could not connect to Prusa SLA" -msgstr "Prusa SLA에 연결할 수 없습니다" +#: src/slic3r/Utils/Duet.cpp:54 +msgid "Could not connect to Duet" +msgstr "듀엣보드에 연결할 수 없습니다" -#: src/slic3r/Utils/PresetUpdater.cpp:614 -#, c-format -msgid "requires min. %s and max. %s" -msgstr "최소. %s 와 최대. %s" +#: src/slic3r/Utils/Duet.cpp:82 src/slic3r/Utils/Duet.cpp:137 +#: src/slic3r/Utils/FlashAir.cpp:119 src/slic3r/Utils/FlashAir.cpp:140 +#: src/slic3r/Utils/FlashAir.cpp:156 +msgid "Unknown error occured" +msgstr "알 수 없는 오류가 발생 했습니다" -#: src/slic3r/Utils/PresetUpdater.cpp:619 -#, c-format -msgid "requires min. %s" -msgstr "최소 %s가 필요 합니다" +#: src/slic3r/Utils/Duet.cpp:131 +msgid "Wrong password" +msgstr "잘못된 암호" -#: src/slic3r/Utils/PresetUpdater.cpp:621 -#, c-format -msgid "requires max. %s" -msgstr "최대 필요 합니다. %s" +#: src/slic3r/Utils/Duet.cpp:134 +msgid "Could not get resources to create a new connection" +msgstr "새 연결을 만들 리소스를 가져올수 없습니다" #: src/slic3r/Utils/FixModelByWin10.cpp:219 #: src/slic3r/Utils/FixModelByWin10.cpp:359 @@ -5123,7 +6216,7 @@ msgid "Mesh repair failed." msgstr "메쉬 복구에 실패 했습니다." #: src/slic3r/Utils/FixModelByWin10.cpp:251 -#: src/slic3r/Utils/FixModelByWin10.cpp:378 +#: src/slic3r/Utils/FixModelByWin10.cpp:385 msgid "Loading repaired model" msgstr "복구 된 모델 로드" @@ -5141,50 +6234,110 @@ msgstr "모델 고정" msgid "Exporting model..." msgstr "소스 모델 내보내기..." -#: src/slic3r/Utils/FixModelByWin10.cpp:368 +#: src/slic3r/Utils/FixModelByWin10.cpp:369 +#: src/slic3r/Utils/FixModelByWin10.cpp:374 msgid "Export of a temporary 3mf file failed" msgstr "임시 3mf 파일을 내보내지 못했습니다" -#: src/slic3r/Utils/FixModelByWin10.cpp:383 +#: src/slic3r/Utils/FixModelByWin10.cpp:390 msgid "Import of the repaired 3mf file failed" msgstr "복구된 3mf 파일을 가져오지 못했습니다" -#: src/slic3r/Utils/FixModelByWin10.cpp:385 +#: src/slic3r/Utils/FixModelByWin10.cpp:392 msgid "Repaired 3MF file does not contain any object" -msgstr "복구된 3MF 파일에 개체가 포함 되어있지 않습니다" +msgstr "복구된 3MF 파일에 객체(object)가 포함 되어있지 않습니다" -#: src/slic3r/Utils/FixModelByWin10.cpp:387 +#: src/slic3r/Utils/FixModelByWin10.cpp:394 msgid "Repaired 3MF file contains more than one object" -msgstr "복구된 3MF 파일에 둘 이상의 개체가 포함되어 있습니다" +msgstr "복구된 3MF 파일에 둘 이상의 객체(object)가 포함되어 있습니다" -#: src/slic3r/Utils/FixModelByWin10.cpp:389 +#: src/slic3r/Utils/FixModelByWin10.cpp:396 msgid "Repaired 3MF file does not contain any volume" -msgstr "복구 된 3MF 파일에 개체가 포함 되어있지 않습니다" +msgstr "복구 된 3MF 파일에 객체(object)가 포함 되어있지 않습니다" -#: src/slic3r/Utils/FixModelByWin10.cpp:391 +#: src/slic3r/Utils/FixModelByWin10.cpp:398 msgid "Repaired 3MF file contains more than one volume" -msgstr "복구된 3MF 파일에 둘 이상의 개체가 포함되어 있습니다" +msgstr "복구된 3MF 파일에 둘 이상의 객체(object)가 포함되어 있습니다" -#: src/slic3r/Utils/FixModelByWin10.cpp:400 +#: src/slic3r/Utils/FixModelByWin10.cpp:407 msgid "Model repair finished" msgstr "모델 수리 완료" -#: src/slic3r/Utils/FixModelByWin10.cpp:406 +#: src/slic3r/Utils/FixModelByWin10.cpp:413 msgid "Model repair canceled" msgstr "모델 복구가 취소 되었습니다" -#: src/slic3r/Utils/FixModelByWin10.cpp:423 +#: src/slic3r/Utils/FixModelByWin10.cpp:430 msgid "Model repaired successfully" msgstr "모델이 성공적으로 복구 되었습니다" -#: src/slic3r/Utils/FixModelByWin10.cpp:423 -#: src/slic3r/Utils/FixModelByWin10.cpp:426 +#: src/slic3r/Utils/FixModelByWin10.cpp:430 +#: src/slic3r/Utils/FixModelByWin10.cpp:433 msgid "Model Repair by the Netfabb service" msgstr "Netfabb 서비스에의 한 모델 복구" -#: src/slic3r/Utils/FixModelByWin10.cpp:426 -msgid "Model repair failed: \n" -msgstr "모델 복구 실패:\n" +#: src/slic3r/Utils/FixModelByWin10.cpp:433 +msgid "Model repair failed:" +msgstr "모델 복구 실패:" + +#: src/slic3r/Utils/FlashAir.cpp:58 +msgid "Upload not enabled on FlashAir card." +msgstr "FlashAir 카드에서 업로드가 활성화되지 않았습니다." + +#: src/slic3r/Utils/FlashAir.cpp:68 +msgid "Connection to FlashAir works correctly and upload is enabled." +msgstr "FlashAir에 대한 연결이 올바르게 작동하고 업로드가 활성화되어 있습니다." + +#: src/slic3r/Utils/FlashAir.cpp:73 +msgid "Could not connect to FlashAir" +msgstr "플래시 에어에 연결할 수 없습니다." + +#: src/slic3r/Utils/FlashAir.cpp:73 +msgid "" +"Note: FlashAir with firmware 2.00.02 or newer and activated upload function " +"is required." +msgstr "" +"참고 : 펌웨어 2.00.02 이상 및 활성화 된 업로드 기능이있는 FlashAir가 필요합니" +"다." + +#: src/slic3r/Utils/OctoPrint.cpp:83 +msgid "Connection to OctoPrint works correctly." +msgstr "OctoPrint에 연결하면 올바르게 작동합니다." + +#: src/slic3r/Utils/OctoPrint.cpp:89 +msgid "Could not connect to OctoPrint" +msgstr "OctoPrint에 연결할 수 없습니다" + +#: src/slic3r/Utils/OctoPrint.cpp:89 +msgid "Note: OctoPrint version at least 1.1.0 is required." +msgstr "참고 : OctoPrint 버전 1.1.0 이상이 필요합니다." + +#: src/slic3r/Utils/OctoPrint.cpp:176 +msgid "Connection to Prusa SL1 works correctly." +msgstr "Prusa SL1에 대한 연결이 제대로 작동 합니다." + +#: src/slic3r/Utils/OctoPrint.cpp:181 +msgid "Could not connect to Prusa SLA" +msgstr "Prusa SLA에 연결할 수 없습니다" + +#: src/slic3r/Utils/PresetUpdater.cpp:705 +#, c-format +msgid "requires min. %s and max. %s" +msgstr "최소. %s 와 최대. %s" + +#: src/slic3r/Utils/PresetUpdater.cpp:710 +#, c-format +msgid "requires min. %s" +msgstr "최소 %s가 필요 합니다" + +#: src/slic3r/Utils/PresetUpdater.cpp:713 +#, c-format +msgid "requires max. %s" +msgstr "최대 필요 합니다. %s" + +#: src/libslic3r/SLA/Pad.cpp:691 +msgid "Pad brim size is too small for the current configuration." +msgstr "패드 테두리 크기가 현재 구성에 너무 작습니다." #: src/libslic3r/Zipper.cpp:32 msgid "undefined error" @@ -5224,7 +6377,7 @@ msgstr "잘못 된 헤더 또는 아카이브가 손상 되었습니다" #: src/libslic3r/Zipper.cpp:50 msgid "unsupported multidisk archive" -msgstr "지원 되지 않는 멀티 디스크 아카이브" +msgstr "지원 되지 않는 다중 디스크 아카이브" #: src/libslic3r/Zipper.cpp:52 msgid "decompression failed or archive is corrupted" @@ -5296,7 +6449,7 @@ msgstr "내부 오류" #: src/libslic3r/Zipper.cpp:86 msgid "file not found" -msgstr "파일을 찾을수 없다" +msgstr "파일을 찾을 수 없습니다." #: src/libslic3r/Zipper.cpp:88 msgid "archive is too large" @@ -5314,33 +6467,77 @@ msgstr "쓰기 다시 실패" msgid "Error with zip archive" msgstr "zip 아카이브와 오류가 발생 했습니다" -#: src/libslic3r/Print.cpp:1093 +#: src/libslic3r/GCode.cpp:637 +msgid "Empty layers detected, the output would not be printable." +msgstr "빈 레이어가 감지되면 출력을 인쇄할 수 없습니다." + +#: src/libslic3r/GCode.cpp:638 +msgid "Print z" +msgstr "인쇄 z" + +#: src/libslic3r/GCode.cpp:639 +msgid "" +"This is usually caused by negligibly small extrusions or by a faulty model. " +"Try to repair the model or change its orientation on the bed." +msgstr "" +"이는 일반적으로 무시할 수 있는 작은 돌출 또는 결함이 있는 모델에 의해 발생합" +"니다. 모델을 수리하거나 배드에서 방향을 변경하십시오." + +#: src/libslic3r/ExtrusionEntity.cpp:323 +msgid "Mixed" +msgstr "혼합" + +#: src/libslic3r/Flow.cpp:55 +msgid "" +"Cannot calculate extrusion width for %1%: Variable \"%2%\" not accessible." +msgstr "" +"%1%에 대한 압출 폭을 계산할 수 없습니다: 변수 \"%2%\"에 액세스할 수 없습니다." + +#: src/libslic3r/Format/3mf.cpp:1630 +msgid "" +"The selected 3mf file has been saved with a newer version of %1% and is not " +"compatible." +msgstr "선택한 3mf 파일은 %1%의 최신 버전으로 저장되었으며 호환되지 않습니다." + +#: src/libslic3r/Format/AMF.cpp:934 +msgid "" +"The selected amf file has been saved with a newer version of %1% and is not " +"compatible." +msgstr "선택한 amf 파일은 %1%의 최신 버전으로 저장되었으며 호환되지 않습니다." + +#: src/libslic3r/Print.cpp:1158 msgid "All objects are outside of the print volume." -msgstr "모든 개체가 인쇄 볼륨 외부에 있습니다." +msgstr "모든 객체(object)가 인쇄 볼륨 외부에 있습니다." -#: src/libslic3r/Print.cpp:1120 +#: src/libslic3r/Print.cpp:1161 +msgid "The supplied settings will cause an empty print." +msgstr "제공된 설정으로 인해 빈 인쇄가 발생합니다." + +#: src/libslic3r/Print.cpp:1188 msgid "Some objects are too close; your extruder will collide with them." -msgstr "일부 개체가 너무 가깝습니다. 귀하의 압출기가 그들과 충돌합니다." +msgstr "" +"일부 객체(object)가 너무 가깝습니다. 귀하의 압출기가 그들과 충돌합니다." -#: src/libslic3r/Print.cpp:1135 +#: src/libslic3r/Print.cpp:1203 msgid "" "Some objects are too tall and cannot be printed without extruder collisions." -msgstr "일부 개체는 너무 크고 익스트루더 충돌없이 인쇄 할 수 없습니다." +msgstr "일부 객체(object)는 너무 크고 익스트루더 충돌없이 인쇄 할 수 없습니다." -#: src/libslic3r/Print.cpp:1145 +#: src/libslic3r/Print.cpp:1213 msgid "The Spiral Vase option can only be used when printing a single object." msgstr "" -"나선형 꽃병(Spiral Vase) 옵션은 단일 개체를 인쇄 할 때만 사용할 수 있습니다." +"나선형 꽃병(Spiral Vase) 옵션은 단일 객체(object)를 인쇄 할 때만 사용할 수 있" +"습니다." -#: src/libslic3r/Print.cpp:1147 +#: src/libslic3r/Print.cpp:1220 msgid "" "The Spiral Vase option can only be used when printing single material " "objects." msgstr "" -"나선형 꽃병 옵션(Spiral Vase)은 단일 재료 객체를 인쇄 할 때만 사용할 수 있습" -"니다." +"나선형 꽃병 옵션(Spiral Vase)은 단일 재료 객체(object)를 인쇄 할 때만 사용할 " +"수 있습니다." -#: src/libslic3r/Print.cpp:1155 +#: src/libslic3r/Print.cpp:1233 msgid "" "The wipe tower is only supported if all extruders have the same nozzle " "diameter and use filaments of the same diameter." @@ -5348,15 +6545,15 @@ msgstr "" "모든 압출기 의 노즐 직경이 동일하고 동일한 직경의 필라멘트를 사용하는 경우에" "만 와이프 타워가 지원됩니다." -#: src/libslic3r/Print.cpp:1159 +#: src/libslic3r/Print.cpp:1238 msgid "" "The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter " "and Repetier G-code flavors." msgstr "" -"와이프 타워는 현재 말린, RepRap/Sprinter 및 리피티어에 대해서만 G-코드지원 됩" +"와이프 타워는 현재 말린, RepRap/Sprinter 및 리피티어에 대해서만 G-code지원 됩" "니다." -#: src/libslic3r/Print.cpp:1161 +#: src/libslic3r/Print.cpp:1240 msgid "" "The Wipe Tower is currently only supported with the relative extruder " "addressing (use_relative_e_distances=1)." @@ -5364,58 +6561,76 @@ msgstr "" "와이프 타워는 현재 상대적 압출기 어드레싱 (use_relative_e_distances = 1)에서" "만 지원됩니다." -#: src/libslic3r/Print.cpp:1165 -msgid "All extruders must have the same diameter for the Wipe Tower." -msgstr "모든 압출기는 와이프 타워의 지름이 같아야 합니다." +#: src/libslic3r/Print.cpp:1242 +msgid "Ooze prevention is currently not supported with the wipe tower enabled." +msgstr "" +"현재 와이프 타워를 사용하도록 설정되어 있는 경우 스모즈 방지 기능이 지원되지 " +"않습니다." -#: src/libslic3r/Print.cpp:1186 +#: src/libslic3r/Print.cpp:1244 +msgid "" +"The Wipe Tower currently does not support volumetric E (use_volumetric_e=0)." +msgstr "와이프 타워는 현재 볼륨 E(use_volumetric_e=0)를 지원하지 않습니다." + +#: src/libslic3r/Print.cpp:1246 +msgid "" +"The Wipe Tower is currently not supported for multimaterial sequential " +"prints." +msgstr "와이프 타워는 현재 다중 재질 순차 인쇄에 지원되지 않습니다." + +#: src/libslic3r/Print.cpp:1267 msgid "" "The Wipe Tower is only supported for multiple objects if they have equal " "layer heights" msgstr "" -"와이프 타워 (Wipe Tower)는 같은 레이어 높이에 경우 여러 객체에 대해서만 지원" -"됩니다" +"와이프 타워 (Wipe Tower)는 같은 레이어 높이에 경우 여러 객체(object)에 대해서" +"만 지원됩니다" -#: src/libslic3r/Print.cpp:1188 +#: src/libslic3r/Print.cpp:1269 msgid "" "The Wipe Tower is only supported for multiple objects if they are printed " "over an equal number of raft layers" msgstr "" -"와이프 타워는 같은 수의 라프트 레이어 위에 인쇄 된 경우 여러 객체에 대해서만 " -"지원됩니다" +"와이프 타워는 같은 수의 라프트 레이어 위에 인쇄 된 경우 여러 객체(object)에 " +"대해서만 지원됩니다" -#: src/libslic3r/Print.cpp:1190 +#: src/libslic3r/Print.cpp:1271 msgid "" "The Wipe Tower is only supported for multiple objects if they are printed " "with the same support_material_contact_distance" msgstr "" "와이프 타워는 동일한 support_material_contact_distance로 인쇄 된 경우 여러 객" -"체에 대해서만 지원됩니다" +"체(object)에 대해서만 지원됩니다" -#: src/libslic3r/Print.cpp:1192 +#: src/libslic3r/Print.cpp:1273 msgid "" "The Wipe Tower is only supported for multiple objects if they are sliced " "equally." msgstr "" -"와이프 타워는 똑같이 슬라이스 된 경우 여러 오브젝트에 대해서만 지원됩니다." +"와이프 타워는 똑같이 슬라이스 된 경우 여러 객체(object)에 대해서만 지원됩니" +"다." -#: src/libslic3r/Print.cpp:1220 +#: src/libslic3r/Print.cpp:1315 msgid "" -"The Wipe tower is only supported if all objects have the same layer height " -"profile" +"The Wipe tower is only supported if all objects have the same variable layer " +"height" msgstr "" -"모든 오브젝트의 레이어 높이 프로필이 동일한 경우에만 와이프 타워가 지원됩니다" +"모든 오브젝트의 가변 레이어 높이가 같은 경우에만 지우기 타워가 지원됩니다." -#: src/libslic3r/Print.cpp:1230 -msgid "The supplied settings will cause an empty print." -msgstr "제공된 설정으로 인해 빈 인쇄가 발생합니다." - -#: src/libslic3r/Print.cpp:1247 +#: src/libslic3r/Print.cpp:1341 msgid "" "One or more object were assigned an extruder that the printer does not have." -msgstr "하나 이상의 개체에 프린터에없는 압출기가 지정되었습니다." +msgstr "하나 이상의 객체(object)에 프린터에없는 압출기가 지정되었습니다." -#: src/libslic3r/Print.cpp:1256 +#: src/libslic3r/Print.cpp:1350 +msgid "%1%=%2% mm is too low to be printable at a layer height %3% mm" +msgstr "%1%=%2% mm가 너무 낮아 레이어 높이 %3% mm에서 인쇄할 수 없습니다." + +#: src/libslic3r/Print.cpp:1353 +msgid "Excessive %1%=%2% mm to be printable with a nozzle diameter %3% mm" +msgstr "노즐 직경 %3% mm로 인쇄할 수 있는 과도한 %1%=%2% mm" + +#: src/libslic3r/Print.cpp:1364 msgid "" "Printing with multiple extruders of differing nozzle diameters. If support " "is to be printed with the current extruder (support_material_extruder == 0 " @@ -5426,15 +6641,15 @@ msgstr "" "(support_material_extruder == 0 또는 support_material_interface_extruder == " "0)로 인쇄되는 경우 모든 노즐은 동일한 지름이어야합니다." -#: src/libslic3r/Print.cpp:1264 +#: src/libslic3r/Print.cpp:1372 msgid "" "For the Wipe Tower to work with the soluble supports, the support layers " "need to be synchronized with the object layers." msgstr "" -"와이프 타워가 가용성 지지체와 함께 작동 하려면 서포트 레이어를 오브젝트 레이" -"어와 동기화 해야 합니다." +"와이프 타워가 가용성 지지체와 함께 작동 하려면 서포트 레이어를 객체(object) " +"레이어와 동기화 해야 합니다." -#: src/libslic3r/Print.cpp:1268 +#: src/libslic3r/Print.cpp:1376 msgid "" "The Wipe Tower currently supports the non-soluble supports only if they are " "printed with the current extruder without triggering a tool change. (both " @@ -5445,63 +6660,35 @@ msgstr "" "에만 비가용성 서포트를 지원 합니다. (support_material_extruder과 " "support_material_interface_extruder 모두 0으로 설정 해야 합니다.)" -#: src/libslic3r/Print.cpp:1290 +#: src/libslic3r/Print.cpp:1398 msgid "First layer height can't be greater than nozzle diameter" msgstr "첫번째 레이어 높이는 노즐 직경보다 클 수 없습니다" -#: src/libslic3r/Print.cpp:1294 +#: src/libslic3r/Print.cpp:1403 msgid "Layer height can't be greater than nozzle diameter" msgstr "레이어 높이는 노즐 직경보다 클 수 없습니다" -#: src/libslic3r/Print.cpp:1438 +#: src/libslic3r/Print.cpp:1557 msgid "Infilling layers" msgstr "레이어 채우기" -#: src/libslic3r/Print.cpp:1446 +#: src/libslic3r/Print.cpp:1577 msgid "Generating skirt" msgstr "스커트 생성" -#: src/libslic3r/Print.cpp:1454 +#: src/libslic3r/Print.cpp:1585 msgid "Generating brim" msgstr "브림 생성" -#: src/libslic3r/Print.cpp:1482 +#: src/libslic3r/Print.cpp:1609 msgid "Exporting G-code" msgstr "G 코드 내보내기" -#: src/libslic3r/Print.cpp:1486 +#: src/libslic3r/Print.cpp:1613 msgid "Generating G-code" msgstr "G 코드 생성" -#: src/libslic3r/SLAPrint.cpp:58 -msgid "Slicing model" -msgstr "슬라이싱 모델" - -#: src/libslic3r/SLAPrint.cpp:59 src/libslic3r/SLAPrint.cpp:871 -msgid "Generating support points" -msgstr "서포트 지점 생성" - -#: src/libslic3r/SLAPrint.cpp:60 -msgid "Generating support tree" -msgstr "서포트 트리 생성" - -#: src/libslic3r/SLAPrint.cpp:61 -msgid "Generating pad" -msgstr "패드 생성" - -#: src/libslic3r/SLAPrint.cpp:62 -msgid "Slicing supports" -msgstr "슬라이싱 서포트즈" - -#: src/libslic3r/SLAPrint.cpp:79 -msgid "Merging slices and calculating statistics" -msgstr "슬라이스 병합 및 통계 계산" - -#: src/libslic3r/SLAPrint.cpp:80 -msgid "Rasterizing layers" -msgstr "레이어 래스터화" - -#: src/libslic3r/SLAPrint.cpp:650 +#: src/libslic3r/SLAPrint.cpp:613 msgid "" "Cannot proceed without support points! Add support points or disable support " "generation." @@ -5509,33 +6696,102 @@ msgstr "" "서포트 포인트 없이 진행할 수 없습니다! 서포트 지점을 추가 하거나 서포트 생성" "을 사용 하지 않도록 설정 합니다." -#: src/libslic3r/SLAPrint.cpp:664 -msgid "Elevation is too low for object." -msgstr "객체 고도가 너무 낮습니다." +#: src/libslic3r/SLAPrint.cpp:625 +msgid "" +"Elevation is too low for object. Use the \"Pad around object\" feature to " +"print the object without elevation." +msgstr "" +"객체의 고도가 너무 낮습니다. \"오브젝트 주위 패드\" 피쳐를 사용하여 고도 없" +"이 오브젝트를 인쇄합니다." -#: src/libslic3r/SLAPrint.cpp:670 +#: src/libslic3r/SLAPrint.cpp:631 msgid "" "The endings of the support pillars will be deployed on the gap between the " "object and the pad. 'Support base safety distance' has to be greater than " "the 'Pad object gap' parameter to avoid this." msgstr "" -"서포트 기둥 끝은 오브젝트와 패드 사이의 간격에 배치됩니다. 이를 방지하기 위" -"해 '베이스 서포트 안전 거리'는 '패드 오브젝트 갭' 매개변수보다 커야 합니다." +"서포트 기둥 끝은 객체(object)와 패드 사이의 간격에 배치됩니다. 이를 방지하기 " +"위해 '베이스 서포트 안전 거리'는 '패드 객체(object) 갭' 매개변수보다 커야 합" +"니다." -#: src/libslic3r/SLAPrint.cpp:759 +#: src/libslic3r/SLAPrint.cpp:646 +msgid "Exposition time is out of printer profile bounds." +msgstr "박람회 시간이 프린터 프로파일 범위를 벗어났습니다." + +#: src/libslic3r/SLAPrint.cpp:653 +msgid "Initial exposition time is out of printer profile bounds." +msgstr "초기 박람회 시간이 프린터 프로파일 범위를 벗어났습니다." + +#: src/libslic3r/SLAPrint.cpp:760 +msgid "Slicing done" +msgstr "슬라이싱 완료" + +#: src/libslic3r/SLAPrintSteps.cpp:41 +msgid "Hollowing model" +msgstr "속이 빈 모델" + +#: src/libslic3r/SLAPrintSteps.cpp:42 +msgid "Drilling holes into model." +msgstr "모델에 구멍을 드릴링합니다." + +#: src/libslic3r/SLAPrintSteps.cpp:43 +msgid "Slicing model" +msgstr "슬라이싱 모델" + +#: src/libslic3r/SLAPrintSteps.cpp:44 src/libslic3r/SLAPrintSteps.cpp:308 +msgid "Generating support points" +msgstr "서포트 지점 생성" + +#: src/libslic3r/SLAPrintSteps.cpp:45 +msgid "Generating support tree" +msgstr "서포트 트리 생성" + +#: src/libslic3r/SLAPrintSteps.cpp:46 +msgid "Generating pad" +msgstr "패드 생성" + +#: src/libslic3r/SLAPrintSteps.cpp:47 +msgid "Slicing supports" +msgstr "슬라이싱 서포트즈" + +#: src/libslic3r/SLAPrintSteps.cpp:62 +msgid "Merging slices and calculating statistics" +msgstr "슬라이스 병합 및 통계 계산" + +#: src/libslic3r/SLAPrintSteps.cpp:63 +msgid "Rasterizing layers" +msgstr "레이어 래스터화" + +#: src/libslic3r/SLAPrintSteps.cpp:154 +msgid "" +"Drilling holes into the mesh failed. This is usually caused by broken model. " +"Try to fix it first." +msgstr "" +"메시에 구멍을 뚫는 데 실패했습니다. 이는 일반적으로 모델이 손상되어 발생합니" +"다. 먼저 해결해 보십시오." + +#: src/libslic3r/SLAPrintSteps.cpp:202 msgid "" "Slicing had to be stopped due to an internal error: Inconsistent slice index." msgstr "" "내부 오류: 일치하지 않는 슬라이스 인덱스로 인해 슬라이싱을 중지해야 했습니다." -#: src/libslic3r/SLAPrint.cpp:954 src/libslic3r/SLAPrint.cpp:964 -#: src/libslic3r/SLAPrint.cpp:1005 +#: src/libslic3r/SLAPrintSteps.cpp:365 src/libslic3r/SLAPrintSteps.cpp:374 +#: src/libslic3r/SLAPrintSteps.cpp:413 msgid "Visualizing supports" msgstr "시각화 지원" -#: src/libslic3r/SLAPrint.cpp:1537 -msgid "Slicing done" -msgstr "슬라이싱 완료" +#: src/libslic3r/SLAPrintSteps.cpp:405 +msgid "No pad can be generated for this model with the current configuration" +msgstr "현재 구성으로 이 모델에 대해 패드를 생성할 수 없습니다." + +#: src/libslic3r/SLAPrintSteps.cpp:579 +msgid "" +"There are unprintable objects. Try to adjust support settings to make the " +"objects printable." +msgstr "" +"인쇄할 수 없는 개체가 있습니다. 지원 설정을 조정하여 개체를 인쇄할 수 있도록 " +"합니다." #: src/libslic3r/PrintBase.cpp:71 msgid "Failed processing of the output_filename_format template." @@ -5551,13 +6807,17 @@ msgstr "배드 모양" #: src/libslic3r/PrintConfig.cpp:56 msgid "Bed custom texture" -msgstr "침대 사용자 정의 질감" +msgstr "배드 사용자 정의 질감" #: src/libslic3r/PrintConfig.cpp:61 msgid "Bed custom model" -msgstr "침대 사용자 정의 모델" +msgstr "배드 사용자 정의 모델" -#: src/libslic3r/PrintConfig.cpp:68 +#: src/libslic3r/PrintConfig.cpp:66 +msgid "Picture sizes to be stored into a .gcode and .sl1 files" +msgstr ".gcode 및 .sl1 파일에 저장할 그림 크기" + +#: src/libslic3r/PrintConfig.cpp:73 msgid "" "This setting controls the height (and thus the total number) of the slices/" "layers. Thinner layers give better accuracy but take more time to print." @@ -5565,21 +6825,21 @@ msgstr "" "이 설정은 슬라이스/레이어의 높이(따라서 총 수)를 제어합니다. 얇은 층은 더 나" "은 정확성을 제공하지만 인쇄하는 데는 더 많은 시간이 걸린다." -#: src/libslic3r/PrintConfig.cpp:75 +#: src/libslic3r/PrintConfig.cpp:80 msgid "Max print height" msgstr "최대 프린트 높이" -#: src/libslic3r/PrintConfig.cpp:76 +#: src/libslic3r/PrintConfig.cpp:81 msgid "" "Set this to the maximum height that can be reached by your extruder while " "printing." msgstr "인쇄 중에 익스트루더가 도달 할 수있는 최대 높이로 설정하십시오." -#: src/libslic3r/PrintConfig.cpp:82 +#: src/libslic3r/PrintConfig.cpp:87 msgid "Slice gap closing radius" msgstr "슬라이스 간격 닫힘 반경" -#: src/libslic3r/PrintConfig.cpp:84 +#: src/libslic3r/PrintConfig.cpp:89 msgid "" "Cracks smaller than 2x gap closing radius are being filled during the " "triangle mesh slicing. The gap closing operation may reduce the final print " @@ -5589,11 +6849,11 @@ msgstr "" "틈 닫기 작업은 최종 인쇄 해상도를 줄일 수 있으므로 값을 합리적으로 낮게 유지 " "하는 것이 좋습니다." -#: src/libslic3r/PrintConfig.cpp:92 +#: src/libslic3r/PrintConfig.cpp:97 msgid "Hostname, IP or URL" msgstr "호스트 이름(Hostname), IP or URL" -#: src/libslic3r/PrintConfig.cpp:93 +#: src/libslic3r/PrintConfig.cpp:98 msgid "" "Slic3r can upload G-code files to a printer host. This field should contain " "the hostname, IP address or URL of the printer host instance." @@ -5601,11 +6861,11 @@ msgstr "" "Slic3r는 프린터 호스트에 G 코드 파일을 업로드할 수 있습니다. 이 필드는 호스" "트 이름, IP 주소 또는 프린터 호스트 복제본의 URL을 포함 해야 합니다." -#: src/libslic3r/PrintConfig.cpp:99 +#: src/libslic3r/PrintConfig.cpp:104 msgid "API Key / Password" msgstr "API 키/암호" -#: src/libslic3r/PrintConfig.cpp:100 +#: src/libslic3r/PrintConfig.cpp:105 msgid "" "Slic3r can upload G-code files to a printer host. This field should contain " "the API Key or the password required for authentication." @@ -5613,11 +6873,11 @@ msgstr "" "Slic3r는 프린터 호스트에 G 코드 파일을 업로드할 수 있습니다. 이 필드는 API " "키 또는 인증에 필요한 암호를 포함 해야 합니다." -#: src/libslic3r/PrintConfig.cpp:106 +#: src/libslic3r/PrintConfig.cpp:111 msgid "HTTPS CA File" msgstr "HTTPS CA 파일" -#: src/libslic3r/PrintConfig.cpp:107 +#: src/libslic3r/PrintConfig.cpp:112 msgid "" "Custom CA certificate file can be specified for HTTPS OctoPrint connections, " "in crt/pem format. If left blank, the default OS CA certificate repository " @@ -5626,25 +6886,25 @@ msgstr "" "사용자 지정 CA 인증서 파일은 crt/pem 형식의 HTTPS 옥토 프린트 연결에 대해 지" "정할 수 있습니다. 비워 두면 기본 OS CA 인증서 리포지토리가 사용 됩니다." -#: src/libslic3r/PrintConfig.cpp:121 +#: src/libslic3r/PrintConfig.cpp:126 msgid "Avoid crossing perimeters" -msgstr "출력된 외측을 피하세요" +msgstr "교체된 둘레를 피하세요." -#: src/libslic3r/PrintConfig.cpp:122 +#: src/libslic3r/PrintConfig.cpp:127 msgid "" "Optimize travel moves in order to minimize the crossing of perimeters. This " "is mostly useful with Bowden extruders which suffer from oozing. This " "feature slows down both the print and the G-code generation." msgstr "" -"둘레의 교차를 최소화하기 위해 여행 이동을 최적화하십시오. 이것은 보 잉 " +"둘레의 교차를 최소화하기 위해 여행 이동을 최적화하십시오. 이것은 보잉 " "(Bowling) 압출기가 흘러 나오기 쉬운 경우에 주로 유용합니다. 이 기능을 사용하" "면 인쇄 및 G 코드 생성 속도가 느려집니다." -#: src/libslic3r/PrintConfig.cpp:129 src/libslic3r/PrintConfig.cpp:2027 +#: src/libslic3r/PrintConfig.cpp:134 src/libslic3r/PrintConfig.cpp:2074 msgid "Other layers" msgstr "다른 레이어" -#: src/libslic3r/PrintConfig.cpp:130 +#: src/libslic3r/PrintConfig.cpp:135 msgid "" "Bed temperature for layers after the first one. Set this to zero to disable " "bed temperature control commands in the output." @@ -5652,11 +6912,11 @@ msgstr "" "첫 번째 레이어 이후의 레이어 온도. 이 값을 0으로 설정하면 출력에서 ​​베드 온도 " "제어 명령을 비활성화합니다." -#: src/libslic3r/PrintConfig.cpp:132 +#: src/libslic3r/PrintConfig.cpp:137 msgid "Bed temperature" msgstr "배드 온도" -#: src/libslic3r/PrintConfig.cpp:139 +#: src/libslic3r/PrintConfig.cpp:144 msgid "" "This custom code is inserted at every layer change, right before the Z move. " "Note that you can use placeholder variables for all Slic3r settings as well " @@ -5666,11 +6926,11 @@ msgstr "" "설정과 [layer_num] 및 [layer_z]에 대한 자리 표시 자 변수를 사용할 수 있습니" "다." -#: src/libslic3r/PrintConfig.cpp:149 +#: src/libslic3r/PrintConfig.cpp:154 msgid "Between objects G-code" -msgstr "객체 간 G 코드" +msgstr "객체(object) 간 G 코드" -#: src/libslic3r/PrintConfig.cpp:150 +#: src/libslic3r/PrintConfig.cpp:155 msgid "" "This code is inserted between objects when using sequential printing. By " "default extruder and bed temperature are reset using non-wait command; " @@ -5679,25 +6939,37 @@ msgid "" "variables for all Slic3r settings, so you can put a \"M109 " "S[first_layer_temperature]\" command wherever you want." msgstr "" -"이 코드는 순차 인쇄를 사용할 때 객체간에 삽입됩니다. 기본적으로 익스트루더 " -"및 베드 온도는 대기 모드가 아닌 명령을 사용하여 재설정됩니다. 그러나 이 사용" -"자 코드에서 M104, M109, M140 또는 M190이 감지되면 Slic3r은 온도 명령을 추가하" -"지 않습니다. 모든 Slic3r 설정에 자리 표시 변수를 사용할 수 있으므로 원하는 위" -"치에 \"M109 S [first_layer_temperature]\"명령을 넣을 수 있습니다." +"이 코드는 순차 인쇄를 사용할 때 객체(object)간에 삽입됩니다. 기본적으로 익스" +"트루더 및 베드 온도는 대기 모드가 아닌 명령을 사용하여 재설정됩니다. 그러나 " +"이 사용자 코드에서 M104, M109, M140 또는 M190이 감지되면 Slic3r은 온도 명령" +"을 추가하지 않습니다. 모든 Slic3r 설정에 자리 표시 변수를 사용할 수 있으므로 " +"원하는 위치에 \"M109 S [first_layer_temperature]\"명령을 넣을 수 있습니다." -#: src/libslic3r/PrintConfig.cpp:161 +#: src/libslic3r/PrintConfig.cpp:166 msgid "Number of solid layers to generate on bottom surfaces." msgstr "바닥면에 생성 할 솔리드 레이어의 수." -#: src/libslic3r/PrintConfig.cpp:162 +#: src/libslic3r/PrintConfig.cpp:167 msgid "Bottom solid layers" msgstr "바닥 단일 레이어" -#: src/libslic3r/PrintConfig.cpp:167 +#: src/libslic3r/PrintConfig.cpp:175 +msgid "" +"The number of bottom solid layers is increased above bottom_solid_layers if " +"necessary to satisfy minimum thickness of bottom shell." +msgstr "" +"바닥 단색 층의 수는 바닥 쉘의 최소 두께를 만족시키기 위해 필요한 경우 " +"bottom_solid_layers 이상으로 증가한다." + +#: src/libslic3r/PrintConfig.cpp:177 +msgid "Minimum bottom shell thickness" +msgstr "최소 하단 쉘 두께" + +#: src/libslic3r/PrintConfig.cpp:183 msgid "Bridge" msgstr "브리지" -#: src/libslic3r/PrintConfig.cpp:168 +#: src/libslic3r/PrintConfig.cpp:184 msgid "" "This is the acceleration your printer will use for bridges. Set zero to " "disable acceleration control for bridges." @@ -5705,18 +6977,18 @@ msgstr "" "이것은 프린터가 브릿지에 사용할 가속도입니다. 브리지의 가속 제어를 사용하지 " "않으려면 0으로 설정하십시오." -#: src/libslic3r/PrintConfig.cpp:170 src/libslic3r/PrintConfig.cpp:313 -#: src/libslic3r/PrintConfig.cpp:840 src/libslic3r/PrintConfig.cpp:961 -#: src/libslic3r/PrintConfig.cpp:1130 src/libslic3r/PrintConfig.cpp:1183 -#: src/libslic3r/PrintConfig.cpp:1194 src/libslic3r/PrintConfig.cpp:1383 +#: src/libslic3r/PrintConfig.cpp:186 src/libslic3r/PrintConfig.cpp:329 +#: src/libslic3r/PrintConfig.cpp:863 src/libslic3r/PrintConfig.cpp:985 +#: src/libslic3r/PrintConfig.cpp:1155 src/libslic3r/PrintConfig.cpp:1208 +#: src/libslic3r/PrintConfig.cpp:1219 src/libslic3r/PrintConfig.cpp:1412 msgid "mm/s²" msgstr "mm/s ²" -#: src/libslic3r/PrintConfig.cpp:176 +#: src/libslic3r/PrintConfig.cpp:192 msgid "Bridging angle" msgstr "브릿지 각도" -#: src/libslic3r/PrintConfig.cpp:178 +#: src/libslic3r/PrintConfig.cpp:194 msgid "" "Bridging angle override. If left to zero, the bridging angle will be " "calculated automatically. Otherwise the provided angle will be used for all " @@ -5726,34 +6998,34 @@ msgstr "" "로 계산됩니다. 그렇지 않으면 제공된 각도가 모든 브리지에 사용됩니다. 각도 제" "로는 180 °를 사용하십시오." -#: src/libslic3r/PrintConfig.cpp:181 src/libslic3r/PrintConfig.cpp:758 -#: src/libslic3r/PrintConfig.cpp:1619 src/libslic3r/PrintConfig.cpp:1629 -#: src/libslic3r/PrintConfig.cpp:1858 src/libslic3r/PrintConfig.cpp:2012 -#: src/libslic3r/PrintConfig.cpp:2197 src/libslic3r/PrintConfig.cpp:2582 -#: src/libslic3r/PrintConfig.cpp:2693 +#: src/libslic3r/PrintConfig.cpp:197 src/libslic3r/PrintConfig.cpp:781 +#: src/libslic3r/PrintConfig.cpp:1649 src/libslic3r/PrintConfig.cpp:1659 +#: src/libslic3r/PrintConfig.cpp:1904 src/libslic3r/PrintConfig.cpp:2059 +#: src/libslic3r/PrintConfig.cpp:2257 src/libslic3r/PrintConfig.cpp:2728 +#: src/libslic3r/PrintConfig.cpp:2849 msgid "°" msgstr "°" -#: src/libslic3r/PrintConfig.cpp:187 +#: src/libslic3r/PrintConfig.cpp:203 msgid "Bridges fan speed" msgstr "브릿지 팬 속도" -#: src/libslic3r/PrintConfig.cpp:188 +#: src/libslic3r/PrintConfig.cpp:204 msgid "This fan speed is enforced during all bridges and overhangs." msgstr "이 팬 속도는 모든 브릿지 및 오버행 중에 적용됩니다." -#: src/libslic3r/PrintConfig.cpp:189 src/libslic3r/PrintConfig.cpp:770 -#: src/libslic3r/PrintConfig.cpp:1203 src/libslic3r/PrintConfig.cpp:1266 -#: src/libslic3r/PrintConfig.cpp:1511 src/libslic3r/PrintConfig.cpp:2366 -#: src/libslic3r/PrintConfig.cpp:2623 +#: src/libslic3r/PrintConfig.cpp:205 src/libslic3r/PrintConfig.cpp:793 +#: src/libslic3r/PrintConfig.cpp:1228 src/libslic3r/PrintConfig.cpp:1291 +#: src/libslic3r/PrintConfig.cpp:1541 src/libslic3r/PrintConfig.cpp:2435 +#: src/libslic3r/PrintConfig.cpp:2768 msgid "%" msgstr "%" -#: src/libslic3r/PrintConfig.cpp:196 +#: src/libslic3r/PrintConfig.cpp:212 msgid "Bridge flow ratio" msgstr "브릿지 유량(flow)값" -#: src/libslic3r/PrintConfig.cpp:198 +#: src/libslic3r/PrintConfig.cpp:214 msgid "" "This factor affects the amount of plastic for bridging. You can decrease it " "slightly to pull the extrudates and prevent sagging, although default " @@ -5764,64 +7036,66 @@ msgstr "" "겨 처짐을 방지하기 위해 약간 줄일 수 있지만 기본 설정은 일반적으로 좋지만이 " "문제를 해결하기 전에 냉각 (팬 사용)을 시도해야합니다." -#: src/libslic3r/PrintConfig.cpp:208 +#: src/libslic3r/PrintConfig.cpp:224 msgid "Bridges" msgstr "브릿지(Bridges)" -#: src/libslic3r/PrintConfig.cpp:210 +#: src/libslic3r/PrintConfig.cpp:226 msgid "Speed for printing bridges." msgstr "브릿지 인쇄 속도." -#: src/libslic3r/PrintConfig.cpp:211 src/libslic3r/PrintConfig.cpp:592 -#: src/libslic3r/PrintConfig.cpp:600 src/libslic3r/PrintConfig.cpp:609 -#: src/libslic3r/PrintConfig.cpp:617 src/libslic3r/PrintConfig.cpp:644 -#: src/libslic3r/PrintConfig.cpp:663 src/libslic3r/PrintConfig.cpp:899 -#: src/libslic3r/PrintConfig.cpp:1026 src/libslic3r/PrintConfig.cpp:1112 -#: src/libslic3r/PrintConfig.cpp:1148 src/libslic3r/PrintConfig.cpp:1161 -#: src/libslic3r/PrintConfig.cpp:1172 src/libslic3r/PrintConfig.cpp:1225 -#: src/libslic3r/PrintConfig.cpp:1284 src/libslic3r/PrintConfig.cpp:1412 -#: src/libslic3r/PrintConfig.cpp:1586 src/libslic3r/PrintConfig.cpp:1595 -#: src/libslic3r/PrintConfig.cpp:1991 src/libslic3r/PrintConfig.cpp:2104 +#: src/libslic3r/PrintConfig.cpp:227 src/libslic3r/PrintConfig.cpp:610 +#: src/libslic3r/PrintConfig.cpp:618 src/libslic3r/PrintConfig.cpp:627 +#: src/libslic3r/PrintConfig.cpp:635 src/libslic3r/PrintConfig.cpp:662 +#: src/libslic3r/PrintConfig.cpp:681 src/libslic3r/PrintConfig.cpp:923 +#: src/libslic3r/PrintConfig.cpp:1051 src/libslic3r/PrintConfig.cpp:1137 +#: src/libslic3r/PrintConfig.cpp:1173 src/libslic3r/PrintConfig.cpp:1186 +#: src/libslic3r/PrintConfig.cpp:1197 src/libslic3r/PrintConfig.cpp:1250 +#: src/libslic3r/PrintConfig.cpp:1309 src/libslic3r/PrintConfig.cpp:1442 +#: src/libslic3r/PrintConfig.cpp:1616 src/libslic3r/PrintConfig.cpp:1625 +#: src/libslic3r/PrintConfig.cpp:2038 src/libslic3r/PrintConfig.cpp:2164 msgid "mm/s" msgstr "mm/s" -#: src/libslic3r/PrintConfig.cpp:218 +#: src/libslic3r/PrintConfig.cpp:234 msgid "Brim width" msgstr "브림 폭" -#: src/libslic3r/PrintConfig.cpp:219 +#: src/libslic3r/PrintConfig.cpp:235 msgid "" "Horizontal width of the brim that will be printed around each object on the " "first layer." -msgstr "첫 번째 레이어의 각 객체 주위에 인쇄 될 가장자리의 가로 폭입니다." +msgstr "" +"첫 번째 레이어의 각 객체(object) 주위에 인쇄 될 가장자리의 가로 폭입니다." -#: src/libslic3r/PrintConfig.cpp:226 +#: src/libslic3r/PrintConfig.cpp:242 msgid "Clip multi-part objects" -msgstr "여러 파트 오브젝트 클립" +msgstr "여러 부품(Part) 객체(object) 클립" -#: src/libslic3r/PrintConfig.cpp:227 +#: src/libslic3r/PrintConfig.cpp:243 msgid "" "When printing multi-material objects, this settings will make Slic3r to clip " "the overlapping object parts one by the other (2nd part will be clipped by " "the 1st, 3rd part will be clipped by the 1st and 2nd etc)." msgstr "" -"멀티 메터리얼(multi-material) 개체를 인쇄 할 때이 설정을 사용하면 겹치는 개" -"체 파트를 서로 겹쳐서 잘라낼 수 있습니다 (두 번째 부분은 첫 번째 부분에서 클" -"리핑되며 세 번째 부분은 첫 번째 및 두 번째 부분에서 잘립니다)." +"다중 메터리얼(multi-material) 객체(object)를 인쇄 할 때이 설정을 사용하면 겹" +"치는 객체(object) 부품(Part)를 서로 겹쳐서 잘라낼 수 있습니다 (두 번째 부분" +"은 첫 번째 부분에서 클리핑되며 세 번째 부분은 첫 번째 및 두 번째 부분에서 잘" +"립니다)." -#: src/libslic3r/PrintConfig.cpp:234 +#: src/libslic3r/PrintConfig.cpp:250 msgid "Colorprint height" msgstr "컬러 인쇄 높이" -#: src/libslic3r/PrintConfig.cpp:235 +#: src/libslic3r/PrintConfig.cpp:251 msgid "Heights at which a filament change is to occur." msgstr "필라멘트 체인지가 발생 하는 높이." -#: src/libslic3r/PrintConfig.cpp:245 +#: src/libslic3r/PrintConfig.cpp:261 msgid "Compatible printers condition" msgstr "호환 가능한 프린터 조건" -#: src/libslic3r/PrintConfig.cpp:246 +#: src/libslic3r/PrintConfig.cpp:262 msgid "" "A boolean expression using the configuration values of an active printer " "profile. If this expression evaluates to true, this profile is considered " @@ -5830,11 +7104,11 @@ msgstr "" "활성 프린터 프로파일의 구성 값을 사용하는 부울 표현식. 이 표현식이 true로 평" "가되면이 프로필은 활성 프린터 프로필과 호환되는 것으로 간주됩니다." -#: src/libslic3r/PrintConfig.cpp:260 +#: src/libslic3r/PrintConfig.cpp:276 msgid "Compatible print profiles condition" msgstr "호환 되는 인쇄 프로 파일 조건" -#: src/libslic3r/PrintConfig.cpp:261 +#: src/libslic3r/PrintConfig.cpp:277 msgid "" "A boolean expression using the configuration values of an active print " "profile. If this expression evaluates to true, this profile is considered " @@ -5843,27 +7117,27 @@ msgstr "" "활성 인쇄 프로 파일의 구성 값을 사용하는 부울식입니다. 이 식이 true로 평가 되" "면, 이 프로필이 활성 인쇄 프로필과 호환 되는 것으로 간주 됩니다." -#: src/libslic3r/PrintConfig.cpp:278 +#: src/libslic3r/PrintConfig.cpp:294 msgid "Complete individual objects" -msgstr "개별 개체 완성" +msgstr "개별 객체(object) 완성" -#: src/libslic3r/PrintConfig.cpp:279 +#: src/libslic3r/PrintConfig.cpp:295 msgid "" "When printing multiple objects or copies, this feature will complete each " "object before moving onto next one (and starting it from its bottom layer). " "This feature is useful to avoid the risk of ruined prints. Slic3r should " "warn and prevent you from extruder collisions, but beware." msgstr "" -"여러 객체 또는 사본을 인쇄 할 때이 객체는 다음 객체로 이동하기 전에 각 객체" -"를 완성합니다 (맨 아래 레이어에서 시작). 이 기능은 인쇄물이 망가지는 위험을 " -"피할 때 유용합니다. Slic3r은 압출기 충돌을 경고하고 예방해야하지만 조심하십시" -"오." +"여러 객체(object) 또는 사본을 인쇄 할 때이 객체(object)는 다음 객체(object)" +"로 이동하기 전에 각 객체(object)를 완성합니다 (맨 아래 레이어에서 시작). 이 " +"기능은 인쇄물이 망가지는 위험을 피할 때 유용합니다. Slic3r은 압출기 충돌을 경" +"고하고 예방해야하지만 조재봉선하십시오." -#: src/libslic3r/PrintConfig.cpp:287 +#: src/libslic3r/PrintConfig.cpp:303 msgid "Enable auto cooling" msgstr "자동 냉각 사용" -#: src/libslic3r/PrintConfig.cpp:288 +#: src/libslic3r/PrintConfig.cpp:304 msgid "" "This flag enables the automatic cooling logic that adjusts print speed and " "fan speed according to layer printing time." @@ -5871,23 +7145,23 @@ msgstr "" "이 플래그는 레이어 인쇄 시간에 따라 인쇄 속도와 팬 속도를 조정하는 자동 냉각 " "논리를 활성화합니다." -#: src/libslic3r/PrintConfig.cpp:293 +#: src/libslic3r/PrintConfig.cpp:309 msgid "Cooling tube position" msgstr "냉각 튜브 위치" -#: src/libslic3r/PrintConfig.cpp:294 +#: src/libslic3r/PrintConfig.cpp:310 msgid "Distance of the center-point of the cooling tube from the extruder tip." -msgstr "압출기 팁에서 냉각 튜브의 중심점까지의 거리 " +msgstr "압출기 팁에서 냉각 튜브의 중재봉선점까지의 거리 " -#: src/libslic3r/PrintConfig.cpp:301 +#: src/libslic3r/PrintConfig.cpp:317 msgid "Cooling tube length" msgstr "냉각 튜브 길이" -#: src/libslic3r/PrintConfig.cpp:302 +#: src/libslic3r/PrintConfig.cpp:318 msgid "Length of the cooling tube to limit space for cooling moves inside it." msgstr "내부의 냉각 이동을 위해 공간을 제한하는 냉각 튜브의 길이 " -#: src/libslic3r/PrintConfig.cpp:310 +#: src/libslic3r/PrintConfig.cpp:326 msgid "" "This is the acceleration your printer will be reset to after the role-" "specific acceleration values are used (perimeter/infill). Set zero to " @@ -5896,11 +7170,11 @@ msgstr "" "역할 별 가속도 값이 사용 된 후에 프린터가 재설정되는 속도입니다 (둘레 / 충" "전). 가속을 전혀 재설정하지 않으려면 0으로 설정하십시오." -#: src/libslic3r/PrintConfig.cpp:319 +#: src/libslic3r/PrintConfig.cpp:335 msgid "Default filament profile" msgstr "기본 필라멘트 프로파일" -#: src/libslic3r/PrintConfig.cpp:320 +#: src/libslic3r/PrintConfig.cpp:336 msgid "" "Default filament profile associated with the current printer profile. On " "selection of the current printer profile, this filament profile will be " @@ -5909,12 +7183,12 @@ msgstr "" "현재 프린터 프로파일과 연관된 기본 필라멘트 프로파일. 현재 프린터 프로파일을 " "선택하면 이 필라멘트 프로파일이 활성화됩니다." -#: src/libslic3r/PrintConfig.cpp:326 +#: src/libslic3r/PrintConfig.cpp:342 msgid "Default print profile" msgstr "기본 인쇄 프로파일" -#: src/libslic3r/PrintConfig.cpp:327 src/libslic3r/PrintConfig.cpp:2447 -#: src/libslic3r/PrintConfig.cpp:2458 +#: src/libslic3r/PrintConfig.cpp:343 src/libslic3r/PrintConfig.cpp:2593 +#: src/libslic3r/PrintConfig.cpp:2604 msgid "" "Default print profile associated with the current printer profile. On " "selection of the current printer profile, this print profile will be " @@ -5923,11 +7197,11 @@ msgstr "" "현재 프린터 프로파일과 연관된 기본 인쇄 프로파일. 현재 프린터 프로파일을 선택" "하면이 인쇄 프로파일이 활성화됩니다." -#: src/libslic3r/PrintConfig.cpp:333 +#: src/libslic3r/PrintConfig.cpp:349 msgid "Disable fan for the first" msgstr "첫 번째 팬 사용 중지" -#: src/libslic3r/PrintConfig.cpp:334 +#: src/libslic3r/PrintConfig.cpp:350 msgid "" "You can set this to a positive value to disable fan at all during the first " "layers, so that it does not make adhesion worse." @@ -5935,37 +7209,30 @@ msgstr "" "이 값을 양수 값으로 설정하면 첫 번째 레이어에서 팬을 사용하지 않도록 설정하" "여 접착력을 악화시키지 않습니다." -#: src/libslic3r/PrintConfig.cpp:336 src/libslic3r/PrintConfig.cpp:971 -#: src/libslic3r/PrintConfig.cpp:1484 src/libslic3r/PrintConfig.cpp:1669 -#: src/libslic3r/PrintConfig.cpp:1730 src/libslic3r/PrintConfig.cpp:1894 -#: src/libslic3r/PrintConfig.cpp:1939 -msgid "layers" -msgstr "레이어" - -#: src/libslic3r/PrintConfig.cpp:343 +#: src/libslic3r/PrintConfig.cpp:359 msgid "Don't support bridges" msgstr "서포트와 브릿지를 사용하지 않음" -#: src/libslic3r/PrintConfig.cpp:345 +#: src/libslic3r/PrintConfig.cpp:361 msgid "" "Experimental option for preventing support material from being generated " "under bridged areas." msgstr "" "브릿지 영역 아래에 서포팅 재료가 생성되는 것을 방지하기위한 실험적 옵션." -#: src/libslic3r/PrintConfig.cpp:351 +#: src/libslic3r/PrintConfig.cpp:367 msgid "Distance between copies" msgstr "복사본 간 거리" -#: src/libslic3r/PrintConfig.cpp:352 +#: src/libslic3r/PrintConfig.cpp:368 msgid "Distance used for the auto-arrange feature of the plater." msgstr "플래이터(plater)의 자동 정렬 기능에 사용되는 거리입니다." -#: src/libslic3r/PrintConfig.cpp:359 +#: src/libslic3r/PrintConfig.cpp:375 msgid "Elephant foot compensation" msgstr "코끼리 발(Elephant foot) 보상값" -#: src/libslic3r/PrintConfig.cpp:361 +#: src/libslic3r/PrintConfig.cpp:377 msgid "" "The first layer will be shrunk in the XY plane by the configured value to " "compensate for the 1st layer squish aka an Elephant Foot effect." @@ -5973,32 +7240,32 @@ msgstr "" "첫 번째 레이어는 구성 요소 값에 따라 XY 평면에서 수축되어 일층 스 퀴시 코끼리" "발(Elephant Foot) 효과를 보완합니다." -#: src/libslic3r/PrintConfig.cpp:370 +#: src/libslic3r/PrintConfig.cpp:386 msgid "" "This end procedure is inserted at the end of the output file. Note that you " -"can use placeholder variables for all Slic3r settings." +"can use placeholder variables for all PrusaSlicer settings." msgstr "" -"이 종료 절차는 출력 파일의 끝에 삽입된다. 모든 Slic3r 설정에 자리 표시자 변수" -"를 사용할 수 있다는 점에 유의하십시오." +"이 끝 프로시저는 출력 파일의 끝에 삽입됩니다. 모든 PrusaSlicer 설정에 자리 표" +"시자 변수를 사용할 수 있습니다." -#: src/libslic3r/PrintConfig.cpp:380 +#: src/libslic3r/PrintConfig.cpp:396 msgid "" "This end procedure is inserted at the end of the output file, before the " "printer end gcode (and before any toolchange from this filament in case of " "multimaterial printers). Note that you can use placeholder variables for all " -"Slic3r settings. If you have multiple extruders, the gcode is processed in " -"extruder order." +"PrusaSlicer settings. If you have multiple extruders, the gcode is processed " +"in extruder order." msgstr "" -"이 종료 절차는 출력 파일의 끝, 프린터 종료 gcode 이전 (및 복합 재료 프린터의 " -"경우이 필라멘트에서 도구를 변경하기 전에)에 삽입됩니다. 모든 Slic3r 설정에 자" -"리 표시 자 변수를 사용할 수 있습니다. 압출기가 여러 개인 경우 gcode는 압출기 " -"순서대로 처리됩니다." +"이 끝 프로시저는 프린터가 gcode를 종료하기 전에 출력 파일의 끝에 삽입됩니다" +"(다중 재질 프린터의 경우 이 필라멘트에서 공구를 변경하기 전에). 모든 " +"PrusaSlicer 설정에 자리 표시자 변수를 사용할 수 있습니다. 압출기가 여러 개 있" +"는 경우 gcode는 압출기 순서로 처리됩니다." -#: src/libslic3r/PrintConfig.cpp:391 +#: src/libslic3r/PrintConfig.cpp:407 msgid "Ensure vertical shell thickness" msgstr "수직 쉘(shell) 두께 확인" -#: src/libslic3r/PrintConfig.cpp:393 +#: src/libslic3r/PrintConfig.cpp:409 msgid "" "Add solid infill near sloping surfaces to guarantee the vertical shell " "thickness (top+bottom solid layers)." @@ -6006,11 +7273,11 @@ msgstr "" "경사 표면 근처에 솔리드 인필을 추가하여 수직 셸 두께(상단+하단 솔리드 레이어)" "를 보장하십시오." -#: src/libslic3r/PrintConfig.cpp:399 +#: src/libslic3r/PrintConfig.cpp:415 msgid "Top fill pattern" msgstr "상단 채우기 패턴" -#: src/libslic3r/PrintConfig.cpp:401 +#: src/libslic3r/PrintConfig.cpp:417 msgid "" "Fill pattern for top infill. This only affects the top visible layer, and " "not its adjacent solid shells." @@ -6018,32 +7285,32 @@ msgstr "" "상단 채우기의 채우기 패턴. 이는 인접 한 솔리드 쉘이 아니라 보이는 상위 레이어" "에만 영향을 줍니다." -#: src/libslic3r/PrintConfig.cpp:409 src/libslic3r/PrintConfig.cpp:821 -#: src/libslic3r/PrintConfig.cpp:1972 +#: src/libslic3r/PrintConfig.cpp:425 src/libslic3r/PrintConfig.cpp:844 +#: src/libslic3r/PrintConfig.cpp:2019 msgid "Rectilinear" msgstr "직선면(Rectilinear)" -#: src/libslic3r/PrintConfig.cpp:410 src/libslic3r/PrintConfig.cpp:827 +#: src/libslic3r/PrintConfig.cpp:426 src/libslic3r/PrintConfig.cpp:850 msgid "Concentric" msgstr "동심원(Concentric)" -#: src/libslic3r/PrintConfig.cpp:411 src/libslic3r/PrintConfig.cpp:831 +#: src/libslic3r/PrintConfig.cpp:427 src/libslic3r/PrintConfig.cpp:854 msgid "Hilbert Curve" msgstr "힐버트 곡선(Hilbert Curve)" -#: src/libslic3r/PrintConfig.cpp:412 src/libslic3r/PrintConfig.cpp:832 +#: src/libslic3r/PrintConfig.cpp:428 src/libslic3r/PrintConfig.cpp:855 msgid "Archimedean Chords" msgstr "아르키메데우스(Archimedean Chords)" -#: src/libslic3r/PrintConfig.cpp:413 src/libslic3r/PrintConfig.cpp:833 +#: src/libslic3r/PrintConfig.cpp:429 src/libslic3r/PrintConfig.cpp:856 msgid "Octagram Spiral" msgstr "옥타그램 나선(Octagram Spiral)" -#: src/libslic3r/PrintConfig.cpp:419 +#: src/libslic3r/PrintConfig.cpp:435 msgid "Bottom fill pattern" msgstr "아래쪽 채우기 패턴" -#: src/libslic3r/PrintConfig.cpp:421 +#: src/libslic3r/PrintConfig.cpp:437 msgid "" "Fill pattern for bottom infill. This only affects the bottom external " "visible layer, and not its adjacent solid shells." @@ -6051,11 +7318,11 @@ msgstr "" "하단 채우기의 채우기 패턴. 이는 인접 한 솔리드 쉘이 아니라 아래쪽에 보이는 외" "부 레이어에만 영향을 줍니다." -#: src/libslic3r/PrintConfig.cpp:430 src/libslic3r/PrintConfig.cpp:440 +#: src/libslic3r/PrintConfig.cpp:446 src/libslic3r/PrintConfig.cpp:457 msgid "External perimeters" msgstr "외측 둘레" -#: src/libslic3r/PrintConfig.cpp:432 +#: src/libslic3r/PrintConfig.cpp:448 msgid "" "Set this to a non-zero value to set a manual extrusion width for external " "perimeters. If left zero, default extrusion width will be used if set, " @@ -6066,16 +7333,16 @@ msgstr "" "오. 0인 경우 기본 압출 너비가 사용되며, 그렇지 않으면 1.125 x 노즐 직경이 사" "용된다. 백분율(예: 200%)로 표현되는 경우, 레이어 높이에 걸쳐 계산됩니다." -#: src/libslic3r/PrintConfig.cpp:435 src/libslic3r/PrintConfig.cpp:543 -#: src/libslic3r/PrintConfig.cpp:860 src/libslic3r/PrintConfig.cpp:872 -#: src/libslic3r/PrintConfig.cpp:992 src/libslic3r/PrintConfig.cpp:1017 -#: src/libslic3r/PrintConfig.cpp:1403 src/libslic3r/PrintConfig.cpp:1741 -#: src/libslic3r/PrintConfig.cpp:1847 src/libslic3r/PrintConfig.cpp:1915 -#: src/libslic3r/PrintConfig.cpp:2074 +#: src/libslic3r/PrintConfig.cpp:451 src/libslic3r/PrintConfig.cpp:560 +#: src/libslic3r/PrintConfig.cpp:883 src/libslic3r/PrintConfig.cpp:896 +#: src/libslic3r/PrintConfig.cpp:1016 src/libslic3r/PrintConfig.cpp:1042 +#: src/libslic3r/PrintConfig.cpp:1432 src/libslic3r/PrintConfig.cpp:1771 +#: src/libslic3r/PrintConfig.cpp:1893 src/libslic3r/PrintConfig.cpp:1961 +#: src/libslic3r/PrintConfig.cpp:2121 msgid "mm or %" msgstr "mm/s 또는 %" -#: src/libslic3r/PrintConfig.cpp:442 +#: src/libslic3r/PrintConfig.cpp:459 msgid "" "This separate setting will affect the speed of external perimeters (the " "visible ones). If expressed as percentage (for example: 80%) it will be " @@ -6085,17 +7352,17 @@ msgstr "" "(예: 80%)로 표현되는 경우 위의 Perimeter 속도 설정에 따라 계산된다. 자동을 위" "해 0으로 설정한다." -#: src/libslic3r/PrintConfig.cpp:445 src/libslic3r/PrintConfig.cpp:881 -#: src/libslic3r/PrintConfig.cpp:1700 src/libslic3r/PrintConfig.cpp:1751 -#: src/libslic3r/PrintConfig.cpp:1958 src/libslic3r/PrintConfig.cpp:2086 +#: src/libslic3r/PrintConfig.cpp:462 src/libslic3r/PrintConfig.cpp:905 +#: src/libslic3r/PrintConfig.cpp:1730 src/libslic3r/PrintConfig.cpp:1782 +#: src/libslic3r/PrintConfig.cpp:2005 src/libslic3r/PrintConfig.cpp:2134 msgid "mm/s or %" msgstr "mm/s 또는 %" -#: src/libslic3r/PrintConfig.cpp:452 +#: src/libslic3r/PrintConfig.cpp:469 msgid "External perimeters first" msgstr "외부 경계선 먼저" -#: src/libslic3r/PrintConfig.cpp:454 +#: src/libslic3r/PrintConfig.cpp:471 msgid "" "Print contour perimeters from the outermost one to the innermost one instead " "of the default inverse order." @@ -6103,12 +7370,12 @@ msgstr "" "기본 역순 대신 가장 바깥쪽부터 가장 안쪽까지 윤곽선을 인쇄하십시오. 타겟 TTS" "복사하기번역 저장번역 저장번역 수정." -#: src/libslic3r/PrintConfig.cpp:460 +#: src/libslic3r/PrintConfig.cpp:477 msgid "Extra perimeters if needed" msgstr "필요한 경우 추가 둘레" -#: src/libslic3r/PrintConfig.cpp:462 -#, c-format +#: src/libslic3r/PrintConfig.cpp:479 +#, no-c-format msgid "" "Add more perimeters when needed for avoiding gaps in sloping walls. Slic3r " "keeps adding perimeters, until more than 70% of the loop immediately above " @@ -6118,7 +7385,7 @@ msgstr "" "오. 위의 루프의 70% of 이상이 지지될 때까지 Slic3r는 계속해서 둘ㄹ를 추가한" "다." -#: src/libslic3r/PrintConfig.cpp:472 +#: src/libslic3r/PrintConfig.cpp:489 msgid "" "The extruder to use (unless more specific extruder settings are specified). " "This value overrides perimeter and infill extruders, but not the support " @@ -6127,7 +7394,7 @@ msgstr "" "사용할 압출부(더 구체적인 압출부 설정이 지정되지 않은 경우) 이 값은 경계 및 " "압출부를 초과하지만 지원 압출자를 주입하지는 않는다." -#: src/libslic3r/PrintConfig.cpp:484 +#: src/libslic3r/PrintConfig.cpp:501 msgid "" "Set this to the vertical distance between your nozzle tip and (usually) the " "X carriage rods. In other words, this is the height of the clearance " @@ -6136,13 +7403,9 @@ msgid "" msgstr "" "이것을 노즐 팁과 (일반적으로) X 캐리지 로드 사이의 수직 거리로 설정하십시오. " "다시 말하면, 이것은 당신의 압출기 주위의 틈새 실린더의 높이이며, 그것은 다른 " -"인쇄된 물체와 충돌하기 전에 압출기가 엿볼 수 있는 최대 깊이를 나타낸다." +"인쇄된 개체와 충돌하기 전에 압출기가 엿볼 수 있는 최대 깊이를 나타낸다." -#: src/libslic3r/PrintConfig.cpp:494 -msgid "Radius" -msgstr "반지름" - -#: src/libslic3r/PrintConfig.cpp:495 +#: src/libslic3r/PrintConfig.cpp:512 msgid "" "Set this to the clearance radius around your extruder. If the extruder is " "not centered, choose the largest value for safety. This setting is used to " @@ -6152,19 +7415,19 @@ msgstr "" "으면 안전을 위해 가장 큰 값을 선택하십시오. 이 설정은 충돌 여부를 확인하고 플" "래터에 그래픽 미리 보기를 표시하기 위해 사용된다." -#: src/libslic3r/PrintConfig.cpp:505 +#: src/libslic3r/PrintConfig.cpp:522 msgid "Extruder Color" msgstr "익스트루더 컬러" -#: src/libslic3r/PrintConfig.cpp:506 src/libslic3r/PrintConfig.cpp:566 +#: src/libslic3r/PrintConfig.cpp:523 src/libslic3r/PrintConfig.cpp:584 msgid "This is only used in the Slic3r interface as a visual help." -msgstr "이것은 시각적 도움말로 Slic3r 인터페이스에서만 사용된다." +msgstr "이것은 시각적 도움말로 Slic3r 접점에서만 사용된다." -#: src/libslic3r/PrintConfig.cpp:512 +#: src/libslic3r/PrintConfig.cpp:529 msgid "Extruder offset" msgstr "익스트루더 오프셋" -#: src/libslic3r/PrintConfig.cpp:513 +#: src/libslic3r/PrintConfig.cpp:530 msgid "" "If your firmware doesn't handle the extruder displacement you need the G-" "code to take it into account. This option lets you specify the displacement " @@ -6175,11 +7438,11 @@ msgstr "" "을 사용하면 첫 번째 것에 대한 각 압출기의 변위를 지정할 수 있습니다. 양의 좌" "표가 필요합니다 (XY 좌표에서 뺍니다)." -#: src/libslic3r/PrintConfig.cpp:522 +#: src/libslic3r/PrintConfig.cpp:539 msgid "Extrusion axis" msgstr "압출 축" -#: src/libslic3r/PrintConfig.cpp:523 +#: src/libslic3r/PrintConfig.cpp:540 msgid "" "Use this option to set the axis letter associated to your printer's extruder " "(usually E but some printers use A)." @@ -6187,11 +7450,11 @@ msgstr "" "이 옵션을 사용하여 프린터의 압출기에 연결된 축 문자를 설정합니다 (보통 E이지" "만 일부 프린터는 A를 사용합니다)." -#: src/libslic3r/PrintConfig.cpp:528 +#: src/libslic3r/PrintConfig.cpp:545 msgid "Extrusion multiplier" msgstr "압출 승수" -#: src/libslic3r/PrintConfig.cpp:529 +#: src/libslic3r/PrintConfig.cpp:546 msgid "" "This factor changes the amount of flow proportionally. You may need to tweak " "this setting to get nice surface finish and correct single wall widths. " @@ -6203,11 +7466,11 @@ msgstr "" "이입니다. 이 값을 더 변경해야한다고 판단되면 필라멘트 직경과 펌웨어 E 단계를 " "확인하십시오." -#: src/libslic3r/PrintConfig.cpp:537 +#: src/libslic3r/PrintConfig.cpp:554 msgid "Default extrusion width" msgstr "기본 압출 폭" -#: src/libslic3r/PrintConfig.cpp:539 +#: src/libslic3r/PrintConfig.cpp:556 msgid "" "Set this to a non-zero value to allow a manual extrusion width. If left to " "zero, Slic3r derives extrusion widths from the nozzle diameter (see the " @@ -6220,11 +7483,11 @@ msgstr "" "의 툴팁 참조). 백분율로 표시되는 경우 (예 : 230 %) 레이어 높이를 기준으로 계" "산됩니다." -#: src/libslic3r/PrintConfig.cpp:548 +#: src/libslic3r/PrintConfig.cpp:566 msgid "Keep fan always on" msgstr "항상 팬 켜기" -#: src/libslic3r/PrintConfig.cpp:549 +#: src/libslic3r/PrintConfig.cpp:567 msgid "" "If this is enabled, fan will never be disabled and will be kept running at " "least at its minimum speed. Useful for PLA, harmful for ABS." @@ -6232,11 +7495,11 @@ msgstr "" "이 기능을 사용하면 팬이 비활성화되지 않으며 최소한 최소 속도로 계속 회전합니" "다. PLA에 유용하며 ABS에 해롭다." -#: src/libslic3r/PrintConfig.cpp:554 +#: src/libslic3r/PrintConfig.cpp:572 msgid "Enable fan if layer print time is below" msgstr "레이어 인쇄 시간이 미만인 경우 팬 활성화" -#: src/libslic3r/PrintConfig.cpp:555 +#: src/libslic3r/PrintConfig.cpp:573 msgid "" "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 " @@ -6245,27 +7508,23 @@ msgstr "" "레이어 인쇄 시간이이 초 미만으로 예상되는 경우 팬이 활성화되고 속도는 최소 " "및 최대 속도를 보간하여 계산됩니다." -#: src/libslic3r/PrintConfig.cpp:557 src/libslic3r/PrintConfig.cpp:1687 +#: src/libslic3r/PrintConfig.cpp:575 src/libslic3r/PrintConfig.cpp:1717 msgid "approximate seconds" msgstr "근사치 초" -#: src/libslic3r/PrintConfig.cpp:565 -msgid "Color" -msgstr "색상" - -#: src/libslic3r/PrintConfig.cpp:571 +#: src/libslic3r/PrintConfig.cpp:589 msgid "Filament notes" msgstr "필라멘트 메모" -#: src/libslic3r/PrintConfig.cpp:572 +#: src/libslic3r/PrintConfig.cpp:590 msgid "You can put your notes regarding the filament here." msgstr "여기에 필라멘트에 관한 메모를 넣을 수 있다." -#: src/libslic3r/PrintConfig.cpp:580 src/libslic3r/PrintConfig.cpp:1231 +#: src/libslic3r/PrintConfig.cpp:598 src/libslic3r/PrintConfig.cpp:1256 msgid "Max volumetric speed" msgstr "최대 체적 속도" -#: src/libslic3r/PrintConfig.cpp:581 +#: src/libslic3r/PrintConfig.cpp:599 msgid "" "Maximum volumetric speed allowed for this filament. Limits the maximum " "volumetric speed of a print to the minimum of print and filament volumetric " @@ -6274,27 +7533,27 @@ msgstr "" "이 필라멘트에 허용되는 최대 체적 속도. 인쇄물의 최대 체적 속도를 인쇄 및 필라" "멘트 체적 속도 최소로 제한한다. 제한 없음에 대해 0으로 설정하십시오." -#: src/libslic3r/PrintConfig.cpp:590 +#: src/libslic3r/PrintConfig.cpp:608 msgid "Loading speed" msgstr "로딩 속도" -#: src/libslic3r/PrintConfig.cpp:591 +#: src/libslic3r/PrintConfig.cpp:609 msgid "Speed used for loading the filament on the wipe tower." -msgstr "와이퍼 탑(wipe)에 필라멘트를 장착하는 데 사용되는 속도. " +msgstr "와이퍼 타워(wipe)에 필라멘트를 장착하는 데 사용되는 속도. " -#: src/libslic3r/PrintConfig.cpp:598 +#: src/libslic3r/PrintConfig.cpp:616 msgid "Loading speed at the start" msgstr "시작시 로딩 속도" -#: src/libslic3r/PrintConfig.cpp:599 +#: src/libslic3r/PrintConfig.cpp:617 msgid "Speed used at the very beginning of loading phase." msgstr "로딩 단계의 시작에 사용 되는 속도." -#: src/libslic3r/PrintConfig.cpp:606 +#: src/libslic3r/PrintConfig.cpp:624 msgid "Unloading speed" msgstr "언로딩 스피드" -#: src/libslic3r/PrintConfig.cpp:607 +#: src/libslic3r/PrintConfig.cpp:625 msgid "" "Speed used for unloading the filament on the wipe tower (does not affect " "initial part of unloading just after ramming)." @@ -6302,20 +7561,20 @@ msgstr "" "와이퍼 타워에서 필라멘트를 언로드하는 데 사용되는 속도(램핑 후 바로 언로딩의 " "초기 부분에는 영향을 주지 않음)." -#: src/libslic3r/PrintConfig.cpp:615 +#: src/libslic3r/PrintConfig.cpp:633 msgid "Unloading speed at the start" msgstr "시작 시 언로드 속도" -#: src/libslic3r/PrintConfig.cpp:616 +#: src/libslic3r/PrintConfig.cpp:634 msgid "" "Speed used for unloading the tip of the filament immediately after ramming." msgstr "속도는 램 밍 직후 필 라 멘 트의 팁을 언로딩 하는 데 사용 됩니다." -#: src/libslic3r/PrintConfig.cpp:623 +#: src/libslic3r/PrintConfig.cpp:641 msgid "Delay after unloading" msgstr "언로드 후 딜레이" -#: src/libslic3r/PrintConfig.cpp:624 +#: src/libslic3r/PrintConfig.cpp:642 msgid "" "Time to wait after the filament is unloaded. May help to get reliable " "toolchanges with flexible materials that may need more time to shrink to " @@ -6324,11 +7583,11 @@ msgstr "" "필라멘트를 내린 후 기다리는 시간. 원래 치수로 축소하는 데, 더 많은 시간이 필" "요할 수 있는 유연한 재료로 신뢰할 수있는 공구 교환을 얻을 수 있습니다." -#: src/libslic3r/PrintConfig.cpp:633 +#: src/libslic3r/PrintConfig.cpp:651 msgid "Number of cooling moves" msgstr "쿨링 이동 숫자" -#: src/libslic3r/PrintConfig.cpp:634 +#: src/libslic3r/PrintConfig.cpp:652 msgid "" "Filament is cooled by being moved back and forth in the cooling tubes. " "Specify desired number of these moves." @@ -6336,19 +7595,19 @@ msgstr "" "필라멘트는 쿨링 튜브에서 앞뒤로 이동 하여 냉각 됩니다. 이러한 이동의 원하는 " "값을 지정 합니다." -#: src/libslic3r/PrintConfig.cpp:642 +#: src/libslic3r/PrintConfig.cpp:660 msgid "Speed of the first cooling move" msgstr "첫 번째 냉각 이동 속도" -#: src/libslic3r/PrintConfig.cpp:643 +#: src/libslic3r/PrintConfig.cpp:661 msgid "Cooling moves are gradually accelerating beginning at this speed." msgstr "냉각 속도가 서서히 빨라지고 있습니다." -#: src/libslic3r/PrintConfig.cpp:650 +#: src/libslic3r/PrintConfig.cpp:668 msgid "Minimal purge on wipe tower" msgstr "와이프(wipe) 탑의 최소 퍼지" -#: src/libslic3r/PrintConfig.cpp:651 +#: src/libslic3r/PrintConfig.cpp:669 msgid "" "After a tool change, the exact position of the newly loaded filament inside " "the nozzle may not be known, and the filament pressure is likely not yet " @@ -6358,27 +7617,27 @@ msgid "" msgstr "" "공구가 변경 된 후 노즐 내부에 새로 로드 된 필라멘트의 정확한 위치를 알 수 없" "으며, 필라멘트 압력이 아직 안정적이지 않을 수 있습니다. 프린트 헤드를 인필 또" -"는 희생(sacrificial) 객체로 소거 하기 전에 Slic3r는 항상이 양의 재료를 와이" -"프 탑에 넣어 연속적인 채우기 또는 희생(sacrificial) 객체 돌출을 안정적으로 생" -"성 합니다." +"는 희생(sacrificial) 객체(object)로 소거 하기 전에 Slic3r는 항상이 양의 재료" +"를 와이프 탑에 넣어 연속적인 채우기 또는 희생(sacrificial) 객체(object) 돌출" +"을 안정적으로 생성 합니다." -#: src/libslic3r/PrintConfig.cpp:655 +#: src/libslic3r/PrintConfig.cpp:673 msgid "mm³" msgstr "mm ³" -#: src/libslic3r/PrintConfig.cpp:661 +#: src/libslic3r/PrintConfig.cpp:679 msgid "Speed of the last cooling move" msgstr "마지막 냉각 이동 속도" -#: src/libslic3r/PrintConfig.cpp:662 +#: src/libslic3r/PrintConfig.cpp:680 msgid "Cooling moves are gradually accelerating towards this speed." msgstr "냉각은 이 속도쪽으로 점차 가속화되고 있습니다. " -#: src/libslic3r/PrintConfig.cpp:669 +#: src/libslic3r/PrintConfig.cpp:687 msgid "Filament load time" msgstr "필라멘트 로드 시간" -#: src/libslic3r/PrintConfig.cpp:670 +#: src/libslic3r/PrintConfig.cpp:688 msgid "" "Time for the printer firmware (or the Multi Material Unit 2.0) to load a new " "filament during a tool change (when executing the T code). This time is " @@ -6388,22 +7647,22 @@ msgstr "" "라멘트를 로드하는 시간입니다. 이 시간은 G 코드 시간 추정기에 의해 총 인쇄 시" "간에 추가 됩니다." -#: src/libslic3r/PrintConfig.cpp:677 +#: src/libslic3r/PrintConfig.cpp:695 msgid "Ramming parameters" msgstr "래밍 파라미터" -#: src/libslic3r/PrintConfig.cpp:678 +#: src/libslic3r/PrintConfig.cpp:696 msgid "" "This string is edited by RammingDialog and contains ramming specific " "parameters." msgstr "" "이 문자열은 RammingDialog에 의해 편집되고 래밍 특정 매개 변수를 포함합니다." -#: src/libslic3r/PrintConfig.cpp:684 +#: src/libslic3r/PrintConfig.cpp:702 msgid "Filament unload time" msgstr "필라멘트 언로드 시간" -#: src/libslic3r/PrintConfig.cpp:685 +#: src/libslic3r/PrintConfig.cpp:703 msgid "" "Time for the printer firmware (or the Multi Material Unit 2.0) to unload a " "filament during a tool change (when executing the T code). This time is " @@ -6413,7 +7672,7 @@ msgstr "" "입니다 (T 코드를 실행할 때). 이 시간은 G 코드 시간추정기에 의해 총 인쇄 시간" "에 추가 됩니다." -#: src/libslic3r/PrintConfig.cpp:693 +#: src/libslic3r/PrintConfig.cpp:711 msgid "" "Enter your filament diameter here. Good precision is required, so use a " "caliper and do multiple measurements along the filament, then compute the " @@ -6422,11 +7681,12 @@ msgstr "" "여기에 필라멘트 직경을 입력하십시오. 정밀도가 필요하므로 캘리퍼를 사용하여 필" "라멘트를 따라 여러 번 측정 한 다음 평균을 계산하십시오." -#: src/libslic3r/PrintConfig.cpp:700 +#: src/libslic3r/PrintConfig.cpp:718 src/libslic3r/PrintConfig.cpp:2504 +#: src/libslic3r/PrintConfig.cpp:2505 msgid "Density" msgstr "밀도" -#: src/libslic3r/PrintConfig.cpp:701 +#: src/libslic3r/PrintConfig.cpp:719 msgid "" "Enter your filament density here. This is only for statistical information. " "A decent way is to weigh a known length of filament and compute the ratio of " @@ -6437,41 +7697,45 @@ msgstr "" "은 알려진 길이의 필라멘트의 무게를 측정하고 길이와 볼륨의 비율을 계산하는 것" "입니다. 변위를 통해 직접적으로 부피를 계산하는 것이 더 좋습니다." -#: src/libslic3r/PrintConfig.cpp:704 +#: src/libslic3r/PrintConfig.cpp:722 msgid "g/cm³" msgstr "g/cm³" -#: src/libslic3r/PrintConfig.cpp:709 +#: src/libslic3r/PrintConfig.cpp:727 msgid "Filament type" msgstr "필라멘트 타입" -#: src/libslic3r/PrintConfig.cpp:710 +#: src/libslic3r/PrintConfig.cpp:728 msgid "The filament material type for use in custom G-codes." msgstr "사용자 지정 G 코드에 사용할 필라멘트재료 유형입니다." -#: src/libslic3r/PrintConfig.cpp:736 +#: src/libslic3r/PrintConfig.cpp:755 msgid "Soluble material" msgstr "수용성 재료" -#: src/libslic3r/PrintConfig.cpp:737 +#: src/libslic3r/PrintConfig.cpp:756 msgid "Soluble material is most likely used for a soluble support." msgstr "수용성 재료눈 물에 녹는 서포트에 가장 많이 사용된다." -#: src/libslic3r/PrintConfig.cpp:743 +#: src/libslic3r/PrintConfig.cpp:762 msgid "" "Enter your filament cost per kg here. This is only for statistical " "information." msgstr "필라멘트(kg당) 비용을 여기에 입력하십시오. 통계를 내기 위해서 입니다." -#: src/libslic3r/PrintConfig.cpp:744 +#: src/libslic3r/PrintConfig.cpp:763 msgid "money/kg" msgstr "원(\\)/kg" -#: src/libslic3r/PrintConfig.cpp:753 +#: src/libslic3r/PrintConfig.cpp:772 src/libslic3r/PrintConfig.cpp:2588 +msgid "(Unknown)" +msgstr "(알 수 없음)" + +#: src/libslic3r/PrintConfig.cpp:776 msgid "Fill angle" msgstr "채움 각도" -#: src/libslic3r/PrintConfig.cpp:755 +#: src/libslic3r/PrintConfig.cpp:778 msgid "" "Default base angle for infill orientation. Cross-hatching will be applied to " "this. Bridges will be infilled using the best direction Slic3r can detect, " @@ -6481,60 +7745,60 @@ msgstr "" "지 할 수있는 최상의 방향을 사용하여 브릿징이 채워지므로이 설정은 영향을 미치" "지 않습니다." -#: src/libslic3r/PrintConfig.cpp:767 +#: src/libslic3r/PrintConfig.cpp:790 msgid "Fill density" msgstr "채우기(fill) 밀도" -#: src/libslic3r/PrintConfig.cpp:769 +#: src/libslic3r/PrintConfig.cpp:792 msgid "Density of internal infill, expressed in the range 0% - 100%." msgstr "0 % - 100 % 범위로 표현 된 내부 채움(infill)의 밀도." -#: src/libslic3r/PrintConfig.cpp:804 +#: src/libslic3r/PrintConfig.cpp:827 msgid "Fill pattern" msgstr "채우기(fill) 패턴" -#: src/libslic3r/PrintConfig.cpp:806 +#: src/libslic3r/PrintConfig.cpp:829 msgid "Fill pattern for general low-density infill." msgstr "일반 낮은 밀도 채움의 패턴." -#: src/libslic3r/PrintConfig.cpp:822 +#: src/libslic3r/PrintConfig.cpp:845 msgid "Grid" msgstr "그리드(Grid)" -#: src/libslic3r/PrintConfig.cpp:823 +#: src/libslic3r/PrintConfig.cpp:846 msgid "Triangles" msgstr "삼각형(Triangles)" -#: src/libslic3r/PrintConfig.cpp:824 +#: src/libslic3r/PrintConfig.cpp:847 msgid "Stars" msgstr "별(Stars)" -#: src/libslic3r/PrintConfig.cpp:825 +#: src/libslic3r/PrintConfig.cpp:848 msgid "Cubic" msgstr "큐빅" -#: src/libslic3r/PrintConfig.cpp:826 +#: src/libslic3r/PrintConfig.cpp:849 msgid "Line" msgstr "선(Line)" -#: src/libslic3r/PrintConfig.cpp:828 src/libslic3r/PrintConfig.cpp:1974 +#: src/libslic3r/PrintConfig.cpp:851 src/libslic3r/PrintConfig.cpp:2021 msgid "Honeycomb" msgstr "벌집" -#: src/libslic3r/PrintConfig.cpp:829 +#: src/libslic3r/PrintConfig.cpp:852 msgid "3D Honeycomb" msgstr "3D 벌집" -#: src/libslic3r/PrintConfig.cpp:830 +#: src/libslic3r/PrintConfig.cpp:853 msgid "Gyroid" msgstr "자이로이드(Gyroid)" -#: src/libslic3r/PrintConfig.cpp:837 src/libslic3r/PrintConfig.cpp:846 -#: src/libslic3r/PrintConfig.cpp:854 src/libslic3r/PrintConfig.cpp:887 +#: src/libslic3r/PrintConfig.cpp:860 src/libslic3r/PrintConfig.cpp:869 +#: src/libslic3r/PrintConfig.cpp:877 src/libslic3r/PrintConfig.cpp:911 msgid "First layer" msgstr "첫 레이어" -#: src/libslic3r/PrintConfig.cpp:838 +#: src/libslic3r/PrintConfig.cpp:861 msgid "" "This is the acceleration your printer will use for first layer. Set zero to " "disable acceleration control for first layer." @@ -6542,7 +7806,7 @@ msgstr "" "이것은 프린터가 첫 번째 레이어에 사용할 가속도입니다. 0을 설정하면 첫 번째 레" "이어에 대한 가속 제어가 사용되지 않습니다." -#: src/libslic3r/PrintConfig.cpp:847 +#: src/libslic3r/PrintConfig.cpp:870 msgid "" "Heated build plate temperature for the first layer. Set this to zero to " "disable bed temperature control commands in the output." @@ -6550,7 +7814,7 @@ msgstr "" "첫 번째 레이어에 대한 빌드 플레이트 온도를 가열. 이 값을 0으로 설정하면 출력" "에서 ​​베드 온도 제어 명령을 비활성화합니다." -#: src/libslic3r/PrintConfig.cpp:856 +#: src/libslic3r/PrintConfig.cpp:879 msgid "" "Set this to a non-zero value to set a manual extrusion width for first " "layer. You can use this to force fatter extrudates for better adhesion. If " @@ -6562,7 +7826,7 @@ msgstr "" "수 있습니다. 백분율 (예 : 120 %)로 표현하면 첫 번째 레이어 높이를 기준으로 계" "산됩니다. 0으로 설정하면 기본 압출 폭이 사용됩니다." -#: src/libslic3r/PrintConfig.cpp:868 +#: src/libslic3r/PrintConfig.cpp:892 msgid "" "When printing with very low layer heights, you might still want to print a " "thicker bottom layer to improve adhesion and tolerance for non perfect build " @@ -6574,11 +7838,11 @@ msgstr "" "다. 이것은 절대값 또는 기본 계층 높이에 대한 백분율(예: 150%)로 표시할 수 있" "다." -#: src/libslic3r/PrintConfig.cpp:877 +#: src/libslic3r/PrintConfig.cpp:901 msgid "First layer speed" msgstr "첫 레이어 속도" -#: src/libslic3r/PrintConfig.cpp:878 +#: src/libslic3r/PrintConfig.cpp:902 msgid "" "If expressed as absolute value in mm/s, this speed will be applied to all " "the print moves of the first layer, regardless of their type. If expressed " @@ -6588,7 +7852,7 @@ msgstr "" "인쇄 이동에 적용된다. 백분율(예: 40%)로 표현되는 경우 기본 속도를 스케일링한" "다." -#: src/libslic3r/PrintConfig.cpp:888 +#: src/libslic3r/PrintConfig.cpp:912 msgid "" "Extruder temperature for first layer. If you want to control temperature " "manually during print, set this to zero to disable temperature control " @@ -6597,7 +7861,7 @@ msgstr "" "첫 번째 층의 외부 온도. 인쇄 중에 온도를 수동으로 제어하려면 출력 파일에서 온" "도 제어 명령을 사용하지 않으려면 이 값을 0으로 설정하십시오." -#: src/libslic3r/PrintConfig.cpp:897 +#: src/libslic3r/PrintConfig.cpp:921 msgid "" "Speed for filling small gaps using short zigzag moves. Keep this reasonably " "low to avoid too much shaking and resonance issues. Set zero to disable gaps " @@ -6607,11 +7871,11 @@ msgstr "" "기 위해 이것을 합리적으로 낮게 유지한다. 간격 채우기를 사용하지 않으려면 0을 " "설정하십시오." -#: src/libslic3r/PrintConfig.cpp:905 +#: src/libslic3r/PrintConfig.cpp:929 msgid "Verbose G-code" -msgstr "세부 G-코드" +msgstr "세부 G-code" -#: src/libslic3r/PrintConfig.cpp:906 +#: src/libslic3r/PrintConfig.cpp:930 msgid "" "Enable this to get a commented G-code file, with each line explained by a " "descriptive text. If you print from SD card, the additional weight of the " @@ -6621,46 +7885,46 @@ msgstr "" "을 선택하십시오. 만일 당신이 SD카드로 인쇄한다면, 파일의 추가 무게로 인해 펌" "웨어의 속도가 느려질 수 있다." -#: src/libslic3r/PrintConfig.cpp:913 +#: src/libslic3r/PrintConfig.cpp:937 msgid "G-code flavor" msgstr "G-code 형식" -#: src/libslic3r/PrintConfig.cpp:914 +#: src/libslic3r/PrintConfig.cpp:938 msgid "" "Some G/M-code commands, including temperature control and others, are not " "universal. Set this option to your printer's firmware to get a compatible " -"output. The \"No extrusion\" flavor prevents Slic3r from exporting any " +"output. The \"No extrusion\" flavor prevents PrusaSlicer from exporting any " "extrusion value at all." msgstr "" -"온도 조절 등을 포함한 일부 G/M-코드 명령은 보편적이지 않습니다. 호환되는 출력" -"을 얻으려면 보드에 적제된 프린터의 펌웨어로 설정하십시오. \"압출 없음\" 형식" -"은 Slic3r가 어떠한 압출 값도 출력하지 못하게 합니다." +"온도 제어 및 기타 를 포함한 일부 G/M 코드 명령은 범용이 아닙니다. 호환되는 출" +"력을 얻으려면 프린터의 펌웨어로 이 옵션을 설정합니다. \"돌출 없음\" 맛은 " +"PrusaSlicer가 압출 값을 전혀 내보내지 못하게 합니다." -#: src/libslic3r/PrintConfig.cpp:937 +#: src/libslic3r/PrintConfig.cpp:961 msgid "No extrusion" msgstr "압출 없음" -#: src/libslic3r/PrintConfig.cpp:942 +#: src/libslic3r/PrintConfig.cpp:966 msgid "Label objects" -msgstr "레이블 개체" +msgstr "레이블 객체(object)" -#: src/libslic3r/PrintConfig.cpp:943 +#: src/libslic3r/PrintConfig.cpp:967 msgid "" "Enable this to add comments into the G-Code labeling print moves with what " "object they belong to, which is useful for the Octoprint CancelObject " "plugin. This settings is NOT compatible with Single Extruder Multi Material " "setup and Wipe into Object / Wipe into Infill." msgstr "" -"이 기능을 사용 하 여 G 코드 레이블 인쇄에 주석이 속해 있는 객체와 함께 이동 " -"하 여 주석을 추가할 수 있으며,이는 옥 토 프린트 캔 커 작업 플러그인에 유용 합" -"니다. 이 설정은 단일 압출 기 다중 재료 설정과 호환 되지 않으며 객체를 닦아 " -"내 고 채우기로 닦습니다." +"이 기능을 사용 하 여 G 코드 레이블 인쇄에 주석이 속해 있는 객체(object)와 함" +"께 이동 하 여 주석을 추가할 수 있으며,이는 옥 토 프린트 캔 커 작업 플러그인" +"에 유용 합니다. 이 설정은 단일 압출 기 다중 재료 설정과 호환 되지 않으며 객체" +"(object)를 닦아 내 고 채우기로 닦습니다." -#: src/libslic3r/PrintConfig.cpp:950 +#: src/libslic3r/PrintConfig.cpp:974 msgid "High extruder current on filament swap" msgstr "필라멘트스왑에 높은 압출 기 전류" -#: src/libslic3r/PrintConfig.cpp:951 +#: src/libslic3r/PrintConfig.cpp:975 msgid "" "It may be beneficial to increase the extruder motor current during the " "filament exchange sequence to allow for rapid ramming feed rates and to " @@ -6670,7 +7934,7 @@ msgstr "" "는 빠른 래밍 공급 속도를 가능 하게하고, 불규칙한 모양의 필라멘트를 로딩할때 " "저항을 극복하기 위한것이다." -#: src/libslic3r/PrintConfig.cpp:959 +#: src/libslic3r/PrintConfig.cpp:983 msgid "" "This is the acceleration your printer will use for infill. Set zero to " "disable acceleration control for infill." @@ -6678,11 +7942,11 @@ msgstr "" "이것은 당신 프린터의 채움 가속력이다. 주입에 대한 가속 제어를 비활성화하려면 " "0을 설정하십시오." -#: src/libslic3r/PrintConfig.cpp:967 +#: src/libslic3r/PrintConfig.cpp:991 msgid "Combine infill every" -msgstr "다음 시간마다 결합" +msgstr "다음 레이어마다 결합" -#: src/libslic3r/PrintConfig.cpp:969 +#: src/libslic3r/PrintConfig.cpp:993 msgid "" "This feature allows to combine infill and speed up your print by extruding " "thicker infill layers while preserving thin perimeters, thus accuracy." @@ -6690,19 +7954,19 @@ msgstr "" "이 기능은 인필을 결합하고 얇은 주변기기를 보존하면서 두꺼운 인필 층을 압출하" "여 인쇄 속도를 높일 수 있도록 하여 정확도를 높인다." -#: src/libslic3r/PrintConfig.cpp:972 +#: src/libslic3r/PrintConfig.cpp:996 msgid "Combine infill every n layers" msgstr "모든 n개 층을 채우기 위해 결합" -#: src/libslic3r/PrintConfig.cpp:978 +#: src/libslic3r/PrintConfig.cpp:1002 msgid "Infill extruder" msgstr "채움(Infill) 익스트루더" -#: src/libslic3r/PrintConfig.cpp:980 +#: src/libslic3r/PrintConfig.cpp:1004 msgid "The extruder to use when printing infill." msgstr "채움으로 사용할 익스트루더." -#: src/libslic3r/PrintConfig.cpp:988 +#: src/libslic3r/PrintConfig.cpp:1012 msgid "" "Set this to a non-zero value to set a manual extrusion width for infill. If " "left zero, default extrusion width will be used if set, otherwise 1.125 x " @@ -6712,25 +7976,25 @@ msgid "" msgstr "" "채움에 수동 압출 폭을 설정하려면이 값을 0이 아닌 값으로 설정합니다. 0으로 설" "정하면 설정된 경우 기본 압출 폭이 사용되고 그렇지 않으면 1.125 x 노즐 직경이 " -"사용됩니다. 채움 속도를 높이고 부품을 더 강하게 만들려면보다 큰 압출 성형물" -"을 사용하는 것이 좋습니다. 백분율 (예 : 90 %)로 표현하면 레이어 높이를 기준으" -"로 계산됩니다." +"사용됩니다. 채움 속도를 높이고 부품(Part)을 더 강하게 만들려면보다 큰 압출 성" +"형물을 사용하는 것이 좋습니다. 백분율 (예 : 90 %)로 표현하면 레이어 높이를 기" +"준으로 계산됩니다." -#: src/libslic3r/PrintConfig.cpp:997 +#: src/libslic3r/PrintConfig.cpp:1022 msgid "Infill before perimeters" msgstr "둘레보다 앞쪽에 채움" -#: src/libslic3r/PrintConfig.cpp:998 +#: src/libslic3r/PrintConfig.cpp:1023 msgid "" "This option will switch the print order of perimeters and infill, making the " "latter first." msgstr "이 옵션은 외부출력과 채움 인쇄 순서를 바꾸어, 후자를 먼저 만든다." -#: src/libslic3r/PrintConfig.cpp:1003 +#: src/libslic3r/PrintConfig.cpp:1028 msgid "Only infill where needed" -msgstr "필요한 경우 채음" +msgstr "필요한 경우 채움" -#: src/libslic3r/PrintConfig.cpp:1005 +#: src/libslic3r/PrintConfig.cpp:1030 msgid "" "This option will limit infill to the areas actually needed for supporting " "ceilings (it will act as internal support material). If enabled, slows down " @@ -6740,11 +8004,11 @@ msgstr "" "을 할 것이다). 활성화된 경우 관련된 여러 번의 점검으로 인해 G-code 생성 속도" "를 늦춰라." -#: src/libslic3r/PrintConfig.cpp:1012 +#: src/libslic3r/PrintConfig.cpp:1037 msgid "Infill/perimeters overlap" msgstr "채움/둘레 겹침(perimeters overlap)" -#: src/libslic3r/PrintConfig.cpp:1014 +#: src/libslic3r/PrintConfig.cpp:1039 msgid "" "This setting applies an additional overlap between infill and perimeters for " "better bonding. Theoretically this shouldn't be needed, but backlash might " @@ -6755,23 +8019,23 @@ msgstr "" "론적으로 이것은 필요하지 않아야하지만 백래시가 갭을 유발할 수 있습니다. 백분" "율 (예 : 15 %)로 표시되는 경우 경계 압출 폭을 기준으로 계산됩니다." -#: src/libslic3r/PrintConfig.cpp:1025 +#: src/libslic3r/PrintConfig.cpp:1050 msgid "Speed for printing the internal fill. Set to zero for auto." msgstr "내부 채우기 인쇄 속도. 자동으로 0으로 설정하십시오." -#: src/libslic3r/PrintConfig.cpp:1033 +#: src/libslic3r/PrintConfig.cpp:1058 msgid "Inherits profile" msgstr "프로필 상속" -#: src/libslic3r/PrintConfig.cpp:1034 +#: src/libslic3r/PrintConfig.cpp:1059 msgid "Name of the profile, from which this profile inherits." msgstr "이 프로파일이 상속되는 프로파일의 이름." -#: src/libslic3r/PrintConfig.cpp:1047 +#: src/libslic3r/PrintConfig.cpp:1072 msgid "Interface shells" -msgstr "인터페이스 셸(shells)" +msgstr "접점 셸(shells)" -#: src/libslic3r/PrintConfig.cpp:1048 +#: src/libslic3r/PrintConfig.cpp:1073 msgid "" "Force the generation of solid shells between adjacent materials/volumes. " "Useful for multi-extruder prints with translucent materials or manual " @@ -6780,7 +8044,7 @@ msgstr "" "인접 재료/볼륨 사이에 고체 쉘 생성을 강제하십시오. 반투명 재료 또는 수동 수용" "성 서포트 재료를 사용한 다중 압ㅊ기 인쇄에 유용함." -#: src/libslic3r/PrintConfig.cpp:1057 +#: src/libslic3r/PrintConfig.cpp:1082 msgid "" "This custom code is inserted at every layer change, right after the Z move " "and before the extruder moves to the first layer point. Note that you can " @@ -6792,11 +8056,11 @@ msgstr "" "[layer_num] 및 [layer_z]에 자리 표시자 변수를 사용할 수 있다는 점에 유의하십" "시오." -#: src/libslic3r/PrintConfig.cpp:1068 +#: src/libslic3r/PrintConfig.cpp:1093 msgid "Supports remaining times" msgstr "남은 시간 지원" -#: src/libslic3r/PrintConfig.cpp:1069 +#: src/libslic3r/PrintConfig.cpp:1094 msgid "" "Emit M73 P[percent printed] R[remaining time in minutes] at 1 minute " "intervals into the G-code to let the firmware show accurate remaining time. " @@ -6808,168 +8072,168 @@ msgstr "" "웨어는 M73를 인식 하 고 있습니다. 또한 i3 MK3 펌웨어는 자동 모드에서 M73 Qxx " "Sxx를 지원 합니다." -#: src/libslic3r/PrintConfig.cpp:1077 +#: src/libslic3r/PrintConfig.cpp:1102 msgid "Supports stealth mode" msgstr "자동 모드 지원" -#: src/libslic3r/PrintConfig.cpp:1078 +#: src/libslic3r/PrintConfig.cpp:1103 msgid "The firmware supports stealth mode" msgstr "펌웨어는 스텔스 모드를 지원 합니다" -#: src/libslic3r/PrintConfig.cpp:1102 +#: src/libslic3r/PrintConfig.cpp:1127 msgid "Maximum feedrate X" msgstr "최대 이송 속도 X" -#: src/libslic3r/PrintConfig.cpp:1103 +#: src/libslic3r/PrintConfig.cpp:1128 msgid "Maximum feedrate Y" msgstr "최대 이송 속도 Y" -#: src/libslic3r/PrintConfig.cpp:1104 +#: src/libslic3r/PrintConfig.cpp:1129 msgid "Maximum feedrate Z" msgstr "최대 이송 속도 Z" -#: src/libslic3r/PrintConfig.cpp:1105 +#: src/libslic3r/PrintConfig.cpp:1130 msgid "Maximum feedrate E" msgstr "최대 이송 속도 E" -#: src/libslic3r/PrintConfig.cpp:1108 +#: src/libslic3r/PrintConfig.cpp:1133 msgid "Maximum feedrate of the X axis" msgstr "X 축의 최대 이송 속도" -#: src/libslic3r/PrintConfig.cpp:1109 +#: src/libslic3r/PrintConfig.cpp:1134 msgid "Maximum feedrate of the Y axis" msgstr "Y 축의 최대 이송 속도" -#: src/libslic3r/PrintConfig.cpp:1110 +#: src/libslic3r/PrintConfig.cpp:1135 msgid "Maximum feedrate of the Z axis" msgstr "Z 축의 최대 이송 속도" -#: src/libslic3r/PrintConfig.cpp:1111 +#: src/libslic3r/PrintConfig.cpp:1136 msgid "Maximum feedrate of the E axis" msgstr "E 축의 최대 이송 속도" -#: src/libslic3r/PrintConfig.cpp:1120 +#: src/libslic3r/PrintConfig.cpp:1145 msgid "Maximum acceleration X" msgstr "최대 가속도 X" -#: src/libslic3r/PrintConfig.cpp:1121 +#: src/libslic3r/PrintConfig.cpp:1146 msgid "Maximum acceleration Y" msgstr "최대 가속도 Y" -#: src/libslic3r/PrintConfig.cpp:1122 +#: src/libslic3r/PrintConfig.cpp:1147 msgid "Maximum acceleration Z" msgstr "최대 가속 Z" -#: src/libslic3r/PrintConfig.cpp:1123 +#: src/libslic3r/PrintConfig.cpp:1148 msgid "Maximum acceleration E" msgstr "최대 가속 E" -#: src/libslic3r/PrintConfig.cpp:1126 +#: src/libslic3r/PrintConfig.cpp:1151 msgid "Maximum acceleration of the X axis" msgstr "X 축의 최대 가속도" -#: src/libslic3r/PrintConfig.cpp:1127 +#: src/libslic3r/PrintConfig.cpp:1152 msgid "Maximum acceleration of the Y axis" msgstr "Y 축의 최대 가속도" -#: src/libslic3r/PrintConfig.cpp:1128 +#: src/libslic3r/PrintConfig.cpp:1153 msgid "Maximum acceleration of the Z axis" msgstr "Z 축의 최대 가속도" -#: src/libslic3r/PrintConfig.cpp:1129 +#: src/libslic3r/PrintConfig.cpp:1154 msgid "Maximum acceleration of the E axis" msgstr "E 축의 최대 가속도" -#: src/libslic3r/PrintConfig.cpp:1138 +#: src/libslic3r/PrintConfig.cpp:1163 msgid "Maximum jerk X" msgstr "최대 저크(jerk) X" -#: src/libslic3r/PrintConfig.cpp:1139 +#: src/libslic3r/PrintConfig.cpp:1164 msgid "Maximum jerk Y" msgstr "최대 저크(jerk) Y" -#: src/libslic3r/PrintConfig.cpp:1140 +#: src/libslic3r/PrintConfig.cpp:1165 msgid "Maximum jerk Z" msgstr "최대 저크(jerk) Z" -#: src/libslic3r/PrintConfig.cpp:1141 +#: src/libslic3r/PrintConfig.cpp:1166 msgid "Maximum jerk E" msgstr "최대 저크(jerk) E" -#: src/libslic3r/PrintConfig.cpp:1144 +#: src/libslic3r/PrintConfig.cpp:1169 msgid "Maximum jerk of the X axis" msgstr "X축 최대 저크(jerk)" -#: src/libslic3r/PrintConfig.cpp:1145 +#: src/libslic3r/PrintConfig.cpp:1170 msgid "Maximum jerk of the Y axis" msgstr "Y축 최대 저크는(jerk)" -#: src/libslic3r/PrintConfig.cpp:1146 +#: src/libslic3r/PrintConfig.cpp:1171 msgid "Maximum jerk of the Z axis" msgstr "Z축 최대 저크(jerk)" -#: src/libslic3r/PrintConfig.cpp:1147 +#: src/libslic3r/PrintConfig.cpp:1172 msgid "Maximum jerk of the E axis" msgstr "E축 최대 저크(jerk)" -#: src/libslic3r/PrintConfig.cpp:1158 +#: src/libslic3r/PrintConfig.cpp:1183 msgid "Minimum feedrate when extruding" msgstr "압출시 최소 공급 속도" -#: src/libslic3r/PrintConfig.cpp:1160 +#: src/libslic3r/PrintConfig.cpp:1185 msgid "Minimum feedrate when extruding (M205 S)" msgstr "압출 시 최소 이송 속도 (M205 S)" -#: src/libslic3r/PrintConfig.cpp:1169 +#: src/libslic3r/PrintConfig.cpp:1194 msgid "Minimum travel feedrate" msgstr "최소 이송 속도" -#: src/libslic3r/PrintConfig.cpp:1171 +#: src/libslic3r/PrintConfig.cpp:1196 msgid "Minimum travel feedrate (M205 T)" msgstr "최소 이동 이송 속도 (M205 T)" -#: src/libslic3r/PrintConfig.cpp:1180 +#: src/libslic3r/PrintConfig.cpp:1205 msgid "Maximum acceleration when extruding" msgstr "압출시 최대 가속도" -#: src/libslic3r/PrintConfig.cpp:1182 +#: src/libslic3r/PrintConfig.cpp:1207 msgid "Maximum acceleration when extruding (M204 S)" msgstr "압출 시 최대 가속도 (M204 S)" -#: src/libslic3r/PrintConfig.cpp:1191 +#: src/libslic3r/PrintConfig.cpp:1216 msgid "Maximum acceleration when retracting" msgstr "리트렉션 최대 가속도" -#: src/libslic3r/PrintConfig.cpp:1193 +#: src/libslic3r/PrintConfig.cpp:1218 msgid "Maximum acceleration when retracting (M204 T)" msgstr "후퇴 시 최대 가속도 (M204 T)" -#: src/libslic3r/PrintConfig.cpp:1201 src/libslic3r/PrintConfig.cpp:1210 +#: src/libslic3r/PrintConfig.cpp:1226 src/libslic3r/PrintConfig.cpp:1235 msgid "Max" msgstr "최대" -#: src/libslic3r/PrintConfig.cpp:1202 +#: src/libslic3r/PrintConfig.cpp:1227 msgid "This setting represents the maximum speed of your fan." msgstr "이 설정은 팬의 최대 속도를 나타냅니다." -#: src/libslic3r/PrintConfig.cpp:1211 -#, c-format +#: src/libslic3r/PrintConfig.cpp:1236 +#, no-c-format msgid "" "This is the highest printable layer height for this extruder, used to cap " "the variable layer height and support layer height. Maximum recommended " "layer height is 75% of the extrusion width to achieve reasonable inter-layer " "adhesion. If set to 0, layer height is limited to 75% of the nozzle diameter." msgstr "" -"이것은이 익스트루더의 가장 높은 인쇄 가능 층 높이이며, 가변 층 높이 및 지지" +"이것은 이 익스트루더의 가장 높은 인쇄 가능 층 높이이며, 가변 층 높이 및 지지" "층 높이를 캡하는 데 사용됩니다. 합당한 층간 접착력을 얻기 위해 최대 권장 높이" "는 압출 폭의 75% of 입니다. 0으로 설정하면 층 높이가 노즐 지름의 75% of로 제" "한됩니다." -#: src/libslic3r/PrintConfig.cpp:1221 +#: src/libslic3r/PrintConfig.cpp:1246 msgid "Max print speed" msgstr "최대 프린트 속도" -#: src/libslic3r/PrintConfig.cpp:1222 +#: src/libslic3r/PrintConfig.cpp:1247 msgid "" "When setting other speed settings to 0 Slic3r will autocalculate the optimal " "speed in order to keep constant extruder pressure. This experimental setting " @@ -6979,18 +8243,18 @@ msgstr "" "의 속도를 자동 계산한다. 이 실험 설정은 허용할 최대 인쇄 속도를 설정하는 데 " "사용된다." -#: src/libslic3r/PrintConfig.cpp:1232 +#: src/libslic3r/PrintConfig.cpp:1257 msgid "" "This experimental setting is used to set the maximum volumetric speed your " "extruder supports." msgstr "" "이 실험 설정은 압출기가 지원하는 최대 체적 속도를 설정하기 위해 사용된다." -#: src/libslic3r/PrintConfig.cpp:1241 +#: src/libslic3r/PrintConfig.cpp:1266 msgid "Max volumetric slope positive" msgstr "최대 체적 기울기 양" -#: src/libslic3r/PrintConfig.cpp:1242 src/libslic3r/PrintConfig.cpp:1253 +#: src/libslic3r/PrintConfig.cpp:1267 src/libslic3r/PrintConfig.cpp:1278 msgid "" "This experimental setting is used to limit the speed of change in extrusion " "rate. A value of 1.8 mm³/s² ensures, that a change from the extrusion rate " @@ -7001,23 +8265,23 @@ msgstr "" "1.8mm3/s(0.45mm 압출 폭, 0.2mm 압출 높이, 공급 속도 20mm/s)에서 5.4mm3/s(공" "급 속도 60mm/s)로 변경하는 데 최소 2초 이상 걸린다." -#: src/libslic3r/PrintConfig.cpp:1246 src/libslic3r/PrintConfig.cpp:1257 +#: src/libslic3r/PrintConfig.cpp:1271 src/libslic3r/PrintConfig.cpp:1282 msgid "mm³/s²" msgstr "mm³/s²" -#: src/libslic3r/PrintConfig.cpp:1252 +#: src/libslic3r/PrintConfig.cpp:1277 msgid "Max volumetric slope negative" msgstr "최대 체적 기울기 음수" -#: src/libslic3r/PrintConfig.cpp:1264 src/libslic3r/PrintConfig.cpp:1273 +#: src/libslic3r/PrintConfig.cpp:1289 src/libslic3r/PrintConfig.cpp:1298 msgid "Min" msgstr "최소" -#: src/libslic3r/PrintConfig.cpp:1265 +#: src/libslic3r/PrintConfig.cpp:1290 msgid "This setting represents the minimum PWM your fan needs to work." msgstr "이 설정은 최소 PWM팬이 활동하는데 필요한를 나타냅니다." -#: src/libslic3r/PrintConfig.cpp:1274 +#: src/libslic3r/PrintConfig.cpp:1299 msgid "" "This is the lowest printable layer height for this extruder and limits the " "resolution for variable layer height. Typical values are between 0.05 mm and " @@ -7026,32 +8290,32 @@ msgstr "" "이것은 이 압출기에 대한 가장 낮은 인쇄 가능한 층 높이이고 가변 층 높이에 대" "한 분해능을 제한한다. 대표적인 값은 0.05mm와 0.1mm이다." -#: src/libslic3r/PrintConfig.cpp:1282 +#: src/libslic3r/PrintConfig.cpp:1307 msgid "Min print speed" msgstr "최소 인쇄 속도" -#: src/libslic3r/PrintConfig.cpp:1283 +#: src/libslic3r/PrintConfig.cpp:1308 msgid "Slic3r will not scale speed down below this speed." msgstr "Slic3r는 이 속도 이하로 속도를 낮추지 않을 것이다." -#: src/libslic3r/PrintConfig.cpp:1290 +#: src/libslic3r/PrintConfig.cpp:1315 msgid "Minimal filament extrusion length" msgstr "최소 필라멘트 압출 길이" -#: src/libslic3r/PrintConfig.cpp:1291 +#: src/libslic3r/PrintConfig.cpp:1316 msgid "" "Generate no less than the number of skirt loops required to consume the " "specified amount of filament on the bottom layer. For multi-extruder " "machines, this minimum applies to each extruder." msgstr "" "하단 레이어에서 지정된 양의 필라멘트를 사용하는 데 필요한 스커트 루프의 수 이" -"상으로 생성한다. 멀티 익스트루더의 경우, 이 최소값은 각 추가기기에 적용된다." +"상으로 생성한다. 다중 익스트루더의 경우, 이 최소값은 각 추가기기에 적용된다." -#: src/libslic3r/PrintConfig.cpp:1300 +#: src/libslic3r/PrintConfig.cpp:1325 msgid "Configuration notes" msgstr "구성 노트" -#: src/libslic3r/PrintConfig.cpp:1301 +#: src/libslic3r/PrintConfig.cpp:1326 msgid "" "You can put here your personal notes. This text will be added to the G-code " "header comments." @@ -7059,16 +8323,16 @@ msgstr "" "여기에 개인 노트를 넣을 수 있다. 이 텍스트는 G-code 헤더 코멘트에 추가될 것이" "다." -#: src/libslic3r/PrintConfig.cpp:1311 +#: src/libslic3r/PrintConfig.cpp:1336 msgid "" "This is the diameter of your extruder nozzle (for example: 0.5, 0.35 etc.)" -msgstr "이 지름은 엑스트루더 노즐의 직경이다(예: 0.5, 0.35 등)." +msgstr "이 지름은 익스트루더 노즐의 직경이다(예: 0.5, 0.35 등)." -#: src/libslic3r/PrintConfig.cpp:1316 +#: src/libslic3r/PrintConfig.cpp:1341 msgid "Host Type" msgstr "호스트 유형" -#: src/libslic3r/PrintConfig.cpp:1317 +#: src/libslic3r/PrintConfig.cpp:1342 msgid "" "Slic3r can upload G-code files to a printer host. This field must contain " "the kind of the host." @@ -7076,11 +8340,11 @@ msgstr "" "Slic3r는 프린터 호스트에 G 코드 파일을 업로드할 수 있습니다. 이 필드에는 호스" "트의 종류가 포함 되어야 합니다." -#: src/libslic3r/PrintConfig.cpp:1328 +#: src/libslic3r/PrintConfig.cpp:1357 msgid "Only retract when crossing perimeters" msgstr "둘레를 횡단 할 때만 수축" -#: src/libslic3r/PrintConfig.cpp:1329 +#: src/libslic3r/PrintConfig.cpp:1358 msgid "" "Disables retraction when the travel path does not exceed the upper layer's " "perimeters (and thus any ooze will be probably invisible)." @@ -7088,7 +8352,7 @@ msgstr "" "이동 경로가 상위 레이어의 경계를 초과하지 않는 경우 리트랙션을 비활성화합니" "다. 따라서 모든 오즈가 보이지 않습니다." -#: src/libslic3r/PrintConfig.cpp:1336 +#: src/libslic3r/PrintConfig.cpp:1365 msgid "" "This option will drop the temperature of the inactive extruders to prevent " "oozing. It will enable a tall skirt automatically and move extruders outside " @@ -7098,11 +8362,11 @@ msgstr "" "변경할 때 키가 큰 스커트를 자동으로 사용하고 스커트 외부로 압출기를 이동합니" "다." -#: src/libslic3r/PrintConfig.cpp:1343 +#: src/libslic3r/PrintConfig.cpp:1372 msgid "Output filename format" msgstr "출력 파일이름 형식" -#: src/libslic3r/PrintConfig.cpp:1344 +#: src/libslic3r/PrintConfig.cpp:1373 msgid "" "You can use all configuration options as variables inside this template. For " "example: [layer_height], [fill_density] etc. You can also use [timestamp], " @@ -7113,11 +8377,11 @@ msgstr "" "[layer_height], [fill_density] 등 또한 [타임 스탬프], [연도], [월], [일], [시" "간], [input_filename], [input_filename_base]을 사용할 수 있습니다." -#: src/libslic3r/PrintConfig.cpp:1353 +#: src/libslic3r/PrintConfig.cpp:1382 msgid "Detect bridging perimeters" msgstr "브릿 징 경계선 감지" -#: src/libslic3r/PrintConfig.cpp:1355 +#: src/libslic3r/PrintConfig.cpp:1384 msgid "" "Experimental option to adjust flow for overhangs (bridge flow will be used), " "to apply bridge speed to them and enable fan." @@ -7125,11 +8389,11 @@ msgstr "" "오버행에 대한 유량을 조정하는 실험 옵션 (브리지 흐름(flow)이 사용됨)에 브릿" "지 속도를 적용하고 팬을 활성화합니다." -#: src/libslic3r/PrintConfig.cpp:1361 +#: src/libslic3r/PrintConfig.cpp:1390 msgid "Filament parking position" msgstr "필라멘트 멈춤 위치" -#: src/libslic3r/PrintConfig.cpp:1362 +#: src/libslic3r/PrintConfig.cpp:1391 msgid "" "Distance of the extruder tip from the position where the filament is parked " "when unloaded. This should match the value in printer firmware." @@ -7137,11 +8401,11 @@ msgstr "" "언로딩시 필라멘트 위치에서 압출기 팁의 거리. 이 값은 프린터 펌웨어의 값과 일" "치해야합니다." -#: src/libslic3r/PrintConfig.cpp:1370 +#: src/libslic3r/PrintConfig.cpp:1399 msgid "Extra loading distance" msgstr "추가 로딩 거리" -#: src/libslic3r/PrintConfig.cpp:1371 +#: src/libslic3r/PrintConfig.cpp:1400 msgid "" "When set to zero, the distance the filament is moved from parking position " "during load is exactly the same as it was moved back during unload. When " @@ -7152,12 +8416,12 @@ msgstr "" "이동 한 거리와 동일합니다. 양수이면 음수가 더 많이 로드되고 로드가 음수 인 경" "우 언로드보다 짧습니다." -#: src/libslic3r/PrintConfig.cpp:1379 src/libslic3r/PrintConfig.cpp:1397 -#: src/libslic3r/PrintConfig.cpp:1409 src/libslic3r/PrintConfig.cpp:1419 +#: src/libslic3r/PrintConfig.cpp:1408 src/libslic3r/PrintConfig.cpp:1426 +#: src/libslic3r/PrintConfig.cpp:1439 src/libslic3r/PrintConfig.cpp:1449 msgid "Perimeters" msgstr "둘레" -#: src/libslic3r/PrintConfig.cpp:1380 +#: src/libslic3r/PrintConfig.cpp:1409 msgid "" "This is the acceleration your printer will use for perimeters. A high value " "like 9000 usually gives good results if your hardware is up to the job. Set " @@ -7167,17 +8431,17 @@ msgstr "" "작동하면 좋은 결과를 제공합니다. 주변을 가속 제어하지 않으려면 0으로 설정하십" "시오." -#: src/libslic3r/PrintConfig.cpp:1388 +#: src/libslic3r/PrintConfig.cpp:1417 msgid "Perimeter extruder" -msgstr "주변 익스트루더" +msgstr "가장자리(Perimeter) 익스트루더" -#: src/libslic3r/PrintConfig.cpp:1390 +#: src/libslic3r/PrintConfig.cpp:1419 msgid "" "The extruder to use when printing perimeters and brim. First extruder is 1." msgstr "" "둘레와 가장자리를 인쇄 할 때 사용할 압출기입니다. 첫 번째 압출기는 1입니다." -#: src/libslic3r/PrintConfig.cpp:1399 +#: src/libslic3r/PrintConfig.cpp:1428 msgid "" "Set this to a non-zero value to set a manual extrusion width for perimeters. " "You may want to use thinner extrudates to get more accurate surfaces. If " @@ -7191,12 +8455,12 @@ msgstr "" "직경이 사용됩니다. 백분율 (예 : 200 %)로 표현하면 레이어 높이를 기준으로 계산" "됩니다." -#: src/libslic3r/PrintConfig.cpp:1411 +#: src/libslic3r/PrintConfig.cpp:1441 msgid "" "Speed for perimeters (contours, aka vertical shells). Set to zero for auto." msgstr "둘레의 속도 (등고선, 일명 세로 셸). 자동으로 0으로 설정하십시오." -#: src/libslic3r/PrintConfig.cpp:1421 +#: src/libslic3r/PrintConfig.cpp:1451 msgid "" "This option sets the number of perimeters to generate for each layer. Note " "that Slic3r may increase this number automatically when it detects sloping " @@ -7207,11 +8471,11 @@ msgstr "" "사용하면 더 큰 주변 수를 사용하는 경사면을 감지 할 때 Slic3r이이 수를 자동으" "로 증가시킬 수 있습니다." -#: src/libslic3r/PrintConfig.cpp:1425 +#: src/libslic3r/PrintConfig.cpp:1455 msgid "(minimum)" msgstr "(최소)" -#: src/libslic3r/PrintConfig.cpp:1433 +#: src/libslic3r/PrintConfig.cpp:1463 msgid "" "If you want to process the output G-code through custom scripts, just list " "their absolute paths here. Separate multiple scripts with a semicolon. " @@ -7224,35 +8488,35 @@ msgstr "" "파일의 절대 경로를 첫 번째 인수로 전달되며 환경 변수를 읽음으로써 Slic3r 구" "성 설정에 액세스 할 수 있습니다." -#: src/libslic3r/PrintConfig.cpp:1445 +#: src/libslic3r/PrintConfig.cpp:1475 msgid "Printer type" msgstr "프린터 타입" -#: src/libslic3r/PrintConfig.cpp:1446 +#: src/libslic3r/PrintConfig.cpp:1476 msgid "Type of the printer." msgstr "프린터 유형." -#: src/libslic3r/PrintConfig.cpp:1451 +#: src/libslic3r/PrintConfig.cpp:1481 msgid "Printer notes" msgstr "프린터 노트" -#: src/libslic3r/PrintConfig.cpp:1452 +#: src/libslic3r/PrintConfig.cpp:1482 msgid "You can put your notes regarding the printer here." msgstr "프린터 관련 메모를 여기에 넣을 수 있습니다." -#: src/libslic3r/PrintConfig.cpp:1460 +#: src/libslic3r/PrintConfig.cpp:1490 msgid "Printer vendor" msgstr "제조 회사" -#: src/libslic3r/PrintConfig.cpp:1461 +#: src/libslic3r/PrintConfig.cpp:1491 msgid "Name of the printer vendor." msgstr "프린터 공급 업체의 이름입니다." -#: src/libslic3r/PrintConfig.cpp:1466 +#: src/libslic3r/PrintConfig.cpp:1496 msgid "Printer variant" msgstr "프린터 변형" -#: src/libslic3r/PrintConfig.cpp:1467 +#: src/libslic3r/PrintConfig.cpp:1497 msgid "" "Name of the printer variant. For example, the printer variants may be " "differentiated by a nozzle diameter." @@ -7260,22 +8524,22 @@ msgstr "" "프린터 변종 이름입니다. 예를 들어, 프린터 변형은 노즐 지름으로 구별 될 수 있" "습니다." -#: src/libslic3r/PrintConfig.cpp:1480 +#: src/libslic3r/PrintConfig.cpp:1510 msgid "Raft layers" msgstr "라프트(Raft) 레이어" -#: src/libslic3r/PrintConfig.cpp:1482 +#: src/libslic3r/PrintConfig.cpp:1512 msgid "" "The object will be raised by this number of layers, and support material " "will be generated under it." msgstr "" -"물체는 이 개수의 층에 의해 상승되며, 그 아래에서 서포트 재료가 생성될 것이다." +"개체는 이 개수의 층에 의해 상승되며, 그 아래에서 서포트 재료가 생성될 것이다." -#: src/libslic3r/PrintConfig.cpp:1490 +#: src/libslic3r/PrintConfig.cpp:1520 msgid "Resolution" -msgstr "해결" +msgstr "해상도" -#: src/libslic3r/PrintConfig.cpp:1491 +#: src/libslic3r/PrintConfig.cpp:1521 msgid "" "Minimum detail resolution, used to simplify the input file for speeding up " "the slicing job and reducing memory usage. High-resolution models often " @@ -7287,20 +8551,20 @@ msgstr "" "있는 것보다 더 많은 디테일을 가지고 있다. 단순화를 사용하지 않고 입력에서 전" "체 해상도를 사용하려면 0으로 설정하십시오." -#: src/libslic3r/PrintConfig.cpp:1501 +#: src/libslic3r/PrintConfig.cpp:1531 msgid "Minimum travel after retraction" msgstr "리트랙션 후 최소 이동 거리" -#: src/libslic3r/PrintConfig.cpp:1502 +#: src/libslic3r/PrintConfig.cpp:1532 msgid "" "Retraction is not triggered when travel moves are shorter than this length." msgstr "이동 거리가 이 길이보다 짧으면 리트렉션이 트리거되지 않습니다." -#: src/libslic3r/PrintConfig.cpp:1508 +#: src/libslic3r/PrintConfig.cpp:1538 msgid "Retract amount before wipe" msgstr "닦아 내기 전의 수축량" -#: src/libslic3r/PrintConfig.cpp:1509 +#: src/libslic3r/PrintConfig.cpp:1539 msgid "" "With bowden extruders, it may be wise to do some amount of quick retract " "before doing the wipe movement." @@ -7308,23 +8572,23 @@ msgstr "" "보우 덴 압출기를 사용하면 와이퍼 동작을하기 전에 약간의 빠른 리트랙션 를하는 " "것이 좋습니다." -#: src/libslic3r/PrintConfig.cpp:1516 +#: src/libslic3r/PrintConfig.cpp:1546 msgid "Retract on layer change" msgstr "레이어 변경 후퇴" -#: src/libslic3r/PrintConfig.cpp:1517 +#: src/libslic3r/PrintConfig.cpp:1547 msgid "This flag enforces a retraction whenever a Z move is done." msgstr "이 플래그는 Z 이동이 완료 될 때마다 취소를 강제 실행합니다." -#: src/libslic3r/PrintConfig.cpp:1522 src/libslic3r/PrintConfig.cpp:1530 +#: src/libslic3r/PrintConfig.cpp:1552 src/libslic3r/PrintConfig.cpp:1560 msgid "Length" msgstr "길이" -#: src/libslic3r/PrintConfig.cpp:1523 +#: src/libslic3r/PrintConfig.cpp:1553 msgid "Retraction Length" msgstr "리트랙션 길이" -#: src/libslic3r/PrintConfig.cpp:1524 +#: src/libslic3r/PrintConfig.cpp:1554 msgid "" "When retraction is triggered, filament is pulled back by the specified " "amount (the length is measured on raw filament, before it enters the " @@ -7333,15 +8597,15 @@ msgstr "" "후퇴가 트리거되면 필라멘트가 지정된 양만큼 뒤로 당겨집니다 (길이는 압출기에 " "들어가기 전에 원시 필라멘트에서 측정됩니다)." -#: src/libslic3r/PrintConfig.cpp:1526 src/libslic3r/PrintConfig.cpp:1535 +#: src/libslic3r/PrintConfig.cpp:1556 src/libslic3r/PrintConfig.cpp:1565 msgid "mm (zero to disable)" msgstr "mm (0은 비활성화)" -#: src/libslic3r/PrintConfig.cpp:1531 +#: src/libslic3r/PrintConfig.cpp:1561 msgid "Retraction Length (Toolchange)" msgstr "리트랙션 길이 (툴 체인지)" -#: src/libslic3r/PrintConfig.cpp:1532 +#: src/libslic3r/PrintConfig.cpp:1562 msgid "" "When retraction is triggered before changing tool, filament is pulled back " "by the specified amount (the length is measured on raw filament, before it " @@ -7350,11 +8614,11 @@ msgstr "" "공구를 교체하기 전에 후퇴가 트리거되면 필라멘트가 지정된 양만큼 뒤로 당겨집니" "다 (길이는 압출기에 들어가기 전에 원시 필라멘트에서 측정됩니다)." -#: src/libslic3r/PrintConfig.cpp:1540 +#: src/libslic3r/PrintConfig.cpp:1570 msgid "Lift Z" msgstr "Z축 올림" -#: src/libslic3r/PrintConfig.cpp:1541 +#: src/libslic3r/PrintConfig.cpp:1571 msgid "" "If you set this to a positive value, Z is quickly raised every time a " "retraction is triggered. When using multiple extruders, only the setting for " @@ -7363,15 +8627,15 @@ msgstr "" "이 값을 양수 값으로 설정하면 철회가 트리거 될 때마다 Z가 빠르게 올라갑니다. " "여러 개의 압출기를 사용하는 경우 첫 번째 압출기의 설정 만 고려됩니다." -#: src/libslic3r/PrintConfig.cpp:1548 +#: src/libslic3r/PrintConfig.cpp:1578 msgid "Above Z" msgstr "Z 위" -#: src/libslic3r/PrintConfig.cpp:1549 +#: src/libslic3r/PrintConfig.cpp:1579 msgid "Only lift Z above" msgstr "오직 Z축 위로만" -#: src/libslic3r/PrintConfig.cpp:1550 +#: src/libslic3r/PrintConfig.cpp:1580 msgid "" "If you set this to a positive value, Z lift will only take place above the " "specified absolute Z. You can tune this setting for skipping lift on the " @@ -7380,15 +8644,15 @@ msgstr "" "이것을 양의 값으로 설정하면, Z 리프트는 지정된 절대 Z 위로만 발생한다. 첫 번" "째 층에서 리프트를 건너뛸 수 있도록 이 설정을 조정할 수 있다." -#: src/libslic3r/PrintConfig.cpp:1557 +#: src/libslic3r/PrintConfig.cpp:1587 msgid "Below Z" msgstr "Z 아래" -#: src/libslic3r/PrintConfig.cpp:1558 +#: src/libslic3r/PrintConfig.cpp:1588 msgid "Only lift Z below" msgstr "Z값 아래만" -#: src/libslic3r/PrintConfig.cpp:1559 +#: src/libslic3r/PrintConfig.cpp:1589 msgid "" "If you set this to a positive value, Z lift will only take place below the " "specified absolute Z. You can tune this setting for limiting lift to the " @@ -7397,11 +8661,11 @@ msgstr "" "이것을 양수 값으로 설정하면 Z 리프트가 지정된 절대 Z 아래에서만 발생합니다. " "첫 번째 레이어로 리프트를 제한하기 위해이 설정을 조정할 수 있습니다." -#: src/libslic3r/PrintConfig.cpp:1567 src/libslic3r/PrintConfig.cpp:1575 +#: src/libslic3r/PrintConfig.cpp:1597 src/libslic3r/PrintConfig.cpp:1605 msgid "Extra length on restart" msgstr "재시작시 여분의 길이" -#: src/libslic3r/PrintConfig.cpp:1568 +#: src/libslic3r/PrintConfig.cpp:1598 msgid "" "When the retraction is compensated after the travel move, the extruder will " "push this additional amount of filament. This setting is rarely needed." @@ -7409,7 +8673,7 @@ msgstr "" "이동 후 리트렉셔이 보정되면 익스트루더가 추가 양의 필라멘트를 밀어냅니다. 이 " "설정은 거의 필요하지 않습니다." -#: src/libslic3r/PrintConfig.cpp:1576 +#: src/libslic3r/PrintConfig.cpp:1606 msgid "" "When the retraction is compensated after changing tool, the extruder will " "push this additional amount of filament." @@ -7417,19 +8681,19 @@ msgstr "" "도구를 교환 한 후 리트렉션를 보정하면 익스트루더가 추가 양의 필라멘트를 밀게" "됩니다." -#: src/libslic3r/PrintConfig.cpp:1583 src/libslic3r/PrintConfig.cpp:1584 +#: src/libslic3r/PrintConfig.cpp:1613 src/libslic3r/PrintConfig.cpp:1614 msgid "Retraction Speed" msgstr "리트랙션 속도" -#: src/libslic3r/PrintConfig.cpp:1585 +#: src/libslic3r/PrintConfig.cpp:1615 msgid "The speed for retractions (it only applies to the extruder motor)." msgstr "리트랙션 속도 (익스트루더 모터에만 적용됨)." -#: src/libslic3r/PrintConfig.cpp:1591 src/libslic3r/PrintConfig.cpp:1592 +#: src/libslic3r/PrintConfig.cpp:1621 src/libslic3r/PrintConfig.cpp:1622 msgid "Deretraction Speed" msgstr "감속 속도" -#: src/libslic3r/PrintConfig.cpp:1593 +#: src/libslic3r/PrintConfig.cpp:1623 msgid "" "The speed for loading of a filament into extruder after retraction (it only " "applies to the extruder motor). If left to zero, the retraction speed is " @@ -7438,79 +8702,79 @@ msgstr "" "리트랙션 후 압출기에 필라멘트를 로드하는 속도 (압출기 모터에만 적용됨). 0으" "로 방치하면 리트랙션 속도가 사용됩니다." -#: src/libslic3r/PrintConfig.cpp:1600 +#: src/libslic3r/PrintConfig.cpp:1630 msgid "Seam position" msgstr "재봉선 위치" -#: src/libslic3r/PrintConfig.cpp:1602 +#: src/libslic3r/PrintConfig.cpp:1632 msgid "Position of perimeters starting points." msgstr "둘레의 시작점의 위치." -#: src/libslic3r/PrintConfig.cpp:1608 +#: src/libslic3r/PrintConfig.cpp:1638 msgid "Random" msgstr "무작위" -#: src/libslic3r/PrintConfig.cpp:1609 +#: src/libslic3r/PrintConfig.cpp:1639 msgid "Nearest" msgstr "가장 가까운" -#: src/libslic3r/PrintConfig.cpp:1610 +#: src/libslic3r/PrintConfig.cpp:1640 msgid "Aligned" msgstr "정렬" -#: src/libslic3r/PrintConfig.cpp:1618 +#: src/libslic3r/PrintConfig.cpp:1648 msgid "Direction" msgstr "방향" -#: src/libslic3r/PrintConfig.cpp:1620 +#: src/libslic3r/PrintConfig.cpp:1650 msgid "Preferred direction of the seam" -msgstr "선호하는 심(seam)의 방향" +msgstr "선호하는 재봉선(seam)의 방향" -#: src/libslic3r/PrintConfig.cpp:1621 +#: src/libslic3r/PrintConfig.cpp:1651 msgid "Seam preferred direction" -msgstr "심(Seam) 선호 방향" +msgstr "재봉선(Seam) 선호 방향" -#: src/libslic3r/PrintConfig.cpp:1628 +#: src/libslic3r/PrintConfig.cpp:1658 msgid "Jitter" msgstr "지터(Jitter)" -#: src/libslic3r/PrintConfig.cpp:1630 +#: src/libslic3r/PrintConfig.cpp:1660 msgid "Seam preferred direction jitter" -msgstr "(Seam) 선호 방향 지터(Jitter)" +msgstr "재봉선 선호 방향 지터(Jitter)" -#: src/libslic3r/PrintConfig.cpp:1631 +#: src/libslic3r/PrintConfig.cpp:1661 msgid "Preferred direction of the seam - jitter" msgstr "재봉선 지터의 선호 방향" -#: src/libslic3r/PrintConfig.cpp:1641 +#: src/libslic3r/PrintConfig.cpp:1671 msgid "USB/serial port for printer connection." msgstr "프린터 연결을 위한 USB/시리얼 포트." -#: src/libslic3r/PrintConfig.cpp:1648 +#: src/libslic3r/PrintConfig.cpp:1678 msgid "Serial port speed" msgstr "시리얼 포트 속도" -#: src/libslic3r/PrintConfig.cpp:1649 +#: src/libslic3r/PrintConfig.cpp:1679 msgid "Speed (baud) of USB/serial port for printer connection." msgstr "프린터 연결을 위한 USB/시리얼 포트의 속도(보드)" -#: src/libslic3r/PrintConfig.cpp:1658 +#: src/libslic3r/PrintConfig.cpp:1688 msgid "Distance from object" -msgstr "객체로부터의 거리" +msgstr "객체(object)로부터의 거리" -#: src/libslic3r/PrintConfig.cpp:1659 +#: src/libslic3r/PrintConfig.cpp:1689 msgid "" "Distance between skirt and object(s). Set this to zero to attach the skirt " "to the object(s) and get a brim for better adhesion." msgstr "" -"스커트와 객체 사이의 거리. 스커트를 객체에 부착하고 접착력을 높이기 위해 이" -"를 0으로 설정한다." +"스커트와 객체(object) 사이의 거리. 스커트를 객체(object)에 부착하고 접착력을 " +"높이기 위해 이를 0으로 설정한다." -#: src/libslic3r/PrintConfig.cpp:1666 +#: src/libslic3r/PrintConfig.cpp:1696 msgid "Skirt height" msgstr "스커트(Skirt) 높이" -#: src/libslic3r/PrintConfig.cpp:1667 +#: src/libslic3r/PrintConfig.cpp:1697 msgid "" "Height of skirt expressed in layers. Set this to a tall value to use skirt " "as a shield against drafts." @@ -7518,15 +8782,15 @@ msgstr "" "스커트의 높이 레이어로 표현된다. 이를 높은 값으로 설정하여 스커트를 드래프트" "에 대한 쉴ㄷ로 활용하십시오." -#: src/libslic3r/PrintConfig.cpp:1674 +#: src/libslic3r/PrintConfig.cpp:1704 msgid "Loops (minimum)" msgstr "루프 (최소)" -#: src/libslic3r/PrintConfig.cpp:1675 +#: src/libslic3r/PrintConfig.cpp:1705 msgid "Skirt Loops" msgstr "스커트 루프" -#: src/libslic3r/PrintConfig.cpp:1676 +#: src/libslic3r/PrintConfig.cpp:1706 msgid "" "Number of loops for the skirt. If the Minimum Extrusion Length option is " "set, the number of loops might be greater than the one configured here. Set " @@ -7535,11 +8799,11 @@ msgstr "" "스커트의 루프 수입니다. 최소 압출 길이 옵션을 설정한 경우 여기에 구성된 루프 " "수보다 클 수 있다. 스커트를 완전히 비활성화하려면 이 값을 0으로 설정하십시오." -#: src/libslic3r/PrintConfig.cpp:1684 +#: src/libslic3r/PrintConfig.cpp:1714 msgid "Slow down if layer print time is below" msgstr "레이어 인쇄 시간이 다음과 같은 경우 속도를 낮추십시오" -#: src/libslic3r/PrintConfig.cpp:1685 +#: src/libslic3r/PrintConfig.cpp:1715 msgid "" "If layer print time is estimated below this number of seconds, print moves " "speed will be scaled down to extend duration to this value." @@ -7547,11 +8811,11 @@ msgstr "" "층 인쇄 시간이 이 시간보다 낮게 추정될 경우, 인쇄 이동 속도는 이 값으로 지속" "되도록 축소된다." -#: src/libslic3r/PrintConfig.cpp:1695 +#: src/libslic3r/PrintConfig.cpp:1725 msgid "Small perimeters" msgstr "작은 둘레" -#: src/libslic3r/PrintConfig.cpp:1697 +#: src/libslic3r/PrintConfig.cpp:1727 msgid "" "This separate setting will affect the speed of perimeters having radius <= " "6.5mm (usually holes). If expressed as percentage (for example: 80%) it will " @@ -7561,34 +8825,34 @@ msgstr "" "분율로 표시되는 경우 (예 : 80 %) 위의 속도 설정에서 계산됩니다. 자동으로 0으" "로 설정하십시오." -#: src/libslic3r/PrintConfig.cpp:1707 +#: src/libslic3r/PrintConfig.cpp:1737 msgid "Solid infill threshold area" -msgstr "솔리드 채우기 임계값 영역" +msgstr "솔리드 채우기 임계값" -#: src/libslic3r/PrintConfig.cpp:1709 +#: src/libslic3r/PrintConfig.cpp:1739 msgid "" "Force solid infill for regions having a smaller area than the specified " "threshold." msgstr "" "지정된 한계값보다 작은 영역을 가진 영역에 대해 솔리드 인필을 강제 적용." -#: src/libslic3r/PrintConfig.cpp:1710 +#: src/libslic3r/PrintConfig.cpp:1740 msgid "mm²" msgstr "mm²" -#: src/libslic3r/PrintConfig.cpp:1716 +#: src/libslic3r/PrintConfig.cpp:1746 msgid "Solid infill extruder" msgstr "솔리드 인필 익스트루더" -#: src/libslic3r/PrintConfig.cpp:1718 +#: src/libslic3r/PrintConfig.cpp:1748 msgid "The extruder to use when printing solid infill." msgstr "꽉찬 면을 인쇄할 때 사용하는 익스트루더." -#: src/libslic3r/PrintConfig.cpp:1724 +#: src/libslic3r/PrintConfig.cpp:1754 msgid "Solid infill every" msgstr "솔리드 인필 간격" -#: src/libslic3r/PrintConfig.cpp:1726 +#: src/libslic3r/PrintConfig.cpp:1756 msgid "" "This feature allows to force a solid layer every given number of layers. " "Zero to disable. You can set this to any value (for example 9999); Slic3r " @@ -7599,7 +8863,7 @@ msgstr "" "음. 당신은 이것을 어떤 값으로도 설정할 수 있다(예: 9999). Slic3r는 노즐 직경" "과 층 높이에 따라 결합할 최대 가능한 층 수를 자동으로 선택한다." -#: src/libslic3r/PrintConfig.cpp:1738 +#: src/libslic3r/PrintConfig.cpp:1768 msgid "" "Set this to a non-zero value to set a manual extrusion width for infill for " "solid surfaces. If left zero, default extrusion width will be used if set, " @@ -7610,7 +8874,7 @@ msgstr "" "하십시오. 0인 경우 기본 압출 너비가 사용되며, 그렇지 않으면 1.125 x 노즐 직경" "이 사용된다. 백분율(예: 90%)로 표현되는 경우, 계층 높이에 걸쳐 계산된다." -#: src/libslic3r/PrintConfig.cpp:1748 +#: src/libslic3r/PrintConfig.cpp:1779 msgid "" "Speed for printing solid regions (top/bottom/internal horizontal shells). " "This can be expressed as a percentage (for example: 80%) over the default " @@ -7619,15 +8883,19 @@ msgstr "" "솔리드 영역(상단/하부/내부 수평 셸) 인쇄 속도 이는 위의 기본 주입 속도에 대" "한 백분율(예: 80%)로 표시할 수 있다. 자동을 위해 0으로 설정한다." -#: src/libslic3r/PrintConfig.cpp:1760 +#: src/libslic3r/PrintConfig.cpp:1791 msgid "Number of solid layers to generate on top and bottom surfaces." msgstr "상단 및 하단 표면에 생성할 솔리드 레이어 수입니다." -#: src/libslic3r/PrintConfig.cpp:1766 -msgid "Spiral vase" -msgstr "스파이럴 바이스" +#: src/libslic3r/PrintConfig.cpp:1797 src/libslic3r/PrintConfig.cpp:1798 +msgid "Minimum thickness of a top / bottom shell" +msgstr "상부/하부 쉘의 최소 두께" -#: src/libslic3r/PrintConfig.cpp:1767 +#: src/libslic3r/PrintConfig.cpp:1804 +msgid "Spiral vase" +msgstr "꽃병 모드(Spiral vase)" + +#: src/libslic3r/PrintConfig.cpp:1805 msgid "" "This feature will raise Z gradually while printing a single-walled object in " "order to remove any visible seam. This option requires a single perimeter, " @@ -7635,17 +8903,17 @@ msgid "" "any number of bottom solid layers as well as skirt/brim loops. It won't work " "when printing more than an object." msgstr "" -"이 기능은 단일 벽 물체를 인쇄하는 동안 눈에 보이는 심을 제거하기 위해 Z를 점" -"진적으로 상승시킨다. 이 옵션은 단일 둘레, 주입, 상단 솔리드 레이어 및 지지 재" -"료가 필요하지 않다. 당신은 스커트/브림 루프뿐만 아니라 아래 솔리드 레이어의 " -"수에 상관없이 설정할 수 있다. 그것은 물체보다 더 많이 인쇄할 때는 작동하지 않" -"을 것이다." +"이 기능은 단일 벽 개체를 인쇄하는 동안 눈에 보이는 재봉선을 제거하기 위해 Z" +"를 점진적으로 상승시킨다. 이 옵션은 단일 둘레, 주입, 상단 솔리드 레이어 및 지" +"지 재료가 필요하지 않다. 당신은 스커트/브림 루프뿐만 아니라 아래 솔리드 레이" +"어의 수에 상관없이 설정할 수 있다. 그것은 개체보다 더 많이 인쇄할 때는 작동하" +"지 않을 것이다." -#: src/libslic3r/PrintConfig.cpp:1775 +#: src/libslic3r/PrintConfig.cpp:1813 msgid "Temperature variation" msgstr "온도 변화" -#: src/libslic3r/PrintConfig.cpp:1776 +#: src/libslic3r/PrintConfig.cpp:1814 msgid "" "Temperature difference to be applied when an extruder is not active. Enables " "a full-height \"sacrificial\" skirt on which the nozzles are periodically " @@ -7654,57 +8922,57 @@ msgstr "" "돌출부가 활성화되지 않은 경우 적용되는 온도 차이. 노즐을 주기적으로 닦는 전" "체 높이 \"인공\" 스커트가 가능하다." -#: src/libslic3r/PrintConfig.cpp:1786 +#: src/libslic3r/PrintConfig.cpp:1824 msgid "" "This start procedure is inserted at the beginning, after bed has reached the " "target temperature and extruder just started heating, and before extruder " -"has finished heating. If Slic3r detects M104 or M190 in your custom codes, " -"such commands will not be prepended automatically so you're free to " +"has finished heating. If PrusaSlicer detects M104 or M190 in your custom " +"codes, such commands will not be prepended automatically so you're free to " "customize the order of heating commands and other custom actions. Note that " -"you can use placeholder variables for all Slic3r settings, so you can put a " -"\"M109 S[first_layer_temperature]\" command wherever you want." +"you can use placeholder variables for all PrusaSlicer settings, so you can " +"put a \"M109 S[first_layer_temperature]\" command wherever you want." msgstr "" -"이 시작 절차는 침대가 목표 온도에 도달하고 압출기가 막 가열을 시작한 직후 및 " -"압출기가 가열을 완료하기 전에 처음에 삽입됩니다. Slic3r이 사용자 지정 코드에" -"서 M104 또는 M190을 감지하면 이러한 명령은 자동으로 추가되지 않으므로 가열 명" -"령 및 기타 사용자 지정 동작의 순서를 자유롭게 사용자 지정할 수 있습니다. 모" -"든 Slic3r 설정에 자리 표시 자 변수를 사용할 수 있으므로 원하는 위치에 \"M109 " -"S [first_layer_temperature]\"명령을 넣을 수 있습니다." +"이 시작 절차는 배드가 목표 온도에 도달하고 압출기가 방금 가열을 시작한 후 압" +"출기가열가 완료되기 전에 처음에 삽입됩니다. PrusaSlicer사용자 지정 코드에서 " +"M104 또는 M190을 감지하는 경우 이러한 명령이 자동으로 준비되지 않으므로 가열 " +"명령 및 기타 사용자 지정 작업의 순서를 자유롭게 사용자 지정할 수 있습니다. 모" +"든 PrusaSlicer 설정에 자리 표시자 변수를 사용할 수 있으므로 원하는 위치에 " +"\"M109 S[first_layer_temperature]\" 명령을 넣을 수 있습니다." -#: src/libslic3r/PrintConfig.cpp:1801 +#: src/libslic3r/PrintConfig.cpp:1839 msgid "" "This start procedure is inserted at the beginning, after any printer start " "gcode (and after any toolchange to this filament in case of multi-material " "printers). This is used to override settings for a specific filament. If " -"Slic3r detects M104, M109, M140 or M190 in your custom codes, such commands " -"will not be prepended automatically so you're free to customize the order of " -"heating commands and other custom actions. Note that you can use placeholder " -"variables for all Slic3r settings, so you can put a \"M109 " +"PrusaSlicer detects M104, M109, M140 or M190 in your custom codes, such " +"commands will not be prepended automatically so you're free to customize the " +"order of heating commands and other custom actions. Note that you can use " +"placeholder variables for all PrusaSlicer settings, so you can put a \"M109 " "S[first_layer_temperature]\" command wherever you want. If you have multiple " "extruders, the gcode is processed in extruder order." msgstr "" "이 시작 절차는 프린터가 gcode를 시작한 후(그리고 다중 재질 프린터의 경우 이 " "필라멘트에 대한 도구 변경 후) 처음에 삽입됩니다. 특정 필라멘트에 대한 설정을 " -"재정의하는 데 사용됩니다. Slic3r가 사용자 지정 코드에서 M104, M109, M140 또" -"는 M190을 감지하면 이러한 명령이 자동으로 준비되지 않으므로 가열 명령 및 기" -"타 사용자 지정 작업의 순서를 자유롭게 사용자 지정할 수 있습니다. 모든 Slic3r " -"설정에 자리 표시자 변수를 사용할 수 있으므로 원하는 위치에 \"M109 " -"S[first_layer_temperature]\" 명령을 넣을 수 있습니다. 압출기가 여러 개 있는 " -"경우 gcode는 압출기 순서로 처리됩니다." +"재정의하는 데 사용됩니다. PrusaSlicer가 사용자 지정 코드에서 M104, M109, " +"M140 또는 M190을 감지하면 이러한 명령이 자동으로 준비되지 않으므로 가열 명령 " +"및 기타 사용자 지정 작업의 순서를 자유롭게 사용자 지정할 수 있습니다. 모든 " +"PrusaSlicer 설정에 자리 표시자 변수를 사용할 수 있으므로 원하는 위치에 " +"\"M109 S[first_layer_temperature]\" 명령을 넣을 수 있습니다. 압출기가 여러 " +"개 있는 경우 gcode는 압출기 순서로 처리됩니다." -#: src/libslic3r/PrintConfig.cpp:1817 +#: src/libslic3r/PrintConfig.cpp:1855 msgid "Single Extruder Multi Material" -msgstr "싱글 익스트루더 멀티메터리얼" +msgstr "싱글 익스트루더 다중메터리얼" -#: src/libslic3r/PrintConfig.cpp:1818 +#: src/libslic3r/PrintConfig.cpp:1856 msgid "The printer multiplexes filaments into a single hot end." -msgstr "프린터는 필라멘트를 하나의 핫 엔드에 멀티플렉싱합니다." +msgstr "프린터는 필라멘트를 하나의 핫 엔드에 다중플렉싱합니다." -#: src/libslic3r/PrintConfig.cpp:1823 +#: src/libslic3r/PrintConfig.cpp:1861 msgid "Prime all printing extruders" msgstr "모든 인쇄 압출기 프라임" -#: src/libslic3r/PrintConfig.cpp:1824 +#: src/libslic3r/PrintConfig.cpp:1862 msgid "" "If enabled, all printing extruders will be primed at the front edge of the " "print bed at the start of the print." @@ -7712,19 +8980,34 @@ msgstr "" "활성화 된 경우, 모든 인쇄 압출기는 인쇄 시작시 프린트 베드의 전면 가장자리에 " "프라이밍 됩니다." -#: src/libslic3r/PrintConfig.cpp:1829 +#: src/libslic3r/PrintConfig.cpp:1867 +msgid "No sparse layers (EXPERIMENTAL)" +msgstr "숨겨진 레이어층 없음(실험적)" + +#: src/libslic3r/PrintConfig.cpp:1868 +msgid "" +"If enabled, the wipe tower will not be printed on layers with no " +"toolchanges. On layers with a toolchange, extruder will travel downward to " +"print the wipe tower. User is responsible for ensuring there is no collision " +"with the print." +msgstr "" +"활성화된 경우 도구 변경 없이 레이어에 와이프 타워가 인쇄되지 않습니다. 툴체인" +"지 레이어에서 압출기는 아래쪽으로 이동하여 와이프 타워를 인쇄합니다. 사용자" +"는 인쇄물과 충돌이 없는지 확인합니다." + +#: src/libslic3r/PrintConfig.cpp:1875 msgid "Generate support material" msgstr "서포트 재료 생성" -#: src/libslic3r/PrintConfig.cpp:1831 +#: src/libslic3r/PrintConfig.cpp:1877 msgid "Enable support material generation." msgstr "서포트 재료를 사용합니다." -#: src/libslic3r/PrintConfig.cpp:1835 +#: src/libslic3r/PrintConfig.cpp:1881 msgid "Auto generated supports" msgstr "자동 생성 지원" -#: src/libslic3r/PrintConfig.cpp:1837 +#: src/libslic3r/PrintConfig.cpp:1883 msgid "" "If checked, supports will be generated automatically based on the overhang " "threshold value. If unchecked, supports will be generated inside the " @@ -7734,29 +9017,29 @@ msgstr "" "인란을 선택 하지 않으면 \"서포트 지원 영역\" 볼륨 내 에서만 지원이 생성 됩니" "다." -#: src/libslic3r/PrintConfig.cpp:1843 +#: src/libslic3r/PrintConfig.cpp:1889 msgid "XY separation between an object and its support" -msgstr "물체와 그 서포트 사이 XY 분리" +msgstr "개체와 그 서포트 사이 XY 분리" -#: src/libslic3r/PrintConfig.cpp:1845 +#: src/libslic3r/PrintConfig.cpp:1891 msgid "" "XY separation between an object and its support. If expressed as percentage " "(for example 50%), it will be calculated over external perimeter width." msgstr "" -"객체와 그 서포트 사이의 XY 분리. 백분율 (예 : 50 %)로 표시되는 경우 외부 둘" -"레 너비를 기준으로 계산됩니다." +"객체(object)와 그 서포트 사이의 XY 분리. 백분율 (예 : 50 %)로 표시되는 경우 " +"외부 둘레 너비를 기준으로 계산됩니다." -#: src/libslic3r/PrintConfig.cpp:1855 +#: src/libslic3r/PrintConfig.cpp:1901 msgid "Pattern angle" msgstr "채움 각도" -#: src/libslic3r/PrintConfig.cpp:1857 +#: src/libslic3r/PrintConfig.cpp:1903 msgid "" "Use this setting to rotate the support material pattern on the horizontal " "plane." msgstr "이 설정을 사용하여지지 평면 패턴을 수평면으로 회전시킵니다." -#: src/libslic3r/PrintConfig.cpp:1867 src/libslic3r/PrintConfig.cpp:2531 +#: src/libslic3r/PrintConfig.cpp:1913 src/libslic3r/PrintConfig.cpp:2677 msgid "" "Only create support if it lies on a build plate. Don't create support on a " "print." @@ -7764,32 +9047,32 @@ msgstr "" "그것이 빌드 플레이트에있는 경우에만 지원을 작성하십시오. 인쇄물에 대한 지원" "을 작성하지 마십시오." -#: src/libslic3r/PrintConfig.cpp:1873 +#: src/libslic3r/PrintConfig.cpp:1919 msgid "Contact Z distance" msgstr "Z 거리 문의" -#: src/libslic3r/PrintConfig.cpp:1875 +#: src/libslic3r/PrintConfig.cpp:1921 msgid "" "The vertical distance between object and support material interface. Setting " "this to 0 will also prevent Slic3r from using bridge flow and speed for the " "first object layer." msgstr "" -"물체와 서포트 사이의 수직 거리. 이 값을 0으로 설정하면 Slic3r이 첫 번째 객체 " -"레이어에 브리지 흐름과 속도를 사용하지 못하게됩니다." +"개체와 서포트 사이의 수직 거리. 이 값을 0으로 설정하면 Slic3r이 첫 번째 객체" +"(object) 레이어에 브리지 흐름과 속도를 사용하지 못하게됩니다." -#: src/libslic3r/PrintConfig.cpp:1882 +#: src/libslic3r/PrintConfig.cpp:1928 msgid "0 (soluble)" msgstr "0 (수용성)" -#: src/libslic3r/PrintConfig.cpp:1883 +#: src/libslic3r/PrintConfig.cpp:1929 msgid "0.2 (detachable)" msgstr "0.2 (분리 가능)" -#: src/libslic3r/PrintConfig.cpp:1888 +#: src/libslic3r/PrintConfig.cpp:1934 msgid "Enforce support for the first" msgstr "첫 번째 서포트 더 강화" -#: src/libslic3r/PrintConfig.cpp:1890 +#: src/libslic3r/PrintConfig.cpp:1936 msgid "" "Generate support material for the specified number of layers counting from " "bottom, regardless of whether normal support material is enabled or not and " @@ -7798,17 +9081,17 @@ msgid "" msgstr "" "일반적인 서포트 소재의 활성화 여부와, 각도 임계 값에 관계없이 하단에서부터 세" "어 지정된 레이어 수에 대한지지 자료를 생성합니다. 이것은 빌드 플레이트에 매" -"우 얇거나 부족한 풋 프린트를 가진 물체를 더 많이 부착 할 때 유용합니다." +"우 얇거나 부족한 풋 프린트를 가진 개체를 더 많이 부착 할 때 유용합니다." -#: src/libslic3r/PrintConfig.cpp:1895 +#: src/libslic3r/PrintConfig.cpp:1941 msgid "Enforce support for the first n layers" msgstr "첫 번째 n 개의 레이어에 대한 서포트 강화" -#: src/libslic3r/PrintConfig.cpp:1901 +#: src/libslic3r/PrintConfig.cpp:1947 msgid "Support material/raft/skirt extruder" msgstr "서포트 재료 / 라프트 / 스커트 익스트루더" -#: src/libslic3r/PrintConfig.cpp:1903 +#: src/libslic3r/PrintConfig.cpp:1949 msgid "" "The extruder to use when printing support material, raft and skirt (1+, 0 to " "use the current extruder to minimize tool changes)." @@ -7816,7 +9099,7 @@ msgstr "" "서포트 재료, 라프트 및 스커트를 인쇄 할 때 사용하는 압출기 (도구 변경을 최소" "화하기 위해 현재 압출기를 사용하려면 1+, 0)." -#: src/libslic3r/PrintConfig.cpp:1912 +#: src/libslic3r/PrintConfig.cpp:1958 msgid "" "Set this to a non-zero value to set a manual extrusion width for support " "material. If left zero, default extrusion width will be used if set, " @@ -7827,97 +9110,95 @@ msgstr "" "0으로 설정하면 설정된 경우 기본 압출 폭이 사용되고 그렇지 않으면 노즐 지름이 " "사용됩니다. 백분율 (예 : 90 %)로 표현하면 레이어 높이를 기준으로 계산됩니다." -#: src/libslic3r/PrintConfig.cpp:1920 +#: src/libslic3r/PrintConfig.cpp:1967 msgid "Interface loops" -msgstr "인터페이스 루프" +msgstr "접점 루프" -#: src/libslic3r/PrintConfig.cpp:1922 +#: src/libslic3r/PrintConfig.cpp:1969 msgid "" "Cover the top contact layer of the supports with loops. Disabled by default." msgstr "지지대의 상단 접촉 층을 루프로 덮으십시오. 기본적으로 사용 안 함." -#: src/libslic3r/PrintConfig.cpp:1927 +#: src/libslic3r/PrintConfig.cpp:1974 msgid "Support material/raft interface extruder" -msgstr "서포트 재료/라프트 인터페이스 익스트루더" +msgstr "서포트 재료/라프트 접점 익스트루더" -#: src/libslic3r/PrintConfig.cpp:1929 +#: src/libslic3r/PrintConfig.cpp:1976 msgid "" "The extruder to use when printing support material interface (1+, 0 to use " "the current extruder to minimize tool changes). This affects raft too." msgstr "" -"서포트 재료 인터페이스를 인쇄 할 때 사용할 익스트루더 (도구 변경을 최소화하" -"기 위해 현재 익스트루더를 사용하려면 1+, 0). 이것은 라프트에도 영향을 미칩니" -"다." +"서포트 재료 접점를 인쇄 할 때 사용할 익스트루더 (도구 변경을 최소화하기 위해 " +"현재 익스트루더를 사용하려면 1+, 0). 이것은 라프트에도 영향을 미칩니다." -#: src/libslic3r/PrintConfig.cpp:1936 +#: src/libslic3r/PrintConfig.cpp:1983 msgid "Interface layers" -msgstr "인터페이스 레이어" +msgstr "접점 레이어" -#: src/libslic3r/PrintConfig.cpp:1938 +#: src/libslic3r/PrintConfig.cpp:1985 msgid "" "Number of interface layers to insert between the object(s) and support " "material." -msgstr "객체와 서포트 재료 사이에 삽입할 인터페이스 레이어 수입니다." +msgstr "객체(object)와 서포트 재료 사이에 삽입할 접점 레이어 수입니다." -#: src/libslic3r/PrintConfig.cpp:1945 +#: src/libslic3r/PrintConfig.cpp:1992 msgid "Interface pattern spacing" -msgstr "인터페이스 패턴 간격" +msgstr "접점 패턴 간격" -#: src/libslic3r/PrintConfig.cpp:1947 +#: src/libslic3r/PrintConfig.cpp:1994 msgid "Spacing between interface lines. Set zero to get a solid interface." -msgstr "" -"인터페이스 라인 간 간격. 솔리드 인터페이스를 가져오려면 0을 설정하십시오." +msgstr "접점 라인 간 간격. 솔리드 접점를 가져오려면 0을 설정하십시오." -#: src/libslic3r/PrintConfig.cpp:1956 +#: src/libslic3r/PrintConfig.cpp:2003 msgid "" "Speed for printing support material interface layers. If expressed as " "percentage (for example 50%) it will be calculated over support material " "speed." msgstr "" -"서포트 재료 인터페이스 레이어 인쇄 속도 백분율(예: 50%)로 표현될 경우 서포트 " -"재료 속도에 따라 계산된다." +"서포트 재료 접점 레이어 인쇄 속도 백분율(예: 50%)로 표현될 경우 서포트 재료 " +"속도에 따라 계산된다." -#: src/libslic3r/PrintConfig.cpp:1965 +#: src/libslic3r/PrintConfig.cpp:2012 msgid "Pattern" msgstr "패턴" -#: src/libslic3r/PrintConfig.cpp:1967 +#: src/libslic3r/PrintConfig.cpp:2014 msgid "Pattern used to generate support material." msgstr "서포트 재료를 생성하는 데 사용되는 패턴." -#: src/libslic3r/PrintConfig.cpp:1973 +#: src/libslic3r/PrintConfig.cpp:2020 msgid "Rectilinear grid" msgstr "직선 그리드" -#: src/libslic3r/PrintConfig.cpp:1979 +#: src/libslic3r/PrintConfig.cpp:2026 msgid "Pattern spacing" msgstr "패턴 간격" -#: src/libslic3r/PrintConfig.cpp:1981 +#: src/libslic3r/PrintConfig.cpp:2028 msgid "Spacing between support material lines." msgstr "서포트 재료 라인 사이의 간격." -#: src/libslic3r/PrintConfig.cpp:1990 +#: src/libslic3r/PrintConfig.cpp:2037 msgid "Speed for printing support material." msgstr "서포트 재료를 인쇄하는 속도." -#: src/libslic3r/PrintConfig.cpp:1997 +#: src/libslic3r/PrintConfig.cpp:2044 msgid "Synchronize with object layers" -msgstr "객체 레이어와 동기화" +msgstr "객체(object) 레이어와 동기화" -#: src/libslic3r/PrintConfig.cpp:1999 +#: src/libslic3r/PrintConfig.cpp:2046 msgid "" "Synchronize support layers with the object print layers. This is useful with " "multi-material printers, where the extruder switch is expensive." msgstr "" -"서포트 레이어를 프린트 레이어와 동기화하십시오. 이것은 스위치가 비싼 멀티 메" +"서포트 레이어를 프린트 레이어와 동기화하십시오. 이것은 스위치가 비싼 다중 메" "터리얼 프린터에서 유용하다." -#: src/libslic3r/PrintConfig.cpp:2005 +#: src/libslic3r/PrintConfig.cpp:2052 msgid "Overhang threshold" msgstr "오버행 한계점" -#: src/libslic3r/PrintConfig.cpp:2007 +#: src/libslic3r/PrintConfig.cpp:2054 msgid "" "Support material will not be generated for overhangs whose slope angle (90° " "= vertical) is above the given threshold. In other words, this value " @@ -7929,11 +9210,11 @@ msgstr "" "성되지 않는다. 즉, 이 값은 서포트 재료 없이 인쇄할 수 있는 가장 수평 경사(수" "평면에서 측정됨)를 나타낸다. 자동 감지를 위해 0으로 설정하십시오(권장)." -#: src/libslic3r/PrintConfig.cpp:2019 +#: src/libslic3r/PrintConfig.cpp:2066 msgid "With sheath around the support" msgstr "서포트 주변이나 외부로" -#: src/libslic3r/PrintConfig.cpp:2021 +#: src/libslic3r/PrintConfig.cpp:2068 msgid "" "Add a sheath (a single perimeter line) around the base support. This makes " "the support more reliable, but also more difficult to remove." @@ -7941,7 +9222,7 @@ msgstr "" "기본 서포트 주위에 외장 (단일 주변 선)을 추가하십시오. 이것은 페이스 업을보" "다 신뢰성있게 만들뿐만 아니라 제거하기도 어렵습니다." -#: src/libslic3r/PrintConfig.cpp:2028 +#: src/libslic3r/PrintConfig.cpp:2075 msgid "" "Extruder temperature for layers after the first one. Set this to zero to " "disable temperature control commands in the output." @@ -7949,21 +9230,21 @@ msgstr "" "첫 번째 것 이후에 레이어에 대한 더 낮은 온도. 이 값을 0으로 설정하면 출력에" "서 ​​온도 제어 명령을 비활성화 할 수 있습니다." -#: src/libslic3r/PrintConfig.cpp:2036 +#: src/libslic3r/PrintConfig.cpp:2083 msgid "Detect thin walls" msgstr "얇은 벽(walls) 감지" -#: src/libslic3r/PrintConfig.cpp:2038 +#: src/libslic3r/PrintConfig.cpp:2085 msgid "" "Detect single-width walls (parts where two extrusions don't fit and we need " "to collapse them into a single trace)." msgstr "싱글 너비 벽 (두 부분이 맞지 않는 부분과 무너지는 부분)을 감지합니다." -#: src/libslic3r/PrintConfig.cpp:2044 +#: src/libslic3r/PrintConfig.cpp:2091 msgid "Threads" msgstr "스레드(Threads)" -#: src/libslic3r/PrintConfig.cpp:2045 +#: src/libslic3r/PrintConfig.cpp:2092 msgid "" "Threads are used to parallelize long-running tasks. Optimal threads number " "is slightly above the number of available cores/processors." @@ -7971,22 +9252,22 @@ msgstr "" "스레드는 장기 실행 태스크를 병렬 처리하는 데 사용됩니다. 최적의 스레드 수는 " "사용 가능한 코어 / 프로세서 수보다 약간 높습니다." -#: src/libslic3r/PrintConfig.cpp:2057 +#: src/libslic3r/PrintConfig.cpp:2104 msgid "" -"This custom code is inserted at every extruder change. If you don't leave " -"this empty, you are expected to take care of the toolchange yourself - " -"PrusaSlicer will not output any other G-code to change the filament. You can " -"use placeholder variables for all Slic3r settings as well as " -"[previous_extruder] and [next_extruder], so e.g. the standard toolchange " -"command can be scripted as T[next_extruder]." +"This custom code is inserted before every toolchange. Placeholder variables " +"for all PrusaSlicer settings as well as {previous_extruder} and " +"{next_extruder} can be used. When a tool-changing command which changes to " +"the correct extruder is included (such as T{next_extruder}), PrusaSlicer " +"will emit no other such command. It is therefore possible to script custom " +"behaviour both before and after the toolchange." msgstr "" -"이 사용자 지정 코드는 모든 압출기 변경 시 삽입됩니다. 이 것을 비워 두지 않으" -"면 도구 변경을 직접 처리해야합니다 - PrusaSlicer는 필라멘트를 변경하기 위해 " -"다른 G 코드를 출력하지 않습니다. 모든 Slic3r 설정과 [이전_압출기] 및 " -"[next_extruder]에 대해 자리 표시자 변수를 사용할 수 있으므로 표준 도구 변경 " -"명령을 T[next_extruder]로 스크립팅할 수 있습니다." +"이 사용자 지정 코드는 모든 도구 변경 전에 삽입됩니다. 모든 PrusaSlicer 설정" +"과 {previous_extruder} 및 {next_extruder}에 대한 자리 표시자 변수를 사용할 " +"수 있습니다. 올바른 압출기로 변경되는 도구 변경 명령(예: T{next_extruder})이 " +"포함되면 PrusaSlicer는 다른 명령을 내림차순을 내보릅니다. 따라서 도구 변경 전" +"후에 사용자 지정 동작을 스크립팅할 수 있습니다." -#: src/libslic3r/PrintConfig.cpp:2070 +#: src/libslic3r/PrintConfig.cpp:2117 msgid "" "Set this to a non-zero value to set a manual extrusion width for infill for " "top surfaces. You may want to use thinner extrudates to fill all narrow " @@ -8000,7 +9281,7 @@ msgstr "" "으면 노즐 지름이 사용됩니다. 백분율 (예 : 90 %)로 표현하면 레이어 높이를 기준" "으로 계산됩니다." -#: src/libslic3r/PrintConfig.cpp:2081 +#: src/libslic3r/PrintConfig.cpp:2129 msgid "" "Speed for printing top solid layers (it only applies to the uppermost " "external layers and not to their internal solid layers). You may want to " @@ -8013,23 +9294,37 @@ msgstr "" "전 속도에 대한 백분율 (예 : 80 %)로 나타낼 수 있습니다. 자동으로 0으로 설정하" "십시오." -#: src/libslic3r/PrintConfig.cpp:2096 +#: src/libslic3r/PrintConfig.cpp:2144 msgid "Number of solid layers to generate on top surfaces." msgstr "상단 표면에 생성 할 솔리드 레이어 수입니다." -#: src/libslic3r/PrintConfig.cpp:2097 +#: src/libslic3r/PrintConfig.cpp:2145 msgid "Top solid layers" msgstr "탑 솔리드 레이어" -#: src/libslic3r/PrintConfig.cpp:2103 +#: src/libslic3r/PrintConfig.cpp:2153 +msgid "" +"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." +msgstr "" +"상단 단층의 수는 상단 쉘의 최소 두께를 충족하기 위해 필요한 경우 " +"top_solid_layers 이상으로 증가한다. 이 기능은 가변 레이어 높이로 인쇄할 때 베" +"개 효과를 방지하는 데 유용합니다." + +#: src/libslic3r/PrintConfig.cpp:2156 +msgid "Minimum top shell thickness" +msgstr "최소 상단 쉘 두께" + +#: src/libslic3r/PrintConfig.cpp:2163 msgid "Speed for travel moves (jumps between distant extrusion points)." msgstr "이동 속도 (먼 돌출 점 사이의 점프)." -#: src/libslic3r/PrintConfig.cpp:2111 +#: src/libslic3r/PrintConfig.cpp:2171 msgid "Use firmware retraction" msgstr "펌웨어 철회" -#: src/libslic3r/PrintConfig.cpp:2112 +#: src/libslic3r/PrintConfig.cpp:2172 msgid "" "This experimental setting uses G10 and G11 commands to have the firmware " "handle the retraction. This is only supported in recent Marlin." @@ -8037,11 +9332,11 @@ msgstr "" "이 실험 설정은 G10 및 G11 명령을 사용하여 펌웨어에서 취소를 처리하도록합니" "다. 이것은 최근의 말린에서만 지원됩니다." -#: src/libslic3r/PrintConfig.cpp:2118 +#: src/libslic3r/PrintConfig.cpp:2178 msgid "Use relative E distances" msgstr "상대적인 E 거리 사용" -#: src/libslic3r/PrintConfig.cpp:2119 +#: src/libslic3r/PrintConfig.cpp:2179 msgid "" "If your firmware requires relative E values, check this, otherwise leave it " "unchecked. Most firmwares use absolute values." @@ -8049,11 +9344,11 @@ msgstr "" "펌웨어에 상대 E 값이 필요한 경우이 값을 선택하고, 그렇지 않으면 선택하지 마십" "시오. 대부분의 회사는 절대 값을 사용합니다." -#: src/libslic3r/PrintConfig.cpp:2125 +#: src/libslic3r/PrintConfig.cpp:2185 msgid "Use volumetric E" msgstr "용적 E 사용" -#: src/libslic3r/PrintConfig.cpp:2126 +#: src/libslic3r/PrintConfig.cpp:2186 msgid "" "This experimental setting uses outputs the E values in cubic millimeters " "instead of linear millimeters. If your firmware doesn't already know " @@ -8068,11 +9363,11 @@ msgstr "" "[filament_diameter_0] T0'과 같은 명령을 입력 할 수 있습니다 Slic3r. 이것은 최" "근의 말린에서만 지원됩니다." -#: src/libslic3r/PrintConfig.cpp:2136 +#: src/libslic3r/PrintConfig.cpp:2196 msgid "Enable variable layer height feature" msgstr "가변 레이어 높이 기능 사용" -#: src/libslic3r/PrintConfig.cpp:2137 +#: src/libslic3r/PrintConfig.cpp:2197 msgid "" "Some printers or printer setups may have difficulties printing with a " "variable layer height. Enabled by default." @@ -8080,11 +9375,11 @@ msgstr "" "일부 프린터 또는 프린터 설정은 가변 레이어 높이로 인쇄하는 데 어려움이있을 " "수 있습니다. 기본적으로 사용됩니다." -#: src/libslic3r/PrintConfig.cpp:2143 +#: src/libslic3r/PrintConfig.cpp:2203 msgid "Wipe while retracting" msgstr "수축시 닦아내십시오" -#: src/libslic3r/PrintConfig.cpp:2144 +#: src/libslic3r/PrintConfig.cpp:2204 msgid "" "This flag will move the nozzle while retracting to minimize the possible " "blob on leaky extruders." @@ -8092,19 +9387,19 @@ msgstr "" "이 플래그는 누출된 리트랙싱의 블럽 가능성을 최소화하기 위해 수축하는 동안 노" "즐을 이동시킨다." -#: src/libslic3r/PrintConfig.cpp:2151 +#: src/libslic3r/PrintConfig.cpp:2211 msgid "" "Multi material printers may need to prime or purge extruders on tool " "changes. Extrude the excess material into the wipe tower." msgstr "" -"멀티 메터리알 프린터는 공구 교환 시 익스트루더를 프라이밍하거나 제거해야 할 " +"다중 메터리알 프린터는 공구 교환 시 익스트루더를 프라이밍하거나 제거해야 할 " "수 있다. 과도한 물질을 와이퍼 타워에 돌출시킨다." -#: src/libslic3r/PrintConfig.cpp:2157 +#: src/libslic3r/PrintConfig.cpp:2217 msgid "Purging volumes - load/unload volumes" msgstr "볼륨 삭제 - 볼륨 로드/언로드" -#: src/libslic3r/PrintConfig.cpp:2158 +#: src/libslic3r/PrintConfig.cpp:2218 msgid "" "This vector saves required volumes to change from/to each tool used on the " "wipe tower. These values are used to simplify creation of the full purging " @@ -8114,11 +9409,11 @@ msgstr "" "을 저장 합니다. 이러한 값은 아래의 전체 퍼징 볼륨의 생성을 단순화 하는 데 사" "용 됩니다." -#: src/libslic3r/PrintConfig.cpp:2164 +#: src/libslic3r/PrintConfig.cpp:2224 msgid "Purging volumes - matrix" msgstr "볼륨 삭제 - 행렬" -#: src/libslic3r/PrintConfig.cpp:2165 +#: src/libslic3r/PrintConfig.cpp:2225 msgid "" "This matrix describes volumes (in cubic milimetres) required to purge the " "new filament on the wipe tower for any given pair of tools." @@ -8126,154 +9421,155 @@ msgstr "" "이 매트릭스는 지정 된 도구 쌍에 대해 와이퍼 타워의 새필라멘트를 제거 하는 데 " "필요한 체적 (입방 밀리 미터)을 설명 합니다." -#: src/libslic3r/PrintConfig.cpp:2174 +#: src/libslic3r/PrintConfig.cpp:2234 msgid "Position X" msgstr "X축 위치" -#: src/libslic3r/PrintConfig.cpp:2175 +#: src/libslic3r/PrintConfig.cpp:2235 msgid "X coordinate of the left front corner of a wipe tower" msgstr "와이프 타워의 좌측 전면 모서리의 X 좌표" -#: src/libslic3r/PrintConfig.cpp:2181 +#: src/libslic3r/PrintConfig.cpp:2241 msgid "Position Y" msgstr "Y축 위치" -#: src/libslic3r/PrintConfig.cpp:2182 +#: src/libslic3r/PrintConfig.cpp:2242 msgid "Y coordinate of the left front corner of a wipe tower" msgstr "와이퍼 작동 타워의 좌측 전방 모서리의 Y 좌표" -#: src/libslic3r/PrintConfig.cpp:2189 +#: src/libslic3r/PrintConfig.cpp:2249 msgid "Width of a wipe tower" msgstr "와이퍼 타워 폭" -#: src/libslic3r/PrintConfig.cpp:2195 +#: src/libslic3r/PrintConfig.cpp:2255 msgid "Wipe tower rotation angle" msgstr "와이퍼 타워 회전각도" -#: src/libslic3r/PrintConfig.cpp:2196 +#: src/libslic3r/PrintConfig.cpp:2256 msgid "Wipe tower rotation angle with respect to x-axis." msgstr "X 축에 대해 타워 회전 각도를 닦습니다." -#: src/libslic3r/PrintConfig.cpp:2203 +#: src/libslic3r/PrintConfig.cpp:2263 msgid "Wipe into this object's infill" -msgstr "이 오브젝트의 채우기로 지우기" +msgstr "이 객체(object)의 채우기로 지우기" -#: src/libslic3r/PrintConfig.cpp:2204 +#: src/libslic3r/PrintConfig.cpp:2264 msgid "" "Purging after toolchange will done inside this object's infills. This lowers " "the amount of waste but may result in longer print time due to additional " "travel moves." msgstr "" -"도구 변경 후 제거는 이 개체의 채우기 내부에서 수행 됩니다. 이렇게 하면 낭비 " -"되는 양이 줄어들지만 추가적인 이동으로 인해 인쇄 시간이 길어질 수 있습니다." +"도구 변경 후 제거는 이 객체(object)의 채우기 내부에서 수행 됩니다. 이렇게 하" +"면 낭비 되는 양이 줄어들지만 추가적인 이동으로 인해 인쇄 시간이 길어질 수 있" +"습니다." -#: src/libslic3r/PrintConfig.cpp:2211 +#: src/libslic3r/PrintConfig.cpp:2271 msgid "Wipe into this object" -msgstr "이 개체로 지우기" +msgstr "이 객체(object)로 지우기" -#: src/libslic3r/PrintConfig.cpp:2212 +#: src/libslic3r/PrintConfig.cpp:2272 msgid "" "Object will be used to purge the nozzle after a toolchange to save material " "that would otherwise end up in the wipe tower and decrease print time. " "Colours of the objects will be mixed as a result." msgstr "" -"객체는 도구 변경 후 노즐을 소거 하는 데 사용 되며, 그렇지 않으면 와이프 타워" -"에서 종료 되는 재료를 저장 하고 인쇄 시간을 줄입니다. 그 결과 개체의 색상이 " -"혼합 됩니다." +"객체(object)는 도구 변경 후 노즐을 소거 하는 데 사용 되며, 그렇지 않으면 와이" +"프 타워에서 종료 되는 재료를 저장 하고 인쇄 시간을 줄입니다. 그 결과 객체" +"(object)의 색상이 혼합 됩니다." -#: src/libslic3r/PrintConfig.cpp:2218 +#: src/libslic3r/PrintConfig.cpp:2278 msgid "Maximal bridging distance" msgstr "최대 브리징 거리" -#: src/libslic3r/PrintConfig.cpp:2219 +#: src/libslic3r/PrintConfig.cpp:2279 msgid "Maximal distance between supports on sparse infill sections." msgstr "드문드문한 인필 섹션에서 지지대 사이의 최대 거리." -#: src/libslic3r/PrintConfig.cpp:2225 +#: src/libslic3r/PrintConfig.cpp:2285 msgid "XY Size Compensation" -msgstr "XY 크기 보정" +msgstr "XY 수평 확장" -#: src/libslic3r/PrintConfig.cpp:2227 +#: src/libslic3r/PrintConfig.cpp:2287 msgid "" "The object will be grown/shrunk in the XY plane by the configured value " "(negative = inwards, positive = outwards). This might be useful for fine-" "tuning hole sizes." msgstr "" -"XY 평면에서 설정된 값(음수 = 안, 양 = 바깥쪽)에 따라 객체가 증가/정격된다. 이" -"는 구멍 크기를 미세 조정하는데 유용할 수 있다." +"XY 평면에서 설정된 값(음수 = 안, 양 = 바깥쪽)에 따라 객체(object)가 증가/정격" +"된다. 이는 구멍 크기를 미세 조정하는데 유용할 수 있다." -#: src/libslic3r/PrintConfig.cpp:2235 +#: src/libslic3r/PrintConfig.cpp:2295 msgid "Z offset" msgstr "Z 오프셋" -#: src/libslic3r/PrintConfig.cpp:2236 +#: src/libslic3r/PrintConfig.cpp:2296 msgid "" "This value will be added (or subtracted) from all the Z coordinates in the " "output G-code. It is used to compensate for bad Z endstop position: for " "example, if your endstop zero actually leaves the nozzle 0.3mm far from the " "print bed, set this to -0.3 (or fix your endstop)." msgstr "" -"이 값은 출력 G-코드의 모든 Z 좌표에서 추가(또는 감산)된다. 예를 들어, 엔드 스" +"이 값은 출력 G-code의 모든 Z 좌표에서 추가(또는 감산)된다. 예를 들어, 엔드 스" "톱 0이 실제로 노즐을 프린트 베드에서 0.3mm 떨어진 곳에 둔 경우, 이를 -0.3(또" "는 엔드 스톱을 고정)으로 설정하십시오." -#: src/libslic3r/PrintConfig.cpp:2294 +#: src/libslic3r/PrintConfig.cpp:2363 msgid "Display width" msgstr "디스플레이 너비" -#: src/libslic3r/PrintConfig.cpp:2295 +#: src/libslic3r/PrintConfig.cpp:2364 msgid "Width of the display" msgstr "디스플레이의 폭입니다" -#: src/libslic3r/PrintConfig.cpp:2300 +#: src/libslic3r/PrintConfig.cpp:2369 msgid "Display height" msgstr "표시 높이" -#: src/libslic3r/PrintConfig.cpp:2301 +#: src/libslic3r/PrintConfig.cpp:2370 msgid "Height of the display" msgstr "디스플레이의 높이" -#: src/libslic3r/PrintConfig.cpp:2306 +#: src/libslic3r/PrintConfig.cpp:2375 msgid "Number of pixels in" msgstr "의 픽셀 수" -#: src/libslic3r/PrintConfig.cpp:2308 +#: src/libslic3r/PrintConfig.cpp:2377 msgid "Number of pixels in X" msgstr "X의 픽셀 수" -#: src/libslic3r/PrintConfig.cpp:2314 +#: src/libslic3r/PrintConfig.cpp:2383 msgid "Number of pixels in Y" msgstr "Y의 픽셀 수" -#: src/libslic3r/PrintConfig.cpp:2319 +#: src/libslic3r/PrintConfig.cpp:2388 msgid "Display horizontal mirroring" msgstr "수평 미러링 표시" -#: src/libslic3r/PrintConfig.cpp:2320 +#: src/libslic3r/PrintConfig.cpp:2389 msgid "Mirror horizontally" msgstr "수평으로 미러" -#: src/libslic3r/PrintConfig.cpp:2321 +#: src/libslic3r/PrintConfig.cpp:2390 msgid "Enable horizontal mirroring of output images" msgstr "출력 이미지의 수평 미러링 사용" -#: src/libslic3r/PrintConfig.cpp:2326 +#: src/libslic3r/PrintConfig.cpp:2395 msgid "Display vertical mirroring" msgstr "수직 미러링 표시" -#: src/libslic3r/PrintConfig.cpp:2327 +#: src/libslic3r/PrintConfig.cpp:2396 msgid "Mirror vertically" msgstr "수직으로 미러" -#: src/libslic3r/PrintConfig.cpp:2328 +#: src/libslic3r/PrintConfig.cpp:2397 msgid "Enable vertical mirroring of output images" msgstr "출력 이미지의 수직 미러링 사용" -#: src/libslic3r/PrintConfig.cpp:2333 +#: src/libslic3r/PrintConfig.cpp:2402 msgid "Display orientation" msgstr "디스플레이 방향" -#: src/libslic3r/PrintConfig.cpp:2334 +#: src/libslic3r/PrintConfig.cpp:2403 msgid "" "Set the actual LCD display orientation inside the SLA printer. Portrait mode " "will flip the meaning of display width and height parameters and the output " @@ -8283,72 +9579,72 @@ msgstr "" "레이 너비 및 높이 매개 변수의 의미를 반전 하 고 출력 이미지는 90도 회전 합니" "다." -#: src/libslic3r/PrintConfig.cpp:2340 +#: src/libslic3r/PrintConfig.cpp:2409 msgid "Landscape" msgstr "가로" -#: src/libslic3r/PrintConfig.cpp:2341 +#: src/libslic3r/PrintConfig.cpp:2410 msgid "Portrait" msgstr "세로모드" -#: src/libslic3r/PrintConfig.cpp:2346 +#: src/libslic3r/PrintConfig.cpp:2415 msgid "Fast" msgstr "빠른" -#: src/libslic3r/PrintConfig.cpp:2347 +#: src/libslic3r/PrintConfig.cpp:2416 msgid "Fast tilt" msgstr "빠른 기울기" -#: src/libslic3r/PrintConfig.cpp:2348 +#: src/libslic3r/PrintConfig.cpp:2417 msgid "Time of the fast tilt" msgstr "기울이기 시간" -#: src/libslic3r/PrintConfig.cpp:2355 +#: src/libslic3r/PrintConfig.cpp:2424 msgid "Slow" msgstr "느리게" -#: src/libslic3r/PrintConfig.cpp:2356 +#: src/libslic3r/PrintConfig.cpp:2425 msgid "Slow tilt" msgstr "슬로우 틸트" -#: src/libslic3r/PrintConfig.cpp:2357 +#: src/libslic3r/PrintConfig.cpp:2426 msgid "Time of the slow tilt" msgstr "느린 기울기의 시간" -#: src/libslic3r/PrintConfig.cpp:2364 +#: src/libslic3r/PrintConfig.cpp:2433 msgid "Area fill" msgstr "영역 채우기" -#: src/libslic3r/PrintConfig.cpp:2365 +#: src/libslic3r/PrintConfig.cpp:2434 msgid "" "The percentage of the bed area. \n" "If the print area exceeds the specified value, \n" "then a slow tilt will be used, otherwise - a fast tilt" msgstr "" -"침대 영역의 비율입니다. \n" +"배드 영역의 비율입니다. \n" "인쇄 영역이 지정 된 값을 초과 하면 \n" "그런 다음 느린 기울기가 사용 됩니다, 그렇지 않으면-빠른 기울기가 됩니다" -#: src/libslic3r/PrintConfig.cpp:2372 src/libslic3r/PrintConfig.cpp:2373 -#: src/libslic3r/PrintConfig.cpp:2374 +#: src/libslic3r/PrintConfig.cpp:2441 src/libslic3r/PrintConfig.cpp:2442 +#: src/libslic3r/PrintConfig.cpp:2443 msgid "Printer scaling correction" msgstr "프린터 스케일링 보정" -#: src/libslic3r/PrintConfig.cpp:2380 src/libslic3r/PrintConfig.cpp:2381 +#: src/libslic3r/PrintConfig.cpp:2449 src/libslic3r/PrintConfig.cpp:2450 msgid "Printer absolute correction" msgstr "프린터 절대 보정" -#: src/libslic3r/PrintConfig.cpp:2382 +#: src/libslic3r/PrintConfig.cpp:2451 msgid "" "Will inflate or deflate the sliced 2D polygons according to the sign of the " "correction." msgstr "교정 기호에 따라 슬라이스된 2D 폴리곤을 팽창 하거나 수축 합니다." -#: src/libslic3r/PrintConfig.cpp:2388 src/libslic3r/PrintConfig.cpp:2389 +#: src/libslic3r/PrintConfig.cpp:2457 src/libslic3r/PrintConfig.cpp:2458 msgid "Printer gamma correction" msgstr "프린터 감마 보정" -#: src/libslic3r/PrintConfig.cpp:2390 +#: src/libslic3r/PrintConfig.cpp:2459 msgid "" "This will apply a gamma correction to the rasterized 2D polygons. A gamma " "value of zero means thresholding with the threshold in the middle. This " @@ -8358,89 +9654,133 @@ msgstr "" "중간에 임계값이 임계화 의미입니다. 이 동작은 폴리곤의 구멍을 잃지 않고 안티알" "리아싱을 제거 합니다." -#: src/libslic3r/PrintConfig.cpp:2401 src/libslic3r/PrintConfig.cpp:2402 +#: src/libslic3r/PrintConfig.cpp:2471 src/libslic3r/PrintConfig.cpp:2472 +msgid "SLA material type" +msgstr "SLA 재질 유형" + +#: src/libslic3r/PrintConfig.cpp:2483 src/libslic3r/PrintConfig.cpp:2484 msgid "Initial layer height" msgstr "초기 레이어 높이" -#: src/libslic3r/PrintConfig.cpp:2408 +#: src/libslic3r/PrintConfig.cpp:2490 src/libslic3r/PrintConfig.cpp:2491 +msgid "Bottle volume" +msgstr "병 볼륨" + +#: src/libslic3r/PrintConfig.cpp:2492 +msgid "ml" +msgstr "광년" + +#: src/libslic3r/PrintConfig.cpp:2497 src/libslic3r/PrintConfig.cpp:2498 +msgid "Bottle weight" +msgstr "병 무게" + +#: src/libslic3r/PrintConfig.cpp:2499 +msgid "kg" +msgstr "kg" + +#: src/libslic3r/PrintConfig.cpp:2506 +msgid "g/ml" +msgstr "g /ml" + +#: src/libslic3r/PrintConfig.cpp:2513 +msgid "money/bottle" +msgstr "돈 /병" + +#: src/libslic3r/PrintConfig.cpp:2518 msgid "Faded layers" msgstr "페이드 레이어" -#: src/libslic3r/PrintConfig.cpp:2409 +#: src/libslic3r/PrintConfig.cpp:2519 msgid "" "Number of the layers needed for the exposure time fade from initial exposure " "time to the exposure time" msgstr "노출 시간에 필요한 레이어 수는 초기 노출 시간에서 노출 시간으로 페이드" -#: src/libslic3r/PrintConfig.cpp:2416 src/libslic3r/PrintConfig.cpp:2417 +#: src/libslic3r/PrintConfig.cpp:2526 src/libslic3r/PrintConfig.cpp:2527 +msgid "Minimum exposure time" +msgstr "최소 노출 시간" + +#: src/libslic3r/PrintConfig.cpp:2534 src/libslic3r/PrintConfig.cpp:2535 +msgid "Maximum exposure time" +msgstr "최대 노출 시간" + +#: src/libslic3r/PrintConfig.cpp:2542 src/libslic3r/PrintConfig.cpp:2543 msgid "Exposure time" msgstr "노출 시간" -#: src/libslic3r/PrintConfig.cpp:2423 src/libslic3r/PrintConfig.cpp:2424 +#: src/libslic3r/PrintConfig.cpp:2549 src/libslic3r/PrintConfig.cpp:2550 +msgid "Minimum initial exposure time" +msgstr "최소 초기 노출 시간" + +#: src/libslic3r/PrintConfig.cpp:2557 src/libslic3r/PrintConfig.cpp:2558 +msgid "Maximum initial exposure time" +msgstr "최대 초기 노출 시간" + +#: src/libslic3r/PrintConfig.cpp:2565 src/libslic3r/PrintConfig.cpp:2566 msgid "Initial exposure time" msgstr "초기 노출 시간" -#: src/libslic3r/PrintConfig.cpp:2430 src/libslic3r/PrintConfig.cpp:2431 +#: src/libslic3r/PrintConfig.cpp:2572 src/libslic3r/PrintConfig.cpp:2573 msgid "Correction for expansion" msgstr "확장 보정" -#: src/libslic3r/PrintConfig.cpp:2437 +#: src/libslic3r/PrintConfig.cpp:2579 msgid "SLA print material notes" msgstr "SLA 인쇄 재료 참고 사항" -#: src/libslic3r/PrintConfig.cpp:2438 +#: src/libslic3r/PrintConfig.cpp:2580 msgid "You can put your notes regarding the SLA print material here." msgstr "여기에서 SLA 인쇄 자료에 대한 메모를 넣을 수 있습니다." -#: src/libslic3r/PrintConfig.cpp:2446 src/libslic3r/PrintConfig.cpp:2457 +#: src/libslic3r/PrintConfig.cpp:2592 src/libslic3r/PrintConfig.cpp:2603 msgid "Default SLA material profile" msgstr "기본 SLA 재질 프로 파일" -#: src/libslic3r/PrintConfig.cpp:2468 +#: src/libslic3r/PrintConfig.cpp:2614 msgid "Generate supports" msgstr "지원 생성" -#: src/libslic3r/PrintConfig.cpp:2470 +#: src/libslic3r/PrintConfig.cpp:2616 msgid "Generate supports for the models" msgstr "모델에 대한 지원 생성" -#: src/libslic3r/PrintConfig.cpp:2475 +#: src/libslic3r/PrintConfig.cpp:2621 msgid "Support head front diameter" msgstr "서포트 헤드 전면 지름" -#: src/libslic3r/PrintConfig.cpp:2477 +#: src/libslic3r/PrintConfig.cpp:2623 msgid "Diameter of the pointing side of the head" msgstr "헤드 포인팅 측면 지름" -#: src/libslic3r/PrintConfig.cpp:2484 +#: src/libslic3r/PrintConfig.cpp:2630 msgid "Support head penetration" msgstr "서포트 헤드 관통" -#: src/libslic3r/PrintConfig.cpp:2486 +#: src/libslic3r/PrintConfig.cpp:2632 msgid "How much the pinhead has to penetrate the model surface" msgstr "핀 헤드가 모델 표면에 침투 하는 정도" -#: src/libslic3r/PrintConfig.cpp:2493 +#: src/libslic3r/PrintConfig.cpp:2639 msgid "Support head width" msgstr "서포트 헤드 폭" -#: src/libslic3r/PrintConfig.cpp:2495 +#: src/libslic3r/PrintConfig.cpp:2641 msgid "Width from the back sphere center to the front sphere center" -msgstr "뒤쪽 구 중심에서 앞쪽 구 중심 까지의 폭입니다" +msgstr "뒤쪽 구 중재봉선에서 앞쪽 구 중재봉선 까지의 폭입니다" -#: src/libslic3r/PrintConfig.cpp:2503 +#: src/libslic3r/PrintConfig.cpp:2649 msgid "Support pillar diameter" msgstr "서포트 기둥 지름" -#: src/libslic3r/PrintConfig.cpp:2505 +#: src/libslic3r/PrintConfig.cpp:2651 msgid "Diameter in mm of the support pillars" msgstr "서포트 기둥의 지름 (mm)" -#: src/libslic3r/PrintConfig.cpp:2513 +#: src/libslic3r/PrintConfig.cpp:2659 msgid "Support pillar connection mode" msgstr "기둥 연결 모드 지원" -#: src/libslic3r/PrintConfig.cpp:2514 +#: src/libslic3r/PrintConfig.cpp:2660 msgid "" "Controls the bridge type between two neighboring pillars. Can be zig-zag, " "cross (double zig-zag) or dynamic which will automatically switch between " @@ -8450,23 +9790,23 @@ msgstr "" "로 처음 두 사이를 전환 하는 지그재그, 크로스 (지그재그 더블 지그재그) 또는 동" "적 수 있습니다." -#: src/libslic3r/PrintConfig.cpp:2522 +#: src/libslic3r/PrintConfig.cpp:2668 msgid "Zig-Zag" msgstr "지그재그" -#: src/libslic3r/PrintConfig.cpp:2523 +#: src/libslic3r/PrintConfig.cpp:2669 msgid "Cross" msgstr "크로스" -#: src/libslic3r/PrintConfig.cpp:2524 +#: src/libslic3r/PrintConfig.cpp:2670 msgid "Dynamic" msgstr "동적" -#: src/libslic3r/PrintConfig.cpp:2536 +#: src/libslic3r/PrintConfig.cpp:2682 msgid "Pillar widening factor" msgstr "기둥 확장 계수" -#: src/libslic3r/PrintConfig.cpp:2538 +#: src/libslic3r/PrintConfig.cpp:2684 msgid "" "Merging bridges or pillars into another pillars can increase the radius. " "Zero means no increase, one means full increase." @@ -8474,27 +9814,27 @@ msgstr "" "브릿지 또는 기둥을 다른 기둥에 병합 하면 반지름을 늘릴 수 있습니다. 0은 증가 " "없음을 의미 하나는 전체 증가를 의미 합니다." -#: src/libslic3r/PrintConfig.cpp:2547 +#: src/libslic3r/PrintConfig.cpp:2693 msgid "Support base diameter" msgstr "서포트 베이스 지름" -#: src/libslic3r/PrintConfig.cpp:2549 +#: src/libslic3r/PrintConfig.cpp:2695 msgid "Diameter in mm of the pillar base" msgstr "기둥 베이스의 mm 직경" -#: src/libslic3r/PrintConfig.cpp:2557 +#: src/libslic3r/PrintConfig.cpp:2703 msgid "Support base height" msgstr "서포트 기준 높이" -#: src/libslic3r/PrintConfig.cpp:2559 +#: src/libslic3r/PrintConfig.cpp:2705 msgid "The height of the pillar base cone" msgstr "서포트 베이스 원추의 높이" -#: src/libslic3r/PrintConfig.cpp:2566 +#: src/libslic3r/PrintConfig.cpp:2712 msgid "Support base safety distance" msgstr "지지기본 안전 거리" -#: src/libslic3r/PrintConfig.cpp:2569 +#: src/libslic3r/PrintConfig.cpp:2715 msgid "" "The minimum distance of the pillar base from the model in mm. Makes sense in " "zero elevation mode where a gap according to this parameter is inserted " @@ -8503,78 +9843,78 @@ msgstr "" "모델에서 기둥 베이스의 최소 거리(mm.mm.)는 이 매개변수에 따른 간격이 모델과 " "패드 사이에 삽입되는 제로 고도 모드에서 의미가 있습니다." -#: src/libslic3r/PrintConfig.cpp:2579 +#: src/libslic3r/PrintConfig.cpp:2725 msgid "Critical angle" msgstr "임계 각도" -#: src/libslic3r/PrintConfig.cpp:2581 +#: src/libslic3r/PrintConfig.cpp:2727 msgid "The default angle for connecting support sticks and junctions." msgstr "서포트 스틱과 접합부를 연결 하는 기본 각도입니다." -#: src/libslic3r/PrintConfig.cpp:2589 +#: src/libslic3r/PrintConfig.cpp:2735 msgid "Max bridge length" msgstr "최대 브리지 길이" -#: src/libslic3r/PrintConfig.cpp:2591 +#: src/libslic3r/PrintConfig.cpp:2737 msgid "The max length of a bridge" msgstr "브릿지의 최대 길이" -#: src/libslic3r/PrintConfig.cpp:2598 +#: src/libslic3r/PrintConfig.cpp:2744 msgid "Max pillar linking distance" msgstr "최대 기둥 연결 거리" -#: src/libslic3r/PrintConfig.cpp:2600 +#: src/libslic3r/PrintConfig.cpp:2746 msgid "" "The max distance of two pillars to get linked with each other. A zero value " "will prohibit pillar cascading." msgstr "" "서로 연결 되는 두기둥의 최대 거리. 0 값은 기둥을 계단식으로 금지 합니다." -#: src/libslic3r/PrintConfig.cpp:2608 +#: src/libslic3r/PrintConfig.cpp:2754 msgid "Object elevation" -msgstr "객체 고도" +msgstr "객체(object) 고도" -#: src/libslic3r/PrintConfig.cpp:2610 +#: src/libslic3r/PrintConfig.cpp:2756 msgid "" -"How much the supports should lift up the supported object. If this value is " -"zero, the bottom of the model geometry will be considered as part of the pad." +"How much the supports should lift up the supported object. If \"Pad around " +"object\" is enabled, this value is ignored." msgstr "" -"지원 대상을 들어 올려야 하는 양입니다. 이 값이 0이면 모델 형상의 맨 아래가 패" -"드의 일부로 간주됩니다." +"지원 대상을 들어 올려야 하는 양입니다. \"개체 주위 패드\"를 사용하도록 설정하" +"면 이 값은 무시됩니다." -#: src/libslic3r/PrintConfig.cpp:2622 +#: src/libslic3r/PrintConfig.cpp:2767 msgid "This is a relative measure of support points density." msgstr "이는 서포트 점 밀도의 상대적인 척도입니다." -#: src/libslic3r/PrintConfig.cpp:2628 +#: src/libslic3r/PrintConfig.cpp:2773 msgid "Minimal distance of the support points" msgstr "서포트 지점의 최소 거리" -#: src/libslic3r/PrintConfig.cpp:2630 +#: src/libslic3r/PrintConfig.cpp:2775 msgid "No support points will be placed closer than this threshold." msgstr "서포트 지점은 이 임계값 보다 더 가깝게 배치 되지 않습니다." -#: src/libslic3r/PrintConfig.cpp:2636 +#: src/libslic3r/PrintConfig.cpp:2781 msgid "Use pad" msgstr "패드 사용" -#: src/libslic3r/PrintConfig.cpp:2638 +#: src/libslic3r/PrintConfig.cpp:2783 msgid "Add a pad underneath the supported model" msgstr "서포트 되는 모델 아래에 패드 추가" -#: src/libslic3r/PrintConfig.cpp:2643 +#: src/libslic3r/PrintConfig.cpp:2788 msgid "Pad wall thickness" msgstr "패드 벽 두께" -#: src/libslic3r/PrintConfig.cpp:2645 +#: src/libslic3r/PrintConfig.cpp:2790 msgid "The thickness of the pad and its optional cavity walls." msgstr "패드의 두께와 옵션 캐비티 벽." -#: src/libslic3r/PrintConfig.cpp:2653 +#: src/libslic3r/PrintConfig.cpp:2798 msgid "Pad wall height" msgstr "패드 벽 높이" -#: src/libslic3r/PrintConfig.cpp:2654 +#: src/libslic3r/PrintConfig.cpp:2799 msgid "" "Defines the pad cavity depth. Set to zero to disable the cavity. Be careful " "when enabling this feature, as some resins may produce an extreme suction " @@ -8585,228 +9925,278 @@ msgstr "" "이 기능을 활성화 할 때 주의 해야할, 일부 수 캐비티 내부 극단적인 흡입 효과를 " "생성 할 수도 있기 때문에, vat 호일 인쇄를 벗겨 어렵게 만든다." -#: src/libslic3r/PrintConfig.cpp:2667 +#: src/libslic3r/PrintConfig.cpp:2812 +msgid "Pad brim size" +msgstr "패드 브럼 사이즈" + +#: src/libslic3r/PrintConfig.cpp:2813 +msgid "How far should the pad extend around the contained geometry" +msgstr "패드가 포함된 형상 주위로 얼마나 확장되어야 하는지" + +#: src/libslic3r/PrintConfig.cpp:2823 msgid "Max merge distance" msgstr "최대 병합 거리" -#: src/libslic3r/PrintConfig.cpp:2669 +#: src/libslic3r/PrintConfig.cpp:2825 msgid "" "Some objects can get along with a few smaller pads instead of a single big " "one. This parameter defines how far the center of two smaller pads should " "be. If theyare closer, they will get merged into one pad." msgstr "" -"일부 개체는 큰 하나 대신 몇 가지 작은 패드와 함께 얻을 수 있습니다. 이 매개 " -"변수는 두 개의 작은 패드의 중심이 얼마나 되어야 하는지 정의 합니다. 그들은 하" -"나의 패드에 병합을 얻을 것이다." +"일부 객체(object)는 큰 하나 대신 몇 가지 작은 패드와 함께 얻을 수 있습니다. " +"이 매개 변수는 두 개의 작은 패드의 중재봉선이 얼마나 되어야 하는지 정의 합니" +"다. 그들은 하나의 패드에 병합을 얻을 것이다." -#: src/libslic3r/PrintConfig.cpp:2680 -msgid "Pad edge radius" -msgstr "패드 가장자리 반경" - -#: src/libslic3r/PrintConfig.cpp:2689 +#: src/libslic3r/PrintConfig.cpp:2845 msgid "Pad wall slope" msgstr "패드 벽 경사" -#: src/libslic3r/PrintConfig.cpp:2691 +#: src/libslic3r/PrintConfig.cpp:2847 msgid "" "The slope of the pad wall relative to the bed plane. 90 degrees means " "straight walls." msgstr "" -"침대 평면을 기준으로 하는 패드 벽의 기울기입니다. 90도는 직선 벽을 의미 합니" +"배드 평면을 기준으로 하는 패드 벽의 기울기입니다. 90도는 직선 벽을 의미 합니" "다." -#: src/libslic3r/PrintConfig.cpp:2700 -msgid "Pad object gap" -msgstr "패드 오브젝트 갭" +#: src/libslic3r/PrintConfig.cpp:2856 +msgid "Pad around object" +msgstr "개체 주위패드" -#: src/libslic3r/PrintConfig.cpp:2702 +#: src/libslic3r/PrintConfig.cpp:2858 +msgid "Create pad around object and ignore the support elevation" +msgstr "오브젝트 주위에 패드를 작성하고 지지 고도를 무시합니다." + +#: src/libslic3r/PrintConfig.cpp:2863 +msgid "Pad around object everywhere" +msgstr "어디서나 개체 주위에 패드" + +#: src/libslic3r/PrintConfig.cpp:2865 +msgid "Force pad around object everywhere" +msgstr "사방 오브젝트 주위의 포스 패드" + +#: src/libslic3r/PrintConfig.cpp:2870 +msgid "Pad object gap" +msgstr "패드 객체(object) 갭" + +#: src/libslic3r/PrintConfig.cpp:2872 msgid "" "The gap between the object bottom and the generated pad in zero elevation " "mode." -msgstr "0 고도 모드에서 오브젝트 바닥과 생성된 패드 사이의 간격입니다." +msgstr "0 고도 모드에서 객체(object) 바닥과 생성된 패드 사이의 간격입니다." -#: src/libslic3r/PrintConfig.cpp:2711 +#: src/libslic3r/PrintConfig.cpp:2881 msgid "Pad object connector stride" -msgstr "패드 오브젝트 커넥터 보폭" +msgstr "패드 객체(object) 커넥터 보폭" -#: src/libslic3r/PrintConfig.cpp:2713 +#: src/libslic3r/PrintConfig.cpp:2883 msgid "" -"Distance between two connector sticks between the object pad and the " +"Distance between two connector sticks which connect the object and the " "generated pad." -msgstr "오브젝트 패드와 생성된 패드 사이의 두 커넥터 스틱 사이의 거리입니다." +msgstr "오브젝트와 생성된 패드를 연결하는 두 커넥터 스틱 사이의 거리입니다." -#: src/libslic3r/PrintConfig.cpp:2721 +#: src/libslic3r/PrintConfig.cpp:2890 msgid "Pad object connector width" -msgstr "패드 오브젝트 커넥터 너비" +msgstr "패드 객체(object) 커넥터 너비" -#: src/libslic3r/PrintConfig.cpp:2723 +#: src/libslic3r/PrintConfig.cpp:2892 msgid "" -"The width of the connectors sticks which connect the object pad and the " -"generated pad." -msgstr "커넥터의 너비는 오브젝트 패드와 생성된 패드를 연결하는 스틱입니다." +"Width of the connector sticks which connect the object and the generated pad." +msgstr "개체와 생성된 패드를 연결하는 커넥터 스틱의 너비입니다." -#: src/libslic3r/PrintConfig.cpp:2731 +#: src/libslic3r/PrintConfig.cpp:2899 msgid "Pad object connector penetration" -msgstr "패드 오브젝트 커넥터 침투" +msgstr "패드 객체(object) 커넥터 침투" -#: src/libslic3r/PrintConfig.cpp:2734 +#: src/libslic3r/PrintConfig.cpp:2902 msgid "How much should the tiny connectors penetrate into the model body." msgstr "작은 커넥터가 모델 본체에 얼마나 침투해야 하는가?" -#: src/libslic3r/PrintConfig.cpp:3094 +#: src/libslic3r/PrintConfig.cpp:2909 +msgid "Enable hollowing" +msgstr "속빈 공동 사용" + +#: src/libslic3r/PrintConfig.cpp:2911 +msgid "Hollow out a model to have an empty interior" +msgstr "빈 내부를 가지고 모델을 비우기" + +#: src/libslic3r/PrintConfig.cpp:2916 +msgid "Hollowing thickness" +msgstr "속빈 공동 두께" + +#: src/libslic3r/PrintConfig.cpp:2918 +msgid "Minimum wall thickness of a hollowed model." +msgstr "속이 빈 모델의 최소 벽 두께입니다." + +#: src/libslic3r/PrintConfig.cpp:2926 +msgid "Hollowing accuracy" +msgstr "속빈 공동 정확도" + +#: src/libslic3r/PrintConfig.cpp:2928 +msgid "" +"Performance vs accuracy of calculation. Lower values may produce unwanted " +"artifacts." +msgstr "" +"성능 대 계산의 정확성. 값이 낮을수록 원치 않는 아티팩트가 생성될 수 있습니다." + +#: src/libslic3r/PrintConfig.cpp:2935 +msgid "Hollowing closing distance" +msgstr "공동화된 닫힘 반경" + +#: src/libslic3r/PrintConfig.cpp:3315 msgid "Export OBJ" msgstr "OBJ 내보내기" -#: src/libslic3r/PrintConfig.cpp:3095 +#: src/libslic3r/PrintConfig.cpp:3316 msgid "Export the model(s) as OBJ." msgstr "모델을 OBJ로 내보냅니다." -#: src/libslic3r/PrintConfig.cpp:3106 +#: src/libslic3r/PrintConfig.cpp:3327 msgid "Export SLA" msgstr "STL로 내보내기" -#: src/libslic3r/PrintConfig.cpp:3107 +#: src/libslic3r/PrintConfig.cpp:3328 msgid "Slice the model and export SLA printing layers as PNG." msgstr "모델을 분할하고 SLA 인쇄 레이어를 PNG로 내보냅니다." -#: src/libslic3r/PrintConfig.cpp:3112 +#: src/libslic3r/PrintConfig.cpp:3333 msgid "Export 3MF" msgstr "3MF 내보내기" -#: src/libslic3r/PrintConfig.cpp:3113 +#: src/libslic3r/PrintConfig.cpp:3334 msgid "Export the model(s) as 3MF." msgstr "모델을 3MF로 내보냅니다." -#: src/libslic3r/PrintConfig.cpp:3117 +#: src/libslic3r/PrintConfig.cpp:3338 msgid "Export AMF" msgstr "AMF 내보내기" -#: src/libslic3r/PrintConfig.cpp:3118 +#: src/libslic3r/PrintConfig.cpp:3339 msgid "Export the model(s) as AMF." msgstr "모델을 AMF로 내보냅니다." -#: src/libslic3r/PrintConfig.cpp:3122 +#: src/libslic3r/PrintConfig.cpp:3343 msgid "Export STL" msgstr "STL 내보내기" -#: src/libslic3r/PrintConfig.cpp:3123 +#: src/libslic3r/PrintConfig.cpp:3344 msgid "Export the model(s) as STL." msgstr "모델을 STL로 내보냅니다." -#: src/libslic3r/PrintConfig.cpp:3128 +#: src/libslic3r/PrintConfig.cpp:3349 msgid "Slice the model and export toolpaths as G-code." msgstr "모델을 슬라이스하고 공구 경로를 G 코드로 내보냅니다." -#: src/libslic3r/PrintConfig.cpp:3133 +#: src/libslic3r/PrintConfig.cpp:3354 msgid "Slice" msgstr "슬라이스" -#: src/libslic3r/PrintConfig.cpp:3134 +#: src/libslic3r/PrintConfig.cpp:3355 msgid "" "Slice the model as FFF or SLA based on the printer_technology configuration " "value." msgstr "" " printer_technology 구성 값을 기반으로 모델을 FFF 또는 SLA로 슬라이스합니다." -#: src/libslic3r/PrintConfig.cpp:3139 +#: src/libslic3r/PrintConfig.cpp:3360 msgid "Help" msgstr "도움말" -#: src/libslic3r/PrintConfig.cpp:3140 +#: src/libslic3r/PrintConfig.cpp:3361 msgid "Show this help." msgstr "이 도움말을 표시 합니다." -#: src/libslic3r/PrintConfig.cpp:3145 +#: src/libslic3r/PrintConfig.cpp:3366 msgid "Help (FFF options)" msgstr "도움말 (FFF 옵션)" -#: src/libslic3r/PrintConfig.cpp:3146 +#: src/libslic3r/PrintConfig.cpp:3367 msgid "Show the full list of print/G-code configuration options." msgstr "인쇄/G 코드 구성 옵션의 전체 목록을 표시 합니다." -#: src/libslic3r/PrintConfig.cpp:3150 +#: src/libslic3r/PrintConfig.cpp:3371 msgid "Help (SLA options)" msgstr "도움말 (SLA 옵션)" -#: src/libslic3r/PrintConfig.cpp:3151 +#: src/libslic3r/PrintConfig.cpp:3372 msgid "Show the full list of SLA print configuration options." msgstr "SLA 인쇄 구성 옵션의 전체 목록을 표시 합니다." -#: src/libslic3r/PrintConfig.cpp:3155 +#: src/libslic3r/PrintConfig.cpp:3376 msgid "Output Model Info" msgstr "출력 모델 정보" -#: src/libslic3r/PrintConfig.cpp:3156 +#: src/libslic3r/PrintConfig.cpp:3377 msgid "Write information about the model to the console." msgstr "모델에 대한 정보를 콘솔에 씁니다." -#: src/libslic3r/PrintConfig.cpp:3160 +#: src/libslic3r/PrintConfig.cpp:3381 msgid "Save config file" msgstr "구성 파일 저장" -#: src/libslic3r/PrintConfig.cpp:3161 +#: src/libslic3r/PrintConfig.cpp:3382 msgid "Save configuration to the specified file." msgstr "지정 된 파일에 구성을 저장 합니다." -#: src/libslic3r/PrintConfig.cpp:3171 +#: src/libslic3r/PrintConfig.cpp:3392 msgid "Align XY" msgstr "XY 정렬" -#: src/libslic3r/PrintConfig.cpp:3172 +#: src/libslic3r/PrintConfig.cpp:3393 msgid "Align the model to the given point." msgstr "모델을 지정된 점에 맞춥니다." -#: src/libslic3r/PrintConfig.cpp:3177 +#: src/libslic3r/PrintConfig.cpp:3398 msgid "Cut model at the given Z." msgstr "지정된 Z에서 모델을 잘라냅니다." -#: src/libslic3r/PrintConfig.cpp:3198 +#: src/libslic3r/PrintConfig.cpp:3419 msgid "Center" msgstr "중앙" -#: src/libslic3r/PrintConfig.cpp:3199 +#: src/libslic3r/PrintConfig.cpp:3420 msgid "Center the print around the given center." -msgstr "지정된 점을 중심으로 인쇄 합니다." +msgstr "지정된 점을 중재봉선으로 인쇄 합니다." -#: src/libslic3r/PrintConfig.cpp:3203 +#: src/libslic3r/PrintConfig.cpp:3424 msgid "Don't arrange" msgstr "준비하지 마십시오" -#: src/libslic3r/PrintConfig.cpp:3204 +#: src/libslic3r/PrintConfig.cpp:3425 msgid "" "Do not rearrange the given models before merging and keep their original XY " "coordinates." msgstr "" "병합하기 전에 지정된 모델을 재정렬하고 원래 XY 좌표를 유지하지 마십시오." -#: src/libslic3r/PrintConfig.cpp:3207 +#: src/libslic3r/PrintConfig.cpp:3428 msgid "Duplicate" msgstr "복사" -#: src/libslic3r/PrintConfig.cpp:3208 +#: src/libslic3r/PrintConfig.cpp:3429 msgid "Multiply copies by this factor." msgstr "이 계수로 복사본을 곱합니다." -#: src/libslic3r/PrintConfig.cpp:3212 +#: src/libslic3r/PrintConfig.cpp:3433 msgid "Duplicate by grid" msgstr "모눈에 따라 복제" -#: src/libslic3r/PrintConfig.cpp:3213 +#: src/libslic3r/PrintConfig.cpp:3434 msgid "Multiply copies by creating a grid." msgstr "그리드를 만들어 복사본을 곱합니다." -#: src/libslic3r/PrintConfig.cpp:3216 +#: src/libslic3r/PrintConfig.cpp:3437 msgid "Merge" msgstr "병합" -#: src/libslic3r/PrintConfig.cpp:3217 +#: src/libslic3r/PrintConfig.cpp:3438 msgid "" "Arrange the supplied models in a plate and merge them in a single model in " "order to perform actions once." msgstr "" "한 번 작업을 수행하기 위해 제공 된 모델을 정렬하고 단일 모델로 병합 합니다." -#: src/libslic3r/PrintConfig.cpp:3222 +#: src/libslic3r/PrintConfig.cpp:3443 msgid "" "Try to repair any non-manifold meshes (this option is implicitly added " "whenever we need to slice the model to perform the requested action)." @@ -8814,58 +10204,59 @@ msgstr "" "메쉬를 복구 하십시오 (요청 된 작업을 수행 하기 위해 모델을 슬라이스 해야 할때" "마다 이 옵션이 암시적으로 추가 됨)." -#: src/libslic3r/PrintConfig.cpp:3226 +#: src/libslic3r/PrintConfig.cpp:3447 msgid "Rotation angle around the Z axis in degrees." msgstr "Z 축 주위 회전 각도입니다." -#: src/libslic3r/PrintConfig.cpp:3230 +#: src/libslic3r/PrintConfig.cpp:3451 msgid "Rotate around X" msgstr "X 주위 회전" -#: src/libslic3r/PrintConfig.cpp:3231 +#: src/libslic3r/PrintConfig.cpp:3452 msgid "Rotation angle around the X axis in degrees." -msgstr "X 축을 중심 회전 각도 입니다." +msgstr "X 축을 중재봉선 회전 각도 입니다." -#: src/libslic3r/PrintConfig.cpp:3235 +#: src/libslic3r/PrintConfig.cpp:3456 msgid "Rotate around Y" msgstr "Y 주위로 회전" -#: src/libslic3r/PrintConfig.cpp:3236 +#: src/libslic3r/PrintConfig.cpp:3457 msgid "Rotation angle around the Y axis in degrees." -msgstr "Y 축을 중심 회전 각도 입니다." +msgstr "Y 축을 중재봉선 회전 각도 입니다." -#: src/libslic3r/PrintConfig.cpp:3241 +#: src/libslic3r/PrintConfig.cpp:3462 msgid "Scaling factor or percentage." msgstr "배율 인수 또는 백분율입니다." -#: src/libslic3r/PrintConfig.cpp:3246 +#: src/libslic3r/PrintConfig.cpp:3467 msgid "" "Detect unconnected parts in the given model(s) and split them into separate " "objects." msgstr "" -"지정 된 모델에서 연결 되지 않은 부품을 감지 하여 별도의 객체로 분할 합니다." +"지정 된 모델에서 연결 되지 않은 부품(Part)을 감지 하여 별도의 객체(object)로 " +"분할 합니다." -#: src/libslic3r/PrintConfig.cpp:3249 +#: src/libslic3r/PrintConfig.cpp:3470 msgid "Scale to Fit" msgstr "크기에 맞게 조정" -#: src/libslic3r/PrintConfig.cpp:3250 +#: src/libslic3r/PrintConfig.cpp:3471 msgid "Scale to fit the given volume." msgstr "지정 된 볼륨에 맞게 크기를 조정 합니다." -#: src/libslic3r/PrintConfig.cpp:3259 +#: src/libslic3r/PrintConfig.cpp:3480 msgid "Ignore non-existent config files" msgstr "존재 하지 않는 구성 파일 무시" -#: src/libslic3r/PrintConfig.cpp:3260 +#: src/libslic3r/PrintConfig.cpp:3481 msgid "Do not fail if a file supplied to --load does not exist." msgstr "로드에 제공 된 파일이 없는 경우 실패 하지 않습니다." -#: src/libslic3r/PrintConfig.cpp:3263 +#: src/libslic3r/PrintConfig.cpp:3484 msgid "Load config file" msgstr "구성 파일 로드" -#: src/libslic3r/PrintConfig.cpp:3264 +#: src/libslic3r/PrintConfig.cpp:3485 msgid "" "Load configuration from the specified file. It can be used more than once to " "load options from multiple files." @@ -8873,22 +10264,22 @@ msgstr "" "지정 된 파일에서 구성을 로드 합니다. 여러 파일에서 옵션을 로드 하는 데 두 번 " "이상 사용할 수 있습니다." -#: src/libslic3r/PrintConfig.cpp:3267 +#: src/libslic3r/PrintConfig.cpp:3488 msgid "Output File" -msgstr "출력 파일" +msgstr "출력파일" -#: src/libslic3r/PrintConfig.cpp:3268 +#: src/libslic3r/PrintConfig.cpp:3489 msgid "" "The file where the output will be written (if not specified, it will be " "based on the input file)." msgstr "" "출력이 기록 되는 파일 (지정 하지 않은 경우 입력 파일을 기반으로 합니다)." -#: src/libslic3r/PrintConfig.cpp:3278 +#: src/libslic3r/PrintConfig.cpp:3499 msgid "Data directory" msgstr "데이터 디렉터리" -#: src/libslic3r/PrintConfig.cpp:3279 +#: src/libslic3r/PrintConfig.cpp:3500 msgid "" "Load and store settings at the given directory. This is useful for " "maintaining different profiles or including configurations from a network " @@ -8897,23 +10288,25 @@ msgstr "" "지정 된 디렉터리에 설정을 로드 하 고 저장 합니다. 이 기능은 다른 프로 파일을 " "유지 관리 하거나 네트워크 스토리지의 구성을 포함 하는 데 유용 합니다." -#: src/libslic3r/PrintConfig.cpp:3282 +#: src/libslic3r/PrintConfig.cpp:3503 msgid "Logging level" msgstr "로깅 수준" -#: src/libslic3r/PrintConfig.cpp:3283 +#: src/libslic3r/PrintConfig.cpp:3504 msgid "" -"Messages with severity lower or eqal to the loglevel will be printed out. 0:" -"trace, 1:debug, 2:info, 3:warning, 4:error, 5:fatal" +"Sets logging sensitivity. 0:fatal, 1:error, 2:warning, 3:info, 4:debug, 5:" +"trace\n" +"For example. loglevel=2 logs fatal, error and warning level messages." msgstr "" -"로그 수준에 대한 심각도가 낮거나 eqal인 메시지가 인쇄됩니다. 0:추적, 1:디버" -"그, 2:정보, 3:경고, 4:오류, 5:치명적" +"로깅 민감도를 설정합니다. 0:치명적, 1:오류, 2:경고, 3:info, 4:디버그, 5:" +"trace\n" +"예를 들어. loglevel=2 치명적인, 오류 및 경고 수준 메시지를 기록합니다." -#: src/libslic3r/PrintConfig.cpp:3288 +#: src/libslic3r/PrintConfig.cpp:3510 msgid "Render with a software renderer" msgstr "소프트웨어 렌더러를 사용 하 여 렌더링" -#: src/libslic3r/PrintConfig.cpp:3289 +#: src/libslic3r/PrintConfig.cpp:3511 msgid "" "Render with a software renderer. The bundled MESA software renderer is " "loaded instead of the default OpenGL driver." @@ -8921,57 +10314,38 @@ msgstr "" "소프트웨어 렌더러를 사용 하여 렌더링 합니다. 번들 메사 소프트웨어 렌더러는 기" "본 OpenGL 드라이버 대신 로드 됩니다." -#: src/libslic3r/PrintObject.cpp:110 +#: src/libslic3r/PrintObject.cpp:108 msgid "Processing triangulated mesh" -msgstr "삼각 측정 메시 처리" +msgstr "삼각 측정 메쉬 처리" -#: src/libslic3r/PrintObject.cpp:141 +#: src/libslic3r/PrintObject.cpp:152 msgid "Generating perimeters" msgstr "둘레 생성" -#: src/libslic3r/PrintObject.cpp:251 +#: src/libslic3r/PrintObject.cpp:255 msgid "Preparing infill" msgstr "채우기 준비" -#: src/libslic3r/PrintObject.cpp:391 +#: src/libslic3r/PrintObject.cpp:395 msgid "Generating support material" msgstr "지원할 서포트 생성" -#: src/libslic3r/GCode/PreviewData.cpp:176 -msgid "Mixed" -msgstr "혼합" - -#: src/libslic3r/GCode/PreviewData.cpp:396 +#: src/libslic3r/GCode/PreviewData.cpp:347 msgid "Height (mm)" msgstr "높이 (mm)" -#: src/libslic3r/GCode/PreviewData.cpp:398 +#: src/libslic3r/GCode/PreviewData.cpp:349 msgid "Width (mm)" msgstr "폭 (mm)" -#: src/libslic3r/GCode/PreviewData.cpp:400 +#: src/libslic3r/GCode/PreviewData.cpp:351 msgid "Speed (mm/s)" msgstr "속도 (mm/s)" -#: src/libslic3r/GCode/PreviewData.cpp:402 -msgid "Volumetric flow rate (mm3/s)" -msgstr "용적 유량값 (mm3/s)" +#: src/libslic3r/GCode/PreviewData.cpp:353 +msgid "Fan Speed (%)" +msgstr "팬 속도(%)" -#: src/libslic3r/GCode/PreviewData.cpp:493 -msgid "Default print color" -msgstr "기본 인쇄 색상" - -#: src/libslic3r/GCode/PreviewData.cpp:500 -#, c-format -msgid "up to %.2f mm" -msgstr "최대%.2f mm" - -#: src/libslic3r/GCode/PreviewData.cpp:504 -#, c-format -msgid "above %.2f mm" -msgstr "above %.2f mm" - -#: src/libslic3r/GCode/PreviewData.cpp:509 -#, c-format -msgid "%.2f - %.2f mm" -msgstr "%.2f - %.2f mm" \ No newline at end of file +#: src/libslic3r/GCode/PreviewData.cpp:355 +msgid "Volumetric flow rate (mm³/s)" +msgstr "체적 유량(mm³/s)" diff --git a/resources/localization/list.txt b/resources/localization/list.txt index 93ee2f441d..3c2a956385 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -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 diff --git a/resources/localization/nl/PrusaSlicer.mo b/resources/localization/nl/PrusaSlicer.mo new file mode 100644 index 0000000000..e76870c765 Binary files /dev/null and b/resources/localization/nl/PrusaSlicer.mo differ diff --git a/resources/localization/nl/PrusaSlicer_nl.po b/resources/localization/nl/PrusaSlicer_nl.po new file mode 100644 index 0000000000..504897a1b0 --- /dev/null +++ b/resources/localization/nl/PrusaSlicer_nl.po @@ -0,0 +1,9960 @@ +msgid "" +msgstr "" +"Language: nl_NL\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 2.0.8\n" +"Project-Id-Version: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: Oleksandra Iushchenko \n" +"Language-Team: \n" + +#: src/slic3r/GUI/AboutDialog.cpp:39 src/slic3r/GUI/AboutDialog.cpp:291 +msgid "Portions copyright" +msgstr "Gedeeltelijk auteursrecht" + +#: src/slic3r/GUI/AboutDialog.cpp:127 src/slic3r/GUI/AboutDialog.cpp:256 +msgid "Copyright" +msgstr "Auteursrecht" + +#. TRN "Slic3r _is licensed under the_ License" +#: src/slic3r/GUI/AboutDialog.cpp:129 +msgid "" +"License agreements of all following programs (libraries) are part of " +"application license agreement" +msgstr "" +"Licentieovereenkomsten van alle programma's (en onderdelen) zijn deel van de " +"applicatielicentieovereenkomst" + +#: src/slic3r/GUI/AboutDialog.cpp:199 +#, c-format +msgid "About %s" +msgstr "Over %s" + +#: src/slic3r/GUI/AboutDialog.cpp:231 src/slic3r/GUI/MainFrame.cpp:63 +msgid "Version" +msgstr "Versie" + +#. TRN "Slic3r _is licensed under the_ License" +#: src/slic3r/GUI/AboutDialog.cpp:258 +msgid "is licensed under the" +msgstr "is gelicenseerd onder de" + +#: src/slic3r/GUI/AboutDialog.cpp:259 +msgid "GNU Affero General Public License, version 3" +msgstr "GNU Affero General Public License, versie 3" + +#: src/slic3r/GUI/AboutDialog.cpp:260 +msgid "" +"PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap " +"community." +msgstr "" +"PrusaSlicer is gebaseerd op 'Slic3r' van Alessandro Ranellucci en de RepRap " +"community." + +#: src/slic3r/GUI/AboutDialog.cpp:261 +msgid "" +"Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, " +"Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and " +"numerous others." +msgstr "" +"Bijdragen van Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr " +"Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik, Simon " +"Tillema en vele anderen." + +#: src/slic3r/GUI/AppConfig.cpp:105 +msgid "" +"Error parsing PrusaSlicer config file, it is probably corrupted. Try to " +"manually delete the file to recover from the error. Your user profiles will " +"not be affected." +msgstr "" +"Fout in het configuratiebestand. Het is waarschijnlijk beschadigd. Probeer " +"de fout handmatig te verwijderen om het te herstellen. Dit heeft geen effect " +"op uw gebruikersprofielen." + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:100 +msgid "" +"To except of redundant tool manipulation, \n" +"Color change(s) for unused extruder(s) was(were) deleted" +msgstr "" +"Kleurveranderingen voor ongebruikte extruders worden verwijderd,\n" +"met uitzondering van overbodige extruders." + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:101 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3292 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3367 src/slic3r/GUI/Plater.cpp:135 +msgid "Info" +msgstr "Info" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:110 +msgid "" +"Copying of the temporary G-code to the output G-code failed. Maybe the SD " +"card is write locked?" +msgstr "" +"Kopiëren van de tijdelijke G-code naar de output is mislukt. Is de SD-kaart " +"geblokkeerd?" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:111 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:461 +msgid "Running post-processing scripts" +msgstr "Uitvoeren van nabewerkingsscripts" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:113 +msgid "G-code file exported to %1%" +msgstr "gcode-bestand geëxporteerd naar %1%" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:117 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:167 +msgid "Slicing complete" +msgstr "Slicen compleet" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:163 +msgid "Masked SLA file exported to %1%" +msgstr "Verborgen SLA-bestand geëxporteerd naar %1%" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:205 +#, c-format +msgid "" +"%s has encountered an error. It was likely caused by running out of memory. " +"If you are sure you have enough RAM on your system, this may also be a bug " +"and we would be glad if you reported it." +msgstr "" +"%s veroorzaakte een fout. Dit komt mogelijk door een geheugentekort. Als u " +"zeker weet dat u genoeg RAM heeft, kan dit ook een andere systeemfout zijn. " +"We waarderen het als u dit meldt." + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:463 +msgid "Copying of the temporary G-code to the output G-code failed" +msgstr "Kopiëren van de tijdelijke G-code naar de output is mislukt" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:488 +msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" +msgstr "" +"Plannen van de upload naar '%1% '. Zie Venster -> Printhost uploadwachtrij" + +#: src/slic3r/GUI/BedShapeDialog.cpp:68 src/slic3r/GUI/GUI_ObjectList.cpp:1932 +msgid "Shape" +msgstr "Vorm" + +#: src/slic3r/GUI/BedShapeDialog.cpp:75 +msgid "Rectangular" +msgstr "Rechthoekig" + +#: src/slic3r/GUI/BedShapeDialog.cpp:79 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:232 src/slic3r/GUI/Plater.cpp:154 +#: src/slic3r/GUI/Tab.cpp:2292 +msgid "Size" +msgstr "Grootte" + +#: src/slic3r/GUI/BedShapeDialog.cpp:80 +msgid "Size in X and Y of the rectangular plate." +msgstr "Breedte en diepte van rechthoekig bed." + +#: src/slic3r/GUI/BedShapeDialog.cpp:86 +msgid "Origin" +msgstr "Nulpunt" + +#: src/slic3r/GUI/BedShapeDialog.cpp:87 +msgid "" +"Distance of the 0,0 G-code coordinate from the front left corner of the " +"rectangle." +msgstr "" +"Afstand vanaf het nulpunt in de G-code tot de linkervoorhoek van de " +"rechthoek." + +#: src/slic3r/GUI/BedShapeDialog.cpp:91 +msgid "Circular" +msgstr "Rond" + +#: src/slic3r/GUI/BedShapeDialog.cpp:94 src/slic3r/GUI/ConfigWizard.cpp:218 +#: src/slic3r/GUI/ConfigWizard.cpp:926 src/slic3r/GUI/ConfigWizard.cpp:940 +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:135 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:333 +#: src/slic3r/GUI/WipeTowerDialog.cpp:85 src/slic3r/GUI/wxExtensions.cpp:628 +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:87 +#: src/libslic3r/PrintConfig.cpp:75 src/libslic3r/PrintConfig.cpp:82 +#: src/libslic3r/PrintConfig.cpp:91 src/libslic3r/PrintConfig.cpp:225 +#: src/libslic3r/PrintConfig.cpp:300 src/libslic3r/PrintConfig.cpp:308 +#: src/libslic3r/PrintConfig.cpp:358 src/libslic3r/PrintConfig.cpp:368 +#: src/libslic3r/PrintConfig.cpp:494 src/libslic3r/PrintConfig.cpp:505 +#: src/libslic3r/PrintConfig.cpp:523 src/libslic3r/PrintConfig.cpp:702 +#: src/libslic3r/PrintConfig.cpp:1228 src/libslic3r/PrintConfig.cpp:1289 +#: src/libslic3r/PrintConfig.cpp:1307 src/libslic3r/PrintConfig.cpp:1325 +#: src/libslic3r/PrintConfig.cpp:1379 src/libslic3r/PrintConfig.cpp:1389 +#: src/libslic3r/PrintConfig.cpp:1511 src/libslic3r/PrintConfig.cpp:1519 +#: src/libslic3r/PrintConfig.cpp:1560 src/libslic3r/PrintConfig.cpp:1568 +#: src/libslic3r/PrintConfig.cpp:1578 src/libslic3r/PrintConfig.cpp:1586 +#: src/libslic3r/PrintConfig.cpp:1594 src/libslic3r/PrintConfig.cpp:1677 +#: src/libslic3r/PrintConfig.cpp:1903 src/libslic3r/PrintConfig.cpp:1974 +#: src/libslic3r/PrintConfig.cpp:2008 src/libslic3r/PrintConfig.cpp:2203 +#: src/libslic3r/PrintConfig.cpp:2210 src/libslic3r/PrintConfig.cpp:2217 +#: src/libslic3r/PrintConfig.cpp:2247 src/libslic3r/PrintConfig.cpp:2257 +#: src/libslic3r/PrintConfig.cpp:2267 src/libslic3r/PrintConfig.cpp:2452 +#: src/libslic3r/PrintConfig.cpp:2591 src/libslic3r/PrintConfig.cpp:2600 +#: src/libslic3r/PrintConfig.cpp:2609 src/libslic3r/PrintConfig.cpp:2619 +#: src/libslic3r/PrintConfig.cpp:2663 src/libslic3r/PrintConfig.cpp:2673 +#: src/libslic3r/PrintConfig.cpp:2685 src/libslic3r/PrintConfig.cpp:2705 +#: src/libslic3r/PrintConfig.cpp:2715 src/libslic3r/PrintConfig.cpp:2725 +#: src/libslic3r/PrintConfig.cpp:2743 src/libslic3r/PrintConfig.cpp:2758 +#: src/libslic3r/PrintConfig.cpp:2772 src/libslic3r/PrintConfig.cpp:2783 +#: src/libslic3r/PrintConfig.cpp:2796 src/libslic3r/PrintConfig.cpp:2841 +#: src/libslic3r/PrintConfig.cpp:2851 src/libslic3r/PrintConfig.cpp:2860 +#: src/libslic3r/PrintConfig.cpp:2870 +msgid "mm" +msgstr "mm" + +#: src/slic3r/GUI/BedShapeDialog.cpp:95 src/libslic3r/PrintConfig.cpp:699 +msgid "Diameter" +msgstr "Diameter" + +#: src/slic3r/GUI/BedShapeDialog.cpp:96 +msgid "" +"Diameter of the print bed. It is assumed that origin (0,0) is located in the " +"center." +msgstr "" +"Diameter van het printbed. Aangenomen wordt dat het nulpunt in het midden " +"ligt." + +#: src/slic3r/GUI/BedShapeDialog.cpp:100 src/slic3r/GUI/GUI_Preview.cpp:248 +#: src/libslic3r/ExtrusionEntity.cpp:322 +msgid "Custom" +msgstr "Custom" + +#: src/slic3r/GUI/BedShapeDialog.cpp:104 +msgid "Load shape from STL..." +msgstr "Laad vorm van STL-bestand..." + +#: src/slic3r/GUI/BedShapeDialog.cpp:157 +msgid "Settings" +msgstr "Instellingen" + +#: src/slic3r/GUI/BedShapeDialog.cpp:174 +msgid "Texture" +msgstr "Textuur" + +#: src/slic3r/GUI/BedShapeDialog.cpp:184 src/slic3r/GUI/BedShapeDialog.cpp:263 +msgid "Load..." +msgstr "Laad..." + +#: src/slic3r/GUI/BedShapeDialog.cpp:192 src/slic3r/GUI/BedShapeDialog.cpp:271 +#: src/slic3r/GUI/Tab.cpp:3080 +msgid "Remove" +msgstr "Verwijder" + +#: src/slic3r/GUI/BedShapeDialog.cpp:225 src/slic3r/GUI/BedShapeDialog.cpp:304 +msgid "Not found: " +msgstr "Niet gevonden: " + +#: src/slic3r/GUI/BedShapeDialog.cpp:253 +msgid "Model" +msgstr "Model" + +#: src/slic3r/GUI/BedShapeDialog.cpp:489 +msgid "Choose an STL file to import bed shape from:" +msgstr "Kies een STL-bestand om te importeren als vorm van het bed:" + +#: src/slic3r/GUI/BedShapeDialog.cpp:496 src/slic3r/GUI/BedShapeDialog.cpp:545 +#: src/slic3r/GUI/BedShapeDialog.cpp:570 +msgid "Invalid file format." +msgstr "Ongeldig bestandsformaat." + +#: src/slic3r/GUI/BedShapeDialog.cpp:507 +msgid "Error! Invalid model" +msgstr "Fout! Ongeldig model" + +#: src/slic3r/GUI/BedShapeDialog.cpp:515 +msgid "The selected file contains no geometry." +msgstr "Het geselecteerde bestand bevat geen geometrie." + +#: src/slic3r/GUI/BedShapeDialog.cpp:519 +msgid "" +"The selected file contains several disjoint areas. This is not supported." +msgstr "" +"Het geselecteerde bestand bevat niet-verbonden delen. Dit wordt niet " +"ondersteund." + +#: src/slic3r/GUI/BedShapeDialog.cpp:534 +msgid "Choose a file to import bed texture from (PNG/SVG):" +msgstr "Kies een PNG- of SVG-bestand als textuur voor het bed:" + +#: src/slic3r/GUI/BedShapeDialog.cpp:559 +msgid "Choose an STL file to import bed model from:" +msgstr "Kies een STL-bestand als vorm voor het bed:" + +#: src/slic3r/GUI/BedShapeDialog.hpp:59 src/slic3r/GUI/ConfigWizard.cpp:885 +msgid "Bed Shape" +msgstr "Vorm van het bed" + +#: src/slic3r/GUI/BonjourDialog.cpp:55 +msgid "Network lookup" +msgstr "Zoeken naar netwerk" + +#: src/slic3r/GUI/BonjourDialog.cpp:72 +msgid "Address" +msgstr "Adres" + +#: src/slic3r/GUI/BonjourDialog.cpp:73 +msgid "Hostname" +msgstr "Hostnaam" + +#: src/slic3r/GUI/BonjourDialog.cpp:74 +msgid "Service name" +msgstr "Servicenaam" + +#: src/slic3r/GUI/BonjourDialog.cpp:76 +msgid "OctoPrint version" +msgstr "OctoPrint-versie" + +#: src/slic3r/GUI/BonjourDialog.cpp:218 +msgid "Searching for devices" +msgstr "Zoeken naar apparaten" + +#: src/slic3r/GUI/BonjourDialog.cpp:225 +msgid "Finished" +msgstr "Voltooid" + +#: src/slic3r/GUI/ButtonsDescription.cpp:16 +msgid "Buttons And Text Colors Description" +msgstr "Knoppen- en tekstkleurbeschrijving" + +#: src/slic3r/GUI/ButtonsDescription.cpp:36 +msgid "Value is the same as the system value" +msgstr "Waarde is gelijk aan standaardwaarde" + +#: src/slic3r/GUI/ButtonsDescription.cpp:53 +msgid "" +"Value was changed and is not equal to the system value or the last saved " +"preset" +msgstr "" +"Waarde is veranderd en is niet gelijk aan standaardwaarde of laatst " +"opgeslagen waarde" + +#: src/slic3r/GUI/ConfigManipulation.cpp:48 +msgid "" +"Zero layer height is not valid.\n" +"\n" +"The layer height will be reset to 0.01." +msgstr "" +"Een laagdikte van 0 is niet mogelijk.\n" +"\n" +"De laagdikte wordt ingesteld op 0,01." + +#: src/slic3r/GUI/ConfigManipulation.cpp:49 +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 src/slic3r/GUI/Tab.cpp:1039 +#: src/libslic3r/PrintConfig.cpp:71 +msgid "Layer height" +msgstr "Laagdikte" + +#: src/slic3r/GUI/ConfigManipulation.cpp:60 +msgid "" +"Zero first layer height is not valid.\n" +"\n" +"The first layer height will be reset to 0.01." +msgstr "" +"Een laagdikte voor de eerste laag van 0 is niet mogelijk.\n" +"\n" +"De laagdikte voor de eerste laag wordt ingesteld op 0,01." + +#: src/slic3r/GUI/ConfigManipulation.cpp:61 src/libslic3r/PrintConfig.cpp:878 +msgid "First layer height" +msgstr "Laagdikte eerste laag" + +#: src/slic3r/GUI/ConfigManipulation.cpp:75 +#, no-c-format +msgid "" +"The Spiral Vase mode requires:\n" +"- one perimeter\n" +"- no top solid layers\n" +"- 0% fill density\n" +"- no support material\n" +"- inactive Ensure vertical shell thickness" +msgstr "" +"De spiraalmodus vereist:\n" +"- één perimeter\n" +"- geen toplagen\n" +"- vullingsdichtheid van 0%\n" +"- geen support\n" +"- instelling 'Garandeer verticale shelldikte' uit" + +#: src/slic3r/GUI/ConfigManipulation.cpp:82 +msgid "Shall I adjust those settings in order to enable Spiral Vase?" +msgstr "" +"Moeten deze instellingen aangepast worden om de spiraalmodus te activeren?" + +#: src/slic3r/GUI/ConfigManipulation.cpp:83 +msgid "Spiral Vase" +msgstr "Spiraalmodus" + +#: src/slic3r/GUI/ConfigManipulation.cpp:107 +msgid "" +"The Wipe Tower currently supports the non-soluble supports only\n" +"if they are printed with the current extruder without triggering a tool " +"change.\n" +"(both support_material_extruder and support_material_interface_extruder need " +"to be set to 0)." +msgstr "" +"Het afveegblok ondersteunt momenteel alleen niet-oplosbare materialen\n" +"als deze met de huidige extruder geprint worden zonder dat er een toolwissel " +"plaatsvindt\n" +"(zowel 'support_material_extruder' als 'support_material_interface_extruder' " +"moeten op 0 gezet worden)." + +#: src/slic3r/GUI/ConfigManipulation.cpp:111 +msgid "Shall I adjust those settings in order to enable the Wipe Tower?" +msgstr "" +"Moeten deze instellingen aangepast worden om het afveegblok te activeren?" + +#: src/slic3r/GUI/ConfigManipulation.cpp:112 +#: src/slic3r/GUI/ConfigManipulation.cpp:132 +msgid "Wipe Tower" +msgstr "Afveegblok" + +#: src/slic3r/GUI/ConfigManipulation.cpp:128 +msgid "" +"For the Wipe Tower to work with the soluble supports, the support layers\n" +"need to be synchronized with the object layers." +msgstr "" +"De supportlagen voor het afveegblok moeten gesynchroniseerd zijn met de\n" +"objectlagen om met oplosbaar support te werken." + +#: src/slic3r/GUI/ConfigManipulation.cpp:131 +msgid "Shall I synchronize support layers in order to enable the Wipe Tower?" +msgstr "" +"Moeten de supportlagen gesynchroniseerd worden om het afveegblok te " +"activeren?" + +#: src/slic3r/GUI/ConfigManipulation.cpp:151 +msgid "" +"Supports work better, if the following feature is enabled:\n" +"- Detect bridging perimeters" +msgstr "" +"Support werkt beter als de volgende instellingen aan staan:\n" +"- Detecteer brugperimeters" + +#: src/slic3r/GUI/ConfigManipulation.cpp:154 +msgid "Shall I adjust those settings for supports?" +msgstr "Moeten deze instellingen aangepast worden voor het support?" + +#: src/slic3r/GUI/ConfigManipulation.cpp:155 +msgid "Support Generator" +msgstr "Supportgenerator" + +#: src/slic3r/GUI/ConfigManipulation.cpp:200 +msgid "The %1% infill pattern is not supposed to work at 100%% density." +msgstr "Het %1% vullingspatroon werkt niet bij een dichtheid van 100%%." + +#: src/slic3r/GUI/ConfigManipulation.cpp:202 +msgid "Shall I switch to rectilinear fill pattern?" +msgstr "Moet omgeschakeld worden naar een rechtlijnig vulpatroon?" + +#: src/slic3r/GUI/ConfigManipulation.cpp:203 +#: src/slic3r/GUI/GUI_ObjectList.cpp:35 src/slic3r/GUI/GUI_ObjectList.cpp:89 +#: src/slic3r/GUI/GUI_ObjectList.cpp:609 src/slic3r/GUI/Plater.cpp:516 +#: src/slic3r/GUI/Tab.cpp:1071 src/slic3r/GUI/Tab.cpp:1072 +#: src/libslic3r/PrintConfig.cpp:182 src/libslic3r/PrintConfig.cpp:405 +#: src/libslic3r/PrintConfig.cpp:425 src/libslic3r/PrintConfig.cpp:765 +#: src/libslic3r/PrintConfig.cpp:779 src/libslic3r/PrintConfig.cpp:816 +#: src/libslic3r/PrintConfig.cpp:970 src/libslic3r/PrintConfig.cpp:980 +#: src/libslic3r/PrintConfig.cpp:998 src/libslic3r/PrintConfig.cpp:1017 +#: src/libslic3r/PrintConfig.cpp:1036 src/libslic3r/PrintConfig.cpp:1724 +#: src/libslic3r/PrintConfig.cpp:1741 +msgid "Infill" +msgstr "Vulling" + +#: src/slic3r/GUI/ConfigManipulation.cpp:304 +msgid "Head penetration should not be greater than the head width." +msgstr "De kopinsteek mag niet groter zijn dan de kopbreedte." + +#: src/slic3r/GUI/ConfigManipulation.cpp:306 +msgid "Invalid Head penetration" +msgstr "Ongeldige kopinsteek" + +#: src/slic3r/GUI/ConfigManipulation.cpp:317 +msgid "Pinhead diameter should be smaller than the pillar diameter." +msgstr "De pinkopdiameter moet kleiner zijn dan de pijlerdiameter." + +#: src/slic3r/GUI/ConfigManipulation.cpp:319 +msgid "Invalid pinhead diameter" +msgstr "Ongeldige pinkopdiameter" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:18 +msgid "Upgrade" +msgstr "Upgrade" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:20 +msgid "Downgrade" +msgstr "Downgrade" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:22 +msgid "Before roll back" +msgstr "Voor het teruggaan" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:24 +msgid "User" +msgstr "Gebruiker" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:27 +msgid "Unknown" +msgstr "Onbekend" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:43 +msgid "Active" +msgstr "Actief" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:50 +msgid "PrusaSlicer version" +msgstr "PrusaSlicer-versie" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:51 src/slic3r/GUI/Preset.cpp:1408 +msgid "print" +msgstr "print" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:52 +msgid "filaments" +msgstr "filamenten" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:53 src/slic3r/GUI/Preset.cpp:1412 +msgid "printer" +msgstr "printer" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:57 src/slic3r/GUI/Tab.cpp:961 +msgid "vendor" +msgstr "leverancier" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:57 +msgid "version" +msgstr "versie" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:58 +msgid "min PrusaSlicer version" +msgstr "minimale PrusaSlicer-versie" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:60 +msgid "max PrusaSlicer version" +msgstr "maximale PrusaSlicer-versie" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:63 +msgid "model" +msgstr "model" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:63 +msgid "variants" +msgstr "varianten" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:75 +#, c-format +msgid "Incompatible with this %s" +msgstr "Incompatibel met deze %s" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:78 +msgid "Activate" +msgstr "Activeer" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:104 +msgid "Configuration Snapshots" +msgstr "Configuratiesnapshots" + +#: src/slic3r/GUI/ConfigWizard.cpp:218 +msgid "nozzle" +msgstr "nozzle" + +#: src/slic3r/GUI/ConfigWizard.cpp:222 +msgid "Alternate nozzles:" +msgstr "Alternatieve nozzles:" + +#: src/slic3r/GUI/ConfigWizard.cpp:289 +msgid "All standard" +msgstr "Alle standaard" + +#: src/slic3r/GUI/ConfigWizard.cpp:289 +msgid "Standard" +msgstr "Standaard" + +#: src/slic3r/GUI/ConfigWizard.cpp:290 src/slic3r/GUI/ConfigWizard.cpp:545 +#: src/slic3r/GUI/Tab.cpp:3130 +msgid "All" +msgstr "Alle" + +#: src/slic3r/GUI/ConfigWizard.cpp:291 src/slic3r/GUI/ConfigWizard.cpp:546 +#: src/slic3r/GUI/Plater.cpp:488 src/slic3r/GUI/Plater.cpp:628 +#: src/libslic3r/ExtrusionEntity.cpp:309 +msgid "None" +msgstr "Geen" + +#: src/slic3r/GUI/ConfigWizard.cpp:412 +#, c-format +msgid "Welcome to the %s Configuration Assistant" +msgstr "Welkom bij de %s configuratie-assistent" + +#: src/slic3r/GUI/ConfigWizard.cpp:414 +#, c-format +msgid "Welcome to the %s Configuration Wizard" +msgstr "Welkom bij de %s configuratiewizard" + +#: src/slic3r/GUI/ConfigWizard.cpp:416 +msgid "Welcome" +msgstr "Welkom" + +#: src/slic3r/GUI/ConfigWizard.cpp:418 +#, c-format +msgid "" +"Hello, welcome to %s! This %s helps you with the initial configuration; just " +"a few settings and you will be ready to print." +msgstr "" +"Hallo, welkom bij %s! Deze %s helpt met de eerste configuratie; nog een paar " +"instellingen en de printer kan gebruikt worden." + +#: src/slic3r/GUI/ConfigWizard.cpp:423 +msgid "" +"Remove user profiles - install from scratch (a snapshot will be taken " +"beforehand)" +msgstr "" +"Verwijder alle gebruiksprofielen; installeer vanaf scratch (vooraf wordt een " +"snapshot genomen)" + +#: src/slic3r/GUI/ConfigWizard.cpp:466 +#, c-format +msgid "%s Family" +msgstr "%s serie" + +#: src/slic3r/GUI/ConfigWizard.cpp:537 +msgid "Vendor:" +msgstr "Leverancier:" + +#: src/slic3r/GUI/ConfigWizard.cpp:538 +msgid "Profile:" +msgstr "Profiel:" + +#: src/slic3r/GUI/ConfigWizard.cpp:575 src/slic3r/GUI/ConfigWizard.cpp:603 +msgid "(All)" +msgstr "(Alle)" + +#: src/slic3r/GUI/ConfigWizard.cpp:704 +msgid "Custom Printer Setup" +msgstr "Custom printersetup" + +#: src/slic3r/GUI/ConfigWizard.cpp:704 +msgid "Custom Printer" +msgstr "Custom printer" + +#: src/slic3r/GUI/ConfigWizard.cpp:706 +msgid "Define a custom printer profile" +msgstr "Definieer een custom-printerprofiel" + +#: src/slic3r/GUI/ConfigWizard.cpp:708 +msgid "Custom profile name:" +msgstr "Custom-profielnaam:" + +#: src/slic3r/GUI/ConfigWizard.cpp:732 +msgid "Automatic updates" +msgstr "Automatische updates" + +#: src/slic3r/GUI/ConfigWizard.cpp:732 +msgid "Updates" +msgstr "Updates" + +#: src/slic3r/GUI/ConfigWizard.cpp:740 src/slic3r/GUI/Preferences.cpp:69 +msgid "Check for application updates" +msgstr "Controleer op programma-updates" + +#: src/slic3r/GUI/ConfigWizard.cpp:744 +#, c-format +msgid "" +"If enabled, %s checks for new application versions online. When a new " +"version becomes available, a notification is displayed at the next " +"application startup (never during program usage). This is only a " +"notification mechanisms, no automatic installation is done." +msgstr "" +"Als dit aan staat zal %s online checken op nieuwe programmaversies. Als er " +"een nieuwe versie beschikbaar komt, zal een melding getoond worden bij de " +"volgende keer opstarten (nooit tijdens gebruik van het programma). Dit is " +"slechts een melding; er zal geen automatische installatie plaatsvinden." + +#: src/slic3r/GUI/ConfigWizard.cpp:750 src/slic3r/GUI/Preferences.cpp:77 +msgid "Update built-in Presets automatically" +msgstr "Update ingebouwde presets automatisch" + +#: src/slic3r/GUI/ConfigWizard.cpp:754 +#, c-format +msgid "" +"If enabled, %s downloads updates of built-in system presets in the " +"background.These updates are downloaded into a separate temporary location." +"When a new preset version becomes available it is offered at application " +"startup." +msgstr "" +"Als dit aan staat zal %s updates of ingebouwde systeem-presets op de " +"achtergrond downloaden. Deze updates worden gedownload naar een tijdelijke " +"locatie. Wanneer een nieuwe versie beschikbaar komt zal deze getoond worden " +"bij het opstarten." + +#: src/slic3r/GUI/ConfigWizard.cpp:757 +msgid "" +"Updates are never applied without user's consent and never overwrite user's " +"customized settings." +msgstr "" +"Updates worden nooit geïnstalleerd en overschreven zonder toestemming van de " +"gebruiker." + +#: src/slic3r/GUI/ConfigWizard.cpp:762 +msgid "" +"Additionally a backup snapshot of the whole configuration is created before " +"an update is applied." +msgstr "" +"Voor een update wordt geïnstalleerd wordt daarnaast een backup-snapshot van " +"de hele configuratie gemaakt." + +#: src/slic3r/GUI/ConfigWizard.cpp:769 +msgid "View mode" +msgstr "Weergavemodus" + +#: src/slic3r/GUI/ConfigWizard.cpp:771 +msgid "" +"PrusaSlicer's user interfaces comes in three variants:\n" +"Simple, Advanced, and Expert.\n" +"The Simple mode shows only the most frequently used settings relevant for " +"regular 3D printing. The other two offer progressively more sophisticated " +"fine-tuning, they are suitable for advanced and expert users, respectively." +msgstr "" +"De gebruikersinterfaces van PrusaSlicer kent drie varianten:\n" +"Eenvoudig, Geavanceerd en Expert.\n" +"De eenvoudige modus laat alleen de meest gebruikte instellingen zien. De " +"andere twee bieden meer geavanceerde finetuning. Ze zijn geschikt voor " +"respectievelijk gevorderde en deskundige gebruikers." + +#: src/slic3r/GUI/ConfigWizard.cpp:776 +msgid "Simple mode" +msgstr "Eenvoudige modus" + +#: src/slic3r/GUI/ConfigWizard.cpp:777 +msgid "Advanced mode" +msgstr "Geavanceerde modus" + +#: src/slic3r/GUI/ConfigWizard.cpp:778 +msgid "Expert mode" +msgstr "Expertmodus" + +#: src/slic3r/GUI/ConfigWizard.cpp:812 +msgid "Other Vendors" +msgstr "Overige leveranciers" + +#: src/slic3r/GUI/ConfigWizard.cpp:816 +#, c-format +msgid "Pick another vendor supported by %s" +msgstr "Kies een andere leverancier die ondersteunt wordt door %s" + +#: src/slic3r/GUI/ConfigWizard.cpp:847 +msgid "Firmware Type" +msgstr "Firmwaretype" + +#: src/slic3r/GUI/ConfigWizard.cpp:847 src/slic3r/GUI/Tab.cpp:1917 +msgid "Firmware" +msgstr "Firmware" + +#: src/slic3r/GUI/ConfigWizard.cpp:851 +msgid "Choose the type of firmware used by your printer." +msgstr "Kies het firmwaretype dat de printer gebruikt." + +#: src/slic3r/GUI/ConfigWizard.cpp:885 +msgid "Bed Shape and Size" +msgstr "Grootte en vorm van het bed" + +#: src/slic3r/GUI/ConfigWizard.cpp:888 +msgid "Set the shape of your printer's bed." +msgstr "Stel de vorm van het printbed in." + +#: src/slic3r/GUI/ConfigWizard.cpp:908 +msgid "Filament and Nozzle Diameters" +msgstr "Filament- en nozzlediameters" + +#: src/slic3r/GUI/ConfigWizard.cpp:908 +msgid "Print Diameters" +msgstr "Printdiameters" + +#: src/slic3r/GUI/ConfigWizard.cpp:922 +msgid "Enter the diameter of your printer's hot end nozzle." +msgstr "Voer de nozzlediameter in." + +#: src/slic3r/GUI/ConfigWizard.cpp:925 +msgid "Nozzle Diameter:" +msgstr "Nozzlediameter:" + +#: src/slic3r/GUI/ConfigWizard.cpp:935 +msgid "Enter the diameter of your filament." +msgstr "Voer de filamentdiameter in." + +#: src/slic3r/GUI/ConfigWizard.cpp:936 +msgid "" +"Good precision is required, so use a caliper and do multiple measurements " +"along the filament, then compute the average." +msgstr "" +"Nauwkeurigheid is belangrijk. Gebruik een schuifmaat en meet de diameter op " +"meerdere plekken over de gehele rol. Bereken daarna het gemiddelde." + +#: src/slic3r/GUI/ConfigWizard.cpp:939 +msgid "Filament Diameter:" +msgstr "Filamentdiameter:" + +#: src/slic3r/GUI/ConfigWizard.cpp:973 +msgid "Extruder and Bed Temperatures" +msgstr "Extruder- en bedtemperaturen" + +#: src/slic3r/GUI/ConfigWizard.cpp:973 +msgid "Temperatures" +msgstr "Temperaturen" + +#: src/slic3r/GUI/ConfigWizard.cpp:989 +msgid "Enter the temperature needed for extruding your filament." +msgstr "Voer de benodigde temperatuur in om het filament te extruderen." + +#: src/slic3r/GUI/ConfigWizard.cpp:990 +msgid "A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS." +msgstr "Een vuistregel is 180 - 230 °C voor PLA en 220 - 260 °C voor ABS." + +#: src/slic3r/GUI/ConfigWizard.cpp:993 +msgid "Extrusion Temperature:" +msgstr "Extrusietemperatuur:" + +#: src/slic3r/GUI/ConfigWizard.cpp:994 src/slic3r/GUI/ConfigWizard.cpp:1008 +msgid "°C" +msgstr "°C" + +#: src/slic3r/GUI/ConfigWizard.cpp:1003 +msgid "" +"Enter the bed temperature needed for getting your filament to stick to your " +"heated bed." +msgstr "" +"Voer de temperatuur van het bed in om het filament goed te laten hechten aan " +"het bed." + +#: src/slic3r/GUI/ConfigWizard.cpp:1004 +msgid "" +"A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have " +"no heated bed." +msgstr "" +"Een vuistregel is 60 °C voor PLA en 110 °C voor ABS. Stel in op 0 als de " +"printer geen verwarmd bed heeft." + +#: src/slic3r/GUI/ConfigWizard.cpp:1007 +msgid "Bed Temperature:" +msgstr "Bedtemperatuur:" + +#: src/slic3r/GUI/ConfigWizard.cpp:1426 src/slic3r/GUI/ConfigWizard.cpp:1862 +msgid "Filaments" +msgstr "Filamenten" + +#: src/slic3r/GUI/ConfigWizard.cpp:1426 src/slic3r/GUI/ConfigWizard.cpp:1864 +msgid "SLA Materials" +msgstr "SLA-materialen" + +#: src/slic3r/GUI/ConfigWizard.cpp:1480 +msgid "FFF Technology Printers" +msgstr "FDM-technologie printers" + +#: src/slic3r/GUI/ConfigWizard.cpp:1485 +msgid "SLA Technology Printers" +msgstr "SLA-technologie printers" + +#: src/slic3r/GUI/ConfigWizard.cpp:1625 +msgid "You have to select at least one filament for selected printers" +msgstr "Selecteer tenminste één filament voor de geselecteerde printer(s)" + +#: src/slic3r/GUI/ConfigWizard.cpp:1631 +msgid "You have to select at least one material for selected printers" +msgstr "Selecteer tenminste één materiaal voor de geselecteerde printer(s)" + +#: src/slic3r/GUI/ConfigWizard.cpp:1831 +msgid "Select all standard printers" +msgstr "Selecteer alle standaard printers" + +#: src/slic3r/GUI/ConfigWizard.cpp:1834 +msgid "< &Back" +msgstr "< Terug" + +#: src/slic3r/GUI/ConfigWizard.cpp:1835 +msgid "&Next >" +msgstr "Volgende >" + +#: src/slic3r/GUI/ConfigWizard.cpp:1836 +msgid "&Finish" +msgstr "Voltooien" + +#: src/slic3r/GUI/ConfigWizard.cpp:1837 src/slic3r/GUI/FirmwareDialog.cpp:151 +#: src/slic3r/GUI/ProgressStatusBar.cpp:27 +msgid "Cancel" +msgstr "Annuleren" + +#: src/slic3r/GUI/ConfigWizard.cpp:1850 +msgid "Prusa FFF Technology Printers" +msgstr "Prusa FDM-technologie printers" + +#: src/slic3r/GUI/ConfigWizard.cpp:1853 +msgid "Prusa MSLA Technology Printers" +msgstr "Prusa MSLA-technologie printers" + +#: src/slic3r/GUI/ConfigWizard.cpp:1862 +msgid "Filament Profiles Selection" +msgstr "Profielselectie voor filament" + +#: src/slic3r/GUI/ConfigWizard.cpp:1862 src/slic3r/GUI/GUI_ObjectList.cpp:3415 +msgid "Type:" +msgstr "Type:" + +#: src/slic3r/GUI/ConfigWizard.cpp:1864 +msgid "SLA Material Profiles Selection" +msgstr "Profielselectie voor SLA materialen" + +#: src/slic3r/GUI/ConfigWizard.cpp:1864 +msgid "Layer height:" +msgstr "Laagdikte:" + +#: src/slic3r/GUI/ConfigWizard.cpp:1962 +msgid "Configuration Assistant" +msgstr "Configuratie-assistent" + +#: src/slic3r/GUI/ConfigWizard.cpp:1963 +msgid "Configuration &Assistant" +msgstr "Configuratie-assistent" + +#: src/slic3r/GUI/ConfigWizard.cpp:1965 +msgid "Configuration Wizard" +msgstr "Configuratiewizard" + +#: src/slic3r/GUI/ConfigWizard.cpp:1966 +msgid "Configuration &Wizard" +msgstr "Configuratiewizard" + +#: src/slic3r/GUI/Field.cpp:131 +msgid "default value" +msgstr "standaardwaarde" + +#: src/slic3r/GUI/Field.cpp:134 +msgid "parameter name" +msgstr "parameternaam" + +#: src/slic3r/GUI/Field.cpp:145 src/slic3r/GUI/OptionsGroup.cpp:570 +msgid "N/A" +msgstr "n.v.t." + +#: src/slic3r/GUI/Field.cpp:170 +#, c-format +msgid "%s doesn't support percentage" +msgstr "%s ondersteunt geen percentage" + +#: src/slic3r/GUI/Field.cpp:190 src/slic3r/GUI/Field.cpp:221 +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:376 +msgid "Invalid numeric input." +msgstr "Ongeldige numerieke invoer." + +#: src/slic3r/GUI/Field.cpp:199 +msgid "Input value is out of range" +msgstr "Ingevoerde waarde valt buiten het bereik" + +#: src/slic3r/GUI/Field.cpp:235 +#, c-format +msgid "" +"Do you mean %s%% instead of %s %s?\n" +"Select YES if you want to change this value to %s%%, \n" +"or NO if you are sure that %s %s is a correct value." +msgstr "" +"Wordt %s%% bedoeld in plaats van %s %s?\n" +"Selecteer JA als de waarden aangepast moeten worden naar %s%%\n" +"of NEE als %s %s is de juiste waarde." + +#: src/slic3r/GUI/Field.cpp:238 +msgid "Parameter validation" +msgstr "Parametervalidatie" + +#: src/slic3r/GUI/FirmwareDialog.cpp:150 +msgid "Flash!" +msgstr "Flash!" + +#: src/slic3r/GUI/FirmwareDialog.cpp:152 +msgid "Flashing in progress. Please do not disconnect the printer!" +msgstr "Flashen in uitvoering. Ontkoppel de printer niet!" + +#: src/slic3r/GUI/FirmwareDialog.cpp:199 +msgid "Flashing failed" +msgstr "Flashen mislukt" + +#: src/slic3r/GUI/FirmwareDialog.cpp:282 +msgid "Flashing succeeded!" +msgstr "Flashen succesvol!" + +#: src/slic3r/GUI/FirmwareDialog.cpp:283 +msgid "Flashing failed. Please see the avrdude log below." +msgstr "Flashen mislukt. Check het AVRDUDE-logboek hieronder." + +#: src/slic3r/GUI/FirmwareDialog.cpp:284 +msgid "Flashing cancelled." +msgstr "Flashen geannuleerd." + +#: src/slic3r/GUI/FirmwareDialog.cpp:332 +#, c-format +msgid "" +"This firmware hex file does not match the printer model.\n" +"The hex file is intended for: %s\n" +"Printer reported: %s\n" +"\n" +"Do you want to continue and flash this hex file anyway?\n" +"Please only continue if you are sure this is the right thing to do." +msgstr "" +"Dit HEX-bestand is niet geschikt voor deze printer.\n" +"Het HEX-bestand is bedoeld voor: %s\n" +"Geïdentificeerde printer: %s\n" +"\n" +"Weet u zeker dat u door wilt gaan met dit HEX-bestand?\n" +"Ga alleen door als u zeker weet dat dit juist is." + +#: src/slic3r/GUI/FirmwareDialog.cpp:419 src/slic3r/GUI/FirmwareDialog.cpp:454 +#, c-format +msgid "" +"Multiple %s devices found. Please only connect one at a time for flashing." +msgstr "" +"Meerdere %s apparaten gevonden. Verbind één printer per keer voor het " +"flashen." + +#: src/slic3r/GUI/FirmwareDialog.cpp:436 +#, c-format +msgid "" +"The %s device was not found.\n" +"If the device is connected, please press the Reset button next to the USB " +"connector ..." +msgstr "" +"Het %s apparaat is niet gevonden.\n" +"Klik op de reset-knop naast de USB-aansluiting als het apparaat is verbonden." + +#: src/slic3r/GUI/FirmwareDialog.cpp:548 +#, c-format +msgid "The %s device could not have been found" +msgstr "Het %s apparaat kon niet gevonden worden" + +#: src/slic3r/GUI/FirmwareDialog.cpp:645 +#, c-format +msgid "Error accessing port at %s: %s" +msgstr "Fout bij het openen van de poort op %s: %s" + +#: src/slic3r/GUI/FirmwareDialog.cpp:647 +#, c-format +msgid "Error: %s" +msgstr "Fout: %s" + +#: src/slic3r/GUI/FirmwareDialog.cpp:777 +msgid "Firmware flasher" +msgstr "Firmwareflasher" + +#: src/slic3r/GUI/FirmwareDialog.cpp:802 +msgid "Firmware image:" +msgstr "Firmwarebestand:" + +#: src/slic3r/GUI/FirmwareDialog.cpp:805 src/slic3r/GUI/Tab.cpp:1635 +#: src/slic3r/GUI/Tab.cpp:1691 +msgid "Browse" +msgstr "Zoek" + +#: src/slic3r/GUI/FirmwareDialog.cpp:807 +msgid "Serial port:" +msgstr "Seriële poort:" + +#: src/slic3r/GUI/FirmwareDialog.cpp:809 +msgid "Autodetected" +msgstr "Automatisch gedetecteerd" + +#: src/slic3r/GUI/FirmwareDialog.cpp:810 +msgid "Rescan" +msgstr "Opnieuw scannen" + +#: src/slic3r/GUI/FirmwareDialog.cpp:817 +msgid "Progress:" +msgstr "Voortgang:" + +#: src/slic3r/GUI/FirmwareDialog.cpp:820 +msgid "Status:" +msgstr "Status:" + +#: src/slic3r/GUI/FirmwareDialog.cpp:821 +msgid "Ready" +msgstr "Klaar" + +#: src/slic3r/GUI/FirmwareDialog.cpp:841 +msgid "Advanced: Output log" +msgstr "Geavanceerd: Output logboek" + +#: src/slic3r/GUI/FirmwareDialog.cpp:852 +#: src/slic3r/GUI/PrintHostDialogs.cpp:161 +msgid "Close" +msgstr "Sluit" + +#: src/slic3r/GUI/FirmwareDialog.cpp:902 +msgid "" +"Are you sure you want to cancel firmware flashing?\n" +"This could leave your printer in an unusable state!" +msgstr "" +"Weet u zeker dat u het firmware flashen wilt stoppen?\n" +"Dit kan er voor zorgen dat de printer onbruikbaar wordt!" + +#: src/slic3r/GUI/FirmwareDialog.cpp:903 +msgid "Confirmation" +msgstr "Bevestiging" + +#: src/slic3r/GUI/FirmwareDialog.cpp:906 +msgid "Cancelling..." +msgstr "Annuleren..." + +#: src/slic3r/GUI/GLCanvas3D.cpp:240 src/slic3r/GUI/GLCanvas3D.cpp:4365 +msgid "Variable layer height" +msgstr "Variabele laagdikte" + +#: src/slic3r/GUI/GLCanvas3D.cpp:243 +msgid "Left mouse button:" +msgstr "Linkermuisknop:" + +#: src/slic3r/GUI/GLCanvas3D.cpp:246 +msgid "Add detail" +msgstr "Voeg detail toe" + +#: src/slic3r/GUI/GLCanvas3D.cpp:249 +msgid "Right mouse button:" +msgstr "Rechtermuisknop:" + +#: src/slic3r/GUI/GLCanvas3D.cpp:252 +msgid "Remove detail" +msgstr "Verwijder detail" + +#: src/slic3r/GUI/GLCanvas3D.cpp:255 +msgid "Shift + Left mouse button:" +msgstr "Shift + linkermuisknop:" + +#: src/slic3r/GUI/GLCanvas3D.cpp:258 +msgid "Reset to base" +msgstr "Reset" + +#: src/slic3r/GUI/GLCanvas3D.cpp:261 +msgid "Shift + Right mouse button:" +msgstr "Shift + rechtermuisknop:" + +#: src/slic3r/GUI/GLCanvas3D.cpp:264 +msgid "Smoothing" +msgstr "Egaliseren" + +#: src/slic3r/GUI/GLCanvas3D.cpp:267 +msgid "Mouse wheel:" +msgstr "Scrollwieltje:" + +#: src/slic3r/GUI/GLCanvas3D.cpp:270 +msgid "Increase/decrease edit area" +msgstr "Vergroot/verklein bewerkgebied" + +#: src/slic3r/GUI/GLCanvas3D.cpp:273 +msgid "Adaptive" +msgstr "Adaptief" + +#: src/slic3r/GUI/GLCanvas3D.cpp:278 +msgid "Cusp (mm)" +msgstr "Drempelwaarde (mm)" + +#: src/slic3r/GUI/GLCanvas3D.cpp:286 +msgid "Smooth" +msgstr "Egaliseer" + +#: src/slic3r/GUI/GLCanvas3D.cpp:291 src/libslic3r/PrintConfig.cpp:500 +msgid "Radius" +msgstr "Radius" + +#: src/slic3r/GUI/GLCanvas3D.cpp:300 +msgid "Keep min" +msgstr "Behoud min" + +#: src/slic3r/GUI/GLCanvas3D.cpp:307 +msgid "Reset" +msgstr "Reset" + +#: src/slic3r/GUI/GLCanvas3D.cpp:695 +msgid "Variable layer height - Manual edit" +msgstr "Variabele laagdikte - handmatig bewerken" + +#: src/slic3r/GUI/GLCanvas3D.cpp:794 +msgid "An object outside the print area was detected" +msgstr "Er is een object buiten het printbereik gedetecteerd" + +#: src/slic3r/GUI/GLCanvas3D.cpp:795 +msgid "A toolpath outside the print area was detected" +msgstr "Er is een printbeweging buiten het printbereik gedetecteerd" + +#: src/slic3r/GUI/GLCanvas3D.cpp:796 +msgid "SLA supports outside the print area were detected" +msgstr "Er is SLA-support buiten het printbereik gedetecteerd" + +#: src/slic3r/GUI/GLCanvas3D.cpp:797 +msgid "Some objects are not visible when editing supports" +msgstr "Sommige objecten zijn niet zichtbaar als het support bewerkt wordt" + +#: src/slic3r/GUI/GLCanvas3D.cpp:799 +msgid "" +"An object outside the print area was detected\n" +"Resolve the current problem to continue slicing" +msgstr "" +"Er is een object buiten het printbereik gedetecteerd\n" +"Los dit probleem op om door te gaan met slicen" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1014 src/slic3r/GUI/GLCanvas3D.cpp:1042 +msgid "Default print color" +msgstr "Standaard printkleur" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1043 src/slic3r/GUI/GLCanvas3D.cpp:1052 +#: src/slic3r/GUI/GLCanvas3D.cpp:1091 +msgid "Pause print or custom G-code" +msgstr "Pauzeer de print of voer custom G-code in" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1064 +#, c-format +msgid "up to %.2f mm" +msgstr "tot %.2f mm" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1068 +#, c-format +msgid "above %.2f mm" +msgstr "boven %.2f mm" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1072 +#, c-format +msgid "%.2f - %.2f mm" +msgstr "%.2f - %.2f mm" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1086 src/slic3r/GUI/Tab.cpp:2288 +#: src/slic3r/GUI/wxExtensions.cpp:3170 src/slic3r/GUI/wxExtensions.cpp:3421 +#: src/libslic3r/GCode/PreviewData.cpp:451 +#, c-format +msgid "Extruder %d" +msgstr "Extruder %d" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1099 +#, c-format +msgid "Color change for Extruder %d at %.2f mm" +msgstr "Kleurwissel voor extruder %d op %.2f mm" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1673 +msgid "Variable layer height - Reset" +msgstr "Variabele laagdikte - reset" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1681 +msgid "Variable layer height - Adaptive" +msgstr "Variabele laagdikte - adaptief" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1689 +msgid "Variable layer height - Smooth all" +msgstr "Variable laagdikte - egaliseer alles" + +#: src/slic3r/GUI/GLCanvas3D.cpp:2026 +msgid "Mirror Object" +msgstr "Spiegel object" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3297 +msgid "Move Object" +msgstr "Verplaats object" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3843 +msgid "Undo History" +msgstr "Geschiedenis ongedaan maken" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3843 +msgid "Redo History" +msgstr "Geschiedenis opnieuw doen" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3861 +#, c-format +msgid "Undo %1$d Action" +msgid_plural "Undo %1$d Actions" +msgstr[0] "Maak %1$d actie ongedaan" +msgstr[1] "Maak %1$d acties ongedaan" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3861 +#, c-format +msgid "Redo %1$d Action" +msgid_plural "Redo %1$d Actions" +msgstr[0] "Doe %1$d actie opnieuw" +msgstr[1] "Doe %1$d acties opnieuw" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4259 +msgid "Add..." +msgstr "Voeg toe..." + +#: src/slic3r/GUI/GLCanvas3D.cpp:4267 src/slic3r/GUI/GUI_ObjectList.cpp:1592 +#: src/slic3r/GUI/Plater.cpp:3712 src/slic3r/GUI/Plater.cpp:3734 +#: src/slic3r/GUI/Tab.cpp:3080 +msgid "Delete" +msgstr "Verwijder" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4276 src/slic3r/GUI/Plater.cpp:4410 +msgid "Delete all" +msgstr "Verwijder alles" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4285 src/slic3r/GUI/KBShortcutsDialog.cpp:137 +#: src/slic3r/GUI/Plater.cpp:2758 +msgid "Arrange" +msgstr "Schikken" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4285 src/slic3r/GUI/KBShortcutsDialog.cpp:138 +msgid "Arrange selection" +msgstr "Schik selectie" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4297 +msgid "Copy" +msgstr "Kopieer" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4306 +msgid "Paste" +msgstr "Plak" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4318 src/slic3r/GUI/Plater.cpp:3569 +#: src/slic3r/GUI/Plater.cpp:3581 src/slic3r/GUI/Plater.cpp:3721 +msgid "Add instance" +msgstr "Voeg instantie toe" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4329 src/slic3r/GUI/Plater.cpp:3723 +msgid "Remove instance" +msgstr "Verwijder instantie" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4342 +msgid "Split to objects" +msgstr "Verdeel in objecten" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4352 src/slic3r/GUI/GUI_ObjectList.cpp:1424 +msgid "Split to parts" +msgstr "Verdeel in onderdelen" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4416 src/slic3r/GUI/MainFrame.cpp:571 +msgid "Undo" +msgstr "Maak ongedaan" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4416 src/slic3r/GUI/GLCanvas3D.cpp:4449 +msgid "Click right mouse button to open History" +msgstr "Open de geschiedenis met de rechtermuisknop" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4433 +msgid "Next Undo action: %1%" +msgstr "Volgende ongedaan maken: %1%" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4449 src/slic3r/GUI/MainFrame.cpp:574 +msgid "Redo" +msgstr "Doe opnieuw" + +#: src/slic3r/GUI/GLCanvas3D.cpp:4465 +msgid "Next Redo action: %1%" +msgstr "Volgende opnieuw doen: %1%" + +#: src/slic3r/GUI/GLCanvas3D.cpp:6380 +msgid "Selection-Add from rectangle" +msgstr "Selectie - Voeg toe van boxselectie" + +#: src/slic3r/GUI/GLCanvas3D.cpp:6399 +msgid "Selection-Remove from rectangle" +msgstr "Selectie - Verwijder van boxselectie" + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:283 +#, c-format +msgid "" +"PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n" +"while OpenGL version %s, render %s, vendor %s was detected." +msgstr "" +"PrusaSlicer vereist een grafische driver die OpenGL 2.0 kan draaien,\n" +"terwijl OpenGL-versie %s, render %s, leverancier %s is gedetecteerd." + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:286 +msgid "You may need to update your graphics card driver." +msgstr "U moet mogelijk uw grafische kaart updaten." + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:289 +msgid "" +"As a workaround, you may run PrusaSlicer with a software rendered 3D " +"graphics by running prusa-slicer.exe with the --sw_renderer parameter." +msgstr "" +"Als oplossing kunt u PrusaSlicer draaien met een softwarematig 3D-" +"renderprogramma door prusa-slicer.exe uit te voeren met de --sw_renderer " +"parameter." + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:291 +msgid "Unsupported OpenGL version" +msgstr "Niet-ondersteunde OpenGL-versie" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:40 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:145 src/libslic3r/PrintConfig.cpp:3329 +msgid "Cut" +msgstr "Snij door" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:150 +msgid "Keep upper part" +msgstr "Behoud bovenste deel" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:151 +msgid "Keep lower part" +msgstr "Behoud onderste deel" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:152 +msgid "Rotate lower part upwards" +msgstr "Roteer onderste deel naar boven" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:155 +msgid "Perform cut" +msgstr "Toepassen" + +#: src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp:45 +msgid "Place on face" +msgstr "Plaats op vlak" + +#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:48 +msgid "Move" +msgstr "Verplaats" + +#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:177 +msgid "Position (mm)" +msgstr "Positie (mm)" + +#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:177 +msgid "Displacement (mm)" +msgstr "Verplaatsing (mm)" + +#: src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp:449 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:480 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:499 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:517 +#: src/libslic3r/PrintConfig.cpp:3378 +msgid "Rotate" +msgstr "Roteer" + +#: src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp:482 +msgid "Rotation (deg)" +msgstr "Rotatie (°)" + +#: src/slic3r/GUI/Gizmos/GLGizmoScale.cpp:47 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:230 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:500 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:518 +#: src/libslic3r/PrintConfig.cpp:3393 +msgid "Scale" +msgstr "Verschaal" + +#: src/slic3r/GUI/Gizmos/GLGizmoScale.cpp:292 +msgid "Scale (%)" +msgstr "Verschaling (%)" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:48 +msgid "Head diameter" +msgstr "Kopdiameter" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:49 +msgid "Lock supports under new islands" +msgstr "Vergrendel support onder nieuwe eilanden" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:50 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1286 +msgid "Remove selected points" +msgstr "Verwijder geselecteerde punten" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:51 +msgid "Remove all points" +msgstr "Verwijdere alle punten" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:52 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1289 +msgid "Apply changes" +msgstr "Wijzigingen toepassen" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:53 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1290 +msgid "Discard changes" +msgstr "Wijzigingen afwijzen" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:54 +msgid "Minimal points distance" +msgstr "Minimale puntafstand" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:55 +#: src/libslic3r/PrintConfig.cpp:2732 +msgid "Support points density" +msgstr "Dichtheid van supportpunten" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:56 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1292 +msgid "Auto-generate points" +msgstr "Genereer automatisch punten" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:57 +msgid "Manual editing" +msgstr "Handmatig bewerken" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:58 +msgid "Clipping of view" +msgstr "Weergave samenvoegen" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:59 +msgid "Reset direction" +msgstr "Reset-richting" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:442 +msgid "Add support point" +msgstr "Voeg supportpunt toe" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:578 +msgid "Delete support point" +msgstr "Verwijder supportpunt" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:754 +msgid "Change point head diameter" +msgstr "Wijzig puntkopdiameter" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:820 +msgid "Support parameter change" +msgstr "Wijzig supportparameter" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:929 +msgid "SLA Support Points" +msgstr "SLA-supportpunten" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:950 +msgid "SLA gizmo turned on" +msgstr "SLA Gizmo aangezet" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:972 +msgid "Do you want to save your manually edited support points?" +msgstr "Wilt u handmatig aangepaste supportpunten opslaan?" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:973 +msgid "Save changes?" +msgstr "Wijzigingen opslaan?" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:985 +msgid "SLA gizmo turned off" +msgstr "SLA Gizmo uitgezet" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1022 +msgid "Move support point" +msgstr "Verplaats supportpunt" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1121 +msgid "Support points edit" +msgstr "Bewerk supportpunten" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1190 +msgid "Autogeneration will erase all manually edited points." +msgstr "" +"Automatisch genereren zal alle handmatig aangepaste punten verwijderen." + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1191 +msgid "Are you sure you want to do it?" +msgstr "Weet u zeker dat u dit wilt doen?" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1192 src/slic3r/GUI/GUI.cpp:246 +#: src/slic3r/GUI/Tab.cpp:3040 src/slic3r/GUI/WipeTowerDialog.cpp:45 +#: src/slic3r/GUI/WipeTowerDialog.cpp:366 +msgid "Warning" +msgstr "Waarschuwing" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1195 +msgid "Autogenerate support points" +msgstr "Automatisch gegenereerde supportpunten" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1249 +msgid "SLA gizmo keyboard shortcuts" +msgstr "SLA sneltoetsen" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1260 +msgid "Note: some shortcuts work in (non)editing mode only." +msgstr "Let op: sommige sneltoetsen werken alleen in bewerkmodus." + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1278 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1281 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1282 +msgid "Left click" +msgstr "Linkermuisknop" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1278 +msgid "Add point" +msgstr "Voeg punt toe" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1279 +msgid "Right click" +msgstr "Rechtermuisknop" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1279 +msgid "Remove point" +msgstr "Verwijder punt" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1280 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1283 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1284 +msgid "Drag" +msgstr "Versleep" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1280 +msgid "Move point" +msgstr "Verplaats punt" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1281 +msgid "Add point to selection" +msgstr "Voeg punt toe aan selectie" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1282 +msgid "Remove point from selection" +msgstr "Verwijder punt uit selectie" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1283 +msgid "Select by rectangle" +msgstr "Selecteer met boxselectie" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1284 +msgid "Deselect by rectangle" +msgstr "Deselecteer met boxselectie" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1285 +msgid "Select all points" +msgstr "Selecteer alle punten" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1287 +msgid "Mouse wheel" +msgstr "Scrollwieltje" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1287 +msgid "Move clipping plane" +msgstr "Verplaats snijvlak" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1288 +msgid "Reset clipping plane" +msgstr "Reset snijvlak" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1291 +msgid "Switch to editing mode" +msgstr "Schakel over naar bewerkmodus" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:477 +msgid "Gizmo-Place on Face" +msgstr "Plaats op vlak" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:550 +msgid "Gizmo-Move" +msgstr "Verplaatsen" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:552 +msgid "Gizmo-Scale" +msgstr "Verschalen" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:554 +msgid "Gizmo-Rotate" +msgstr "Roteren" + +#: src/slic3r/GUI/GUI.cpp:240 +msgid "Notice" +msgstr "Let op" + +#: src/slic3r/GUI/GUI_App.cpp:137 +#, c-format +msgid "" +"%s has encountered an error. It was likely caused by running out of memory. " +"If you are sure you have enough RAM on your system, this may also be a bug " +"and we would be glad if you reported it.\n" +"\n" +"The application will now terminate." +msgstr "" +"%s veroorzaakte een fout. Dit komt mogelijk door een geheugentekort. Als u " +"zeker weet dat u genoeg RAM-geheugen heeft, kan dit ook een andere fout " +"zijn. We waarderen het als u dit meldt.\n" +"\n" +"Het programma zal nu afsluiten." + +#: src/slic3r/GUI/GUI_App.cpp:140 +msgid "Fatal error" +msgstr "Fatale fout" + +#: src/slic3r/GUI/GUI_App.cpp:450 +msgid "Changing of an application language" +msgstr "Veranderen van de taal van het programma" + +#: src/slic3r/GUI/GUI_App.cpp:458 src/slic3r/GUI/GUI_App.cpp:467 +msgid "Recreating" +msgstr "Opnieuw aanmaken" + +#: src/slic3r/GUI/GUI_App.cpp:471 +msgid "Loading of current presets" +msgstr "Laden van huidige presets" + +#: src/slic3r/GUI/GUI_App.cpp:479 +msgid "Loading of a mode view" +msgstr "Laden van de weergavemodus" + +#: src/slic3r/GUI/GUI_App.cpp:560 +msgid "Choose one file (3MF/AMF):" +msgstr "Kies een 3MF- of AMF-bestand:" + +#: src/slic3r/GUI/GUI_App.cpp:572 +msgid "Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):" +msgstr "Kies één of meer STL-, OBJ-, AMF-, 3MF-, of PRUSA-bestanden:" + +#: src/slic3r/GUI/GUI_App.cpp:634 +msgid "Select the language" +msgstr "Selecteer taal" + +#: src/slic3r/GUI/GUI_App.cpp:634 +msgid "Language" +msgstr "Taal" + +#: src/slic3r/GUI/GUI_App.cpp:802 +#, c-format +msgid "Run %s" +msgstr "Voer %s uit" + +#: src/slic3r/GUI/GUI_App.cpp:805 +msgid "&Configuration Snapshots" +msgstr "Configuratiesnapshots" + +#: src/slic3r/GUI/GUI_App.cpp:805 +msgid "Inspect / activate configuration snapshots" +msgstr "Inspecteer/activeer configuratiesnapshots" + +#: src/slic3r/GUI/GUI_App.cpp:806 +msgid "Take Configuration &Snapshot" +msgstr "Neem configuratiesnapshot" + +#: src/slic3r/GUI/GUI_App.cpp:806 +msgid "Capture a configuration snapshot" +msgstr "Neem een configuratiesnapshot op" + +#: src/slic3r/GUI/GUI_App.cpp:809 +msgid "&Preferences" +msgstr "Voorkeuren" + +#: src/slic3r/GUI/GUI_App.cpp:815 +msgid "Application preferences" +msgstr "Programmavoorkeuren" + +#: src/slic3r/GUI/GUI_App.cpp:818 src/slic3r/GUI/wxExtensions.cpp:3824 +msgid "Simple" +msgstr "Eenvoudig" + +#: src/slic3r/GUI/GUI_App.cpp:818 +msgid "Simple View Mode" +msgstr "Eenvoudige weergave" + +#: src/slic3r/GUI/GUI_App.cpp:819 src/slic3r/GUI/GUI_ObjectList.cpp:97 +#: src/slic3r/GUI/GUI_ObjectList.cpp:617 src/slic3r/GUI/Tab.cpp:1067 +#: src/slic3r/GUI/Tab.cpp:1082 src/slic3r/GUI/Tab.cpp:1181 +#: src/slic3r/GUI/Tab.cpp:1184 src/slic3r/GUI/Tab.cpp:1450 +#: src/slic3r/GUI/Tab.cpp:1937 src/slic3r/GUI/Tab.cpp:3614 +#: src/slic3r/GUI/wxExtensions.cpp:3825 src/libslic3r/PrintConfig.cpp:88 +#: src/libslic3r/PrintConfig.cpp:202 src/libslic3r/PrintConfig.cpp:365 +#: src/libslic3r/PrintConfig.cpp:1026 src/libslic3r/PrintConfig.cpp:2253 +msgid "Advanced" +msgstr "Geavanceerd" + +#: src/slic3r/GUI/GUI_App.cpp:819 +msgid "Advanced View Mode" +msgstr "Geavanceerde weergave" + +#: src/slic3r/GUI/GUI_App.cpp:820 src/slic3r/GUI/wxExtensions.cpp:3826 +msgid "Expert" +msgstr "Expert" + +#: src/slic3r/GUI/GUI_App.cpp:820 +msgid "Expert View Mode" +msgstr "Expertweergave" + +#: src/slic3r/GUI/GUI_App.cpp:825 +msgid "Mode" +msgstr "Modus" + +#: src/slic3r/GUI/GUI_App.cpp:825 +#, c-format +msgid "%s View Mode" +msgstr "%s-weergavemodus" + +#: src/slic3r/GUI/GUI_App.cpp:827 +msgid "Change Application &Language" +msgstr "Wijzig programmataal" + +#: src/slic3r/GUI/GUI_App.cpp:829 +msgid "Flash printer &firmware" +msgstr "Flash printerfirmware" + +#: src/slic3r/GUI/GUI_App.cpp:829 +msgid "Upload a firmware image into an Arduino based printer" +msgstr "Upload een firmwarebestand op een Arduino-gebaseerde printer" + +#: src/slic3r/GUI/GUI_App.cpp:841 +msgid "Taking configuration snapshot" +msgstr "Neem configuratiesnapshot" + +#: src/slic3r/GUI/GUI_App.cpp:841 +msgid "Snapshot name" +msgstr "Snapshotnaam" + +#: src/slic3r/GUI/GUI_App.cpp:884 +msgid "" +"Switching the language will trigger application restart.\n" +"You will lose content of the plater." +msgstr "" +"Het veranderen van de taal zorgt dat het programma opnieuw opstart.\n" +"U verliest de geladen inhoud zoals getoond in de modelweergave." + +#: src/slic3r/GUI/GUI_App.cpp:886 +msgid "Do you want to proceed?" +msgstr "Weet u zeker dat u door wilt gaan?" + +#: src/slic3r/GUI/GUI_App.cpp:887 +msgid "Language selection" +msgstr "Taalselectie" + +#: src/slic3r/GUI/GUI_App.cpp:910 +msgid "&Configuration" +msgstr "Configuratie" + +#: src/slic3r/GUI/GUI_App.cpp:934 +msgid "The presets on the following tabs were modified" +msgstr "De instellingen in de volgende tabs zijn aangepast" + +#: src/slic3r/GUI/GUI_App.cpp:934 src/slic3r/GUI/Tab.cpp:2902 +msgid "Discard changes and continue anyway?" +msgstr "Wijzigingen afwijzen en doorgaan?" + +#: src/slic3r/GUI/GUI_App.cpp:937 +msgid "Unsaved Presets" +msgstr "Niet-opgeslagen presets" + +#: src/slic3r/GUI/GUI_App.cpp:1083 src/slic3r/GUI/Tab.cpp:2914 +msgid "It's impossible to print multi-part object(s) with SLA technology." +msgstr "" +"Het is niet mogelijk meerdelige objecten te printen met de SLA-technologie." + +#: src/slic3r/GUI/GUI_App.cpp:1084 +msgid "Please check and fix your object list." +msgstr "Controleer en repareer de objectenlijst." + +#: src/slic3r/GUI/GUI_App.cpp:1085 src/slic3r/GUI/Plater.cpp:2317 +#: src/slic3r/GUI/Tab.cpp:2916 +msgid "Attention!" +msgstr "Attentie!" + +#: src/slic3r/GUI/GUI_App.cpp:1102 +msgid "Select a gcode file:" +msgstr "Selecteer een gcode-bestand:" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 +msgid "Start at height" +msgstr "Start op hoogte" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 +msgid "Stop at height" +msgstr "Stop op hoogte" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:153 +msgid "Remove layer range" +msgstr "Verwijder laagbereik" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:162 +msgid "Add layer range" +msgstr "Voeg laagbereik toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:34 src/slic3r/GUI/GUI_ObjectList.cpp:88 +#: src/slic3r/GUI/GUI_ObjectList.cpp:608 src/libslic3r/PrintConfig.cpp:72 +#: src/libslic3r/PrintConfig.cpp:165 src/libslic3r/PrintConfig.cpp:397 +#: src/libslic3r/PrintConfig.cpp:459 src/libslic3r/PrintConfig.cpp:467 +#: src/libslic3r/PrintConfig.cpp:879 src/libslic3r/PrintConfig.cpp:1064 +#: src/libslic3r/PrintConfig.cpp:1369 src/libslic3r/PrintConfig.cpp:1436 +#: src/libslic3r/PrintConfig.cpp:1617 src/libslic3r/PrintConfig.cpp:2063 +#: src/libslic3r/PrintConfig.cpp:2122 +msgid "Layers and Perimeters" +msgstr "Lagen en perimeters" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:36 src/slic3r/GUI/GUI_ObjectList.cpp:90 +#: src/slic3r/GUI/GUI_ObjectList.cpp:610 src/slic3r/GUI/GUI_Preview.cpp:245 +#: src/slic3r/GUI/Tab.cpp:1100 src/slic3r/GUI/Tab.cpp:1101 +#: src/libslic3r/ExtrusionEntity.cpp:319 src/libslic3r/PrintConfig.cpp:349 +#: src/libslic3r/PrintConfig.cpp:1497 src/libslic3r/PrintConfig.cpp:1855 +#: src/libslic3r/PrintConfig.cpp:1861 src/libslic3r/PrintConfig.cpp:1869 +#: src/libslic3r/PrintConfig.cpp:1881 src/libslic3r/PrintConfig.cpp:1891 +#: src/libslic3r/PrintConfig.cpp:1899 src/libslic3r/PrintConfig.cpp:1914 +#: src/libslic3r/PrintConfig.cpp:1935 src/libslic3r/PrintConfig.cpp:1947 +#: src/libslic3r/PrintConfig.cpp:1963 src/libslic3r/PrintConfig.cpp:1972 +#: src/libslic3r/PrintConfig.cpp:1981 src/libslic3r/PrintConfig.cpp:1992 +#: src/libslic3r/PrintConfig.cpp:2006 src/libslic3r/PrintConfig.cpp:2014 +#: src/libslic3r/PrintConfig.cpp:2015 src/libslic3r/PrintConfig.cpp:2024 +#: src/libslic3r/PrintConfig.cpp:2032 src/libslic3r/PrintConfig.cpp:2046 +msgid "Support material" +msgstr "Support" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:39 src/slic3r/GUI/GUI_ObjectList.cpp:94 +#: src/slic3r/GUI/GUI_ObjectList.cpp:614 src/libslic3r/PrintConfig.cpp:2229 +#: src/libslic3r/PrintConfig.cpp:2237 +msgid "Wipe options" +msgstr "Afveegopties" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:45 +msgid "Pad and Support" +msgstr "Basisplaat en support" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:51 +msgid "Add part" +msgstr "Voeg onderdeel toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:52 +msgid "Add modifier" +msgstr "Voeg modificator toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:53 +msgid "Add support enforcer" +msgstr "Voeg supportforcering toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:54 +msgid "Add support blocker" +msgstr "Voeg supportblokkering toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:91 src/slic3r/GUI/GUI_ObjectList.cpp:611 +#: src/slic3r/GUI/GUI_Preview.cpp:223 src/slic3r/GUI/Tab.cpp:1125 +#: src/libslic3r/PrintConfig.cpp:214 src/libslic3r/PrintConfig.cpp:447 +#: src/libslic3r/PrintConfig.cpp:908 src/libslic3r/PrintConfig.cpp:1037 +#: src/libslic3r/PrintConfig.cpp:1426 src/libslic3r/PrintConfig.cpp:1663 +#: src/libslic3r/PrintConfig.cpp:1712 src/libslic3r/PrintConfig.cpp:1764 +#: src/libslic3r/PrintConfig.cpp:2107 +msgid "Speed" +msgstr "Snelheid" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:92 src/slic3r/GUI/GUI_ObjectList.cpp:612 +#: src/slic3r/GUI/Tab.cpp:1160 src/slic3r/GUI/Tab.cpp:1808 +#: src/libslic3r/PrintConfig.cpp:477 src/libslic3r/PrintConfig.cpp:991 +#: src/libslic3r/PrintConfig.cpp:1404 src/libslic3r/PrintConfig.cpp:1733 +#: src/libslic3r/PrintConfig.cpp:1927 src/libslic3r/PrintConfig.cpp:1954 +msgid "Extruders" +msgstr "Extruders" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:93 src/slic3r/GUI/GUI_ObjectList.cpp:613 +#: src/libslic3r/PrintConfig.cpp:436 src/libslic3r/PrintConfig.cpp:544 +#: src/libslic3r/PrintConfig.cpp:866 src/libslic3r/PrintConfig.cpp:999 +#: src/libslic3r/PrintConfig.cpp:1413 src/libslic3r/PrintConfig.cpp:1753 +#: src/libslic3r/PrintConfig.cpp:1936 src/libslic3r/PrintConfig.cpp:2095 +msgid "Extrusion Width" +msgstr "Extrusiebreedte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:99 src/slic3r/GUI/GUI_ObjectList.cpp:619 +#: src/slic3r/GUI/Plater.cpp:484 src/slic3r/GUI/Tab.cpp:3564 +#: src/slic3r/GUI/Tab.cpp:3565 src/libslic3r/PrintConfig.cpp:2582 +#: src/libslic3r/PrintConfig.cpp:2589 src/libslic3r/PrintConfig.cpp:2598 +#: src/libslic3r/PrintConfig.cpp:2607 src/libslic3r/PrintConfig.cpp:2617 +#: src/libslic3r/PrintConfig.cpp:2643 src/libslic3r/PrintConfig.cpp:2650 +#: src/libslic3r/PrintConfig.cpp:2661 src/libslic3r/PrintConfig.cpp:2671 +#: src/libslic3r/PrintConfig.cpp:2680 src/libslic3r/PrintConfig.cpp:2693 +#: src/libslic3r/PrintConfig.cpp:2703 src/libslic3r/PrintConfig.cpp:2712 +#: src/libslic3r/PrintConfig.cpp:2722 src/libslic3r/PrintConfig.cpp:2733 +#: src/libslic3r/PrintConfig.cpp:2741 +msgid "Supports" +msgstr "Support" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:100 src/slic3r/GUI/GUI_ObjectList.cpp:620 +#: src/slic3r/GUI/Plater.cpp:624 src/slic3r/GUI/Tab.cpp:3596 +#: src/slic3r/GUI/Tab.cpp:3597 src/libslic3r/PrintConfig.cpp:2749 +#: src/libslic3r/PrintConfig.cpp:2756 src/libslic3r/PrintConfig.cpp:2770 +#: src/libslic3r/PrintConfig.cpp:2781 src/libslic3r/PrintConfig.cpp:2791 +#: src/libslic3r/PrintConfig.cpp:2813 src/libslic3r/PrintConfig.cpp:2824 +#: src/libslic3r/PrintConfig.cpp:2831 src/libslic3r/PrintConfig.cpp:2838 +#: src/libslic3r/PrintConfig.cpp:2849 src/libslic3r/PrintConfig.cpp:2858 +#: src/libslic3r/PrintConfig.cpp:2867 +msgid "Pad" +msgstr "Basisplaat" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:262 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:153 +msgid "Name" +msgstr "Naam" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:270 src/slic3r/GUI/Tab.cpp:1414 +#: src/slic3r/GUI/wxExtensions.cpp:549 src/libslic3r/PrintConfig.cpp:476 +msgid "Extruder" +msgstr "Extruder" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:274 src/slic3r/GUI/GUI_ObjectList.cpp:386 +msgid "Editing" +msgstr "Bewerken" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:331 +#, c-format +msgid "Auto-repaired (%d errors):" +msgstr "Automatisch gerepareerd (%d fouten):" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:338 +msgid "degenerate facets" +msgstr "vlakken gedegenereerd" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:339 +msgid "edges fixed" +msgstr "randen vastgezet" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:340 +msgid "facets removed" +msgstr "vlakken verwijderd" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:341 +msgid "facets added" +msgstr "vlakken toegevoegd" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:342 +msgid "facets reversed" +msgstr "vlakken omgekeerd" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:343 +msgid "backwards edges" +msgstr "omgekeerde lijnen" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:351 +msgid "Right button click the icon to fix STL through Netfabb" +msgstr "" +"Rechtermuisklik op het pictogram om het STL-bestand met NetFabb te repareren" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:388 +msgid "Right button click the icon to change the object settings" +msgstr "Rechtermuisklik op het icoontje om de objectinstellingen te wijzigen" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:390 +msgid "Click the icon to change the object settings" +msgstr "Klik op het pictogram om de objectinstellingen te wijzigen" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:394 +msgid "Right button click the icon to change the object printable property" +msgstr "Rechtermuisklik op het pictogram om de printinstellingen te wijzigen" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:396 +msgid "Click the icon to change the object printable property" +msgstr "Klik op het pictogram om de printinstellingen te wijzigen" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:449 src/slic3r/GUI/GUI_ObjectList.cpp:461 +#: src/slic3r/GUI/GUI_ObjectList.cpp:907 src/slic3r/GUI/GUI_ObjectList.cpp:3822 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3832 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3867 src/slic3r/GUI/wxExtensions.cpp:734 +#: src/slic3r/GUI/wxExtensions.cpp:791 src/slic3r/GUI/wxExtensions.cpp:816 +#: src/slic3r/GUI/wxExtensions.cpp:1024 src/slic3r/GUI/wxExtensions.cpp:2240 +msgid "default" +msgstr "standaard" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:528 +msgid "Change Extruder" +msgstr "Wijzig extruder" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:543 +msgid "Rename Object" +msgstr "Hernoem object" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:543 +msgid "Rename Sub-object" +msgstr "Hernoem subobject" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1068 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3643 +msgid "Instances to Separated Objects" +msgstr "Zet instanties om in objecten" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1086 +msgid "Volumes in Object reordered" +msgstr "Volumes in object opnieuw geordend" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1086 +msgid "Object reordered" +msgstr "Object opnieuw geordend" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1141 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1460 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1466 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1723 +#, c-format +msgid "Quick Add Settings (%s)" +msgstr "Snel instellingen toevoegen (%s)" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1218 +msgid "Select showing settings" +msgstr "Selecteer getoonde instellingen" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1267 +msgid "Add Settings for Layers" +msgstr "Voeg laaginstellingen toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1268 +msgid "Add Settings for Sub-object" +msgstr "Voeg instellingen voor subobject toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1269 +msgid "Add Settings for Object" +msgstr "Voeg instellingen voor object toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1330 +msgid "Add Settings Bundle for Height range" +msgstr "Voeg instellingen voor hoogtebereik toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1331 +msgid "Add Settings Bundle for Sub-object" +msgstr "Voeg instellingen voor subobject toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1332 +msgid "Add Settings Bundle for Object" +msgstr "Voeg instellingen voor een object toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1371 +msgid "Load" +msgstr "Laad" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1376 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1404 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1407 +msgid "Box" +msgstr "Blok" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1376 +msgid "Cylinder" +msgstr "Cilinder" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1376 +msgid "Sphere" +msgstr "Bol" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1376 +msgid "Slab" +msgstr "Plaat" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1431 +msgid "Height range Modifier" +msgstr "Modificator voor hoogtebereik" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1439 +msgid "Add settings" +msgstr "Voeg instellingen toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1507 +msgid "Change type" +msgstr "Wijzig type" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1514 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1671 +msgid "Set as a Separated Object" +msgstr "Stel in als apart object" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1520 +msgid "Printable" +msgstr "Printbaar" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1527 +msgid "Rename" +msgstr "Hernoem" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1538 +msgid "Fix through the Netfabb" +msgstr "Repareer met NetFabb" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1548 src/slic3r/GUI/Plater.cpp:3747 +msgid "Export as STL" +msgstr "Exporteer als STL-bestand" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1555 src/slic3r/GUI/Plater.cpp:3161 +#: src/slic3r/GUI/Plater.cpp:3715 src/slic3r/GUI/Plater.cpp:3744 +msgid "Reload from disk" +msgstr "Herlaad vanaf schijf" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1555 src/slic3r/GUI/Plater.cpp:3715 +msgid "Reload the selected volumes from disk" +msgstr "Herlaad de geselecteerde volumes vanaf schijf" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1561 src/slic3r/GUI/wxExtensions.cpp:3176 +#: src/slic3r/GUI/wxExtensions.cpp:3432 +msgid "Change extruder" +msgstr "Wijzig extruder" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1580 src/slic3r/GUI/wxExtensions.cpp:3170 +#: src/slic3r/GUI/wxExtensions.cpp:3421 src/libslic3r/PrintConfig.cpp:314 +msgid "Default" +msgstr "Standaard" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1586 +msgid "Select new extruder for the object/part" +msgstr "Selecteer nieuwe extruder voor het object/onderdeel" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1598 +msgid "Scale to print volume" +msgstr "Verschaal tot printvolume" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1598 +msgid "Scale the selected object to fit the print volume" +msgstr "Verschaal het geselecteerde object tot deze in het printvolume past" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1671 +msgid "Set as a Separated Objects" +msgstr "Stel in als aparte objecten" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1678 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1924 +msgid "Add Shape" +msgstr "Voeg vorm toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1752 +msgid "Load Part" +msgstr "Laad onderdeel" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1791 +msgid "Error!" +msgstr "Fout!" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1866 +msgid "Add Generic Subobject" +msgstr "Voeg algemene subobjecten toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1895 +msgid "Generic" +msgstr "Algemeen" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2013 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2115 +msgid "Last instance of an object cannot be deleted." +msgstr "Laatste instantie van een object kan niet verwijderd worden." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2025 +msgid "Delete Settings" +msgstr "Verwijder instellingen" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2049 +msgid "Delete All Instances from Object" +msgstr "Verwijder alle instanties van het object" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2065 +msgid "Delete Height Range" +msgstr "Verwijder hoogtebereik" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2096 +msgid "From Object List You can't delete the last solid part from object." +msgstr "Het laatste onderdeel van de objectenlijst kan niet verwijderd worden." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2100 +msgid "Delete Subobject" +msgstr "Verwijder subobject" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2119 +msgid "Delete Instance" +msgstr "Verwijder instantie" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2143 src/slic3r/GUI/Plater.cpp:2914 +msgid "" +"The selected object couldn't be split because it contains only one part." +msgstr "" +"Het geselecteerde object kan niet opgedeeld worden omdat het maar één " +"geometrie bevat." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2147 +msgid "Split to Parts" +msgstr "Verdeel in onderdelen" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2195 +msgid "Add Layers" +msgstr "Voeg lagen toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2321 +msgid "Group manipulation" +msgstr "Groep bewerken" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2333 +msgid "Object manipulation" +msgstr "Object bewerken" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2346 +msgid "Object Settings to modify" +msgstr "Objectinstellingen om te bewerken" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2350 +msgid "Part Settings to modify" +msgstr "Onderdeelinstellingen om te bewerken" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2355 +msgid "Layer range Settings to modify" +msgstr "Laagbereikinstellingen om te bewerken" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2361 +msgid "Part manipulation" +msgstr "Onderdeel bewerken" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2367 +msgid "Instance manipulation" +msgstr "Instantie bewerken" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2374 +msgid "Height ranges" +msgstr "Hoogtebereik" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2374 +msgid "Settings for height range" +msgstr "Instellingen voor hoogtebereik" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2560 +msgid "Delete Selected Item" +msgstr "Verwijder geselecteerd item" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2697 +msgid "Delete Selected" +msgstr "Verwijder selectie" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2763 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2792 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2810 +msgid "Add Height Range" +msgstr "Voeg hoogtebereik toe" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2869 +msgid "Edit Height Range" +msgstr "Bewerk hoogtebereik" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3153 +msgid "Selection-Remove from list" +msgstr "Selectie - Verwijder van lijst" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3161 +msgid "Selection-Add from list" +msgstr "Selectie - Voeg toe aan lijst" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3279 +msgid "Object or Instance" +msgstr "Object of instantie" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3280 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3413 +msgid "Part" +msgstr "Onderdeel" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3280 +msgid "Layer" +msgstr "Laag" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3282 +msgid "Unsupported selection" +msgstr "Niet-ondersteunde selectie" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3283 +#, c-format +msgid "You started your selection with %s Item." +msgstr "De selectie is gestart met item %s." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3284 +#, c-format +msgid "In this mode you can select only other %s Items%s" +msgstr "In deze modus kunt u alleen andere %s items %s selecteren" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3287 +msgid "of a current Object" +msgstr "van het huidige object" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3408 +msgid "You can't change a type of the last solid part of the object." +msgstr "" +"U kunt het type van het laatste onderdeel van een object niet wijzigen." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3413 +msgid "Modifier" +msgstr "Modificator" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3413 +msgid "Support Enforcer" +msgstr "Supportforcering" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3413 +msgid "Support Blocker" +msgstr "Supportblokkering" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3415 +msgid "Select type of part" +msgstr "Selecteer onderdeeltype" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3420 +msgid "Change Part Type" +msgstr "Wijzig onderdeeltype" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3665 +msgid "Enter new name" +msgstr "Voer nieuwe naam in" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3665 +msgid "Renaming" +msgstr "Hernoemen" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3681 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3788 src/slic3r/GUI/Tab.cpp:3412 +#: src/slic3r/GUI/Tab.cpp:3416 +msgid "The supplied name is not valid;" +msgstr "De ingevoerde naam is niet geldig;" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3682 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3789 src/slic3r/GUI/Tab.cpp:3413 +msgid "the following characters are not allowed:" +msgstr "de volgende karakters zijn niet toegestaan:" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3812 +msgid "Set extruder for selected items" +msgstr "Stel extruder in voor de geselecteerde items" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3813 +msgid "Select extruder number for selected objects and/or parts" +msgstr "" +"Selecteer extrudernummer voor de geselecteerde objecten en/of onderdelen" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3826 +msgid "Select extruder number:" +msgstr "Selecteer extrudernummer:" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3827 +msgid "This extruder will be set for selected items" +msgstr "Deze extruder wordt ingesteld voor de geselecteerde items" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3852 +msgid "Change Extruders" +msgstr "Wijzig extruders" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3942 src/slic3r/GUI/Selection.cpp:1473 +msgid "Set Printable" +msgstr "Stel in op printbaar" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3942 src/slic3r/GUI/Selection.cpp:1473 +msgid "Set Unprintable" +msgstr "Stel in op niet-printbaar" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:62 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:105 +msgid "World coordinates" +msgstr "Wereldcoördinaten" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:63 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:106 +msgid "Local coordinates" +msgstr "Lokale coördinaten" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:82 +msgid "Select coordinate space, in which the transformation will be performed." +msgstr "" +"Stel een coördinatenstelsel in. Hierin wordt de verandering uitgevoerd." + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:155 src/libslic3r/GCode.cpp:634 +msgid "Object name" +msgstr "Objectnaam" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:215 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:457 +msgid "Position" +msgstr "Positie" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:216 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:458 +#: src/slic3r/GUI/Mouse3DController.cpp:274 +#: src/slic3r/GUI/Mouse3DController.cpp:287 +msgid "Rotation" +msgstr "Rotatie" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:263 +#, c-format +msgid "Toggle %c axis mirroring" +msgstr "Zet %c-asspiegeling aan" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:297 +msgid "Set Mirror" +msgstr "Stel spiegeling in" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:337 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:349 +msgid "Drop to bed" +msgstr "Plaats op het bed" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:363 +msgid "Reset rotation" +msgstr "Reset rotatie" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:385 +msgid "Reset Rotation" +msgstr "Reset rotatie" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:397 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:399 +msgid "Reset scale" +msgstr "Reset verschaling" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:459 +msgid "Scale factors" +msgstr "Verschalingsfactoren" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:516 +msgid "Translate" +msgstr "Verplaats" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:578 +msgid "" +"You cannot use non-uniform scaling mode for multiple objects/parts selection" +msgstr "" +"Niet-gelijke verschaling kan niet gebruikt worden voor meerdere objecten of " +"onderdelen" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:750 +msgid "Set Position" +msgstr "Stel positie in" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:781 +msgid "Set Orientation" +msgstr "Stel oriëntatie in" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:846 +msgid "Set Scale" +msgstr "Stel verschaling in" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:875 +msgid "" +"The currently manipulated object is tilted (rotation angles are not " +"multiples of 90°).\n" +"Non-uniform scaling of tilted objects is only possible in the World " +"coordinate system,\n" +"once the rotation is embedded into the object coordinates." +msgstr "" +"Het huidige aangepaste object is gekanteld (rotatiehoeken zijn geen " +"veelvouden van 90°).\n" +"Niet-gelijke verschaling van gekantelde objecten is alleen mogelijk in het " +"wereldcoördinatensysteem\n" +"als de rotatie is ingebouwd in de objectcoördinaten." + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:878 +msgid "" +"This operation is irreversible.\n" +"Do you want to proceed?" +msgstr "" +"Deze bewerking werkt onomkeerbaar.\n" +"Weet u zeker dat u wilt doorgaan?" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:59 +msgid "Additional Settings" +msgstr "Extra instellingen" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:95 +msgid "Remove parameter" +msgstr "Verwijder parameter" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:101 +#, c-format +msgid "Delete Option %s" +msgstr "Verwijder optie %s" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:152 +#, c-format +msgid "Change Option %s" +msgstr "Wijzig optie %s" + +#: src/slic3r/GUI/GUI_Preview.cpp:217 +msgid "View" +msgstr "Weergave" + +#: src/slic3r/GUI/GUI_Preview.cpp:220 src/slic3r/GUI/GUI_Preview.cpp:577 +#: src/libslic3r/GCode/PreviewData.cpp:360 +msgid "Feature type" +msgstr "Objecttype" + +#: src/slic3r/GUI/GUI_Preview.cpp:221 src/libslic3r/PrintConfig.cpp:489 +msgid "Height" +msgstr "Hoogte" + +#: src/slic3r/GUI/GUI_Preview.cpp:222 src/libslic3r/PrintConfig.cpp:2215 +msgid "Width" +msgstr "Breedte" + +#: src/slic3r/GUI/GUI_Preview.cpp:224 src/slic3r/GUI/Tab.cpp:1437 +msgid "Fan speed" +msgstr "Ventilatorsnelheid" + +#: src/slic3r/GUI/GUI_Preview.cpp:225 +msgid "Volumetric flow rate" +msgstr "Volumetrisch debiet" + +#: src/slic3r/GUI/GUI_Preview.cpp:226 src/slic3r/GUI/GUI_Preview.cpp:334 +#: src/slic3r/GUI/GUI_Preview.cpp:523 src/slic3r/GUI/GUI_Preview.cpp:576 +#: src/slic3r/GUI/GUI_Preview.cpp:772 src/libslic3r/GCode/PreviewData.cpp:372 +msgid "Tool" +msgstr "Tool" + +#: src/slic3r/GUI/GUI_Preview.cpp:227 src/slic3r/GUI/GUI_Preview.cpp:574 +#: src/libslic3r/GCode/PreviewData.cpp:374 +msgid "Color Print" +msgstr "Kleurenprint" + +#: src/slic3r/GUI/GUI_Preview.cpp:230 +msgid "Show" +msgstr "Toon" + +#: src/slic3r/GUI/GUI_Preview.cpp:233 src/slic3r/GUI/GUI_Preview.cpp:234 +msgid "Feature types" +msgstr "Featuretypes" + +#: src/slic3r/GUI/GUI_Preview.cpp:236 src/libslic3r/ExtrusionEntity.cpp:310 +msgid "Perimeter" +msgstr "Perimeter" + +#: src/slic3r/GUI/GUI_Preview.cpp:237 src/libslic3r/ExtrusionEntity.cpp:311 +msgid "External perimeter" +msgstr "Buitenste perimeter" + +#: src/slic3r/GUI/GUI_Preview.cpp:238 src/libslic3r/ExtrusionEntity.cpp:312 +msgid "Overhang perimeter" +msgstr "Overhangende perimeter" + +#: src/slic3r/GUI/GUI_Preview.cpp:239 src/libslic3r/ExtrusionEntity.cpp:313 +msgid "Internal infill" +msgstr "Inwendige vulling" + +#: src/slic3r/GUI/GUI_Preview.cpp:240 src/libslic3r/ExtrusionEntity.cpp:314 +#: src/libslic3r/PrintConfig.cpp:1752 src/libslic3r/PrintConfig.cpp:1763 +msgid "Solid infill" +msgstr "Dichte vulling" + +#: src/slic3r/GUI/GUI_Preview.cpp:241 src/libslic3r/ExtrusionEntity.cpp:315 +#: src/libslic3r/PrintConfig.cpp:2094 src/libslic3r/PrintConfig.cpp:2106 +msgid "Top solid infill" +msgstr "Bovenste dichte vulling" + +#: src/slic3r/GUI/GUI_Preview.cpp:242 src/libslic3r/ExtrusionEntity.cpp:316 +msgid "Bridge infill" +msgstr "Brugvulling" + +#: src/slic3r/GUI/GUI_Preview.cpp:243 src/libslic3r/ExtrusionEntity.cpp:317 +#: src/libslic3r/PrintConfig.cpp:907 +msgid "Gap fill" +msgstr "Gatenvulling" + +#: src/slic3r/GUI/GUI_Preview.cpp:244 src/slic3r/GUI/Tab.cpp:1091 +#: src/libslic3r/ExtrusionEntity.cpp:318 +msgid "Skirt" +msgstr "Skirt" + +#: src/slic3r/GUI/GUI_Preview.cpp:246 src/libslic3r/ExtrusionEntity.cpp:320 +#: src/libslic3r/PrintConfig.cpp:1980 +msgid "Support material interface" +msgstr "Supportinterface" + +#: src/slic3r/GUI/GUI_Preview.cpp:247 src/slic3r/GUI/Tab.cpp:1171 +#: src/libslic3r/ExtrusionEntity.cpp:321 +msgid "Wipe tower" +msgstr "Afveegblok" + +#: src/slic3r/GUI/GUI_Preview.cpp:252 src/libslic3r/PrintConfig.cpp:2129 +msgid "Travel" +msgstr "Beweging" + +#: src/slic3r/GUI/GUI_Preview.cpp:253 +msgid "Retractions" +msgstr "Retracties" + +#: src/slic3r/GUI/GUI_Preview.cpp:254 +msgid "Unretractions" +msgstr "Deretracties" + +#: src/slic3r/GUI/GUI_Preview.cpp:255 +msgid "Shells" +msgstr "Shells" + +#: src/slic3r/GUI/GUI_Preview.cpp:256 +msgid "Legend" +msgstr "Legenda" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:14 src/slic3r/GUI/MainFrame.cpp:684 +msgid "Keyboard Shortcuts" +msgstr "Sneltoetsen" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:107 +msgid "Open project STL/OBJ/AMF/3MF with config, delete bed" +msgstr "" +"Open STL-, OBJ-, AMF- of 3MF-bestanden (met configuratie) en verwijder " +"geladen bestanden" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:108 +msgid "Import STL/OBJ/AMF/3MF without config, keep bed" +msgstr "" +"Importeer STL-, OBJ-, AMF- of 3MF-bestanden (zonder configuratie) en behoud " +"geladen bestanden" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:109 +msgid "Load Config from .ini/amf/3mf/gcode" +msgstr "Laad INI-, AMF-, 3MF- of gcode-configuratiebestand" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:110 src/slic3r/GUI/Plater.cpp:858 +#: src/slic3r/GUI/Plater.cpp:5142 src/libslic3r/PrintConfig.cpp:3280 +msgid "Export G-code" +msgstr "Exporteer gcode-bestand" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:111 +msgid "Save project (3MF)" +msgstr "Sla 3MF-project op" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:112 +msgid "Load Config from .ini/amf/3mf/gcode and merge" +msgstr "Laad INI-, AMF-, 3MF- of gcode-configuratiebestand en verenig" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:113 +msgid "(Re)slice" +msgstr "(Her)slice" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:116 +msgid "Select Plater Tab" +msgstr "Selecteer modelweergave" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:118 +msgid "Select Print Settings Tab" +msgstr "Selecteer printinstellingentab" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:119 +msgid "Select Filament Settings Tab" +msgstr "Selecteer filamentinstellingentab" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:120 +msgid "Select Printer Settings Tab" +msgstr "Selecteer printerinstellingentab" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:121 +msgid "Switch to 3D" +msgstr "Schakel over naar 3D" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:122 +msgid "Switch to Preview" +msgstr "Schakel over naar voorbeeldweergave" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:123 src/slic3r/GUI/Preferences.cpp:10 +msgid "Preferences" +msgstr "Voorkeuren" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:124 +#: src/slic3r/GUI/PrintHostDialogs.cpp:136 +msgid "Print host upload queue" +msgstr "Printhost uploadwachtrij" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:125 +msgid "Camera view" +msgstr "Weergave" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:126 +msgid "Add Instance of the selected object" +msgstr "Voeg instantie van het geselecteerde object toe" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:127 +msgid "Remove Instance of the selected object" +msgstr "Verwijder instanties van het geselecteerde object" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:128 +msgid "Show keyboard shortcuts list" +msgstr "Toon lijst met sneltoetsen" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:129 +msgid "Press to select multiple object or move multiple object with mouse" +msgstr "" +"Druk in om meerdere objecten te selecteren en verplaats de objecten met de " +"muis" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:131 +msgid "Main Shortcuts" +msgstr "Belangrijkste sneltoetsen" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:139 +msgid "Select All objects" +msgstr "Selecteer alle objecten" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:140 +msgid "Delete selected" +msgstr "Verwijder selectie" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:141 +msgid "Delete All" +msgstr "Verwijder alles" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:142 +msgid "Copy to clipboard" +msgstr "Kopieer naar klembord" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:143 +msgid "Paste from clipboard" +msgstr "Plak van klembord" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:144 +msgid "Gizmo move" +msgstr "Verplaatsen" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:145 +msgid "Gizmo scale" +msgstr "Verschalen" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:146 +msgid "Gizmo rotate" +msgstr "Roteren" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:147 +msgid "Gizmo cut" +msgstr "Snijden" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:148 +msgid "Gizmo Place face on bed" +msgstr "Plaats vlak op bed" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:149 +msgid "Gizmo SLA support points" +msgstr "SLA-supportpunten" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:150 +#, no-c-format +msgid "" +"Press to activate selection rectangle\n" +"or to snap by 5% in Gizmo scale\n" +"or to snap by 1mm in Gizmo move" +msgstr "" +"Druk in om boxselectie te activeren\n" +"of te snappen op 5% in de verschaling\n" +"of te snappen op 1mm in de verplaatsing" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:151 +msgid "" +"Press to scale selection to fit print volume\n" +"in Gizmo scale" +msgstr "Druk in om de selectie te verschalen tot printvolume" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:152 +msgid "" +"Press to activate deselection rectangle\n" +"or to scale or rotate selected objects\n" +"around their own center" +msgstr "" +"Druk in om de deselectiebox te activeren\n" +"of de selectie te schalen of roteren\n" +"rond zijn middelpunt" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:153 +msgid "Press to activate one direction scaling in Gizmo scale" +msgstr "Druk in om verschaling toepassen in één richting te activeren" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:154 +msgid "Change camera type (perspective, orthographic)" +msgstr "Wijzig weergavetype (perspectief of orthografisch)" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:155 +msgid "Zoom to Bed" +msgstr "Zoom in op bed" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:156 +msgid "Zoom to all objects in scene, if none selected" +msgstr "Zoom in op alle niet-geselecteerde objecten" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:157 +msgid "Zoom to selected object" +msgstr "Zoom in op geselecteerde objecten" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:158 +msgid "Zoom in" +msgstr "Zoom in" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:159 +msgid "Zoom out" +msgstr "Zoom uit" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:160 +msgid "Show/Hide 3Dconnexion devices settings dialog" +msgstr "Toon/verberg 3Dconnexion-apparaten instellingen-dialoogvenster" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:161 +msgid "Unselect gizmo / Clear selection" +msgstr "Selectie ongedaan maken" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:167 +msgid "Plater Shortcuts" +msgstr "Sneltoetsen voor modelweergave" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:182 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:194 +msgid "Arrow Up" +msgstr "Pijltje naar boven" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:182 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:184 +msgid "Upper Layer" +msgstr "Bovenste laag" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:183 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:195 +msgid "Arrow Down" +msgstr "Pijltje naar beneden" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:183 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 +msgid "Lower Layer" +msgstr "Onderste laag" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:186 +msgid "Show/Hide (L)egend" +msgstr "Toon/verberg legenda" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:188 +msgid "Preview Shortcuts" +msgstr "Toon sneltoetsen" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:194 +msgid "Move current slider thumb Up" +msgstr "Verplaats huidige schuif naar boven" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:195 +msgid "Move current slider thumb Down" +msgstr "Verplaats huidige schuif naar beneden" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:196 +msgid "Arrow Left" +msgstr "Pijltje naar links" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:196 +msgid "Set upper thumb to current slider thumb" +msgstr "Stel de bovenste schuif in op het huidige punt" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 +msgid "Arrow Right" +msgstr "Pijltje naar rechts" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 +msgid "Set lower thumb to current slider thumb" +msgstr "Stel de onderste schuif in op het huidige punt" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:198 +msgid "Add color change marker for current layer" +msgstr "Voeg kleurwisseling toe voor de huidige laag" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:199 +msgid "Delete color change marker for current layer" +msgstr "Verwijder kleurwisseling voor de huidige laag" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:201 +msgid "Layers Slider Shortcuts" +msgstr "Sneltoetsen schuifregelaar" + +#: src/slic3r/GUI/MainFrame.cpp:65 +msgid "" +" - Remember to check for updates at http://github.com/prusa3d/PrusaSlicer/" +"releases" +msgstr "" +" - Vergeet niet op updates te checken op http://github.com/prusa3d/" +"PrusaSlicer/releases" + +#: src/slic3r/GUI/MainFrame.cpp:160 +msgid "based on Slic3r" +msgstr "gebaseerd op Slic3r" + +#: src/slic3r/GUI/MainFrame.cpp:190 +msgid "Plater" +msgstr "Modelweergave" + +#: src/slic3r/GUI/MainFrame.cpp:401 +msgid "&New Project" +msgstr "Nieuw project" + +#: src/slic3r/GUI/MainFrame.cpp:401 +msgid "Start a new project" +msgstr "Start nieuw project" + +#: src/slic3r/GUI/MainFrame.cpp:404 +msgid "&Open Project" +msgstr "Open project" + +#: src/slic3r/GUI/MainFrame.cpp:404 +msgid "Open a project file" +msgstr "Open een projectbestand" + +#: src/slic3r/GUI/MainFrame.cpp:409 +msgid "Recent projects" +msgstr "Huidige projecten" + +#: src/slic3r/GUI/MainFrame.cpp:418 +msgid "The selected project is no more available" +msgstr "Het geselecteerde project is niet meer beschikbaar" + +#: src/slic3r/GUI/MainFrame.cpp:418 src/slic3r/GUI/MainFrame.cpp:761 +#: src/slic3r/GUI/PrintHostDialogs.cpp:231 +msgid "Error" +msgstr "Fout" + +#: src/slic3r/GUI/MainFrame.cpp:442 +msgid "&Save Project" +msgstr "Project opslaan" + +#: src/slic3r/GUI/MainFrame.cpp:442 +msgid "Save current project file" +msgstr "Projectbestand opslaan" + +#: src/slic3r/GUI/MainFrame.cpp:446 src/slic3r/GUI/MainFrame.cpp:448 +msgid "Save Project &as" +msgstr "Project opslaan als" + +#: src/slic3r/GUI/MainFrame.cpp:446 src/slic3r/GUI/MainFrame.cpp:448 +msgid "Save current project file as" +msgstr "Projectbestand opslaan als" + +#: src/slic3r/GUI/MainFrame.cpp:456 +msgid "Import STL/OBJ/AM&F/3MF" +msgstr "Importeer STL-, OBJ-, AMF- of 3MF-bestanden" + +#: src/slic3r/GUI/MainFrame.cpp:456 +msgid "Load a model" +msgstr "Laad een model" + +#: src/slic3r/GUI/MainFrame.cpp:460 +msgid "Import &Config" +msgstr "Importeer configuratie" + +#: src/slic3r/GUI/MainFrame.cpp:460 +msgid "Load exported configuration file" +msgstr "Laad geëxporteerd configuratiebestand" + +#: src/slic3r/GUI/MainFrame.cpp:462 +msgid "Import Config from &project" +msgstr "Importeer configuratie van project" + +#: src/slic3r/GUI/MainFrame.cpp:462 +msgid "Load configuration from project file" +msgstr "Laad configuratie van projectbestand" + +#: src/slic3r/GUI/MainFrame.cpp:465 +msgid "Import Config &Bundle" +msgstr "Importeer configuratiebundel" + +#: src/slic3r/GUI/MainFrame.cpp:465 +msgid "Load presets from a bundle" +msgstr "Laad presets van een bundel" + +#: src/slic3r/GUI/MainFrame.cpp:467 +msgid "&Import" +msgstr "Importeer" + +#: src/slic3r/GUI/MainFrame.cpp:470 src/slic3r/GUI/MainFrame.cpp:725 +msgid "Export &G-code" +msgstr "Exporteer G-code" + +#: src/slic3r/GUI/MainFrame.cpp:470 +msgid "Export current plate as G-code" +msgstr "Exporteer huidige modellen als gcode-bestand" + +#: src/slic3r/GUI/MainFrame.cpp:474 src/slic3r/GUI/MainFrame.cpp:726 +msgid "S&end G-code" +msgstr "Stuur G-code" + +#: src/slic3r/GUI/MainFrame.cpp:474 +msgid "Send to print current plate as G-code" +msgstr "Stuur huidige weergave als G-code" + +#: src/slic3r/GUI/MainFrame.cpp:479 +msgid "Export plate as &STL" +msgstr "Exporteer huidige modellen als STL-bestand" + +#: src/slic3r/GUI/MainFrame.cpp:479 +msgid "Export current plate as STL" +msgstr "Exporteer huidige modellen als STL-bestand" + +#: src/slic3r/GUI/MainFrame.cpp:482 +msgid "Export plate as STL &including supports" +msgstr "Exporteer modellen met support als STL-bestand" + +#: src/slic3r/GUI/MainFrame.cpp:482 +msgid "Export current plate as STL including supports" +msgstr "Exporteer huidige modellen met support als STL-bestand" + +#: src/slic3r/GUI/MainFrame.cpp:485 +msgid "Export plate as &AMF" +msgstr "Exporteer huidige modellen als AMF-bestand" + +#: src/slic3r/GUI/MainFrame.cpp:485 +msgid "Export current plate as AMF" +msgstr "Exporteer huidige modellen als AMF-bestand" + +#: src/slic3r/GUI/MainFrame.cpp:489 +msgid "Export &toolpaths as OBJ" +msgstr "Exporteer paden als OBJ-bestand" + +#: src/slic3r/GUI/MainFrame.cpp:489 +msgid "Export toolpaths as OBJ" +msgstr "Exporteer paden als OBJ-bestand" + +#: src/slic3r/GUI/MainFrame.cpp:493 +msgid "Export &Config" +msgstr "Exporteer configuratie" + +#: src/slic3r/GUI/MainFrame.cpp:493 +msgid "Export current configuration to file" +msgstr "Exporteer huidige configuratie naar bestand" + +#: src/slic3r/GUI/MainFrame.cpp:495 +msgid "Export Config &Bundle" +msgstr "Exporteer configuratiebundel" + +#: src/slic3r/GUI/MainFrame.cpp:495 +msgid "Export all presets to file" +msgstr "Exporteer alle presets naar bestand" + +#: src/slic3r/GUI/MainFrame.cpp:497 +msgid "&Export" +msgstr "Exporteer" + +#: src/slic3r/GUI/MainFrame.cpp:503 +msgid "Quick Slice" +msgstr "Snel slicen" + +#: src/slic3r/GUI/MainFrame.cpp:503 +msgid "Slice a file into a G-code" +msgstr "Slice naar een gcode-bestand" + +#: src/slic3r/GUI/MainFrame.cpp:509 +msgid "Quick Slice and Save As" +msgstr "Snel slicen en opslaan als" + +#: src/slic3r/GUI/MainFrame.cpp:509 +msgid "Slice a file into a G-code, save as" +msgstr "Slice naar gcode-bestand, opslaan als" + +#: src/slic3r/GUI/MainFrame.cpp:515 +msgid "Repeat Last Quick Slice" +msgstr "Herhaal laatste snelle slice" + +#: src/slic3r/GUI/MainFrame.cpp:515 +msgid "Repeat last quick slice" +msgstr "Herhaal laatste snelle slice" + +#: src/slic3r/GUI/MainFrame.cpp:523 +msgid "(Re)Slice No&w" +msgstr "(Her)slice nu" + +#: src/slic3r/GUI/MainFrame.cpp:523 +msgid "Start new slicing process" +msgstr "Start nieuw sliceproces" + +#: src/slic3r/GUI/MainFrame.cpp:527 +msgid "&Repair STL file" +msgstr "Repareer STL-bestand" + +#: src/slic3r/GUI/MainFrame.cpp:527 +msgid "Automatically repair an STL file" +msgstr "Automatisch een STL-bestand repareren" + +#: src/slic3r/GUI/MainFrame.cpp:530 +msgid "&Quit" +msgstr "Afsluiten" + +#: src/slic3r/GUI/MainFrame.cpp:530 +#, c-format +msgid "Quit %s" +msgstr "%s afsluiten" + +#: src/slic3r/GUI/MainFrame.cpp:555 +msgid "&Select all" +msgstr "Selecteer alle" + +#: src/slic3r/GUI/MainFrame.cpp:556 +msgid "Selects all objects" +msgstr "Selecteer alle objecten" + +#: src/slic3r/GUI/MainFrame.cpp:558 +msgid "D&eselect all" +msgstr "Deselecteer alles" + +#: src/slic3r/GUI/MainFrame.cpp:559 +msgid "Deselects all objects" +msgstr "Deselecteer alle objecten" + +#: src/slic3r/GUI/MainFrame.cpp:562 +msgid "&Delete selected" +msgstr "Verwijder selectie" + +#: src/slic3r/GUI/MainFrame.cpp:563 +msgid "Deletes the current selection" +msgstr "Verwijdert huidige selectie" + +#: src/slic3r/GUI/MainFrame.cpp:565 +msgid "Delete &all" +msgstr "Verwijder alles" + +#: src/slic3r/GUI/MainFrame.cpp:566 +msgid "Deletes all objects" +msgstr "Verwijdert alle objecten" + +#: src/slic3r/GUI/MainFrame.cpp:570 +msgid "&Undo" +msgstr "Maak ongedaan" + +#: src/slic3r/GUI/MainFrame.cpp:573 +msgid "&Redo" +msgstr "Doe opnieuw" + +#: src/slic3r/GUI/MainFrame.cpp:578 +msgid "&Copy" +msgstr "Kopieer" + +#: src/slic3r/GUI/MainFrame.cpp:579 +msgid "Copy selection to clipboard" +msgstr "Kopieer selectie naar klembord" + +#: src/slic3r/GUI/MainFrame.cpp:581 +msgid "&Paste" +msgstr "Plak" + +#: src/slic3r/GUI/MainFrame.cpp:582 +msgid "Paste clipboard" +msgstr "Plak van klembord" + +#: src/slic3r/GUI/MainFrame.cpp:591 +msgid "&Plater Tab" +msgstr "Modelweergavetab" + +#: src/slic3r/GUI/MainFrame.cpp:591 +msgid "Show the plater" +msgstr "Toon de modelweergave" + +#: src/slic3r/GUI/MainFrame.cpp:598 +msgid "P&rint Settings Tab" +msgstr "Printinstellingentab" + +#: src/slic3r/GUI/MainFrame.cpp:598 +msgid "Show the print settings" +msgstr "Toon de printinstellingen" + +#: src/slic3r/GUI/MainFrame.cpp:600 src/slic3r/GUI/MainFrame.cpp:728 +msgid "&Filament Settings Tab" +msgstr "Filamentinstellingentab" + +#: src/slic3r/GUI/MainFrame.cpp:600 +msgid "Show the filament settings" +msgstr "Toon de filamentinstellingentab" + +#: src/slic3r/GUI/MainFrame.cpp:603 +msgid "Print&er Settings Tab" +msgstr "Printerinstellingentab" + +#: src/slic3r/GUI/MainFrame.cpp:603 +msgid "Show the printer settings" +msgstr "Toon de printerinstellingen" + +#: src/slic3r/GUI/MainFrame.cpp:607 +msgid "3&D" +msgstr "3D" + +#: src/slic3r/GUI/MainFrame.cpp:607 +msgid "Show the 3D editing view" +msgstr "Toon de 3D-bewerkingsweergave" + +#: src/slic3r/GUI/MainFrame.cpp:610 +msgid "Pre&view" +msgstr "Voorbeeld" + +#: src/slic3r/GUI/MainFrame.cpp:610 +msgid "Show the 3D slices preview" +msgstr "Toon de 3D-weergave van de slice" + +#: src/slic3r/GUI/MainFrame.cpp:629 +msgid "Print &Host Upload Queue" +msgstr "Printhost uploadwachtrij" + +#: src/slic3r/GUI/MainFrame.cpp:629 +msgid "Display the Print Host Upload Queue window" +msgstr "Toon het venster van de printhost uploadwachtrij" + +#: src/slic3r/GUI/MainFrame.cpp:638 +msgid "Iso" +msgstr "Isometrisch" + +#: src/slic3r/GUI/MainFrame.cpp:638 +msgid "Iso View" +msgstr "Isometrisch aanzicht" + +#. TRN To be shown in the main menu View->Top +#. TRN To be shown in Print Settings "Top solid layers" +#: src/slic3r/GUI/MainFrame.cpp:642 src/libslic3r/PrintConfig.cpp:2121 +msgid "Top" +msgstr "Bovenkant" + +#: src/slic3r/GUI/MainFrame.cpp:642 +msgid "Top View" +msgstr "Bovenaanzicht" + +#. TRN To be shown in the main menu View->Bottom +#. TRN To be shown in Print Settings "Bottom solid layers" +#: src/slic3r/GUI/MainFrame.cpp:645 src/libslic3r/PrintConfig.cpp:164 +msgid "Bottom" +msgstr "Onderkant" + +#: src/slic3r/GUI/MainFrame.cpp:645 +msgid "Bottom View" +msgstr "Onderaanzicht" + +#: src/slic3r/GUI/MainFrame.cpp:647 +msgid "Front" +msgstr "Voorkant" + +#: src/slic3r/GUI/MainFrame.cpp:647 +msgid "Front View" +msgstr "Vooraanzicht" + +#: src/slic3r/GUI/MainFrame.cpp:649 src/libslic3r/PrintConfig.cpp:1627 +msgid "Rear" +msgstr "Achterkant" + +#: src/slic3r/GUI/MainFrame.cpp:649 +msgid "Rear View" +msgstr "Achteraanzicht" + +#: src/slic3r/GUI/MainFrame.cpp:651 +msgid "Left" +msgstr "Links" + +#: src/slic3r/GUI/MainFrame.cpp:651 +msgid "Left View" +msgstr "Linkerzijaanzicht" + +#: src/slic3r/GUI/MainFrame.cpp:653 +msgid "Right" +msgstr "Rechts" + +#: src/slic3r/GUI/MainFrame.cpp:653 +msgid "Right View" +msgstr "Rechterzijaanzicht" + +#: src/slic3r/GUI/MainFrame.cpp:660 +msgid "Prusa 3D &Drivers" +msgstr "Prusa 3D stuurprogramma" + +#: src/slic3r/GUI/MainFrame.cpp:660 +msgid "Open the Prusa3D drivers download page in your browser" +msgstr "Open de Prusa3D drivers-downloadpagina in uw browser" + +#: src/slic3r/GUI/MainFrame.cpp:662 +msgid "Software &Releases" +msgstr "Software-uitgaven" + +#: src/slic3r/GUI/MainFrame.cpp:662 +msgid "Open the software releases page in your browser" +msgstr "Open de software-uitgaven pagina in uw browser" + +#: src/slic3r/GUI/MainFrame.cpp:668 +#, c-format +msgid "%s &Website" +msgstr "%s-website" + +#: src/slic3r/GUI/MainFrame.cpp:669 +#, c-format +msgid "Open the %s website in your browser" +msgstr "Open de %s website in uw browser" + +#: src/slic3r/GUI/MainFrame.cpp:675 +msgid "System &Info" +msgstr "Systeeminfo" + +#: src/slic3r/GUI/MainFrame.cpp:675 +msgid "Show system information" +msgstr "Toon systeeminformatie" + +#: src/slic3r/GUI/MainFrame.cpp:677 +msgid "Show &Configuration Folder" +msgstr "Toon configuratiemap" + +#: src/slic3r/GUI/MainFrame.cpp:677 +msgid "Show user configuration folder (datadir)" +msgstr "Toon gebruikersconfiguratiemap (datadir)" + +#: src/slic3r/GUI/MainFrame.cpp:679 +msgid "Report an I&ssue" +msgstr "Rapporteer een fout" + +#: src/slic3r/GUI/MainFrame.cpp:679 +#, c-format +msgid "Report an issue on %s" +msgstr "Rapporteer een fout op %s" + +#: src/slic3r/GUI/MainFrame.cpp:681 +#, c-format +msgid "&About %s" +msgstr "Over %s" + +#: src/slic3r/GUI/MainFrame.cpp:681 +msgid "Show about dialog" +msgstr "Toon Over-dialoogvenster" + +#: src/slic3r/GUI/MainFrame.cpp:684 +msgid "Show the list of the keyboard shortcuts" +msgstr "Toon de lijst met sneltoetsen" + +#: src/slic3r/GUI/MainFrame.cpp:697 +msgid "&File" +msgstr "Bestand" + +#: src/slic3r/GUI/MainFrame.cpp:698 +msgid "&Edit" +msgstr "Bewerk" + +#: src/slic3r/GUI/MainFrame.cpp:699 +msgid "&Window" +msgstr "Venster" + +#: src/slic3r/GUI/MainFrame.cpp:700 +msgid "&View" +msgstr "Weergave" + +#: src/slic3r/GUI/MainFrame.cpp:703 +msgid "&Help" +msgstr "Help" + +#: src/slic3r/GUI/MainFrame.cpp:725 +msgid "E&xport" +msgstr "Exporteer" + +#: src/slic3r/GUI/MainFrame.cpp:726 +msgid "S&end to print" +msgstr "Stuur om te printen" + +#: src/slic3r/GUI/MainFrame.cpp:728 +msgid "Mate&rial Settings Tab" +msgstr "Materiaalinstellingentab" + +#: src/slic3r/GUI/MainFrame.cpp:749 +msgid "Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):" +msgstr "Kies een STL-, OBJ-, AMF-, 3MF- of PRUSA-bestand om te slicen:" + +#: src/slic3r/GUI/MainFrame.cpp:760 +msgid "No previously sliced file." +msgstr "Niet eerder gesliced bestand." + +#: src/slic3r/GUI/MainFrame.cpp:766 +msgid "Previously sliced file (" +msgstr "Eerder gesliced bestand (" + +#: src/slic3r/GUI/MainFrame.cpp:766 +msgid ") not found." +msgstr ") niet gevonden." + +#: src/slic3r/GUI/MainFrame.cpp:767 +msgid "File Not Found" +msgstr "Bestand niet gevonden" + +#: src/slic3r/GUI/MainFrame.cpp:802 +#, c-format +msgid "Save %s file as:" +msgstr "%s-bestand opslaan als:" + +#: src/slic3r/GUI/MainFrame.cpp:802 +msgid "SVG" +msgstr "SVG" + +#: src/slic3r/GUI/MainFrame.cpp:802 +msgid "G-code" +msgstr "G-code" + +#: src/slic3r/GUI/MainFrame.cpp:814 +msgid "Save zip file as:" +msgstr "ZIP-bestand opslaan als:" + +#: src/slic3r/GUI/MainFrame.cpp:823 src/slic3r/GUI/Plater.cpp:3058 +#: src/slic3r/GUI/Plater.cpp:4781 src/slic3r/GUI/Tab.cpp:1201 +#: src/slic3r/GUI/Tab.cpp:3615 +msgid "Slicing" +msgstr "Slicen" + +#. TRN "Processing input_file_basename" +#: src/slic3r/GUI/MainFrame.cpp:825 +#, c-format +msgid "Processing %s" +msgstr "%s verwerken" + +#: src/slic3r/GUI/MainFrame.cpp:848 +msgid " was successfully sliced." +msgstr " succesvol gesliced." + +#: src/slic3r/GUI/MainFrame.cpp:850 +msgid "Slicing Done!" +msgstr "Slicen klaar!" + +#: src/slic3r/GUI/MainFrame.cpp:865 +msgid "Select the STL file to repair:" +msgstr "Selecteer het STL-bestand om te repareren:" + +#: src/slic3r/GUI/MainFrame.cpp:875 +msgid "Save OBJ file (less prone to coordinate errors than STL) as:" +msgstr "OBJ-bestand opslaan als:" + +#: src/slic3r/GUI/MainFrame.cpp:887 +msgid "Your file was repaired." +msgstr "Het bestand is gerepareerd." + +#: src/slic3r/GUI/MainFrame.cpp:887 src/libslic3r/PrintConfig.cpp:3374 +msgid "Repair" +msgstr "Repareer" + +#: src/slic3r/GUI/MainFrame.cpp:901 +msgid "Save configuration as:" +msgstr "Configuratie opslaan als:" + +#: src/slic3r/GUI/MainFrame.cpp:920 src/slic3r/GUI/MainFrame.cpp:982 +msgid "Select configuration to load:" +msgstr "Selecteer configuratie om te laden:" + +#: src/slic3r/GUI/MainFrame.cpp:956 +msgid "Save presets bundle as:" +msgstr "Presetbundel opslaan als:" + +#: src/slic3r/GUI/MainFrame.cpp:1003 +#, c-format +msgid "%d presets successfully imported." +msgstr "%d presets succesvol geïmporteerd." + +#: src/slic3r/GUI/Mouse3DController.cpp:255 +msgid "3Dconnexion settings" +msgstr "3Dconnexion-instellingen" + +#: src/slic3r/GUI/Mouse3DController.cpp:259 +msgid "Device:" +msgstr "Apparaat:" + +#: src/slic3r/GUI/Mouse3DController.cpp:266 +msgid "Speed:" +msgstr "Snelheid:" + +#: src/slic3r/GUI/Mouse3DController.cpp:270 +#: src/slic3r/GUI/Mouse3DController.cpp:283 +msgid "Translation" +msgstr "Verplaatsing" + +#: src/slic3r/GUI/Mouse3DController.cpp:279 +msgid "Deadzone:" +msgstr "Deadzone:" + +#: src/slic3r/GUI/MsgDialog.cpp:73 +#, c-format +msgid "%s error" +msgstr "%s fout" + +#: src/slic3r/GUI/MsgDialog.cpp:74 +#, c-format +msgid "%s has encountered an error" +msgstr "%s heeft een fout veroorzaakt" + +#: src/slic3r/GUI/OptionsGroup.cpp:249 +msgctxt "Layers" +msgid "Top" +msgstr "Boven" + +#: src/slic3r/GUI/OptionsGroup.cpp:249 +msgctxt "Layers" +msgid "Bottom" +msgstr "Bodem" + +#: src/slic3r/GUI/Plater.cpp:155 +msgid "Volume" +msgstr "Volume" + +#: src/slic3r/GUI/Plater.cpp:156 +msgid "Facets" +msgstr "Vlakken" + +#: src/slic3r/GUI/Plater.cpp:157 +msgid "Materials" +msgstr "Materialen" + +#: src/slic3r/GUI/Plater.cpp:160 +msgid "Manifold" +msgstr "Gesloten model" + +#: src/slic3r/GUI/Plater.cpp:210 +msgid "Sliced Info" +msgstr "Slice info" + +#: src/slic3r/GUI/Plater.cpp:229 src/slic3r/GUI/Plater.cpp:1179 +msgid "Used Filament (m)" +msgstr "Filamentverbruik (m)" + +#: src/slic3r/GUI/Plater.cpp:230 +msgid "Used Filament (mm³)" +msgstr "Filamentverbruik (mm³)" + +#: src/slic3r/GUI/Plater.cpp:231 +msgid "Used Filament (g)" +msgstr "Filamentverbruik (g)" + +#: src/slic3r/GUI/Plater.cpp:232 +msgid "Used Material (unit)" +msgstr "Materiaalverbruik (eenheid)" + +#: src/slic3r/GUI/Plater.cpp:233 +msgid "Cost (money)" +msgstr "Kosten (€)" + +#: src/slic3r/GUI/Plater.cpp:234 src/slic3r/GUI/Plater.cpp:1166 +#: src/slic3r/GUI/Plater.cpp:1208 +msgid "Estimated printing time" +msgstr "Geschatte printtijd" + +#: src/slic3r/GUI/Plater.cpp:235 +msgid "Number of tool changes" +msgstr "Aantal toolwisselingen" + +#: src/slic3r/GUI/Plater.cpp:332 +msgid "Click to edit preset" +msgstr "Klik om de preset te wijzigen" + +#: src/slic3r/GUI/Plater.cpp:487 +msgid "Select what kind of support do you need" +msgstr "Selecteer welk type support nodig is" + +#: src/slic3r/GUI/Plater.cpp:489 src/libslic3r/PrintConfig.cpp:1890 +#: src/libslic3r/PrintConfig.cpp:2642 +msgid "Support on build plate only" +msgstr "Support alleen op het bed" + +#: src/slic3r/GUI/Plater.cpp:490 src/slic3r/GUI/Plater.cpp:613 +msgid "For support enforcers only" +msgstr "Alleen voor supportforceringen" + +#: src/slic3r/GUI/Plater.cpp:491 +msgid "Everywhere" +msgstr "Overal" + +#: src/slic3r/GUI/Plater.cpp:523 src/slic3r/GUI/Tab.cpp:1097 +msgid "Brim" +msgstr "Brim" + +#: src/slic3r/GUI/Plater.cpp:525 +msgid "" +"This flag enables the brim that will be printed around each object on the " +"first layer." +msgstr "Door dit aan te vinken zal een brim rond elke object geprint worden." + +#: src/slic3r/GUI/Plater.cpp:533 +msgid "Purging volumes" +msgstr "Afveegvolume" + +#: src/slic3r/GUI/Plater.cpp:627 +msgid "Select what kind of pad do you need" +msgstr "Selecteer welk soort basisplaat nodig is" + +#: src/slic3r/GUI/Plater.cpp:629 +msgid "Below object" +msgstr "Onder het object" + +#: src/slic3r/GUI/Plater.cpp:630 +msgid "Around object" +msgstr "Rondom het object" + +#: src/slic3r/GUI/Plater.cpp:802 +msgid "Print settings" +msgstr "Printinstellingen" + +#: src/slic3r/GUI/Plater.cpp:803 src/slic3r/GUI/Tab.cpp:1405 +#: src/slic3r/GUI/Tab.cpp:1406 +msgid "Filament" +msgstr "Filament" + +#: src/slic3r/GUI/Plater.cpp:804 +msgid "SLA print settings" +msgstr "SLA-printinstellingen" + +#: src/slic3r/GUI/Plater.cpp:805 src/slic3r/GUI/Preset.cpp:1411 +msgid "SLA material" +msgstr "SLA-materiaal" + +#: src/slic3r/GUI/Plater.cpp:806 +msgid "Printer" +msgstr "Printer" + +#: src/slic3r/GUI/Plater.cpp:856 src/slic3r/GUI/Plater.cpp:5143 +msgid "Send to printer" +msgstr "Stuur naar printer" + +#: src/slic3r/GUI/Plater.cpp:859 src/slic3r/GUI/Plater.cpp:3058 +#: src/slic3r/GUI/Plater.cpp:4784 +msgid "Slice now" +msgstr "Slice nu" + +#: src/slic3r/GUI/Plater.cpp:999 +msgid "Hold Shift to Slice & Export G-code" +msgstr "Houdt shift ingedrukt om te slicen en de G-code te exporteren" + +#: src/slic3r/GUI/Plater.cpp:1102 +#, c-format +msgid "%d (%d shells)" +msgstr "%d (%d shells)" + +#: src/slic3r/GUI/Plater.cpp:1107 +#, c-format +msgid "Auto-repaired (%d errors)" +msgstr "Automatisch gerepareerd (%d fouten)" + +#: src/slic3r/GUI/Plater.cpp:1110 +#, c-format +msgid "" +"%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d " +"facets reversed, %d backwards edges" +msgstr "" +"%d degenereer vlakken, %d randen vastgezet, %d vlakken verwijderd, %d " +"vlakken toegevoegd, %d vlakken omgekeerd, %d randen omgekeerd" + +#: src/slic3r/GUI/Plater.cpp:1120 +msgid "Yes" +msgstr "Ja" + +#: src/slic3r/GUI/Plater.cpp:1141 +msgid "Used Material (ml)" +msgstr "Materiaalgebruik (ml)" + +#: src/slic3r/GUI/Plater.cpp:1144 +msgid "object(s)" +msgstr "object(en)" + +#: src/slic3r/GUI/Plater.cpp:1144 +msgid "supports and pad" +msgstr "support en basisplaat" + +#: src/slic3r/GUI/Plater.cpp:1181 src/slic3r/GUI/Plater.cpp:1196 +msgid "objects" +msgstr "objecten" + +#: src/slic3r/GUI/Plater.cpp:1181 src/slic3r/GUI/Plater.cpp:1196 +msgid "wipe tower" +msgstr "afveegblok" + +#: src/slic3r/GUI/Plater.cpp:1194 src/libslic3r/PrintConfig.cpp:749 +#: src/libslic3r/PrintConfig.cpp:2478 src/libslic3r/PrintConfig.cpp:2479 +msgid "Cost" +msgstr "Kosten" + +#: src/slic3r/GUI/Plater.cpp:1211 +msgid "normal mode" +msgstr "normale modus" + +#: src/slic3r/GUI/Plater.cpp:1215 src/slic3r/GUI/Plater.cpp:1224 +#: src/libslic3r/PrintConfig.cpp:572 +msgid "Color" +msgstr "Kleur" + +#: src/slic3r/GUI/Plater.cpp:1220 +msgid "stealth mode" +msgstr "stille modus" + +#: src/slic3r/GUI/Plater.cpp:1324 +msgid "Load File" +msgstr "Laad bestand" + +#: src/slic3r/GUI/Plater.cpp:1328 +msgid "Load Files" +msgstr "Laad bestanden" + +#: src/slic3r/GUI/Plater.cpp:1561 +msgid "ERROR: not enough resources to execute a new job." +msgstr "Fout: niet genoeg middelen om nieuwe job te starten." + +#: src/slic3r/GUI/Plater.cpp:2158 +msgid "New Project" +msgstr "Nieuw project" + +#: src/slic3r/GUI/Plater.cpp:2277 +msgid "Loading" +msgstr "Aan het laden" + +#: src/slic3r/GUI/Plater.cpp:2287 +#, c-format +msgid "Processing input file %s" +msgstr "Verwerken van inputbestand %s" + +#: src/slic3r/GUI/Plater.cpp:2315 +msgid "" +"You can't load SLA project if there is at least one multi-part object on the " +"bed" +msgstr "" +"U kan geen SLA-project laden als er tenminste één meerdelig object op het " +"bed is" + +#: src/slic3r/GUI/Plater.cpp:2316 src/slic3r/GUI/Tab.cpp:2915 +msgid "Please check your object list before preset changing." +msgstr "Controleer de objectenlijst voor het wijzigen van de preset." + +#: src/slic3r/GUI/Plater.cpp:2361 +msgid "" +"This file contains several objects positioned at multiple heights.\n" +"Instead of considering them as multiple objects, should I consider\n" +"this file as a single object having multiple parts?" +msgstr "" +"Dit bestand bevat meerdere objecten die op meerdere hoogten zijn " +"gepositioneerd.\n" +"Moet dit bestand beschouwd worden als een enkel object met meerdere " +"onderdelen\n" +"in plaats van als meerdere objecten?" + +#: src/slic3r/GUI/Plater.cpp:2364 src/slic3r/GUI/Plater.cpp:2417 +msgid "Multi-part object detected" +msgstr "Meerdelig object gedetecteerd" + +#: src/slic3r/GUI/Plater.cpp:2371 +msgid "" +"This file cannot be loaded in a simple mode. Do you want to switch to an " +"advanced mode?" +msgstr "" +"Dit bestand kan niet geladen worden in eenvoudige modus. Wilt u overstappen " +"op geavanceerde modus?" + +#: src/slic3r/GUI/Plater.cpp:2372 +msgid "Detected advanced data" +msgstr "Geavanceerde data gedetecteerd" + +#: src/slic3r/GUI/Plater.cpp:2394 +#, c-format +msgid "" +"You can't to add the object(s) from %s because of one or some of them " +"is(are) multi-part" +msgstr "" +"U kan geen objecten toevoegen van %s, omdat sommige daarvan meerdelig kunnen " +"zijn" + +#: src/slic3r/GUI/Plater.cpp:2414 +msgid "" +"Multiple objects were loaded for a multi-material printer.\n" +"Instead of considering them as multiple objects, should I consider\n" +"these files to represent a single object having multiple parts?" +msgstr "" +"Meerdere objecten zijn geladen voor een multi-materialprinter.\n" +"Moeten deze objecten beschouwd worden als één object\n" +"met meerdere onderdelen, of als meerdere objecten?" + +#: src/slic3r/GUI/Plater.cpp:2430 +msgid "Loaded" +msgstr "Geladen" + +#: src/slic3r/GUI/Plater.cpp:2532 +msgid "" +"Your object appears to be too large, so it was automatically scaled down to " +"fit your print bed." +msgstr "" +"Het object is te groot. Daarom is het automatisch verschaald tot de grootte " +"van het printbed." + +#: src/slic3r/GUI/Plater.cpp:2533 +msgid "Object too large?" +msgstr "Object te groot?" + +#: src/slic3r/GUI/Plater.cpp:2595 +msgid "Export STL file:" +msgstr "Exporteer STL-bestand:" + +#: src/slic3r/GUI/Plater.cpp:2602 +msgid "Export AMF file:" +msgstr "Exporteer AMF-bestand:" + +#: src/slic3r/GUI/Plater.cpp:2608 +msgid "Save file as:" +msgstr "Bestand opslaan als:" + +#: src/slic3r/GUI/Plater.cpp:2614 +msgid "Export OBJ file:" +msgstr "Exporteer OBJ-bestand:" + +#: src/slic3r/GUI/Plater.cpp:2716 +msgid "Delete Object" +msgstr "Verwijder object" + +#: src/slic3r/GUI/Plater.cpp:2727 +msgid "Reset Project" +msgstr "Reset project" + +#: src/slic3r/GUI/Plater.cpp:2765 +msgid "Optimize Rotation" +msgstr "Optimaliseer rotatie" + +#: src/slic3r/GUI/Plater.cpp:2811 +msgid "Arranging" +msgstr "Schikken" + +#: src/slic3r/GUI/Plater.cpp:2833 +msgid "Could not arrange model objects! Some geometries may be invalid." +msgstr "Kan modellen niet schikken. Sommige vormen kunnen ongeldig zijn." + +#: src/slic3r/GUI/Plater.cpp:2839 +msgid "Arranging canceled." +msgstr "Schikken geannuleerd." + +#: src/slic3r/GUI/Plater.cpp:2840 +msgid "Arranging done." +msgstr "Schikken voltooid." + +#: src/slic3r/GUI/Plater.cpp:2856 +msgid "Searching for optimal orientation" +msgstr "Zoeken naar optimale oriëntatie" + +#: src/slic3r/GUI/Plater.cpp:2889 +msgid "Orientation search canceled." +msgstr "Oriëntatie zoeken geannuleerd." + +#: src/slic3r/GUI/Plater.cpp:2890 +msgid "Orientation found." +msgstr "Oriëntatie gevonden." + +#: src/slic3r/GUI/Plater.cpp:2906 +msgid "" +"The selected object can't be split because it contains more than one volume/" +"material." +msgstr "" +"Het geselecteerde object kan niet opgedeeld worden omdat het meer dan één " +"volume bevat." + +#: src/slic3r/GUI/Plater.cpp:2917 +msgid "Split to Objects" +msgstr "Verdeel in objecten" + +#: src/slic3r/GUI/Plater.cpp:3043 +msgid "Invalid data" +msgstr "Ongeldige data" + +#: src/slic3r/GUI/Plater.cpp:3052 +msgid "Ready to slice" +msgstr "Klaar om te slicen" + +#: src/slic3r/GUI/Plater.cpp:3090 src/slic3r/GUI/PrintHostDialogs.cpp:232 +msgid "Cancelling" +msgstr "Annuleren" + +#: src/slic3r/GUI/Plater.cpp:3107 +msgid "Another export job is currently running." +msgstr "Een andere export loopt op dit moment." + +#: src/slic3r/GUI/Plater.cpp:3276 +msgid "Fix Throught NetFabb" +msgstr "Repareer met NetFabb" + +#: src/slic3r/GUI/Plater.cpp:3467 +msgid "Export failed" +msgstr "Exporteren mislukt" + +#: src/slic3r/GUI/Plater.cpp:3472 src/slic3r/GUI/PrintHostDialogs.cpp:233 +msgid "Cancelled" +msgstr "Geannuleerd" + +#: src/slic3r/GUI/Plater.cpp:3712 src/slic3r/GUI/Plater.cpp:3734 +msgid "Remove the selected object" +msgstr "Verwijder het geselecteerde object" + +#: src/slic3r/GUI/Plater.cpp:3721 +msgid "Add one more instance of the selected object" +msgstr "Voeg een instantie van het geselecteerde object toe" + +#: src/slic3r/GUI/Plater.cpp:3723 +msgid "Remove one instance of the selected object" +msgstr "Verwijder een instantie van het geselecteerde object" + +#: src/slic3r/GUI/Plater.cpp:3725 +msgid "Set number of instances" +msgstr "Stel aantal instanties in" + +#: src/slic3r/GUI/Plater.cpp:3725 +msgid "Change the number of instances of the selected object" +msgstr "Wijzig het aantal instanties van het geselecteerde object" + +#: src/slic3r/GUI/Plater.cpp:3744 +msgid "Reload the selected object from disk" +msgstr "Herlaad het geselecteerde object van de schijf" + +#: src/slic3r/GUI/Plater.cpp:3747 +msgid "Export the selected object as STL file" +msgstr "Exporteer de geselecteerde objecten als STL-bestand" + +#: src/slic3r/GUI/Plater.cpp:3772 +msgid "Along X axis" +msgstr "Over de X-as" + +#: src/slic3r/GUI/Plater.cpp:3772 +msgid "Mirror the selected object along the X axis" +msgstr "Spiegel het geselecteerde object over de X-as" + +#: src/slic3r/GUI/Plater.cpp:3774 +msgid "Along Y axis" +msgstr "Over de Y-as" + +#: src/slic3r/GUI/Plater.cpp:3774 +msgid "Mirror the selected object along the Y axis" +msgstr "Spiegel het geselecteerde object over de Y-as" + +#: src/slic3r/GUI/Plater.cpp:3776 +msgid "Along Z axis" +msgstr "Over de Z-as" + +#: src/slic3r/GUI/Plater.cpp:3776 +msgid "Mirror the selected object along the Z axis" +msgstr "Spiegel het geselecteerde object over de Z-as" + +#: src/slic3r/GUI/Plater.cpp:3779 +msgid "Mirror" +msgstr "Spiegelen" + +#: src/slic3r/GUI/Plater.cpp:3779 +msgid "Mirror the selected object" +msgstr "Spiegel het geselecteerde object" + +#: src/slic3r/GUI/Plater.cpp:3791 +msgid "To objects" +msgstr "Aan objecten" + +#: src/slic3r/GUI/Plater.cpp:3791 src/slic3r/GUI/Plater.cpp:3811 +msgid "Split the selected object into individual objects" +msgstr "Verdeel het geselecteerde object in individuele objecten" + +#: src/slic3r/GUI/Plater.cpp:3793 +msgid "To parts" +msgstr "Aan onderdelen" + +#: src/slic3r/GUI/Plater.cpp:3793 src/slic3r/GUI/Plater.cpp:3825 +msgid "Split the selected object into individual sub-parts" +msgstr "Deel het geselecteerde object op in meerdere subonderdelen" + +#: src/slic3r/GUI/Plater.cpp:3796 src/slic3r/GUI/Plater.cpp:3811 +#: src/slic3r/GUI/Plater.cpp:3825 src/libslic3r/PrintConfig.cpp:3398 +msgid "Split" +msgstr "Verdeel" + +#: src/slic3r/GUI/Plater.cpp:3796 +msgid "Split the selected object" +msgstr "Verdeel het geselecteerde object" + +#: src/slic3r/GUI/Plater.cpp:3817 +msgid "Optimize orientation" +msgstr "Optimaliseer oriëntatie" + +#: src/slic3r/GUI/Plater.cpp:3817 +msgid "Optimize the rotation of the object for better print results." +msgstr "Optimaliseer de rotatie van het object voor betere printresultaten." + +#: src/slic3r/GUI/Plater.cpp:3857 +msgid "3D editor view" +msgstr "3D bewerkingsweergave" + +#: src/slic3r/GUI/Plater.cpp:3865 src/slic3r/GUI/Tab.cpp:2358 +msgid "Preview" +msgstr "Voorbeeldweergave" + +#: src/slic3r/GUI/Plater.cpp:4144 +msgid "" +"%1% printer was active at the time the target Undo / Redo snapshot was " +"taken. Switching to %1% printer requires reloading of %1% presets." +msgstr "" +"%1% de printer was actief op het moment een 'ongedaan maken'- of 'opnieuw " +"doen'-snapshot werd genomen. Schakelen naar %1% printer vereist herladen van " +"%1% presets." + +#: src/slic3r/GUI/Plater.cpp:4319 +msgid "Load Project" +msgstr "Laad project" + +#: src/slic3r/GUI/Plater.cpp:4347 +msgid "Import Object" +msgstr "Importeer object" + +#: src/slic3r/GUI/Plater.cpp:4351 +msgid "Import Objects" +msgstr "Importeer objecten" + +#: src/slic3r/GUI/Plater.cpp:4410 +msgid "All objects will be removed, continue ?" +msgstr "Alle objecten worden verwijderd. Weet u zeker dat u wilt doorgaan?" + +#: src/slic3r/GUI/Plater.cpp:4418 +msgid "Delete Selected Objects" +msgstr "Verwijder geselecteerde objecten" + +#: src/slic3r/GUI/Plater.cpp:4426 +msgid "Increase Instances" +msgstr "Verhoog aantal instanties" + +#: src/slic3r/GUI/Plater.cpp:4461 +msgid "Decrease Instances" +msgstr "Verlaag aantal instanties" + +#: src/slic3r/GUI/Plater.cpp:4497 +#, c-format +msgid "Set numbers of copies to %d" +msgstr "Stel aantal kopieën in voor %d" + +#: src/slic3r/GUI/Plater.cpp:4527 +msgid "Cut by Plane" +msgstr "Snij met behulp van vlak" + +#: src/slic3r/GUI/Plater.cpp:4559 +msgid "Save G-code file as:" +msgstr "gcode-bestand opslaan als:" + +#: src/slic3r/GUI/Plater.cpp:4559 +msgid "Save SL1 file as:" +msgstr "SL1-bestand opslaan als:" + +#: src/slic3r/GUI/Plater.cpp:4671 +#, c-format +msgid "STL file exported to %s" +msgstr "STL-bestand geëxporteerd naar %s" + +#: src/slic3r/GUI/Plater.cpp:4687 +#, c-format +msgid "AMF file exported to %s" +msgstr "AMF-bestand geëxporteerd naar %s" + +#: src/slic3r/GUI/Plater.cpp:4690 +#, c-format +msgid "Error exporting AMF file %s" +msgstr "Fout bij het exporteren van AMF-bestand %s" + +#: src/slic3r/GUI/Plater.cpp:4722 +#, c-format +msgid "3MF file exported to %s" +msgstr "3MF-bestand geëxporteerd naar %s" + +#: src/slic3r/GUI/Plater.cpp:4727 +#, c-format +msgid "Error exporting 3MF file %s" +msgstr "Fout bij het exporteren van 3MF-bestand %s" + +#: src/slic3r/GUI/Plater.cpp:5142 +msgid "Export" +msgstr "Exporteer" + +#: src/slic3r/GUI/Plater.cpp:5143 +msgid "Send G-code" +msgstr "Stuur G-code" + +#: src/slic3r/GUI/Plater.cpp:5227 +msgid "Paste From Clipboard" +msgstr "Plak van klembord" + +#: src/slic3r/GUI/Preferences.cpp:22 src/slic3r/GUI/Tab.cpp:1766 +#: src/slic3r/GUI/Tab.cpp:2010 +msgid "General" +msgstr "Algemeen" + +#: src/slic3r/GUI/Preferences.cpp:44 +msgid "Remember output directory" +msgstr "Onthoud de outputmap" + +#: src/slic3r/GUI/Preferences.cpp:46 +msgid "" +"If this is enabled, Slic3r will prompt the last output directory instead of " +"the one containing the input files." +msgstr "" +"Als dit is ingeschakeld zal PrusaSlicer de laatst gebruikte exportmap " +"gebruiken in plaats van de importmap." + +#: src/slic3r/GUI/Preferences.cpp:52 +msgid "Auto-center parts" +msgstr "Centreer onderdelen automatisch" + +#: src/slic3r/GUI/Preferences.cpp:54 +msgid "" +"If this is enabled, Slic3r will auto-center objects around the print bed " +"center." +msgstr "" +"Als dit is ingeschakeld zal PrusaSlicer objecten rondom het midden centreren." + +#: src/slic3r/GUI/Preferences.cpp:60 +msgid "Background processing" +msgstr "Achtergrondverwerking" + +#: src/slic3r/GUI/Preferences.cpp:62 +msgid "" +"If this is enabled, Slic3r will pre-process objects as soon as they're " +"loaded in order to save time when exporting G-code." +msgstr "" +"Als dit is ingeschakeld zal PrusaSlicer objecten voorbewerken zodra deze " +"zijn geladen om tijd te besparen bij het exporteren van de G-code." + +#: src/slic3r/GUI/Preferences.cpp:71 +msgid "" +"If enabled, PrusaSlicer will check for the new versions of itself online. " +"When a new version becomes available a notification is displayed at the next " +"application startup (never during program usage). This is only a " +"notification mechanisms, no automatic installation is done." +msgstr "" +"Als dit is ingeschakeld zal PrusaSlicer zelf controleren op nieuwe versies. " +"Als een nieuwe versie beschikbaar is, wordt een melding weergegeven bij de " +"volgende keer opstarten. Dit is slechts een melding; er wordt niets " +"automatisch geïnstalleerd." + +#: src/slic3r/GUI/Preferences.cpp:79 +msgid "" +"If enabled, Slic3r downloads updates of built-in system presets in the " +"background. These updates are downloaded into a separate temporary location. " +"When a new preset version becomes available it is offered at application " +"startup." +msgstr "" +"Als dit is ingeschakeld zal PrusaSlicer updates of ingebouwde presets op de " +"achtergrond downloaden. Deze updates worden gedownload naar een tijdelijke " +"locatie. Als een nieuwe preset beschikbaar komt, zal dit gemeld worden bij " +"de eerstvolgende keer opstarten." + +#: src/slic3r/GUI/Preferences.cpp:84 +msgid "Suppress \" - default - \" presets" +msgstr "Verberg 'standaard'-presets" + +#: src/slic3r/GUI/Preferences.cpp:86 +msgid "" +"Suppress \" - default - \" presets in the Print / Filament / Printer " +"selections once there are any other valid presets available." +msgstr "" +"Verberg 'standaard'-presets in de print-, filament- en printerselecties als " +"er andere geldige presets beschikbaar zijn." + +#: src/slic3r/GUI/Preferences.cpp:92 +msgid "Show incompatible print and filament presets" +msgstr "Toon incompatibele print- en filamentpresets" + +#: src/slic3r/GUI/Preferences.cpp:94 +msgid "" +"When checked, the print and filament presets are shown in the preset editor " +"even if they are marked as incompatible with the active printer" +msgstr "" +"Als dit aan staat worden de print- en filamentpresets getoond in de presets-" +"editor, zelfs als ze als incompatibel met de actieve printer zijn gemarkeerd" + +#: src/slic3r/GUI/Preferences.cpp:101 +msgid "Use Retina resolution for the 3D scene" +msgstr "Gebruik hoge resolutie voor de 3D-scène" + +#: src/slic3r/GUI/Preferences.cpp:103 +msgid "" +"If enabled, the 3D scene will be rendered in Retina resolution. If you are " +"experiencing 3D performance problems, disabling this option may help." +msgstr "" +"Als dit is ingeschakeld zal de 3D-scène worden gerenderd in hoge resolutie. " +"Als u problemen ondervindt met de prestaties kan het uitschakelen van deze " +"optie mogelijk helpen." + +#: src/slic3r/GUI/Preferences.cpp:110 +msgid "Use perspective camera" +msgstr "Gebruik perspectiefweergave" + +#: src/slic3r/GUI/Preferences.cpp:112 +msgid "" +"If enabled, use perspective camera. If not enabled, use orthographic camera." +msgstr "" +"Als dit is ingeschakeld zal de weergave op perspectief worden gezet. Anders " +"wordt een orthografische weergave gebruikt." + +#: src/slic3r/GUI/Preferences.cpp:117 +msgid "Use custom size for toolbar icons" +msgstr "Gebruik een aangepaste grootte voor werkbalkpictogrammen" + +#: src/slic3r/GUI/Preferences.cpp:119 +msgid "If enabled, you can change size of toolbar icons manually." +msgstr "" +"Grootte van werkbalkpictogrammen handmatig instellen als dit is ingeschakeld." + +#: src/slic3r/GUI/Preferences.cpp:144 +#, c-format +msgid "You need to restart %s to make the changes effective." +msgstr "U moet %s opnieuw opstarten om wijzigingen door te voeren." + +#: src/slic3r/GUI/Preferences.cpp:192 +msgid "Icon size in a respect to the default size" +msgstr "Pictogramgrootte vergeleken met de originele grootte" + +#: src/slic3r/GUI/Preferences.cpp:207 +msgid "Select toolbar icon size in respect to the default one." +msgstr "Selecteer werkbalk-pictogramgrootte in verhouding tot de originele." + +#: src/slic3r/GUI/Preset.cpp:237 +msgid "modified" +msgstr "aangepast" + +#: src/slic3r/GUI/Preset.cpp:1034 src/slic3r/GUI/Preset.cpp:1081 +#: src/slic3r/GUI/Preset.cpp:1157 src/slic3r/GUI/Preset.cpp:1191 +#: src/slic3r/GUI/PresetBundle.cpp:1576 src/slic3r/GUI/PresetBundle.cpp:1660 +msgid "System presets" +msgstr "Systeempresets" + +#: src/slic3r/GUI/Preset.cpp:1085 src/slic3r/GUI/Preset.cpp:1195 +#: src/slic3r/GUI/PresetBundle.cpp:1665 +msgid "User presets" +msgstr "Presets van de gebruiker" + +#: src/slic3r/GUI/Preset.cpp:1116 src/slic3r/GUI/Tab.cpp:243 +msgid "Add a new printer" +msgstr "Voeg een nieuwe printer toe" + +#: src/slic3r/GUI/Preset.cpp:1118 +msgid "Add/Remove materials" +msgstr "Verwijder of voeg materialen toe" + +#: src/slic3r/GUI/Preset.cpp:1409 +msgid "filament" +msgstr "filament" + +#: src/slic3r/GUI/Preset.cpp:1410 +msgid "SLA print" +msgstr "SLA-print" + +#: src/slic3r/GUI/PresetBundle.cpp:1676 +msgid "Add/Remove filaments" +msgstr "Verwijder of voeg filamenten toe" + +#: src/slic3r/GUI/PresetHints.cpp:28 +msgid "" +"If estimated layer time is below ~%1%s, fan will run at %2%%% and print " +"speed will be reduced so that no less than %3%s are spent on that layer " +"(however, speed will never be reduced below %4%mm/s)." +msgstr "" +"Als de geschatte laagtijd onder de ~%1%s komt, zal de ventilator draaien op " +"%2%%% en de printsnelheid wordt zover gereduceerd dat niet meer dan %3%s " +"worden gebruikt op die laag (echter nooit langzamer dan %4%mm/s)." + +#: src/slic3r/GUI/PresetHints.cpp:35 +msgid "" +"If estimated layer time is greater, but still below ~%1%s, fan will run at a " +"proportionally decreasing speed between %2%%% and %3%%%." +msgstr "" +"Als de geschatte laagtijd groter is, maar nog steeds onder de ~%1%s, zal de " +"ventilator draaien op een proportioneel verlagende snelheid tussen %2%%% en " +"%3%%%." + +#: src/slic3r/GUI/PresetHints.cpp:39 +msgid "During the other layers, fan" +msgstr "Tijdens de overige lagen, ventilator" + +#: src/slic3r/GUI/PresetHints.cpp:41 +msgid "Fan" +msgstr "Ventilator" + +#: src/slic3r/GUI/PresetHints.cpp:47 +msgid "will always run at %1%%%" +msgstr "zal altijd draaien op %1%%%" + +#: src/slic3r/GUI/PresetHints.cpp:50 +msgid "except for the first %1% layers." +msgstr "behalve voor de eerste %s% lagen." + +#: src/slic3r/GUI/PresetHints.cpp:52 +msgid "except for the first layer." +msgstr "behalve voor de eerste laag." + +#: src/slic3r/GUI/PresetHints.cpp:54 +msgid "will be turned off." +msgstr "wordt uitgeschakeld." + +#: src/slic3r/GUI/PresetHints.cpp:155 +msgid "external perimeters" +msgstr "buitenperimeters" + +#: src/slic3r/GUI/PresetHints.cpp:164 +msgid "perimeters" +msgstr "perimeters" + +#: src/slic3r/GUI/PresetHints.cpp:173 +msgid "infill" +msgstr "vulling" + +#: src/slic3r/GUI/PresetHints.cpp:183 +msgid "solid infill" +msgstr "dichte vulling" + +#: src/slic3r/GUI/PresetHints.cpp:191 +msgid "top solid infill" +msgstr "bovenste dichte vulling" + +#: src/slic3r/GUI/PresetHints.cpp:202 +msgid "support" +msgstr "support" + +#: src/slic3r/GUI/PresetHints.cpp:212 +msgid "support interface" +msgstr "supportinterface" + +#: src/slic3r/GUI/PresetHints.cpp:218 +msgid "First layer volumetric" +msgstr "Eerste laag volumetrisch" + +#: src/slic3r/GUI/PresetHints.cpp:218 +msgid "Bridging volumetric" +msgstr "Volumetrische bruggen" + +#: src/slic3r/GUI/PresetHints.cpp:218 +msgid "Volumetric" +msgstr "Volumetrisch" + +#: src/slic3r/GUI/PresetHints.cpp:219 +msgid "flow rate is maximized" +msgstr "Debiet is gemaximaliseerd" + +#: src/slic3r/GUI/PresetHints.cpp:222 +msgid "by the print profile maximum" +msgstr "door het printprofiel maximaal" + +#: src/slic3r/GUI/PresetHints.cpp:223 +msgid "when printing" +msgstr "tijdens het printen" + +#: src/slic3r/GUI/PresetHints.cpp:224 +msgid "with a volumetric rate" +msgstr "met een volumetrische ratio" + +#: src/slic3r/GUI/PresetHints.cpp:228 +#, c-format +msgid "%3.2f mm³/s at filament speed %3.2f mm/s." +msgstr "%3.2f mm³/s met een filamentsnelheid van %3.2f mm/s." + +#: src/slic3r/GUI/PresetHints.cpp:246 +msgid "" +"Recommended object thin wall thickness: Not available due to invalid layer " +"height." +msgstr "" +"Aanbevolen minimale wanddikte. Niet beschikbaar in verband met ongeldige " +"laagdikte." + +#: src/slic3r/GUI/PresetHints.cpp:262 +#, c-format +msgid "Recommended object thin wall thickness for layer height %.2f and" +msgstr "Aanbevolen objecten met dunne wanden voor laagdikte %.2f en" + +#: src/slic3r/GUI/PresetHints.cpp:268 +#, c-format +msgid "%d lines: %.2f mm" +msgstr "%d lijnen: %.2f mm" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:33 +msgid "Send G-Code to printer host" +msgstr "Stuur G-code naar printerhost" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:33 +msgid "Upload to Printer Host with the following filename:" +msgstr "Upload naar printerhost met de volgende bestandsnaam:" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:35 +msgid "Start printing after upload" +msgstr "Print starten na het uploaden" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:42 +msgid "Use forward slashes ( / ) as a directory separator if needed." +msgstr "" +"Gebruik schuine streepjes ( / ) als mapscheidingstekens als dat nodig is." + +#: src/slic3r/GUI/PrintHostDialogs.cpp:149 +msgid "ID" +msgstr "ID" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:150 +msgid "Progress" +msgstr "Voortgang" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:151 +msgid "Status" +msgstr "Status" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:152 +msgid "Host" +msgstr "Host" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:153 +msgid "Filename" +msgstr "Bestandsnaam" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:154 +msgid "Error Message" +msgstr "Foutbericht" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:157 +msgid "Cancel selected" +msgstr "Annuleren geselecteerd" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:159 +msgid "Show error message" +msgstr "Toon foutbericht" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:198 +#: src/slic3r/GUI/PrintHostDialogs.cpp:229 +msgid "Enqueued" +msgstr "In de wachtrij geplaatst" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:230 +msgid "Uploading" +msgstr "Uploaden" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:234 +msgid "Completed" +msgstr "Voltooid" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:272 +msgid "Error uploading to print host:" +msgstr "Fout bij het uploaden naar de printhost:" + +#: src/slic3r/GUI/RammingChart.cpp:23 +msgid "NO RAMMING AT ALL" +msgstr "Fout: geen ramming" + +#: src/slic3r/GUI/RammingChart.cpp:76 +msgid "Time" +msgstr "Tijd" + +#: src/slic3r/GUI/RammingChart.cpp:76 src/slic3r/GUI/WipeTowerDialog.cpp:83 +#: src/libslic3r/PrintConfig.cpp:634 src/libslic3r/PrintConfig.cpp:678 +#: src/libslic3r/PrintConfig.cpp:693 src/libslic3r/PrintConfig.cpp:2385 +#: src/libslic3r/PrintConfig.cpp:2394 src/libslic3r/PrintConfig.cpp:2495 +#: src/libslic3r/PrintConfig.cpp:2503 src/libslic3r/PrintConfig.cpp:2511 +#: src/libslic3r/PrintConfig.cpp:2518 src/libslic3r/PrintConfig.cpp:2526 +#: src/libslic3r/PrintConfig.cpp:2534 +msgid "s" +msgstr "s" + +#: src/slic3r/GUI/RammingChart.cpp:81 +msgid "Volumetric speed" +msgstr "Volumetrische snelheid" + +#: src/slic3r/GUI/RammingChart.cpp:81 src/libslic3r/PrintConfig.cpp:591 +#: src/libslic3r/PrintConfig.cpp:1247 +msgid "mm³/s" +msgstr "mm³/s" + +#: src/slic3r/GUI/Selection.cpp:146 +msgid "Selection-Add" +msgstr "Selectie - Voeg toe" + +#: src/slic3r/GUI/Selection.cpp:187 +msgid "Selection-Remove" +msgstr "Selectie - Verwijder" + +#: src/slic3r/GUI/Selection.cpp:219 +msgid "Selection-Add Object" +msgstr "Selectie - Voeg object toe" + +#: src/slic3r/GUI/Selection.cpp:238 +msgid "Selection-Remove Object" +msgstr "Selectie - Verwijder object" + +#: src/slic3r/GUI/Selection.cpp:256 +msgid "Selection-Add Instance" +msgstr "Selectie - Voeg instantie toe" + +#: src/slic3r/GUI/Selection.cpp:275 +msgid "Selection-Remove Instance" +msgstr "Selectie - Verwijder instantie" + +#: src/slic3r/GUI/Selection.cpp:376 +msgid "Selection-Add All" +msgstr "Selectie - Voeg alle toe" + +#: src/slic3r/GUI/Selection.cpp:402 +msgid "Selection-Remove All" +msgstr "Selectie - Verwijder alle" + +#: src/slic3r/GUI/Selection.cpp:939 +msgid "Scale To Fit" +msgstr "Verschaal tot het past" + +#: src/slic3r/GUI/Selection.cpp:1474 +msgid "Set Printable Instance" +msgstr "Stel printbare instanties in" + +#: src/slic3r/GUI/Selection.cpp:1474 +msgid "Set Unprintable Instance" +msgstr "Stel instantie in op niet-printbaar" + +#: src/slic3r/GUI/SysInfoDialog.cpp:78 +msgid "System Information" +msgstr "Systeeminformatie" + +#: src/slic3r/GUI/SysInfoDialog.cpp:154 +msgid "Copy to Clipboard" +msgstr "Kopieer naar klembord" + +#: src/slic3r/GUI/Tab.cpp:52 src/libslic3r/PrintConfig.cpp:244 +msgid "Compatible printers" +msgstr "Compatibele printers" + +#: src/slic3r/GUI/Tab.cpp:53 +msgid "Select the printers this profile is compatible with." +msgstr "Selecteer de printers die compatibel met dit profiel zijn." + +#: src/slic3r/GUI/Tab.cpp:58 src/libslic3r/PrintConfig.cpp:259 +msgid "Compatible print profiles" +msgstr "Compatibele printprofielen" + +#: src/slic3r/GUI/Tab.cpp:59 +msgid "Select the print profiles this profile is compatible with." +msgstr "Selecteer de printprofielen die compatibel met dit profiel zijn." + +#. TRN "Save current Settings" +#: src/slic3r/GUI/Tab.cpp:135 +#, c-format +msgid "Save current %s" +msgstr "Huidige %s opslaan" + +#: src/slic3r/GUI/Tab.cpp:136 +msgid "Delete this preset" +msgstr "Verwijder preset" + +#: src/slic3r/GUI/Tab.cpp:141 +msgid "" +"Hover the cursor over buttons to find more information \n" +"or click this button." +msgstr "" +"Beweeg de cursor over de knoppen voor meer informatie\n" +"of klik op deze knop." + +#: src/slic3r/GUI/Tab.cpp:943 +msgid "This is a default preset." +msgstr "Dit is een standaard preset." + +#: src/slic3r/GUI/Tab.cpp:945 +msgid "This is a system preset." +msgstr "Dit is een systeempreset." + +#: src/slic3r/GUI/Tab.cpp:947 +msgid "Current preset is inherited from the default preset." +msgstr "Huidige preset is gebaseerd op de standaard preset." + +#: src/slic3r/GUI/Tab.cpp:950 +#, c-format +msgid "" +"Current preset is inherited from:\n" +"\t%s" +msgstr "" +"Huidige preset is gebaseerd op:\n" +"\t%s" + +#: src/slic3r/GUI/Tab.cpp:954 +msgid "It can't be deleted or modified." +msgstr "Kan niet verwijderd of aangepast worden." + +#: src/slic3r/GUI/Tab.cpp:955 +msgid "" +"Any modifications should be saved as a new preset inherited from this one." +msgstr "" +"Eventuele wijzigingen moet worden opgeslagen als een nieuwe preset die is " +"gebaseerd op de huidige." + +#: src/slic3r/GUI/Tab.cpp:956 +msgid "To do that please specify a new name for the preset." +msgstr "Geef daarvoor een nieuwe naam aan de preset." + +#: src/slic3r/GUI/Tab.cpp:960 +msgid "Additional information:" +msgstr "Additionele informatie:" + +#: src/slic3r/GUI/Tab.cpp:966 +msgid "printer model" +msgstr "printermodel" + +#: src/slic3r/GUI/Tab.cpp:974 +msgid "default print profile" +msgstr "standaard printprofiel" + +#: src/slic3r/GUI/Tab.cpp:977 +msgid "default filament profile" +msgstr "standaard filamentprofiel" + +#: src/slic3r/GUI/Tab.cpp:991 +msgid "default SLA material profile" +msgstr "standaard SLA-materiaalprofiel" + +#: src/slic3r/GUI/Tab.cpp:995 +msgid "default SLA print profile" +msgstr "standaard SLA-printprofiel" + +#: src/slic3r/GUI/Tab.cpp:1003 +msgid "full profile name" +msgstr "volledige profielnaam" + +#: src/slic3r/GUI/Tab.cpp:1004 +msgid "symbolic profile name" +msgstr "symbolische profielnaam" + +#: src/slic3r/GUI/Tab.cpp:1038 src/slic3r/GUI/Tab.cpp:3558 +msgid "Layers and perimeters" +msgstr "Lagen en perimeters" + +#: src/slic3r/GUI/Tab.cpp:1043 +msgid "Vertical shells" +msgstr "Verticale shells" + +#: src/slic3r/GUI/Tab.cpp:1054 +msgid "Horizontal shells" +msgstr "Horizontale shells" + +#: src/slic3r/GUI/Tab.cpp:1055 src/libslic3r/PrintConfig.cpp:1776 +msgid "Solid layers" +msgstr "Dichte lagen" + +#: src/slic3r/GUI/Tab.cpp:1060 +msgid "Quality (slower slicing)" +msgstr "Kwaliteit (slicen kan langer duren)" + +#: src/slic3r/GUI/Tab.cpp:1078 +msgid "Reducing printing time" +msgstr "Printtijd verkorten" + +#: src/slic3r/GUI/Tab.cpp:1090 +msgid "Skirt and brim" +msgstr "Skirt en brim" + +#: src/slic3r/GUI/Tab.cpp:1107 +msgid "Raft" +msgstr "Raft" + +#: src/slic3r/GUI/Tab.cpp:1111 +msgid "Options for support material and raft" +msgstr "Opties voor support en raft" + +#: src/slic3r/GUI/Tab.cpp:1126 +msgid "Speed for print moves" +msgstr "Snelheid voor printbewegingen" + +#: src/slic3r/GUI/Tab.cpp:1138 +msgid "Speed for non-print moves" +msgstr "Snelheid voor niet-print bewegingen" + +#: src/slic3r/GUI/Tab.cpp:1141 +msgid "Modifiers" +msgstr "Modificators" + +#: src/slic3r/GUI/Tab.cpp:1144 +msgid "Acceleration control (advanced)" +msgstr "Acceleraties (geavanceerd)" + +#: src/slic3r/GUI/Tab.cpp:1151 +msgid "Autospeed (advanced)" +msgstr "Automatische snelheid (geavanceerd)" + +#: src/slic3r/GUI/Tab.cpp:1159 +msgid "Multiple Extruders" +msgstr "Meerdere extruders" + +#: src/slic3r/GUI/Tab.cpp:1167 +msgid "Ooze prevention" +msgstr "Druippreventie" + +#: src/slic3r/GUI/Tab.cpp:1185 +msgid "Extrusion width" +msgstr "Extrusiebreedte" + +#: src/slic3r/GUI/Tab.cpp:1195 +msgid "Overlap" +msgstr "Overlapping" + +#: src/slic3r/GUI/Tab.cpp:1198 +msgid "Flow" +msgstr "Stroom" + +#: src/slic3r/GUI/Tab.cpp:1207 +msgid "Other" +msgstr "Overige" + +#: src/slic3r/GUI/Tab.cpp:1210 src/slic3r/GUI/Tab.cpp:3618 +msgid "Output options" +msgstr "Output-opties" + +#: src/slic3r/GUI/Tab.cpp:1211 +msgid "Sequential printing" +msgstr "Achtereenvolgens printen" + +#: src/slic3r/GUI/Tab.cpp:1213 +msgid "Extruder clearance (mm)" +msgstr "Extruderruimte (mm)" + +#: src/slic3r/GUI/Tab.cpp:1222 src/slic3r/GUI/Tab.cpp:3619 +msgid "Output file" +msgstr "Outputbestand" + +#: src/slic3r/GUI/Tab.cpp:1229 src/libslic3r/PrintConfig.cpp:1448 +msgid "Post-processing scripts" +msgstr "Scripts voor nabewerking" + +#: src/slic3r/GUI/Tab.cpp:1235 src/slic3r/GUI/Tab.cpp:1236 +#: src/slic3r/GUI/Tab.cpp:1517 src/slic3r/GUI/Tab.cpp:1518 +#: src/slic3r/GUI/Tab.cpp:1982 src/slic3r/GUI/Tab.cpp:1983 +#: src/slic3r/GUI/Tab.cpp:2096 src/slic3r/GUI/Tab.cpp:2097 +#: src/slic3r/GUI/Tab.cpp:3495 src/slic3r/GUI/Tab.cpp:3496 +msgid "Notes" +msgstr "Opmerkingen" + +#: src/slic3r/GUI/Tab.cpp:1242 src/slic3r/GUI/Tab.cpp:1525 +#: src/slic3r/GUI/Tab.cpp:1989 src/slic3r/GUI/Tab.cpp:2103 +#: src/slic3r/GUI/Tab.cpp:3503 src/slic3r/GUI/Tab.cpp:3624 +msgid "Dependencies" +msgstr "Afhankelijkheden" + +#: src/slic3r/GUI/Tab.cpp:1243 src/slic3r/GUI/Tab.cpp:1526 +#: src/slic3r/GUI/Tab.cpp:1990 src/slic3r/GUI/Tab.cpp:2104 +#: src/slic3r/GUI/Tab.cpp:3504 src/slic3r/GUI/Tab.cpp:3625 +msgid "Profile dependencies" +msgstr "Profielafhankelijkheden" + +#: src/slic3r/GUI/Tab.cpp:1303 src/slic3r/GUI/Tab.cpp:1358 +msgid "Filament Overrides" +msgstr "Overschrijven door filament" + +#: src/slic3r/GUI/Tab.cpp:1304 src/slic3r/GUI/Tab.cpp:1363 +#: src/slic3r/GUI/Tab.cpp:2338 +msgid "Retraction" +msgstr "Retractie" + +#: src/slic3r/GUI/Tab.cpp:1413 src/libslic3r/PrintConfig.cpp:2056 +msgid "Temperature" +msgstr "Temperatuur" + +#: src/slic3r/GUI/Tab.cpp:1419 +msgid "Bed" +msgstr "Bed" + +#: src/slic3r/GUI/Tab.cpp:1424 +msgid "Cooling" +msgstr "Koeling" + +#: src/slic3r/GUI/Tab.cpp:1425 src/libslic3r/PrintConfig.cpp:1350 +#: src/libslic3r/PrintConfig.cpp:2177 +msgid "Enable" +msgstr "Toestaan" + +#: src/slic3r/GUI/Tab.cpp:1436 +msgid "Fan settings" +msgstr "Ventilatorinstellingen" + +#: src/slic3r/GUI/Tab.cpp:1445 +msgid "Cooling thresholds" +msgstr "Koeldrempels" + +#: src/slic3r/GUI/Tab.cpp:1451 +msgid "Filament properties" +msgstr "Filamenteigenschappen" + +#: src/slic3r/GUI/Tab.cpp:1455 +msgid "Print speed override" +msgstr "Printsnelheid overschrijven" + +#: src/slic3r/GUI/Tab.cpp:1465 +msgid "Wipe tower parameters" +msgstr "Afveegblokparameters" + +#: src/slic3r/GUI/Tab.cpp:1468 +msgid "Toolchange parameters with single extruder MM printers" +msgstr "Toolwisselparameter voor multi-materialprinters met één extruder" + +#: src/slic3r/GUI/Tab.cpp:1482 +msgid "Ramming settings" +msgstr "Ramming-instellingen" + +#: src/slic3r/GUI/Tab.cpp:1504 src/slic3r/GUI/Tab.cpp:1945 +msgid "Custom G-code" +msgstr "Custom G-code" + +#: src/slic3r/GUI/Tab.cpp:1505 src/slic3r/GUI/Tab.cpp:1946 +#: src/libslic3r/PrintConfig.cpp:1802 src/libslic3r/PrintConfig.cpp:1817 +msgid "Start G-code" +msgstr "Start G-code" + +#: src/slic3r/GUI/Tab.cpp:1511 src/slic3r/GUI/Tab.cpp:1952 +#: src/libslic3r/PrintConfig.cpp:374 src/libslic3r/PrintConfig.cpp:384 +msgid "End G-code" +msgstr "Eind G-code" + +#: src/slic3r/GUI/Tab.cpp:1568 +msgid "Volumetric flow hints not available" +msgstr "Volumetrische stroom - opmerkingen niet beschikbaar" + +#: src/slic3r/GUI/Tab.cpp:1654 src/slic3r/GUI/Tab.cpp:1885 +msgid "Test" +msgstr "Test" + +#: src/slic3r/GUI/Tab.cpp:1664 +msgid "Could not get a valid Printer Host reference" +msgstr "Kan geen geldige printerhost-referentie krijgen" + +#: src/slic3r/GUI/Tab.cpp:1670 src/slic3r/GUI/Tab.cpp:1898 +msgid "Success!" +msgstr "Gelukt!" + +#: src/slic3r/GUI/Tab.cpp:1685 +msgid "" +"HTTPS CA file is optional. It is only needed if you use HTTPS with a self-" +"signed certificate." +msgstr "" +"HTTPS-CA-bestand is optioneel. Het is alleen nodig als u werkt met een zelf " +"ondertekend certificaat." + +#: src/slic3r/GUI/Tab.cpp:1698 +msgid "Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*" +msgstr "Certificaatbestanden (*.crt, *.pem)|*.crt;*.pem|Alle betanden|*.*" + +#: src/slic3r/GUI/Tab.cpp:1699 +msgid "Open CA certificate file" +msgstr "Open een CA-certificaatbestand" + +#: src/slic3r/GUI/Tab.cpp:1727 +#, c-format +msgid "" +"HTTPS CA File:\n" +" \tOn this system, %s uses HTTPS certificates from the system Certificate " +"Store or Keychain.\n" +" \tTo use a custom CA file, please import your CA file into Certificate " +"Store / Keychain." +msgstr "" +"HTTPS-CA-bestand:\n" +" \t%s gebruikt op dit systeem HTTPS-certificaten van de Certificate Store " +"of Keychain.\n" +" \tOm een aangepast CA-bestand te gebruiken moet uw CA-bestand in de " +"Certificate Store of Keychain geïmporteerd worden." + +#: src/slic3r/GUI/Tab.cpp:1767 src/slic3r/GUI/Tab.cpp:2011 +msgid "Size and coordinates" +msgstr "Grootte en coördinaten" + +#: src/slic3r/GUI/Tab.cpp:1772 src/slic3r/GUI/Tab.cpp:2016 +#: src/slic3r/GUI/Tab.cpp:3132 +msgid "Set" +msgstr "Stel in" + +#: src/slic3r/GUI/Tab.cpp:1804 +msgid "Capabilities" +msgstr "Mogelijkheden" + +#: src/slic3r/GUI/Tab.cpp:1809 +msgid "Number of extruders of the printer." +msgstr "Aantal extruders van de printer." + +#: src/slic3r/GUI/Tab.cpp:1837 +msgid "" +"Single Extruder Multi Material is selected, \n" +"and all extruders must have the same diameter.\n" +"Do you want to change the diameter for all extruders to first extruder " +"nozzle diameter value?" +msgstr "" +"Multi-material met één extruder is geselecteerd.\n" +"Alle extruders moeten daarvoor dezelfde diameter hebben.\n" +"Wilt u de diameters voor alle extruders aanpassen gelijk aan die van de " +"eerste extruder?" + +#: src/slic3r/GUI/Tab.cpp:1840 src/slic3r/GUI/Tab.cpp:2308 +#: src/libslic3r/PrintConfig.cpp:1323 +msgid "Nozzle diameter" +msgstr "Nozzlediameter" + +#: src/slic3r/GUI/Tab.cpp:1870 +msgid "USB/Serial connection" +msgstr "USB/seriële verbinding" + +#: src/slic3r/GUI/Tab.cpp:1871 src/libslic3r/PrintConfig.cpp:1656 +msgid "Serial port" +msgstr "Seriële poort" + +#: src/slic3r/GUI/Tab.cpp:1876 +msgid "Rescan serial ports" +msgstr "Seriële poorten opnieuw scannen" + +#: src/slic3r/GUI/Tab.cpp:1898 +msgid "Connection to printer works correctly." +msgstr "Verbinding met de printer werkt naar behoren." + +#: src/slic3r/GUI/Tab.cpp:1901 +msgid "Connection failed." +msgstr "Verbinding mislukt." + +#: src/slic3r/GUI/Tab.cpp:1914 src/slic3r/GUI/Tab.cpp:2091 +msgid "Print Host upload" +msgstr "Printhost upload" + +#: src/slic3r/GUI/Tab.cpp:1958 src/libslic3r/PrintConfig.cpp:143 +msgid "Before layer change G-code" +msgstr "G-code die komt vóór de laagwisseling" + +#: src/slic3r/GUI/Tab.cpp:1964 src/libslic3r/PrintConfig.cpp:1069 +msgid "After layer change G-code" +msgstr "G-code die komt na de laagwisseling" + +#: src/slic3r/GUI/Tab.cpp:1970 src/libslic3r/PrintConfig.cpp:2082 +msgid "Tool change G-code" +msgstr "Toolwisseling G-code" + +#: src/slic3r/GUI/Tab.cpp:1976 +msgid "Between objects G-code (for sequential printing)" +msgstr "G-code die komt tussen objecten (bij achtereenvolgens printen)" + +#: src/slic3r/GUI/Tab.cpp:2048 +msgid "Display" +msgstr "Scherm" + +#: src/slic3r/GUI/Tab.cpp:2063 +msgid "Tilt" +msgstr "Kanteling" + +#: src/slic3r/GUI/Tab.cpp:2064 +msgid "Tilt time" +msgstr "Kanteltijd" + +#: src/slic3r/GUI/Tab.cpp:2070 src/slic3r/GUI/Tab.cpp:3477 +msgid "Corrections" +msgstr "Correcties" + +#: src/slic3r/GUI/Tab.cpp:2085 src/slic3r/GUI/Tab.cpp:3473 +msgid "Exposure" +msgstr "Belichtingstijd" + +#: src/slic3r/GUI/Tab.cpp:2156 src/slic3r/GUI/Tab.cpp:2241 +#: src/libslic3r/PrintConfig.cpp:1119 src/libslic3r/PrintConfig.cpp:1137 +#: src/libslic3r/PrintConfig.cpp:1155 src/libslic3r/PrintConfig.cpp:1172 +#: src/libslic3r/PrintConfig.cpp:1183 src/libslic3r/PrintConfig.cpp:1194 +#: src/libslic3r/PrintConfig.cpp:1205 +msgid "Machine limits" +msgstr "Machinelimieten" + +#: src/slic3r/GUI/Tab.cpp:2170 +msgid "Values in this column are for Normal mode" +msgstr "Waarden in deze kolom zijn voor de normale modus" + +#: src/slic3r/GUI/Tab.cpp:2171 +msgid "Normal" +msgstr "Normaal" + +#: src/slic3r/GUI/Tab.cpp:2176 +msgid "Values in this column are for Stealth mode" +msgstr "Waarden in deze kolom zijn voor de stille modus" + +#: src/slic3r/GUI/Tab.cpp:2177 +msgid "Stealth" +msgstr "Stille modus" + +#: src/slic3r/GUI/Tab.cpp:2185 +msgid "Maximum feedrates" +msgstr "Maximale voedingssnelheden" + +#: src/slic3r/GUI/Tab.cpp:2190 +msgid "Maximum accelerations" +msgstr "Maximale acceleraties" + +#: src/slic3r/GUI/Tab.cpp:2197 +msgid "Jerk limits" +msgstr "Ruklimieten" + +#: src/slic3r/GUI/Tab.cpp:2202 +msgid "Minimum feedrates" +msgstr "Minimale voedingssnelheden" + +#: src/slic3r/GUI/Tab.cpp:2266 src/slic3r/GUI/Tab.cpp:2274 +msgid "Single extruder MM setup" +msgstr "Multi-materialsetup met één extruder" + +#: src/slic3r/GUI/Tab.cpp:2275 +msgid "Single extruder multimaterial parameters" +msgstr "Parameter voor multi-material met één extruder" + +#: src/slic3r/GUI/Tab.cpp:2306 +msgid "" +"This is a single extruder multimaterial printer, diameters of all extruders " +"will be set to the new value. Do you want to proceed?" +msgstr "" +"Dit is een multi-materialprinter met één extruder. De diameters van alle " +"extruders worden ingesteld op de nieuwe waarde. Weet u zeker dat u wilt " +"doorgaan?" + +#: src/slic3r/GUI/Tab.cpp:2330 +msgid "Layer height limits" +msgstr "Laagdiktelimieten" + +#: src/slic3r/GUI/Tab.cpp:2335 +msgid "Position (for multi-extruder printers)" +msgstr "Positie (voor multi-extruderprinters)" + +#: src/slic3r/GUI/Tab.cpp:2341 +msgid "Only lift Z" +msgstr "Beweeg alleen Z omhoog" + +#: src/slic3r/GUI/Tab.cpp:2354 +msgid "" +"Retraction when tool is disabled (advanced settings for multi-extruder " +"setups)" +msgstr "" +"Retractie als de tool uit staat (geavanceerde instelling voor multi-" +"extrudersetups)" + +#: src/slic3r/GUI/Tab.cpp:2362 +msgid "Reset to Filament Color" +msgstr "Reset naar filamentkleur" + +#: src/slic3r/GUI/Tab.cpp:2543 +msgid "" +"The Wipe option is not available when using the Firmware Retraction mode.\n" +"\n" +"Shall I disable it in order to enable Firmware Retraction?" +msgstr "" +"De afveegoptie is niet beschikbaar als firmwareretractie aanstaat.\n" +"\n" +"Moet deze uitgezet worden om firmwareretractie te gebruiken?" + +#: src/slic3r/GUI/Tab.cpp:2545 +msgid "Firmware Retraction" +msgstr "Firmwareretractie" + +#: src/slic3r/GUI/Tab.cpp:2875 +#, c-format +msgid "Default preset (%s)" +msgstr "Standaard preset (%s)" + +#: src/slic3r/GUI/Tab.cpp:2876 +#, c-format +msgid "Preset (%s)" +msgstr "Preset (%s)" + +#: src/slic3r/GUI/Tab.cpp:2893 +msgid "has the following unsaved changes:" +msgstr "heeft de volgende niet-opgeslagen wijzigingen:" + +#: src/slic3r/GUI/Tab.cpp:2896 +msgid "is not compatible with printer" +msgstr "is niet compatibel met printer" + +#: src/slic3r/GUI/Tab.cpp:2897 +msgid "is not compatible with print profile" +msgstr "is niet compatibel met printprofiel" + +#: src/slic3r/GUI/Tab.cpp:2899 +msgid "and it has the following unsaved changes:" +msgstr "en het heeft de volgende niet-opgeslagen wijzigingen:" + +#: src/slic3r/GUI/Tab.cpp:2903 +msgid "Unsaved Changes" +msgstr "Niet-opgeslagen wijzigingen" + +#: src/slic3r/GUI/Tab.cpp:3001 +msgctxt "PresetName" +msgid "%1% - Copy" +msgstr "%1% - Kopie" + +#: src/slic3r/GUI/Tab.cpp:3024 +msgid "The supplied name is empty. It can't be saved." +msgstr "De ingevoerde naam is leeg. Kan niet opgeslagen worden." + +#: src/slic3r/GUI/Tab.cpp:3029 +msgid "Cannot overwrite a system profile." +msgstr "Een systeemprofiel kan niet overschreven worden." + +#: src/slic3r/GUI/Tab.cpp:3033 +msgid "Cannot overwrite an external profile." +msgstr "Een extern profiel kan niet overschreven worden." + +#: src/slic3r/GUI/Tab.cpp:3038 +msgid "Preset with name \"%1%\" already exist." +msgstr "Preset met de naam '%s% bestaat al." + +#: src/slic3r/GUI/Tab.cpp:3039 +msgid "Replace?" +msgstr "Vervangen?" + +#: src/slic3r/GUI/Tab.cpp:3077 +msgid "remove" +msgstr "verwijderen" + +#: src/slic3r/GUI/Tab.cpp:3077 +msgid "delete" +msgstr "verwijderen" + +#. TRN remove/delete +#: src/slic3r/GUI/Tab.cpp:3079 +msgid "Are you sure you want to %1% the selected preset?" +msgstr "Weet u zeker dat u de geselecteerde preset %1% wilt?" + +#. TRN Remove/Delete +#: src/slic3r/GUI/Tab.cpp:3082 +msgid "%1% Preset" +msgstr "Preset %1%" + +#: src/slic3r/GUI/Tab.cpp:3208 +msgid "LOCKED LOCK" +msgstr "Vergrendeld" + +#. TRN Description for "LOCKED LOCK" +#: src/slic3r/GUI/Tab.cpp:3210 +msgid "" +"indicates that the settings are the same as the system (or default) values " +"for the current option group" +msgstr "" +"geeft aan dat de instellingen gelijk zijn aan de systeemwaarden voor de " +"huidige optiegroep" + +#: src/slic3r/GUI/Tab.cpp:3212 +msgid "UNLOCKED LOCK" +msgstr "Ontgrendeld" + +#. TRN Description for "UNLOCKED LOCK" +#: src/slic3r/GUI/Tab.cpp:3214 +msgid "" +"indicates that some settings were changed and are not equal to the system " +"(or default) values for the current option group.\n" +"Click the UNLOCKED LOCK icon to reset all settings for current option group " +"to the system (or default) values." +msgstr "" +"geeft aan dat sommige instellingen zijn veranderd en niet gelijk zijn aan de " +"standaardwaarde voor de huidige optiegroep.\n" +"Klik op het ontgrendeld-pictogram om de instelling te resetten naar de " +"systeemwaarden voor de huidige optiegroep." + +#: src/slic3r/GUI/Tab.cpp:3219 +msgid "WHITE BULLET" +msgstr "Wit bolletje" + +#. TRN Description for "WHITE BULLET" +#: src/slic3r/GUI/Tab.cpp:3221 +msgid "" +"for the left button: \tindicates a non-system (or non-default) preset,\n" +"for the right button: \tindicates that the settings hasn't been modified." +msgstr "" +"linker knop: \tgeeft een niet-systeempreset aan,\n" +"rechter knop: \tgeeft aan dat de instelling niet veranderd is." + +#: src/slic3r/GUI/Tab.cpp:3224 +msgid "BACK ARROW" +msgstr "Pijltje terug" + +#. TRN Description for "BACK ARROW" +#: src/slic3r/GUI/Tab.cpp:3226 +msgid "" +"indicates that the settings were changed and are not equal to the last saved " +"preset for the current option group.\n" +"Click the BACK ARROW icon to reset all settings for the current option group " +"to the last saved preset." +msgstr "" +"geeft aan dat de instellingen zijn veranderd en niet gelijk zijn aan de " +"laatst opgeslagen preset voor de huidige optiegroep.\n" +"Klik op het pijltje-terug-pictogram om alle instellingen te resetten naar de " +"laatst opgeslagen preset voor de huidige optiegroep." + +#: src/slic3r/GUI/Tab.cpp:3236 +msgid "" +"LOCKED LOCK icon indicates that the settings are the same as the system (or " +"default) values for the current option group" +msgstr "" +"Vergrendeld-pictogram geeft aan dat de instellingen gelijk zijn aan de " +"systeemwaarde van de huidige optiegroep" + +#: src/slic3r/GUI/Tab.cpp:3238 +msgid "" +"UNLOCKED LOCK icon indicates that some settings were changed and are not " +"equal to the system (or default) values for the current option group.\n" +"Click to reset all settings for current option group to the system (or " +"default) values." +msgstr "" +"Ontgrendeld-pictogram geeft aan dat sommige instellingen zijn veranderd en " +"niet gelijk zijn aan de systeemwaarde voor de huidige optiegroep.\n" +"Klik om alle instellingen voor de huidige optiegroep te resetten naar " +"systeemwaarden." + +#: src/slic3r/GUI/Tab.cpp:3241 +msgid "WHITE BULLET icon indicates a non system (or non default) preset." +msgstr "Het witte bolletje geeft aan dat het geen systeempreset betreft." + +#: src/slic3r/GUI/Tab.cpp:3244 +msgid "" +"WHITE BULLET icon indicates that the settings are the same as in the last " +"saved preset for the current option group." +msgstr "" +"Het witte bolletje geeft aan dat de instelling gelijk is aan de laatst " +"opgeslagen preset voor de huidige optiegroep." + +#: src/slic3r/GUI/Tab.cpp:3246 +msgid "" +"BACK ARROW icon indicates that the settings were changed and are not equal " +"to the last saved preset for the current option group.\n" +"Click to reset all settings for the current option group to the last saved " +"preset." +msgstr "" +"Het pijltje-terug-pictogram geeft aan dat de instellingen zijn gewijzigd en " +"niet gelijk zijn aan de laatst opgeslagen preset voor de huidige " +"optiegroep.\n" +"Klik om alle instellingen terug te zetten voor de huidige optiegroep naar de " +"laatst opgeslagen preset." + +#: src/slic3r/GUI/Tab.cpp:3252 +msgid "" +"LOCKED LOCK icon indicates that the value is the same as the system (or " +"default) value." +msgstr "" +"Vergrendeld-pictogram geeft aan dat de waarde gelijk is aan de systeemwaarde." + +#: src/slic3r/GUI/Tab.cpp:3253 +msgid "" +"UNLOCKED LOCK icon indicates that the value was changed and is not equal to " +"the system (or default) value.\n" +"Click to reset current value to the system (or default) value." +msgstr "" +"Ontgrendeld-pictogram geeft aan dat de waarde is veranderd en niet gelijk is " +"aan de systeemwaarde.\n" +"Klik om de huidige waarde te resetten naar de systeemwaarde." + +#: src/slic3r/GUI/Tab.cpp:3259 +msgid "" +"WHITE BULLET icon indicates that the value is the same as in the last saved " +"preset." +msgstr "" +"Het witte bolletje geeft aan dat de waarde gelijk is aan de laatst " +"opgeslagen preset." + +#: src/slic3r/GUI/Tab.cpp:3260 +msgid "" +"BACK ARROW icon indicates that the value was changed and is not equal to the " +"last saved preset.\n" +"Click to reset current value to the last saved preset." +msgstr "" +"Het pijltje-terug-pictogram geeft aan dat de waarde is veranderd en niet " +"gelijk is aan de laatst opgeslagen preset.\n" +"Klik om de waarde te resetten naar de laatst opgeslagen preset." + +#. TRN Preset +#: src/slic3r/GUI/Tab.cpp:3373 +#, c-format +msgid "Save %s as:" +msgstr "%s opslaan als:" + +#: src/slic3r/GUI/Tab.cpp:3417 +msgid "the following suffix is not allowed:" +msgstr "het volgende achtervoegsel is niet toegestaan:" + +#: src/slic3r/GUI/Tab.cpp:3421 +msgid "The supplied name is not available." +msgstr "De ingevoerde naam is niet beschikbaar." + +#: src/slic3r/GUI/Tab.cpp:3434 src/slic3r/GUI/Tab.cpp:3436 +msgid "Material" +msgstr "Materiaal" + +#: src/slic3r/GUI/Tab.cpp:3470 src/slic3r/GUI/Tab.cpp:3560 +#: src/slic3r/GUI/wxExtensions.cpp:601 +msgid "Layers" +msgstr "Lagen" + +#: src/slic3r/GUI/Tab.cpp:3568 +msgid "Support head" +msgstr "Supportkop" + +#: src/slic3r/GUI/Tab.cpp:3573 +msgid "Support pillar" +msgstr "Supportpijler" + +#: src/slic3r/GUI/Tab.cpp:3587 +msgid "Connection of the support sticks and junctions" +msgstr "Verbindingen van de supporttakken en kruisingen" + +#: src/slic3r/GUI/Tab.cpp:3592 +msgid "Automatic generation" +msgstr "Automatisch genereren" + +#: src/slic3r/GUI/Tab.hpp:327 src/slic3r/GUI/Tab.hpp:427 +msgid "Print Settings" +msgstr "Printinstellingen" + +#: src/slic3r/GUI/Tab.hpp:352 +msgid "Filament Settings" +msgstr "Filamentinstellingen" + +#: src/slic3r/GUI/Tab.hpp:388 +msgid "Printer Settings" +msgstr "Printerinstellingen" + +#: src/slic3r/GUI/Tab.hpp:412 +msgid "Material Settings" +msgstr "Materiaalinstellingen" + +#: src/slic3r/GUI/Tab.hpp:439 +msgid "Save preset" +msgstr "Preset opslaan" + +#: src/slic3r/GUI/UpdateDialogs.cpp:38 +msgid "Update available" +msgstr "Update beschikbaar" + +#: src/slic3r/GUI/UpdateDialogs.cpp:38 +#, c-format +msgid "New version of %s is available" +msgstr "Nieuwe versie van %s beschikbaar" + +#: src/slic3r/GUI/UpdateDialogs.cpp:43 +msgid "Current version:" +msgstr "Huidige versie:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:45 +msgid "New version:" +msgstr "Nieuwe versie:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:53 +msgid "Changelog && Download" +msgstr "Wijzigingslogboek && Download" + +#: src/slic3r/GUI/UpdateDialogs.cpp:60 src/slic3r/GUI/UpdateDialogs.cpp:125 +msgid "Open changelog page" +msgstr "Open wijzigingslogboekpagina" + +#: src/slic3r/GUI/UpdateDialogs.cpp:65 +msgid "Open download page" +msgstr "Open downloadpagina" + +#: src/slic3r/GUI/UpdateDialogs.cpp:71 +msgid "Don't notify about new releases any more" +msgstr "Geef geen meldingen over nieuwe versies meer" + +#: src/slic3r/GUI/UpdateDialogs.cpp:89 src/slic3r/GUI/UpdateDialogs.cpp:205 +msgid "Configuration update" +msgstr "Configuratie-update" + +#: src/slic3r/GUI/UpdateDialogs.cpp:89 +msgid "Configuration update is available" +msgstr "Er is een configuratie-update beschikbaar" + +#: src/slic3r/GUI/UpdateDialogs.cpp:92 +msgid "" +"Would you like to install it?\n" +"\n" +"Note that a full configuration snapshot will be created first. It can then " +"be restored at any time should there be a problem with the new version.\n" +"\n" +"Updated configuration bundles:" +msgstr "" +"Wilt u het installeren?\n" +"\n" +"Er wordt eerst een configuratiesnapshot gemaakt. Deze kan op elk moment " +"hersteld worden en zal geen probleem geven bij nieuwere versies.\n" +"\n" +"Geüpdatete configuratiebundels:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:113 +msgid "Comment:" +msgstr "Opmerking:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:149 +#, c-format +msgid "%s incompatibility" +msgstr "%s incompatibiliteit" + +#: src/slic3r/GUI/UpdateDialogs.cpp:150 +#, c-format +msgid "%s configuration is incompatible" +msgstr "%s configuratie is niet compatibel" + +#: src/slic3r/GUI/UpdateDialogs.cpp:155 +#, c-format +msgid "" +"This version of %s is not compatible with currently installed configuration " +"bundles.\n" +"This probably happened as a result of running an older %s after using a " +"newer one.\n" +"\n" +"You may either exit %s and try again with a newer version, or you may re-run " +"the initial configuration. Doing so will create a backup snapshot of the " +"existing configuration before installing files compatible with this %s." +msgstr "" +"Deze versie van %s is niet compatibel met de huidig geïnstalleerde " +"configuratiebundels.\n" +"Dit kan mogelijk ontstaan als resultaat van het draaien van een ouder %s na " +"het gebruik van een nieuwere.\n" +"\n" +"U kunt kiezen om %s af te sluiten en opnieuw te proberen met een nieuwere " +"versie of de startconfiguratie opnieuw te draaien. In geval van dat laatste " +"wordt een backup-snapshot gemaakt van de bestaande configuratie voor het " +"installeren van bestanden die compatibel zijn met deze %s." + +#: src/slic3r/GUI/UpdateDialogs.cpp:164 +#, c-format +msgid "This %s version: %s" +msgstr "Deze %s versie: %s" + +#: src/slic3r/GUI/UpdateDialogs.cpp:169 +msgid "Incompatible bundles:" +msgstr "Incompatibele bundels:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:185 +#, c-format +msgid "Exit %s" +msgstr "%s afsluiten" + +#: src/slic3r/GUI/UpdateDialogs.cpp:188 +msgid "Re-configure" +msgstr "Herconfigureer" + +#: src/slic3r/GUI/UpdateDialogs.cpp:209 +#, c-format +msgid "" +"%s now uses an updated configuration structure.\n" +"\n" +"So called 'System presets' have been introduced, which hold the built-in " +"default settings for various printers. These System presets cannot be " +"modified, instead, users now may create their own presets inheriting " +"settings from one of the System presets.\n" +"An inheriting preset may either inherit a particular value from its parent " +"or override it with a customized value.\n" +"\n" +"Please proceed with the %s that follows to set up the new presets and to " +"choose whether to enable automatic preset updates." +msgstr "" +"%s gebruikt nu een geüpdatete configuratiestructuur.\n" +"\n" +"Presets van het systeem zijn geïntroduceerd. Deze bevatten ingebouwde " +"standaardinstellingen voor meerdere printers. Deze systemen kunnen niet " +"aangepast worden. In plaats daarvan kunt u nu uw eigen presets creëren op " +"basis van een van de preset.\n" +"Een overgenomen preset kan een bepaalde waarde van bovenliggende " +"instellingen meekrijgen, maar ook overschrijven met een aangepaste waarde.\n" +"\n" +"Ga verdere met de %s die volgt om de nieuwe presets in te stellen en om te " +"kiezen of automatische presets moeten worden ingeschakeld." + +#: src/slic3r/GUI/UpdateDialogs.cpp:225 +msgid "For more information please visit our wiki page:" +msgstr "Voor meer informatie kunt u naar onze wiki-pagina gaan:" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:15 +msgid "Ramming customization" +msgstr "Ramming aanpassen" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:41 +msgid "" +"Ramming denotes the rapid extrusion just before a tool change in a single-" +"extruder MM printer. Its purpose is to properly shape the end of the " +"unloaded filament so it does not prevent insertion of the new filament and " +"can itself be reinserted later. This phase is important and different " +"materials can require different extrusion speeds to get the good shape. For " +"this reason, the extrusion rates during ramming are adjustable.\n" +"\n" +"This is an expert-level setting, incorrect adjustment will likely lead to " +"jams, extruder wheel grinding into filament etc." +msgstr "" +"Ramming wordt gebruikt voor het snel extruderen vlak voor een toolwissel in " +"multi-materialprinters met één extruder. Het doel daarvan is om het einde " +"van het ongeladen filament goed te vormen (zodat het later weer geladen kan " +"worden) en nieuwe filament niet verhinderd wordt. Deze fase is belangrijk. " +"Verschillende materialen vereisen verschillende extrusiesnelheden voor de " +"juiste vorm. Daarom zijn de waarden tijdens de ramming aan te passen.\n" +"\n" +"Dit is een expert-level instelling. Onjuiste aanpassingen kunnen zorgen voor " +"verstoppingen en andere problemen." + +#: src/slic3r/GUI/WipeTowerDialog.cpp:83 +msgid "Total ramming time" +msgstr "Totale ramming-tijd" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:85 +msgid "Total rammed volume" +msgstr "Totaal ramming-volume" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:89 +msgid "Ramming line width" +msgstr "Lijnbreedte voor ramming" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:91 +msgid "Ramming line spacing" +msgstr "Lijnafstand voor ramming" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:142 +msgid "Wipe tower - Purging volume adjustment" +msgstr "Afveegblok - afveegvolume aanpassen" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:254 +msgid "" +"Here you can adjust required purging volume (mm³) for any given pair of " +"tools." +msgstr "" +"Hier kunt u het benodigde afveegvolume (mm³) voor elk soort tool aanpassen." + +#: src/slic3r/GUI/WipeTowerDialog.cpp:255 +msgid "Extruder changed to" +msgstr "Extruder veranderd naar" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:263 +msgid "unloaded" +msgstr "ontladen" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:264 +msgid "loaded" +msgstr "geladen" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:276 +msgid "Tool #" +msgstr "Tool #" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:285 +msgid "" +"Total purging volume is calculated by summing two values below, depending on " +"which tools are loaded/unloaded." +msgstr "" +"Totaal afveegvolume dat de som is van de twee onderstaande waarden, " +"afhankelijk van welke tools zijn geladen/ontladen." + +#: src/slic3r/GUI/WipeTowerDialog.cpp:286 +msgid "Volume to purge (mm³) when the filament is being" +msgstr "Afveegvolume (mm³) als het filament wordt gebruikt" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:300 +msgid "From" +msgstr "Van" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:365 +msgid "" +"Switching to simple settings will discard changes done in the advanced " +"mode!\n" +"\n" +"Do you want to proceed?" +msgstr "" +"Overschakelen naar eenvoudige modus zorgt dat de gewijzigde instellingen uit " +"de geavanceerde modus vervallen!\n" +"\n" +"Weet u zeker dat u wilt doorgaan?" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:377 +msgid "Show simplified settings" +msgstr "Toon eenvoudige instellingen" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:377 +msgid "Show advanced settings" +msgstr "Toon geavanceerde instellingen" + +#: src/slic3r/GUI/wxExtensions.cpp:590 +msgid "Instances" +msgstr "Instanties" + +#: src/slic3r/GUI/wxExtensions.cpp:594 src/slic3r/GUI/wxExtensions.cpp:750 +#, c-format +msgid "Instance %d" +msgstr "Instantie %d" + +#: src/slic3r/GUI/wxExtensions.cpp:628 +msgid "Range" +msgstr "Bereik" + +#: src/slic3r/GUI/wxExtensions.cpp:2325 +msgid "Place bearings in slots and resume" +msgstr "Plaats inserts en ga door" + +#: src/slic3r/GUI/wxExtensions.cpp:3075 +msgid "One layer mode" +msgstr "Een-laags-modus" + +#: src/slic3r/GUI/wxExtensions.cpp:3078 +msgid "Discard all custom changes" +msgstr "Alle aangepaste wijzigingen afwijzen" + +#: src/slic3r/GUI/wxExtensions.cpp:3080 +msgid "Set extruder sequence for whole print" +msgstr "Stel extrudervolgorde in voor de hele print" + +#: src/slic3r/GUI/wxExtensions.cpp:3086 +msgid "For add color change use left mouse button click" +msgstr "Voor het toevoegen van een kleurwissel gebruikt u de linkermuisknop" + +#: src/slic3r/GUI/wxExtensions.cpp:3087 +msgid "For add change extruder use left mouse button click" +msgstr "Voor het toevoegen van een toolwissel gebruikt u de linkermuisknop" + +#: src/slic3r/GUI/wxExtensions.cpp:3088 +msgid "For add another code use right mouse button click" +msgstr "Voor het toevoegen van een andere code gebruikt u de rechtermuisknop" + +#: src/slic3r/GUI/wxExtensions.cpp:3090 +msgid "" +"For Delete color change use left mouse button click\n" +"For Edit color use right mouse button click" +msgstr "" +"Voor het verwijderen van een kleurwissel gebruikt u de linkermuisknop\n" +"Voor het bewerken gebruikt u de rechtermuisknop" + +#: src/slic3r/GUI/wxExtensions.cpp:3092 +msgid "Delete color change for Extruder %1%" +msgstr "Kleurverandering verwijderen voor extruder %1%" + +#: src/slic3r/GUI/wxExtensions.cpp:3095 +msgid "Delete extruder change to \"%1%\"" +msgstr "Extruderwissel verwijderen naar '%1%'" + +#: src/slic3r/GUI/wxExtensions.cpp:3096 +msgid "" +"For Delete \"%1%\" code use left mouse button click\n" +"For Edit \"%1%\" code use right mouse button click" +msgstr "" +"Voor het verwijderen van de '%1%'-code gebruikt u de linkermuisknop\n" +"Voor het bewerken van de '%1%'-code gebruikt u de rechtermuisknop" + +#: src/slic3r/GUI/wxExtensions.cpp:3176 src/slic3r/GUI/wxExtensions.cpp:3432 +msgid "Use another extruder" +msgstr "Gebruik een andere extruder" + +#: src/slic3r/GUI/wxExtensions.cpp:3435 +msgid "Add color change (%1%) for:" +msgstr "Voeg kleurwissel (%1%) toe voor:" + +#: src/slic3r/GUI/wxExtensions.cpp:3441 +msgid "Add color change" +msgstr "Voeg kleurwissel toe" + +#: src/slic3r/GUI/wxExtensions.cpp:3444 +msgid "Add pause print" +msgstr "Voeg printpauze toe" + +#: src/slic3r/GUI/wxExtensions.cpp:3447 +msgid "Add custom G-code" +msgstr "Voeg custom G-code toe" + +#: src/slic3r/GUI/wxExtensions.cpp:3460 +msgid "Edit color" +msgstr "Bewerk kleur" + +#: src/slic3r/GUI/wxExtensions.cpp:3461 +msgid "Edit pause print message" +msgstr "Bewerk printpauze-bericht" + +#: src/slic3r/GUI/wxExtensions.cpp:3462 +msgid "Edit custom G-code" +msgstr "Bewerk custom G-code" + +#: src/slic3r/GUI/wxExtensions.cpp:3465 +msgid "Delete color change" +msgstr "Verwijder kleurwissel" + +#: src/slic3r/GUI/wxExtensions.cpp:3466 +msgid "Delete pause print" +msgstr "Verwijder printpauze" + +#: src/slic3r/GUI/wxExtensions.cpp:3467 +msgid "Delete custom G-code" +msgstr "Verwijder custom G-code" + +#: src/slic3r/GUI/wxExtensions.cpp:3499 +msgid "Enter custom G-code used on current layer" +msgstr "Voer custom G-code in voor de huidige laag" + +#: src/slic3r/GUI/wxExtensions.cpp:3500 +msgid "Custom Gcode on current layer (%1% mm)." +msgstr "Custom G-code op de huidige laag (%1% mm)." + +#: src/slic3r/GUI/wxExtensions.cpp:3513 +msgid "Enter short message shown on Printer display during pause print" +msgstr "" +"Voer kort bericht in dat op het printerscherm wordt getoond tijdens de " +"printpauze" + +#: src/slic3r/GUI/wxExtensions.cpp:3514 +msgid "Message for pause print on current layer (%1% mm)." +msgstr "Kort bericht voor printpauze bij huidige laag (%1% mm)." + +#: src/slic3r/GUI/wxExtensions.cpp:3774 +#, c-format +msgid "Switch to the %s mode" +msgstr "Schakel over naar de %s modus" + +#: src/slic3r/GUI/wxExtensions.cpp:3775 +#, c-format +msgid "Current mode is %s" +msgstr "Huidige modus is: %s" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:23 +msgid "Set extruder sequence" +msgstr "Stel extrudervolgorde in" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:39 +msgid "Set extruder change for every" +msgstr "Stel toolwissel in voor elke" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:52 +#: src/libslic3r/PrintConfig.cpp:341 src/libslic3r/PrintConfig.cpp:983 +#: src/libslic3r/PrintConfig.cpp:1500 src/libslic3r/PrintConfig.cpp:1685 +#: src/libslic3r/PrintConfig.cpp:1746 src/libslic3r/PrintConfig.cpp:1919 +#: src/libslic3r/PrintConfig.cpp:1965 +msgid "layers" +msgstr "lagen" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:136 +msgid "Set extruder(tool) sequence" +msgstr "Stel toolvolgorde in" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:182 +msgid "Remove extruder from sequence" +msgstr "Verwijder extruder uit de reeks" + +#: src/slic3r/GUI/ExtruderSequenceDialog.cpp:192 +msgid "Add extruder to sequence" +msgstr "Voeg extruder toe aan de reeks" + +#: src/slic3r/Utils/Duet.cpp:51 +msgid "Connection to Duet works correctly." +msgstr "Verbinding met Duet werkt naar behoren." + +#: src/slic3r/Utils/Duet.cpp:56 +msgid "Could not connect to Duet" +msgstr "Kan niet verbinden met Duet" + +#: src/slic3r/Utils/Duet.cpp:84 src/slic3r/Utils/Duet.cpp:154 +#: src/slic3r/Utils/FlashAir.cpp:115 src/slic3r/Utils/FlashAir.cpp:132 +msgid "Unknown error occured" +msgstr "Onbekende fout opgetreden" + +#: src/slic3r/Utils/Duet.cpp:148 +msgid "Wrong password" +msgstr "Verkeerd wachtwoord" + +#: src/slic3r/Utils/Duet.cpp:151 +msgid "Could not get resources to create a new connection" +msgstr "Kan geen middelen krijgen om nieuwe verbinding te maken" + +#: src/slic3r/Utils/OctoPrint.cpp:70 +#, c-format +msgid "Mismatched type of print host: %s" +msgstr "Onjuist type printhost: %s" + +#: src/slic3r/Utils/OctoPrint.cpp:85 +msgid "Connection to OctoPrint works correctly." +msgstr "Verbinding met OctoPrint werkt naar behoren." + +#: src/slic3r/Utils/OctoPrint.cpp:91 +msgid "Could not connect to OctoPrint" +msgstr "Kan niet verbinden met OctoPrint" + +#: src/slic3r/Utils/OctoPrint.cpp:91 +msgid "Note: OctoPrint version at least 1.1.0 is required." +msgstr "Let op: de minimaal vereiste versie van OctoPrint is 1.1.0." + +#: src/slic3r/Utils/OctoPrint.cpp:196 +msgid "Connection to Prusa SL1 works correctly." +msgstr "Verbinding met Prusa SL1 werkt naar behoren." + +#: src/slic3r/Utils/OctoPrint.cpp:201 +msgid "Could not connect to Prusa SLA" +msgstr "Kan niet verbinden met Prusa SLA" + +#: src/slic3r/Utils/FlashAir.cpp:60 +msgid "Upload not enabled on FlashAir card." +msgstr "Uploaden naar een FlashAir-kaart niet toegestaan." + +#: src/slic3r/Utils/FlashAir.cpp:70 +msgid "Connection to FlashAir works correctly and upload is enabled." +msgstr "Verbinding met FlashAir werkt naar behoren en uploaden is toegestaan." + +#: src/slic3r/Utils/FlashAir.cpp:75 +msgid "Could not connect to FlashAir" +msgstr "Kan niet verbinden met FlashAir" + +#: src/slic3r/Utils/FlashAir.cpp:75 +msgid "" +"Note: FlashAir with firmware 2.00.02 or newer and activated upload function " +"is required." +msgstr "" +"Let op: FlashAir met firmware 2.00.02 (of nieuwer) en een geactiveerde " +"upload zijn vereist." + +#: src/slic3r/Utils/PresetUpdater.cpp:632 +#, c-format +msgid "requires min. %s and max. %s" +msgstr "vereist minimaal %s en maximaal %s" + +#: src/slic3r/Utils/PresetUpdater.cpp:637 +#, c-format +msgid "requires min. %s" +msgstr "vereist minimaal %s" + +#: src/slic3r/Utils/PresetUpdater.cpp:639 +#, c-format +msgid "requires max. %s" +msgstr "vereist maximaal %s" + +#: src/slic3r/Utils/FixModelByWin10.cpp:219 +#: src/slic3r/Utils/FixModelByWin10.cpp:359 +msgid "Exporting source model" +msgstr "Exporteer bronmodel" + +#: src/slic3r/Utils/FixModelByWin10.cpp:235 +msgid "Failed loading the input model." +msgstr "Laden van het model mislukt." + +#: src/slic3r/Utils/FixModelByWin10.cpp:242 +msgid "Repairing model by the Netfabb service" +msgstr "Repareer model met de NetFabb-service" + +#: src/slic3r/Utils/FixModelByWin10.cpp:248 +msgid "Mesh repair failed." +msgstr "Mesh-reparatie mislukt." + +#: src/slic3r/Utils/FixModelByWin10.cpp:251 +#: src/slic3r/Utils/FixModelByWin10.cpp:378 +msgid "Loading repaired model" +msgstr "Gerepareerd model aan het laden" + +#: src/slic3r/Utils/FixModelByWin10.cpp:263 +#: src/slic3r/Utils/FixModelByWin10.cpp:270 +#: src/slic3r/Utils/FixModelByWin10.cpp:302 +msgid "Saving mesh into the 3MF container failed." +msgstr "Opslaan van mesh in 3MF-container mislukt." + +#: src/slic3r/Utils/FixModelByWin10.cpp:340 +msgid "Model fixing" +msgstr "Model repareren" + +#: src/slic3r/Utils/FixModelByWin10.cpp:341 +msgid "Exporting model..." +msgstr "Model exporteren..." + +#: src/slic3r/Utils/FixModelByWin10.cpp:368 +msgid "Export of a temporary 3mf file failed" +msgstr "Exporteren van tijdelijk 3MF-bestand mislukt" + +#: src/slic3r/Utils/FixModelByWin10.cpp:383 +msgid "Import of the repaired 3mf file failed" +msgstr "Importeren van het gerepareerde 3MF-bestand mislukt" + +#: src/slic3r/Utils/FixModelByWin10.cpp:385 +msgid "Repaired 3MF file does not contain any object" +msgstr "Gerepareerd 3MF-bestand bevat geen object" + +#: src/slic3r/Utils/FixModelByWin10.cpp:387 +msgid "Repaired 3MF file contains more than one object" +msgstr "Gerepareerd 3MF-bestand bevat meer dan 1 object" + +#: src/slic3r/Utils/FixModelByWin10.cpp:389 +msgid "Repaired 3MF file does not contain any volume" +msgstr "Gerepareerd 3MF-bestand bevat geen volume" + +#: src/slic3r/Utils/FixModelByWin10.cpp:391 +msgid "Repaired 3MF file contains more than one volume" +msgstr "Gerepareerd 3MF-bestand bevat meer dan 1 volume" + +#: src/slic3r/Utils/FixModelByWin10.cpp:400 +msgid "Model repair finished" +msgstr "Model repareren voltooid" + +#: src/slic3r/Utils/FixModelByWin10.cpp:406 +msgid "Model repair canceled" +msgstr "Model repareren geannuleerd" + +#: src/slic3r/Utils/FixModelByWin10.cpp:423 +msgid "Model repaired successfully" +msgstr "Model repareren was succesvol" + +#: src/slic3r/Utils/FixModelByWin10.cpp:423 +#: src/slic3r/Utils/FixModelByWin10.cpp:426 +msgid "Model Repair by the Netfabb service" +msgstr "Model repareren met de NetFabb-service" + +#: src/slic3r/Utils/FixModelByWin10.cpp:426 +msgid "Model repair failed:" +msgstr "Model repareren mislukt:" + +#: src/libslic3r/SLA/SLAPad.cpp:690 +msgid "Pad brim size is too small for the current configuration." +msgstr "Brimgrootte is te klein voor de huidige configuratie." + +#: src/libslic3r/Zipper.cpp:32 +msgid "undefined error" +msgstr "onbekende fout" + +#: src/libslic3r/Zipper.cpp:34 +msgid "too many files" +msgstr "te veel bestanden" + +#: src/libslic3r/Zipper.cpp:36 +msgid "file too large" +msgstr "bestand te groot" + +#: src/libslic3r/Zipper.cpp:38 +msgid "unsupported method" +msgstr "niet-ondersteunde methode" + +#: src/libslic3r/Zipper.cpp:40 +msgid "unsupported encryption" +msgstr "niet-ondersteunde encryptie" + +#: src/libslic3r/Zipper.cpp:42 +msgid "unsupported feature" +msgstr "niet-ondersteunde optie" + +#: src/libslic3r/Zipper.cpp:44 +msgid "failed finding central directory" +msgstr "centrale map niet gevonden" + +#: src/libslic3r/Zipper.cpp:46 +msgid "not a ZIP archive" +msgstr "geen ZIP-archief" + +#: src/libslic3r/Zipper.cpp:48 +msgid "invalid header or archive is corrupted" +msgstr "ongeldige koptekst of het archief is beschadigd" + +#: src/libslic3r/Zipper.cpp:50 +msgid "unsupported multidisk archive" +msgstr "niet-ondersteund multi-disk archief" + +#: src/libslic3r/Zipper.cpp:52 +msgid "decompression failed or archive is corrupted" +msgstr "decompressie mislukt of archief is beschadigd" + +#: src/libslic3r/Zipper.cpp:54 +msgid "compression failed" +msgstr "compressie mislukt" + +#: src/libslic3r/Zipper.cpp:56 +msgid "unexpected decompressed size" +msgstr "onverwachte gedecomprimeerde grootte" + +#: src/libslic3r/Zipper.cpp:58 +msgid "CRC-32 check failed" +msgstr "CRC-32 check mislukt" + +#: src/libslic3r/Zipper.cpp:60 +msgid "unsupported central directory size" +msgstr "niet-ondersteunde centrale mapgrootte" + +#: src/libslic3r/Zipper.cpp:62 +msgid "allocation failed" +msgstr "toewijzing mislukt" + +#: src/libslic3r/Zipper.cpp:64 +msgid "file open failed" +msgstr "bestand openen mislukt" + +#: src/libslic3r/Zipper.cpp:66 +msgid "file create failed" +msgstr "bestand creëren mislukt" + +#: src/libslic3r/Zipper.cpp:68 +msgid "file write failed" +msgstr "bestand schrijven mislukt" + +#: src/libslic3r/Zipper.cpp:70 +msgid "file read failed" +msgstr "bestand lezen mislukt" + +#: src/libslic3r/Zipper.cpp:72 +msgid "file close failed" +msgstr "bestand sluiten mislukt" + +#: src/libslic3r/Zipper.cpp:74 +msgid "file seek failed" +msgstr "bestand zoeken mislukt" + +#: src/libslic3r/Zipper.cpp:76 +msgid "file stat failed" +msgstr "bestandsstatus mislukt" + +#: src/libslic3r/Zipper.cpp:78 +msgid "invalid parameter" +msgstr "ongeldige parameter" + +#: src/libslic3r/Zipper.cpp:80 +msgid "invalid filename" +msgstr "ongeldige bestandsnaam" + +#: src/libslic3r/Zipper.cpp:82 +msgid "buffer too small" +msgstr "buffer te klein" + +#: src/libslic3r/Zipper.cpp:84 +msgid "internal error" +msgstr "interne fout" + +#: src/libslic3r/Zipper.cpp:86 +msgid "file not found" +msgstr "bestand niet gevonden" + +#: src/libslic3r/Zipper.cpp:88 +msgid "archive is too large" +msgstr "archief te groot" + +#: src/libslic3r/Zipper.cpp:90 +msgid "validation failed" +msgstr "validatie mislukt" + +#: src/libslic3r/Zipper.cpp:92 +msgid "write calledback failed" +msgstr "terugschrijven mislukt" + +#: src/libslic3r/Zipper.cpp:102 +msgid "Error with zip archive" +msgstr "Fout bij ZIP-archief" + +#: src/libslic3r/GCode.cpp:633 +msgid "Empty layers detected, the output would not be printable." +msgstr "Lege lagen gedetecteerd. De output is mogelijk niet-printbaar." + +#: src/libslic3r/GCode.cpp:634 +msgid "Print z" +msgstr "Print Z" + +#: src/libslic3r/GCode.cpp:635 +msgid "" +"This is usually caused by negligibly small extrusions or by a faulty model. " +"Try to repair the model or change its orientation on the bed." +msgstr "" +"Dit komt meestal voor bij verwaarloosbare extrusiehoeveelheden of door een " +"foutief model. Probeer het model te repareren of verander de oriëntatie." + +#: src/libslic3r/ExtrusionEntity.cpp:323 +msgid "Mixed" +msgstr "Gemengd" + +#: src/libslic3r/Format/3mf.cpp:1519 +msgid "" +"The selected 3mf file has been saved with a newer version of %1% and is not " +"compatible." +msgstr "" +"Het geselecteerde 3MF-bestand is opgeslagen in een nieuwere versie van %1% " +"en is niet compatibel." + +#: src/libslic3r/Format/AMF.cpp:917 +msgid "" +"The selected amf file has been saved with a newer version of %1% and is not " +"compatible." +msgstr "" +"Het geselecteerde AMF-bestand is opgeslagen in een nieuwere versie van %1% " +"en is niet compatibel." + +#: src/libslic3r/Print.cpp:1168 +msgid "All objects are outside of the print volume." +msgstr "Alle objecten bevinden zich buiten het printvolume." + +#: src/libslic3r/Print.cpp:1171 +msgid "The supplied settings will cause an empty print." +msgstr "De ingevoerde instellingen resulteren in een lege print." + +#: src/libslic3r/Print.cpp:1198 +msgid "Some objects are too close; your extruder will collide with them." +msgstr "" +"Sommige objecten staan te dicht op elkaar. De extruder zal er tegenaan " +"botsen." + +#: src/libslic3r/Print.cpp:1213 +msgid "" +"Some objects are too tall and cannot be printed without extruder collisions." +msgstr "" +"Sommige objecten zijn te groot en kunnen niet geprint worden zonder " +"botsingen." + +#: src/libslic3r/Print.cpp:1223 +msgid "The Spiral Vase option can only be used when printing a single object." +msgstr "De spiraalmodus kan alleen gebruikt worden voor enkeldelige objecten." + +#: src/libslic3r/Print.cpp:1230 +msgid "" +"The Spiral Vase option can only be used when printing single material " +"objects." +msgstr "" +"De spiraalmodus kan alleen gebruikt worden met enkel-materiaal objecten." + +#: src/libslic3r/Print.cpp:1243 +msgid "" +"The wipe tower is only supported if all extruders have the same nozzle " +"diameter and use filaments of the same diameter." +msgstr "" +"Het afveegblok wordt alleen ondersteunt als alle extruders dezelfde nozzle- " +"en filamentdiameter hebben." + +#: src/libslic3r/Print.cpp:1248 +msgid "" +"The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter " +"and Repetier G-code flavors." +msgstr "" +"Het afveegblok wordt alleen ondersteunt in Marlin, RepRap/Sprinter en " +"Repetier G-code-soorten." + +#: src/libslic3r/Print.cpp:1250 +msgid "" +"The Wipe Tower is currently only supported with the relative extruder " +"addressing (use_relative_e_distances=1)." +msgstr "" +"Het afveegblok wordt alleen ondersteunt met de relatieve extruderinstelling " +"('use_relative_e_distances' = 1)." + +#: src/libslic3r/Print.cpp:1252 +msgid "Ooze prevention is currently not supported with the wipe tower enabled." +msgstr "" +"Druippreventie wordt niet ondersteund als het afveegblok is geactiveerd." + +#: src/libslic3r/Print.cpp:1254 +msgid "" +"The Wipe Tower currently does not support volumetric E (use_volumetric_e=0)." +msgstr "" +"Het afveegblok niet ondersteunt bij volumetrische extrusiewaarden " +"('use_volumetric_e' = 0)." + +#: src/libslic3r/Print.cpp:1275 +msgid "" +"The Wipe Tower is only supported for multiple objects if they have equal " +"layer heights" +msgstr "" +"Het afveegblok wordt alleen ondersteunt voor meerdere objecten als deze een " +"gelijke laagdikte hebben" + +#: src/libslic3r/Print.cpp:1277 +msgid "" +"The Wipe Tower is only supported for multiple objects if they are printed " +"over an equal number of raft layers" +msgstr "" +"Het afveegblok wordt alleen ondersteunt voor meerdere objecten als deze op " +"een gelijk aantal raftlagen zijn geplaatst" + +#: src/libslic3r/Print.cpp:1279 +msgid "" +"The Wipe Tower is only supported for multiple objects if they are printed " +"with the same support_material_contact_distance" +msgstr "" +"Het afveegblok wordt alleen ondersteunt voor meerdere objecten als de " +"instelling 'support_material_contact_distance' gelijk staat" + +#: src/libslic3r/Print.cpp:1281 +msgid "" +"The Wipe Tower is only supported for multiple objects if they are sliced " +"equally." +msgstr "" +"Het afveegblok wordt alleen ondersteunt voor meerdere objecten als ze " +"tegelijk gesliced worden." + +#: src/libslic3r/Print.cpp:1323 +msgid "" +"The Wipe tower is only supported if all objects have the same variable layer " +"height" +msgstr "" +"Het afveegblok wordt alleen ondersteunt als alle objecten dezelfde variabele " +"laagdikte hebben" + +#: src/libslic3r/Print.cpp:1349 +msgid "" +"One or more object were assigned an extruder that the printer does not have." +msgstr "" +"Een of meer objecten staan ingesteld op een extruder die de printer niet " +"heeft." + +#: src/libslic3r/Print.cpp:1358 +msgid "%1%=%2% mm is too low to be printable at a layer height %3% mm" +msgstr "%1%=%2% mm is te weinig om te printen bij een laagdikte van %3% mm" + +#: src/libslic3r/Print.cpp:1361 +msgid "Excessive %1%=%2% mm to be printable with a nozzle diameter %3% mm" +msgstr "Te veel %1%=%2% mm om te printen met een nozzlediameter van %3% mm" + +#: src/libslic3r/Print.cpp:1372 +msgid "" +"Printing with multiple extruders of differing nozzle diameters. If support " +"is to be printed with the current extruder (support_material_extruder == 0 " +"or support_material_interface_extruder == 0), all nozzles have to be of the " +"same diameter." +msgstr "" +"Printen met meerdere extruders met verschillende nozzlediameters. Als " +"support met de huidige extruder geprint moet worden, moeten alle nozzles " +"dezelfde diameter hebben ('support_material_extruder' = 0 of " +"'support_material_interface_extruder' = 0)." + +#: src/libslic3r/Print.cpp:1380 +msgid "" +"For the Wipe Tower to work with the soluble supports, the support layers " +"need to be synchronized with the object layers." +msgstr "" +"Om het afveegblok te laten samenwerken met oplosbare support, moeten de " +"supportlagen gesynchroniseerd worden met de objectlagen." + +#: src/libslic3r/Print.cpp:1384 +msgid "" +"The Wipe Tower currently supports the non-soluble supports only if they are " +"printed with the current extruder without triggering a tool change. (both " +"support_material_extruder and support_material_interface_extruder need to be " +"set to 0)." +msgstr "" +"Het afveegblok ondersteunt alleen niet-oplosbaar support als dat geprint " +"worden met de huidige extruder zonder toolwissel (Zet zowel " +"'support_material_extruder' en 'support_material_interface_extruder' op 0)." + +#: src/libslic3r/Print.cpp:1406 +msgid "First layer height can't be greater than nozzle diameter" +msgstr "" +"Laagdikte van de eerste laag kan niet groter zijn dan de nozzlediameter" + +#: src/libslic3r/Print.cpp:1411 +msgid "Layer height can't be greater than nozzle diameter" +msgstr "Laagdikte kan niet groter zijn dan de nozzlediameter" + +#: src/libslic3r/Print.cpp:1566 +msgid "Infilling layers" +msgstr "Vullingslagen" + +#: src/libslic3r/Print.cpp:1582 +msgid "Generating skirt" +msgstr "Skirt genereren" + +#: src/libslic3r/Print.cpp:1590 +msgid "Generating brim" +msgstr "Brim genereren" + +#: src/libslic3r/Print.cpp:1614 +msgid "Exporting G-code" +msgstr "G-code exporteren" + +#: src/libslic3r/Print.cpp:1618 +msgid "Generating G-code" +msgstr "G-code genereren" + +#: src/libslic3r/SLAPrint.cpp:66 +msgid "Slicing model" +msgstr "Model slicen" + +#: src/libslic3r/SLAPrint.cpp:67 src/libslic3r/SLAPrint.cpp:905 +msgid "Generating support points" +msgstr "Supportpunten genereren" + +#: src/libslic3r/SLAPrint.cpp:68 +msgid "Generating support tree" +msgstr "Supportboom genereren" + +#: src/libslic3r/SLAPrint.cpp:69 +msgid "Generating pad" +msgstr "Basisplaat genereren" + +#: src/libslic3r/SLAPrint.cpp:70 +msgid "Slicing supports" +msgstr "Support slicen" + +#: src/libslic3r/SLAPrint.cpp:87 +msgid "Merging slices and calculating statistics" +msgstr "Slices samenvoegen en statistieken berekenen" + +#: src/libslic3r/SLAPrint.cpp:88 +msgid "Rasterizing layers" +msgstr "Lagen roosteren" + +#: src/libslic3r/SLAPrint.cpp:670 +msgid "" +"Cannot proceed without support points! Add support points or disable support " +"generation." +msgstr "" +"Kan niet doorgaan zonder supportpunten! Voeg supportpunten toe of schakel " +"supportgeneratie uit." + +#: src/libslic3r/SLAPrint.cpp:682 +msgid "" +"Elevation is too low for object. Use the \"Pad around object\" feature to " +"print the object without elevation." +msgstr "" +"Verhoging is te klein voor het object. Gebruik de 'Basisplaat rondom object'-" +"optie om het object zonder verhoging te printen." + +#: src/libslic3r/SLAPrint.cpp:688 +msgid "" +"The endings of the support pillars will be deployed on the gap between the " +"object and the pad. 'Support base safety distance' has to be greater than " +"the 'Pad object gap' parameter to avoid this." +msgstr "" +"De uiteinden van de pijlers worden ingezet op de gaten tussen het object en " +"de basisplaat. De instelling 'Veilige afstand voor supportbasis' moet groter " +"zijn dan de 'Basisplaat-objectgat'-parameter hiervoor." + +#: src/libslic3r/SLAPrint.cpp:703 +msgid "Exposition time is out of printer profile bounds." +msgstr "De belichtingstijd valt buiten de grenzen van het printerprofiel." + +#: src/libslic3r/SLAPrint.cpp:710 +msgid "Initial exposition time is out of printer profile bounds." +msgstr "Initiële belichtingstijd valt buiten de printerprofielgrenzen." + +#: src/libslic3r/SLAPrint.cpp:794 +msgid "" +"Slicing had to be stopped due to an internal error: Inconsistent slice index." +msgstr "" +"Slicen moest gestopt worden door een interne fout: inconsistente slice-index." + +#: src/libslic3r/SLAPrint.cpp:964 src/libslic3r/SLAPrint.cpp:973 +#: src/libslic3r/SLAPrint.cpp:1018 +msgid "Visualizing supports" +msgstr "Visualiseer support" + +#: src/libslic3r/SLAPrint.cpp:1009 +msgid "No pad can be generated for this model with the current configuration" +msgstr "" +"Met de huidige configuratie kan voor dit model geen basisplaat gegenereerd " +"worden" + +#: src/libslic3r/SLAPrint.cpp:1554 +msgid "Slicing done" +msgstr "Slicen voltooid" + +#: src/libslic3r/PrintBase.cpp:71 +msgid "Failed processing of the output_filename_format template." +msgstr "Verwerken van de 'output_filename_format'-template mislukt." + +#: src/libslic3r/PrintConfig.cpp:43 src/libslic3r/PrintConfig.cpp:44 +msgid "Printer technology" +msgstr "Printertechnologie" + +#: src/libslic3r/PrintConfig.cpp:51 +msgid "Bed shape" +msgstr "Bedvorm" + +#: src/libslic3r/PrintConfig.cpp:56 +msgid "Bed custom texture" +msgstr "Custom bedtextuur" + +#: src/libslic3r/PrintConfig.cpp:61 +msgid "Bed custom model" +msgstr "Custom bedmodel" + +#: src/libslic3r/PrintConfig.cpp:66 +msgid "Picture sizes to be stored into a .gcode and .sl1 files" +msgstr "Afbeeldingsgrootte om op te slaan in gcode- en SL1-bestanden" + +#: src/libslic3r/PrintConfig.cpp:73 +msgid "" +"This setting controls the height (and thus the total number) of the slices/" +"layers. Thinner layers give better accuracy but take more time to print." +msgstr "" +"Deze instelling is voor de laagdikte (en dus het totaal aantal lagen). " +"Dunnere lagen geven een betere nauwkeurigheid, maar het printen duurt langer." + +#: src/libslic3r/PrintConfig.cpp:80 +msgid "Max print height" +msgstr "Maximale printhoogte" + +#: src/libslic3r/PrintConfig.cpp:81 +msgid "" +"Set this to the maximum height that can be reached by your extruder while " +"printing." +msgstr "" +"Stel dit in als maximale hoogte die bereikt kan worden door de printer." + +#: src/libslic3r/PrintConfig.cpp:87 +msgid "Slice gap closing radius" +msgstr "Gatvulradius" + +#: src/libslic3r/PrintConfig.cpp:89 +msgid "" +"Cracks smaller than 2x gap closing radius are being filled during the " +"triangle mesh slicing. The gap closing operation may reduce the final print " +"resolution, therefore it is advisable to keep the value reasonably low." +msgstr "" +"Gaten die kleiner zijn dan tweemaal de gatvulradius worden opgevuld tijdens " +"het slicen. Het vullen kan zorgen dat de printresolutie minder wordt. Daarom " +"wordt geadviseerd de waarde laag te houden." + +#: src/libslic3r/PrintConfig.cpp:97 +msgid "Hostname, IP or URL" +msgstr "Hostnaam, IP of URL" + +#: src/libslic3r/PrintConfig.cpp:98 +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the hostname, IP address or URL of the printer host instance." +msgstr "" +"PrusaSlicer kan gcode-bestanden naar een printerhost uploaden. Dit veld moet " +"de hostnaam, IP-adres of URL van de printerhostomgeving bevatten." + +#: src/libslic3r/PrintConfig.cpp:104 +msgid "API Key / Password" +msgstr "API-key / wachtwoord" + +#: src/libslic3r/PrintConfig.cpp:105 +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the API Key or the password required for authentication." +msgstr "" +"PrusaSlicer kan gcode-bestanden naar een printerhost uploaden. Dit veld moet " +"de API-key of het wachtwoord voor authenticatie bevatten." + +#: src/libslic3r/PrintConfig.cpp:111 +msgid "HTTPS CA File" +msgstr "HTTPS-CA-bestand" + +#: src/libslic3r/PrintConfig.cpp:112 +msgid "" +"Custom CA certificate file can be specified for HTTPS OctoPrint connections, " +"in crt/pem format. If left blank, the default OS CA certificate repository " +"is used." +msgstr "" +"Aangepast CA-certificaatbestand kan gespecificeerd worden voor HTTPS-" +"OctoPrint verbindingen in CRT/PEM-formaat. Als er niets wordt ingevuld, " +"wordt de standaard OS-CA-certificaatopslaglocatie gebruikt." + +#: src/libslic3r/PrintConfig.cpp:126 +msgid "Avoid crossing perimeters" +msgstr "Vermijd kruisende perimeters" + +#: src/libslic3r/PrintConfig.cpp:127 +msgid "" +"Optimize travel moves in order to minimize the crossing of perimeters. This " +"is mostly useful with Bowden extruders which suffer from oozing. This " +"feature slows down both the print and the G-code generation." +msgstr "" +"Optimaliseer de bewegingen om kruisende perimeters te minimaliseren. Dit is " +"handig bij Bowden-extruders die gevoelig zijn voor druipen. Deze aanpassing " +"vertraagd zowel de print als de G-code-generatie." + +#: src/libslic3r/PrintConfig.cpp:134 src/libslic3r/PrintConfig.cpp:2053 +msgid "Other layers" +msgstr "Overige lagen" + +#: src/libslic3r/PrintConfig.cpp:135 +msgid "" +"Bed temperature for layers after the first one. Set this to zero to disable " +"bed temperature control commands in the output." +msgstr "" +"Bedtemperatuur voor lagen na de eerste laag. Als dit ingesteld is op 0, " +"worden bedverwarmingscommando's uitgezet." + +#: src/libslic3r/PrintConfig.cpp:137 +msgid "Bed temperature" +msgstr "Bedtemperatuur" + +#: src/libslic3r/PrintConfig.cpp:144 +msgid "" +"This custom code is inserted at every layer change, right before the Z move. " +"Note that you can use placeholder variables for all Slic3r settings as well " +"as [layer_num] and [layer_z]." +msgstr "" +"Deze custom code wordt toegevoegd bij elke laagwisseling, vlak voor de Z-" +"beweging. U kunt hier variabelen gebruiken voor alle instellingen zoals " +"'layer_num' en 'layer_z'." + +#: src/libslic3r/PrintConfig.cpp:154 +msgid "Between objects G-code" +msgstr "G-code die komt tussen objecten" + +#: src/libslic3r/PrintConfig.cpp:155 +msgid "" +"This code is inserted between objects when using sequential printing. By " +"default extruder and bed temperature are reset using non-wait command; " +"however if M104, M109, M140 or M190 are detected in this custom code, Slic3r " +"will not add temperature commands. Note that you can use placeholder " +"variables for all Slic3r settings, so you can put a \"M109 " +"S[first_layer_temperature]\" command wherever you want." +msgstr "" +"Deze code wordt ingevoegd tussen objecten bij het achtereenvolgens printen. " +"Standaard worden de extruder- en bedtemperatuur gereset met een wacht-niet-" +"commando, hoewel, als M104, M109, M140 of M190 in de custom code worden " +"gedetecteerd zal PrusaSlicer deze commando's niet meenemen. Merk op dat " +"variabelen voor alle instellingen gebruikt kunnen worden." + +#: src/libslic3r/PrintConfig.cpp:166 +msgid "Number of solid layers to generate on bottom surfaces." +msgstr "Aantal te genereren dichte lagen voor bodemvlakken." + +#: src/libslic3r/PrintConfig.cpp:167 +msgid "Bottom solid layers" +msgstr "Dichte bodemlagen" + +#: src/libslic3r/PrintConfig.cpp:172 +msgid "Bridge" +msgstr "Brug" + +#: src/libslic3r/PrintConfig.cpp:173 +msgid "" +"This is the acceleration your printer will use for bridges. Set zero to " +"disable acceleration control for bridges." +msgstr "" +"Deze acceleratie zal uw printer gebruiken voor bruggen. Als dit ingesteld is " +"op 0, wordt de acceleratie-instelling voor bruggen uitgezet." + +#: src/libslic3r/PrintConfig.cpp:175 src/libslic3r/PrintConfig.cpp:318 +#: src/libslic3r/PrintConfig.cpp:851 src/libslic3r/PrintConfig.cpp:973 +#: src/libslic3r/PrintConfig.cpp:1143 src/libslic3r/PrintConfig.cpp:1196 +#: src/libslic3r/PrintConfig.cpp:1207 src/libslic3r/PrintConfig.cpp:1398 +msgid "mm/s²" +msgstr "mm/s²" + +#: src/libslic3r/PrintConfig.cpp:181 +msgid "Bridging angle" +msgstr "Brughoek" + +#: src/libslic3r/PrintConfig.cpp:183 +msgid "" +"Bridging angle override. If left to zero, the bridging angle will be " +"calculated automatically. Otherwise the provided angle will be used for all " +"bridges. Use 180° for zero angle." +msgstr "" +"Brughoek overschrijven. Als dit ingesteld is op 0, wordt de optimale hoek " +"automatisch berekend, anders wordt de opgegeven hoek voor alle bruggen " +"gebruikt. 180° staat gelijk aan 0°." + +#: src/libslic3r/PrintConfig.cpp:186 src/libslic3r/PrintConfig.cpp:769 +#: src/libslic3r/PrintConfig.cpp:1635 src/libslic3r/PrintConfig.cpp:1645 +#: src/libslic3r/PrintConfig.cpp:1883 src/libslic3r/PrintConfig.cpp:2038 +#: src/libslic3r/PrintConfig.cpp:2224 src/libslic3r/PrintConfig.cpp:2695 +#: src/libslic3r/PrintConfig.cpp:2816 +msgid "°" +msgstr "°" + +#: src/libslic3r/PrintConfig.cpp:192 +msgid "Bridges fan speed" +msgstr "Ventilatorsnelheid voor bruggen" + +#: src/libslic3r/PrintConfig.cpp:193 +msgid "This fan speed is enforced during all bridges and overhangs." +msgstr "Deze ventilatorsnelheid wordt aangehouden bij bruggen en overhanging." + +#: src/libslic3r/PrintConfig.cpp:194 src/libslic3r/PrintConfig.cpp:781 +#: src/libslic3r/PrintConfig.cpp:1216 src/libslic3r/PrintConfig.cpp:1279 +#: src/libslic3r/PrintConfig.cpp:1527 src/libslic3r/PrintConfig.cpp:2402 +#: src/libslic3r/PrintConfig.cpp:2735 +msgid "%" +msgstr "%" + +#: src/libslic3r/PrintConfig.cpp:201 +msgid "Bridge flow ratio" +msgstr "Brugextrusieverhouding" + +#: src/libslic3r/PrintConfig.cpp:203 +msgid "" +"This factor affects the amount of plastic for bridging. You can decrease it " +"slightly to pull the extrudates and prevent sagging, although default " +"settings are usually good and you should experiment with cooling (use a fan) " +"before tweaking this." +msgstr "" +"Deze factor beïnvloedt de hoeveelheid plastic voor bruggen. Deze kan " +"verlaagd worden om het extrudaat strak te trekken en doorzakking te " +"voorkomen. Hoewel de systeemwaarden goed zijn, kan geëxperimenteerd worden " +"met de koeling voor dit aangepast wordt." + +#: src/libslic3r/PrintConfig.cpp:213 +msgid "Bridges" +msgstr "Bruggen" + +#: src/libslic3r/PrintConfig.cpp:215 +msgid "Speed for printing bridges." +msgstr "Printsnelheid voor bruggen." + +#: src/libslic3r/PrintConfig.cpp:216 src/libslic3r/PrintConfig.cpp:599 +#: src/libslic3r/PrintConfig.cpp:607 src/libslic3r/PrintConfig.cpp:616 +#: src/libslic3r/PrintConfig.cpp:624 src/libslic3r/PrintConfig.cpp:651 +#: src/libslic3r/PrintConfig.cpp:670 src/libslic3r/PrintConfig.cpp:911 +#: src/libslic3r/PrintConfig.cpp:1039 src/libslic3r/PrintConfig.cpp:1125 +#: src/libslic3r/PrintConfig.cpp:1161 src/libslic3r/PrintConfig.cpp:1174 +#: src/libslic3r/PrintConfig.cpp:1185 src/libslic3r/PrintConfig.cpp:1238 +#: src/libslic3r/PrintConfig.cpp:1297 src/libslic3r/PrintConfig.cpp:1428 +#: src/libslic3r/PrintConfig.cpp:1602 src/libslic3r/PrintConfig.cpp:1611 +#: src/libslic3r/PrintConfig.cpp:2017 src/libslic3r/PrintConfig.cpp:2131 +msgid "mm/s" +msgstr "mm/s" + +#: src/libslic3r/PrintConfig.cpp:223 +msgid "Brim width" +msgstr "Breedte van de brim" + +#: src/libslic3r/PrintConfig.cpp:224 +msgid "" +"Horizontal width of the brim that will be printed around each object on the " +"first layer." +msgstr "" +"Horizontale breedte van de brim die rond elk object op de eerste laag wordt " +"geprint." + +#: src/libslic3r/PrintConfig.cpp:231 +msgid "Clip multi-part objects" +msgstr "Meerdelige objecten samenvoegen" + +#: src/libslic3r/PrintConfig.cpp:232 +msgid "" +"When printing multi-material objects, this settings will make Slic3r to clip " +"the overlapping object parts one by the other (2nd part will be clipped by " +"the 1st, 3rd part will be clipped by the 1st and 2nd etc)." +msgstr "" +"Als meerdere multi-materialobjecten worden geprint, zorgt deze instelling " +"dat PrusaSlicer de overlappende delen met de vorige in de reeks combineert " +"(2e deel wordt gecombineerd met het 1e, 3e deel met het 1e en 2e, etc)." + +#: src/libslic3r/PrintConfig.cpp:239 +msgid "Colorprint height" +msgstr "Kleurenprinthoogte" + +#: src/libslic3r/PrintConfig.cpp:240 +msgid "Heights at which a filament change is to occur." +msgstr "Hoogte waarbij de filamentwissel plaatsvindt." + +#: src/libslic3r/PrintConfig.cpp:250 +msgid "Compatible printers condition" +msgstr "Voorwaarden compatibele printers" + +#: src/libslic3r/PrintConfig.cpp:251 +msgid "" +"A boolean expression using the configuration values of an active printer " +"profile. If this expression evaluates to true, this profile is considered " +"compatible with the active printer profile." +msgstr "" +"Een waar/niet waar aanduiding die gebruik maakt van configuratiewaarden van " +"een actief printerprofiel. Als deze aanduiding op waar staat, wordt dit " +"profiel beschouwd als compatibel met het actieve printerprofiel." + +#: src/libslic3r/PrintConfig.cpp:265 +msgid "Compatible print profiles condition" +msgstr "Voorwaarden compatibele printprofielen" + +#: src/libslic3r/PrintConfig.cpp:266 +msgid "" +"A boolean expression using the configuration values of an active print " +"profile. If this expression evaluates to true, this profile is considered " +"compatible with the active print profile." +msgstr "" +"Een waar/niet waar aanduiding die gebruik maakt van configuratiewaarden van " +"een actief printprofiel. Als deze aanduiding op waar staat, wordt dit " +"profiel beschouwd als compatibel met het actieve printprofiel." + +#: src/libslic3r/PrintConfig.cpp:283 +msgid "Complete individual objects" +msgstr "Voltooi individuele objecten" + +#: src/libslic3r/PrintConfig.cpp:284 +msgid "" +"When printing multiple objects or copies, this feature will complete each " +"object before moving onto next one (and starting it from its bottom layer). " +"This feature is useful to avoid the risk of ruined prints. Slic3r should " +"warn and prevent you from extruder collisions, but beware." +msgstr "" +"Als meerdere objecten geprint worden, zorgt deze optie dat de objecten " +"afzonderlijk voltooid worden voordat bewogen wordt naar de volgende. " +"PrusaSlicer voorkomt botsingen van de extruder tegen eerder geprinte " +"objecten en zal u daar ook voor waarschuwen, maar blijf wel alert." + +#: src/libslic3r/PrintConfig.cpp:292 +msgid "Enable auto cooling" +msgstr "Automatisch koelen toestaan" + +#: src/libslic3r/PrintConfig.cpp:293 +msgid "" +"This flag enables the automatic cooling logic that adjusts print speed and " +"fan speed according to layer printing time." +msgstr "" +"Dit vinkje zorgt dat automatisch gekoeld wordt; de print- en " +"ventilatorsnelheid worden aangepast op basis van de laagprinttijd." + +#: src/libslic3r/PrintConfig.cpp:298 +msgid "Cooling tube position" +msgstr "Koelbuispositie" + +#: src/libslic3r/PrintConfig.cpp:299 +msgid "Distance of the center-point of the cooling tube from the extruder tip." +msgstr "Afstand vanaf de nozzle tot het middelpunt van de koelbuis." + +#: src/libslic3r/PrintConfig.cpp:306 +msgid "Cooling tube length" +msgstr "Koelbuislengte" + +#: src/libslic3r/PrintConfig.cpp:307 +msgid "Length of the cooling tube to limit space for cooling moves inside it." +msgstr "" +"Lengte van de koelbuis om de ruimte voor koelbewegingen daarin te beperken." + +#: src/libslic3r/PrintConfig.cpp:315 +msgid "" +"This is the acceleration your printer will be reset to after the role-" +"specific acceleration values are used (perimeter/infill). Set zero to " +"prevent resetting acceleration at all." +msgstr "" +"Dit is de acceleratie waarop uw printer wordt ingesteld na een specifieke " +"acceleratiewaarde (perimeters/vulling). Als dit ingesteld is op 0, worden " +"geen acceleratiewaarden opnieuw ingesteld." + +#: src/libslic3r/PrintConfig.cpp:324 +msgid "Default filament profile" +msgstr "Standaard filamentprofiel" + +#: src/libslic3r/PrintConfig.cpp:325 +msgid "" +"Default filament profile associated with the current printer profile. On " +"selection of the current printer profile, this filament profile will be " +"activated." +msgstr "" +"Standaard filamentprofiel dat geassocieerd wordt met huidig printerprofiel. " +"Bij selectie van het huidige printerprofiel wordt dit filamentprofiel " +"geactiveerd." + +#: src/libslic3r/PrintConfig.cpp:331 +msgid "Default print profile" +msgstr "Standaard printprofiel" + +#: src/libslic3r/PrintConfig.cpp:332 src/libslic3r/PrintConfig.cpp:2560 +#: src/libslic3r/PrintConfig.cpp:2571 +msgid "" +"Default print profile associated with the current printer profile. On " +"selection of the current printer profile, this print profile will be " +"activated." +msgstr "" +"Standaard printprofiel dat geassocieerd wordt met huidig printerprofiel. Bij " +"selectie van het huidige printerprofiel wordt dit printprofiel geactiveerd." + +#: src/libslic3r/PrintConfig.cpp:338 +msgid "Disable fan for the first" +msgstr "Zet ventilator uit voor de eerste" + +#: src/libslic3r/PrintConfig.cpp:339 +msgid "" +"You can set this to a positive value to disable fan at all during the first " +"layers, so that it does not make adhesion worse." +msgstr "" +"U kunt dit instellen op een positieve waarde om de ventilator uit te " +"schakelen tijdens het printen van de eerste lagen voor een betere adhesie." + +#: src/libslic3r/PrintConfig.cpp:348 +msgid "Don't support bridges" +msgstr "Geen support voor bruggen" + +#: src/libslic3r/PrintConfig.cpp:350 +msgid "" +"Experimental option for preventing support material from being generated " +"under bridged areas." +msgstr "Experimentele optie om support onder brugvlakken te vermijden." + +#: src/libslic3r/PrintConfig.cpp:356 +msgid "Distance between copies" +msgstr "Ruimte tussen kopieën" + +#: src/libslic3r/PrintConfig.cpp:357 +msgid "Distance used for the auto-arrange feature of the plater." +msgstr "" +"Afstand die gebruikt wordt tussen objecten bij automatisch rangschikken." + +#: src/libslic3r/PrintConfig.cpp:364 +msgid "Elephant foot compensation" +msgstr "Squishcompensatie" + +#: src/libslic3r/PrintConfig.cpp:366 +msgid "" +"The first layer will be shrunk in the XY plane by the configured value to " +"compensate for the 1st layer squish aka an Elephant Foot effect." +msgstr "" +"De eerste laag wordt verkleind in horizontale richting met de ingestelde " +"waarde ter compensatie van het platdrukken." + +#: src/libslic3r/PrintConfig.cpp:375 +msgid "" +"This end procedure is inserted at the end of the output file. Note that you " +"can use placeholder variables for all PrusaSlicer settings." +msgstr "" +"Deze eindprocedure wordt aan het eind van het outputbestand ingevoegd. Merk " +"op dat variabelen voor alle instellingen gebruikt kunnen worden." + +#: src/libslic3r/PrintConfig.cpp:385 +msgid "" +"This end procedure is inserted at the end of the output file, before the " +"printer end gcode (and before any toolchange from this filament in case of " +"multimaterial printers). Note that you can use placeholder variables for all " +"PrusaSlicer settings. If you have multiple extruders, the gcode is processed " +"in extruder order." +msgstr "" +"Deze eindprocedure is ingevoegd aan het eind van het outputbestand, nog voor " +"de eind G-code (en voor de toolwisselingen). Merk op dat variabelen voor " +"alle instellingen gebruikt kunnen worden. Als de printer meerdere extruders " +"heeft, wordt deze G-code in de extrudervolgorde uitgevoerd." + +#: src/libslic3r/PrintConfig.cpp:396 +msgid "Ensure vertical shell thickness" +msgstr "Garandeer verticale shelldikte" + +#: src/libslic3r/PrintConfig.cpp:398 +msgid "" +"Add solid infill near sloping surfaces to guarantee the vertical shell " +"thickness (top+bottom solid layers)." +msgstr "" +"Voeg dichte vulling toe bij hellende vlakken om de verticale shelldikte te " +"garanderen." + +#: src/libslic3r/PrintConfig.cpp:404 +msgid "Top fill pattern" +msgstr "Vulpatroon voor bovenzijde" + +#: src/libslic3r/PrintConfig.cpp:406 +msgid "" +"Fill pattern for top infill. This only affects the top visible layer, and " +"not its adjacent solid shells." +msgstr "" +"Vullingspatroon voor bovenste lagen. Dit heeft alleen invloed op de bovenste " +"zichtbare laag en niet de aangrenzende horizontale dichte shells." + +#: src/libslic3r/PrintConfig.cpp:414 src/libslic3r/PrintConfig.cpp:832 +#: src/libslic3r/PrintConfig.cpp:1998 +msgid "Rectilinear" +msgstr "Rechtlijnig" + +#: src/libslic3r/PrintConfig.cpp:415 src/libslic3r/PrintConfig.cpp:838 +msgid "Concentric" +msgstr "Concentrisch" + +#: src/libslic3r/PrintConfig.cpp:416 src/libslic3r/PrintConfig.cpp:842 +msgid "Hilbert Curve" +msgstr "Hilbert-kromme" + +#: src/libslic3r/PrintConfig.cpp:417 src/libslic3r/PrintConfig.cpp:843 +msgid "Archimedean Chords" +msgstr "Archimedes-spiraal" + +#: src/libslic3r/PrintConfig.cpp:418 src/libslic3r/PrintConfig.cpp:844 +msgid "Octagram Spiral" +msgstr "Octagramspiraal" + +#: src/libslic3r/PrintConfig.cpp:424 +msgid "Bottom fill pattern" +msgstr "Vulpatroon voor onderzijde" + +#: src/libslic3r/PrintConfig.cpp:426 +msgid "" +"Fill pattern for bottom infill. This only affects the bottom external " +"visible layer, and not its adjacent solid shells." +msgstr "" +"Vulpatroon voor de bodemlaag. Dit heeft alleen invloed op de onderste " +"zichtbare laag en niet de aangrenzende horizontale dichte shells." + +#: src/libslic3r/PrintConfig.cpp:435 src/libslic3r/PrintConfig.cpp:446 +msgid "External perimeters" +msgstr "Buitenste perimeters" + +#: src/libslic3r/PrintConfig.cpp:437 +msgid "" +"Set this to a non-zero value to set a manual extrusion width for external " +"perimeters. If left zero, default extrusion width will be used if set, " +"otherwise 1.125 x nozzle diameter will be used. If expressed as percentage " +"(for example 200%), it will be computed over layer height." +msgstr "" +"Stel in op een niet-nulwaarde om te werken met manuele extrusiebreedte voor " +"de buitenste perimeters. Als die op 0 blijft staan, zal PrusaSlicer de " +"breedte instellen op 1,125x de nozzlediameter. Als dit is uitgedrukt als " +"percentage, wordt dit berekend over de laagdikte." + +#: src/libslic3r/PrintConfig.cpp:440 src/libslic3r/PrintConfig.cpp:549 +#: src/libslic3r/PrintConfig.cpp:871 src/libslic3r/PrintConfig.cpp:884 +#: src/libslic3r/PrintConfig.cpp:1004 src/libslic3r/PrintConfig.cpp:1030 +#: src/libslic3r/PrintConfig.cpp:1418 src/libslic3r/PrintConfig.cpp:1757 +#: src/libslic3r/PrintConfig.cpp:1872 src/libslic3r/PrintConfig.cpp:1940 +#: src/libslic3r/PrintConfig.cpp:2100 +msgid "mm or %" +msgstr "mm of %" + +#: src/libslic3r/PrintConfig.cpp:448 +msgid "" +"This separate setting will affect the speed of external perimeters (the " +"visible ones). If expressed as percentage (for example: 80%) it will be " +"calculated on the perimeters speed setting above. Set to zero for auto." +msgstr "" +"Deze instelling heeft effect op de snelheid van buitenste perimeters. Als " +"dit is uitgedrukt als percentage, wordt deze genomen over de snelheid van de " +"perimeters. Als dit ingesteld is op 0, wordt een automatische snelheid " +"genomen." + +#: src/libslic3r/PrintConfig.cpp:451 src/libslic3r/PrintConfig.cpp:893 +#: src/libslic3r/PrintConfig.cpp:1716 src/libslic3r/PrintConfig.cpp:1768 +#: src/libslic3r/PrintConfig.cpp:1984 src/libslic3r/PrintConfig.cpp:2113 +msgid "mm/s or %" +msgstr "mm/s of %" + +#: src/libslic3r/PrintConfig.cpp:458 +msgid "External perimeters first" +msgstr "Buitenste perimeters eerst" + +#: src/libslic3r/PrintConfig.cpp:460 +msgid "" +"Print contour perimeters from the outermost one to the innermost one instead " +"of the default inverse order." +msgstr "" +"Print de buitenste perimeters eerder dan de binnenste in plaats van andersom." + +#: src/libslic3r/PrintConfig.cpp:466 +msgid "Extra perimeters if needed" +msgstr "Extra perimeters indien nodig" + +#: src/libslic3r/PrintConfig.cpp:468 +#, no-c-format +msgid "" +"Add more perimeters when needed for avoiding gaps in sloping walls. Slic3r " +"keeps adding perimeters, until more than 70% of the loop immediately above " +"is supported." +msgstr "" +"Voeg meer perimeters toe als dat nodig is voor het voorkomen van gaten in " +"hellende wanden. PrusaSlicer blijft perimeters toevoegen tot meer dan 70% " +"van de perimeters daarboven direct is ondersteund." + +#: src/libslic3r/PrintConfig.cpp:478 +msgid "" +"The extruder to use (unless more specific extruder settings are specified). " +"This value overrides perimeter and infill extruders, but not the support " +"extruders." +msgstr "" +"De extruder die gebruikt wordt (behalve als meer specifieke " +"extruderinstellingen zijn aangegeven). Deze waarde overschrijft de " +"perimeter- en vullingsextruder, maar niet de supportextruders." + +#: src/libslic3r/PrintConfig.cpp:490 +msgid "" +"Set this to the vertical distance between your nozzle tip and (usually) the " +"X carriage rods. In other words, this is the height of the clearance " +"cylinder around your extruder, and it represents the maximum depth the " +"extruder can peek before colliding with other printed objects." +msgstr "" +"Stel dit in als verticale afstand tussen de nozzlepunt en de geleidestangen " +"van de X-as. In andere woorden; dit is de hoogte van de vrijloopcilinder " +"rond de extruder en geeft de maximale diepte weer die de extruder kan halen " +"zonder te botsen met eerder geprinte objecten." + +#: src/libslic3r/PrintConfig.cpp:501 +msgid "" +"Set this to the clearance radius around your extruder. If the extruder is " +"not centered, choose the largest value for safety. This setting is used to " +"check for collisions and to display the graphical preview in the plater." +msgstr "" +"Stel dit in als vrijloopradius rond de extruder. Kies de hoogste waarde " +"(voor de veiligheid) als de extruder niet is gecentreerd. Deze instelling " +"wordt gebruikt om te controleren op botsingen en om te tonen in de " +"modelweergave." + +#: src/libslic3r/PrintConfig.cpp:511 +msgid "Extruder Color" +msgstr "Extruderkleur" + +#: src/libslic3r/PrintConfig.cpp:512 src/libslic3r/PrintConfig.cpp:573 +msgid "This is only used in the Slic3r interface as a visual help." +msgstr "" +"Dit wordt alleen gebruikt in de PrusaSlicer-interface als een visueel " +"hulpmiddel." + +#: src/libslic3r/PrintConfig.cpp:518 +msgid "Extruder offset" +msgstr "Extruder-offset" + +#: src/libslic3r/PrintConfig.cpp:519 +msgid "" +"If your firmware doesn't handle the extruder displacement you need the G-" +"code to take it into account. This option lets you specify the displacement " +"of each extruder with respect to the first one. It expects positive " +"coordinates (they will be subtracted from the XY coordinate)." +msgstr "" +"Als uw firmware extrudercompensatie niet toestaat, kan daar rekening mee " +"gehouden worden in de G-code. Met deze optie kunt u de compensatie per " +"extruder specificeren op basis van de eerste extruder. Hiervoor zijn " +"positieve coördinaten nodig (die worden van de X- en Y-coördinaten " +"afgetrokken)." + +#: src/libslic3r/PrintConfig.cpp:528 +msgid "Extrusion axis" +msgstr "Extrusie-as" + +#: src/libslic3r/PrintConfig.cpp:529 +msgid "" +"Use this option to set the axis letter associated to your printer's extruder " +"(usually E but some printers use A)." +msgstr "" +"Gebruik deze optie om de naam van de as van de extruder in te stellen " +"(normaal gesproken E, maar soms A)." + +#: src/libslic3r/PrintConfig.cpp:534 +msgid "Extrusion multiplier" +msgstr "Extrusie vermenigvuldigingsfactor" + +#: src/libslic3r/PrintConfig.cpp:535 +msgid "" +"This factor changes the amount of flow proportionally. You may need to tweak " +"this setting to get nice surface finish and correct single wall widths. " +"Usual values are between 0.9 and 1.1. If you think you need to change this " +"more, check filament diameter and your firmware E steps." +msgstr "" +"Deze factor verandert het debiet proportioneel. U kunt deze fijnstellen om " +"een mooi oppervlak en dunne wanden te krijgen. Waarden liggen normaal tussen " +"0.9 en 1.1. Check eventueel de filamentdiameter en de extruderstappen (uit " +"de firmware) als u denkt dat dit aangepast moet worden." + +#: src/libslic3r/PrintConfig.cpp:543 +msgid "Default extrusion width" +msgstr "Standaard extrusiebreedte" + +#: src/libslic3r/PrintConfig.cpp:545 +msgid "" +"Set this to a non-zero value to allow a manual extrusion width. If left to " +"zero, Slic3r derives extrusion widths from the nozzle diameter (see the " +"tooltips for perimeter extrusion width, infill extrusion width etc). If " +"expressed as percentage (for example: 230%), it will be computed over layer " +"height." +msgstr "" +"Stel in op een niet-nulwaarde om te werken met manuele extrusiebreedte. Als " +"die op 0 blijft staan, zal PrusaSlicer de extrusiebreedte aanpassen op basis " +"van de nozzlediameter. Als dit is uitgedrukt als percentage, wordt dit " +"berekend over de laagdikte." + +#: src/libslic3r/PrintConfig.cpp:555 +msgid "Keep fan always on" +msgstr "Laat ventilator altijd aan" + +#: src/libslic3r/PrintConfig.cpp:556 +msgid "" +"If this is enabled, fan will never be disabled and will be kept running at " +"least at its minimum speed. Useful for PLA, harmful for ABS." +msgstr "" +"Als dit is ingeschakeld zal de ventilator nooit uitgezet worden, maar " +"tenminste de ingestelde minimale snelheid aanhouden." + +#: src/libslic3r/PrintConfig.cpp:561 +msgid "Enable fan if layer print time is below" +msgstr "Schakel de ventilator in bij een printtijd korter dan" + +#: src/libslic3r/PrintConfig.cpp:562 +msgid "" +"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." +msgstr "" +"Als de printtijd voor een laag onder dit aantal seconden komt, zal de " +"ventilator aangezet worden en wordt de snelheid berekend door te " +"interpoleren tussen de minimale en maximale snelheid." + +#: src/libslic3r/PrintConfig.cpp:564 src/libslic3r/PrintConfig.cpp:1703 +msgid "approximate seconds" +msgstr "geschat aantal seconden" + +#: src/libslic3r/PrintConfig.cpp:578 +msgid "Filament notes" +msgstr "Filamentopmerkingen" + +#: src/libslic3r/PrintConfig.cpp:579 +msgid "You can put your notes regarding the filament here." +msgstr "Hier kunt u opmerkingen over het filament plaatsen." + +#: src/libslic3r/PrintConfig.cpp:587 src/libslic3r/PrintConfig.cpp:1244 +msgid "Max volumetric speed" +msgstr "Maximale volumetrische snelheid" + +#: src/libslic3r/PrintConfig.cpp:588 +msgid "" +"Maximum volumetric speed allowed for this filament. Limits the maximum " +"volumetric speed of a print to the minimum of print and filament volumetric " +"speed. Set to zero for no limit." +msgstr "" +"Maximale volumetrische snelheid is toegestaan voor dit filament. Dit beperkt " +"de maximale volumetrische snelheid van een print tot het minimum van de " +"maximale volumetrische snelheid van de print en het filament. Als dit " +"ingesteld is op 0, geldt er geen limiet." + +#: src/libslic3r/PrintConfig.cpp:597 +msgid "Loading speed" +msgstr "Laadsnelheid" + +#: src/libslic3r/PrintConfig.cpp:598 +msgid "Speed used for loading the filament on the wipe tower." +msgstr "Snelheid die gebruikt wordt voor het afveegblok." + +#: src/libslic3r/PrintConfig.cpp:605 +msgid "Loading speed at the start" +msgstr "Laadsnelheid aan het begin" + +#: src/libslic3r/PrintConfig.cpp:606 +msgid "Speed used at the very beginning of loading phase." +msgstr "Snelheid die gebruikt wordt aan het begin van de laadfase." + +#: src/libslic3r/PrintConfig.cpp:613 +msgid "Unloading speed" +msgstr "Ontlaadsnelheid" + +#: src/libslic3r/PrintConfig.cpp:614 +msgid "" +"Speed used for unloading the filament on the wipe tower (does not affect " +"initial part of unloading just after ramming)." +msgstr "" +"Snelheid die gebruikt wordt voor het ontladen van het afveegblok (heeft geen " +"effect op het initiële onderdeel van het ontladen direct na de ramming)." + +#: src/libslic3r/PrintConfig.cpp:622 +msgid "Unloading speed at the start" +msgstr "Ontlaadsnelheid in het begin" + +#: src/libslic3r/PrintConfig.cpp:623 +msgid "" +"Speed used for unloading the tip of the filament immediately after ramming." +msgstr "" +"Snelheid die gebruikt wordt voor het ontladen van het filament direct na de " +"ramming." + +#: src/libslic3r/PrintConfig.cpp:630 +msgid "Delay after unloading" +msgstr "Vertraging na het ontladen" + +#: src/libslic3r/PrintConfig.cpp:631 +msgid "" +"Time to wait after the filament is unloaded. May help to get reliable " +"toolchanges with flexible materials that may need more time to shrink to " +"original dimensions." +msgstr "" +"Wachttijd voor het ontladen van het filament. Dit kan helpen om betrouwbare " +"toolwisselingen te krijgen met flexibele materialen die meer tijd nodig " +"hebben om te krimpen naar de originele afmetingen." + +#: src/libslic3r/PrintConfig.cpp:640 +msgid "Number of cooling moves" +msgstr "Aantal koelbewegingen" + +#: src/libslic3r/PrintConfig.cpp:641 +msgid "" +"Filament is cooled by being moved back and forth in the cooling tubes. " +"Specify desired number of these moves." +msgstr "" +"Het filament wordt gekoeld tijdens het terug en voorwaarts bewegen in de " +"koelbuis. Specificeer het benodigd aantal bewegingen." + +#: src/libslic3r/PrintConfig.cpp:649 +msgid "Speed of the first cooling move" +msgstr "Snelheid voor de eerste koelbeweging" + +#: src/libslic3r/PrintConfig.cpp:650 +msgid "Cooling moves are gradually accelerating beginning at this speed." +msgstr "" +"Koelbewegingen worden gelijkmatig versneld, beginnend vanaf deze snelheid." + +#: src/libslic3r/PrintConfig.cpp:657 +msgid "Minimal purge on wipe tower" +msgstr "Minimale afstand op afveegblok" + +#: src/libslic3r/PrintConfig.cpp:658 +msgid "" +"After a tool change, the exact position of the newly loaded filament inside " +"the nozzle may not be known, and the filament pressure is likely not yet " +"stable. Before purging the print head into an infill or a sacrificial " +"object, Slic3r will always prime this amount of material into the wipe tower " +"to produce successive infill or sacrificial object extrusions reliably." +msgstr "" +"Na een toolwissel is de exacte locatie van het geladen filament onbekend en " +"zal de druk op het filament niet stabiel zijn. Voor het afvegen van de " +"printkop in de vulling zal PrusaSlicer eerst deze hoeveelheid materiaal " +"afvegen aan het afveegblok om vervolgens de vulling of overige objecten goed " +"te kunnen printen." + +#: src/libslic3r/PrintConfig.cpp:662 +msgid "mm³" +msgstr "mm³" + +#: src/libslic3r/PrintConfig.cpp:668 +msgid "Speed of the last cooling move" +msgstr "Snelheid voor de laatste koelbeweging" + +#: src/libslic3r/PrintConfig.cpp:669 +msgid "Cooling moves are gradually accelerating towards this speed." +msgstr "Koelbewegingen versnellen gelijkmatig tot aan deze snelheid." + +#: src/libslic3r/PrintConfig.cpp:676 +msgid "Filament load time" +msgstr "Laadtijd van het filament" + +#: src/libslic3r/PrintConfig.cpp:677 +msgid "" +"Time for the printer firmware (or the Multi Material Unit 2.0) to load a new " +"filament during a tool change (when executing the T code). This time is " +"added to the total print time by the G-code time estimator." +msgstr "" +"Tijd voor de printerfirmware (of de MMU 2.0) om nieuw filament te laden " +"tijdens een toolwissel (tijdens het uitvoeren van de T-code). Deze tijd " +"wordt toegevoegd aan de totale printtijd in de tijdsschatting." + +#: src/libslic3r/PrintConfig.cpp:684 +msgid "Ramming parameters" +msgstr "Rammingparameters" + +#: src/libslic3r/PrintConfig.cpp:685 +msgid "" +"This string is edited by RammingDialog and contains ramming specific " +"parameters." +msgstr "" +"Deze frase is bewerkt door het Rammingdialoog en bevat parameters voor de " +"ramming." + +#: src/libslic3r/PrintConfig.cpp:691 +msgid "Filament unload time" +msgstr "Ontlaadtijd voor filament" + +#: src/libslic3r/PrintConfig.cpp:692 +msgid "" +"Time for the printer firmware (or the Multi Material Unit 2.0) to unload a " +"filament during a tool change (when executing the T code). This time is " +"added to the total print time by the G-code time estimator." +msgstr "" +"Tijd voor de printerfirmware (of de MMU 2.0) om filament te ontladen tijdens " +"een toolwissel (tijdens het uitvoeren van de T-code). Deze tijd wordt " +"toegevoegd aan de totale printtijd in de tijdsschatting." + +#: src/libslic3r/PrintConfig.cpp:700 +msgid "" +"Enter your filament diameter here. Good precision is required, so use a " +"caliper and do multiple measurements along the filament, then compute the " +"average." +msgstr "" +"Stel hier de filamentdiameter in. De juiste precisie is benodigd. Gebruik " +"daarom een schuifmaat en doe meerdere metingen over het hele filament. " +"Bereken vervolgens het gemiddelde." + +#: src/libslic3r/PrintConfig.cpp:707 src/libslic3r/PrintConfig.cpp:2471 +#: src/libslic3r/PrintConfig.cpp:2472 +msgid "Density" +msgstr "Dichtheid" + +#: src/libslic3r/PrintConfig.cpp:708 +msgid "" +"Enter your filament density here. This is only for statistical information. " +"A decent way is to weigh a known length of filament and compute the ratio of " +"the length to volume. Better is to calculate the volume directly through " +"displacement." +msgstr "" +"Stel hier de dichtheid van het filament in. Dit is slechts voor de " +"statistieken. Formule voor dichtheid: dichtheid[g/cm³] = massa[g] / " +"volume[cm³]. Formule voor volume: volume[cm³] = 1000 * (diameter[mm])² * π / " +"4 * lengte[mm]. Bepaal het gewicht door te wegen en het volume door te meten." + +#: src/libslic3r/PrintConfig.cpp:711 +msgid "g/cm³" +msgstr "g/cm³" + +#: src/libslic3r/PrintConfig.cpp:716 +msgid "Filament type" +msgstr "Filamenttype" + +#: src/libslic3r/PrintConfig.cpp:717 +msgid "The filament material type for use in custom G-codes." +msgstr "Het filamenttype voor het gebruik van de custom G-codes." + +#: src/libslic3r/PrintConfig.cpp:743 +msgid "Soluble material" +msgstr "Oplosbaar materiaal" + +#: src/libslic3r/PrintConfig.cpp:744 +msgid "Soluble material is most likely used for a soluble support." +msgstr "Oplosbaar materiaal wordt vaak gebruikt voor oplosbaar support." + +#: src/libslic3r/PrintConfig.cpp:750 +msgid "" +"Enter your filament cost per kg here. This is only for statistical " +"information." +msgstr "" +"Voer hier de filamentkosten per kilogram in. Dit is alleen voor statistische " +"informatie." + +#: src/libslic3r/PrintConfig.cpp:751 +msgid "money/kg" +msgstr "€/kg" + +#: src/libslic3r/PrintConfig.cpp:760 src/libslic3r/PrintConfig.cpp:2555 +msgid "(Unknown)" +msgstr "(Onbekend)" + +#: src/libslic3r/PrintConfig.cpp:764 +msgid "Fill angle" +msgstr "Vullingshoek" + +#: src/libslic3r/PrintConfig.cpp:766 +msgid "" +"Default base angle for infill orientation. Cross-hatching will be applied to " +"this. Bridges will be infilled using the best direction Slic3r can detect, " +"so this setting does not affect them." +msgstr "" +"Standaard basishoek voor de vullingsrichting. Hier wordt kruislings overheen " +"geprint. Bruggen worden geprint met de optimale richting. Deze instelling " +"zal die richting niet beïnvloeden." + +#: src/libslic3r/PrintConfig.cpp:778 +msgid "Fill density" +msgstr "Vullingsdichtheid" + +#: src/libslic3r/PrintConfig.cpp:780 +msgid "Density of internal infill, expressed in the range 0% - 100%." +msgstr "" +"Dichtheid van inwendige vulling, uitgedrukt in een percentage (0 - 100%)" + +#: src/libslic3r/PrintConfig.cpp:815 +msgid "Fill pattern" +msgstr "Vullingspatroon" + +#: src/libslic3r/PrintConfig.cpp:817 +msgid "Fill pattern for general low-density infill." +msgstr "Vulpatroon voor algemene lagere-dichtheidsvulling." + +#: src/libslic3r/PrintConfig.cpp:833 +msgid "Grid" +msgstr "Raster" + +#: src/libslic3r/PrintConfig.cpp:834 +msgid "Triangles" +msgstr "Driehoeken" + +#: src/libslic3r/PrintConfig.cpp:835 +msgid "Stars" +msgstr "Sterren" + +#: src/libslic3r/PrintConfig.cpp:836 +msgid "Cubic" +msgstr "Kubisch" + +#: src/libslic3r/PrintConfig.cpp:837 +msgid "Line" +msgstr "Lijnen" + +#: src/libslic3r/PrintConfig.cpp:839 src/libslic3r/PrintConfig.cpp:2000 +msgid "Honeycomb" +msgstr "Honingraat" + +#: src/libslic3r/PrintConfig.cpp:840 +msgid "3D Honeycomb" +msgstr "3D-honingraat" + +#: src/libslic3r/PrintConfig.cpp:841 +msgid "Gyroid" +msgstr "Gyroïde" + +#: src/libslic3r/PrintConfig.cpp:848 src/libslic3r/PrintConfig.cpp:857 +#: src/libslic3r/PrintConfig.cpp:865 src/libslic3r/PrintConfig.cpp:899 +msgid "First layer" +msgstr "Eerste laag" + +#: src/libslic3r/PrintConfig.cpp:849 +msgid "" +"This is the acceleration your printer will use for first layer. Set zero to " +"disable acceleration control for first layer." +msgstr "" +"Deze acceleratie zal uw printer gebruiken voor de eerste laag. Als dit " +"ingesteld is op 0, wordt de standaard acceleratie gebruikt." + +#: src/libslic3r/PrintConfig.cpp:858 +msgid "" +"Heated build plate temperature for the first layer. Set this to zero to " +"disable bed temperature control commands in the output." +msgstr "" +"Temperatuur van het verwarmd bed voor de eerste laag. Als dit ingesteld is " +"op 0, worden bedtemperatuur-commando's weggelaten in de output." + +#: src/libslic3r/PrintConfig.cpp:867 +msgid "" +"Set this to a non-zero value to set a manual extrusion width for first " +"layer. You can use this to force fatter extrudates for better adhesion. If " +"expressed as percentage (for example 120%) it will be computed over first " +"layer height. If set to zero, it will use the default extrusion width." +msgstr "" +"Stel in op een niet-nulwaarde om te werken met manuele extrusiebreedte voor " +"de eerste laag. Dit kan gebruikt worden om dikkere lagen voor een betere " +"hechting aan het bed te gebruiken. Als dit is uitgedrukt als percentage, " +"wordt dit berekend over de laagdikte van de eerste laag. Als dit is " +"ingesteld op 0, wordt de standaard extrusiebreedte gebruikt." + +#: src/libslic3r/PrintConfig.cpp:880 +msgid "" +"When printing with very low layer heights, you might still want to print a " +"thicker bottom layer to improve adhesion and tolerance for non perfect build " +"plates. This can be expressed as an absolute value or as a percentage (for " +"example: 150%) over the default layer height." +msgstr "" +"Als geprint wordt met hele kleine laagdiktes, moet de eerste laag iets " +"dikker geprint worden voor een betere bedhechting en tolerantie voor " +"imperfecte printplatformen. Dit kan uitgedrukt worden als een absolute " +"waarde of als percentage (bijvoorbeeld 150%) over de standaard laaghoogte." + +#: src/libslic3r/PrintConfig.cpp:889 +msgid "First layer speed" +msgstr "Snelheid eerste laag" + +#: src/libslic3r/PrintConfig.cpp:890 +msgid "" +"If expressed as absolute value in mm/s, this speed will be applied to all " +"the print moves of the first layer, regardless of their type. If expressed " +"as a percentage (for example: 40%) it will scale the default speeds." +msgstr "" +"Als dit uitgedrukt wordt als absolute waarde (in mm/s), zal deze snelheid " +"toegepast worden bij alle printbewegingen van de eerste laag, onafhankelijk " +"van het type. Als dit is uitgedrukt als percentage, wordt dit berekend over " +"de standaardsnelheid." + +#: src/libslic3r/PrintConfig.cpp:900 +msgid "" +"Extruder temperature for first layer. If you want to control temperature " +"manually during print, set this to zero to disable temperature control " +"commands in the output file." +msgstr "" +"Printtemperatuur voor de eerste laag. Als dit ingesteld is op 0, worden " +"extrudertemperatuur-commando's weggelaten in de output." + +#: src/libslic3r/PrintConfig.cpp:909 +msgid "" +"Speed for filling small gaps using short zigzag moves. Keep this reasonably " +"low to avoid too much shaking and resonance issues. Set zero to disable gaps " +"filling." +msgstr "" +"Printsnelheid voor het gatvullen waarbij zigzag-bewegingen worden gebruikt. " +"Houdt dit laag om schudden te voorkomen (wat resulteert in " +"resonantieproblemen). Als dit is ingesteld op 0, worden gaten niet gevuld." + +#: src/libslic3r/PrintConfig.cpp:917 +msgid "Verbose G-code" +msgstr "Opmerkingen in G-code" + +#: src/libslic3r/PrintConfig.cpp:918 +msgid "" +"Enable this to get a commented G-code file, with each line explained by a " +"descriptive text. If you print from SD card, the additional weight of the " +"file could make your firmware slow down." +msgstr "" +"Sta dit toe om een G-code met opmerkingen te genereren. Bij elke lijn wordt " +"een opmerking geplaatst. Als u print vanaf een SD-kaart, kan de extra " +"grootte van het bestand de firmware vertragen." + +#: src/libslic3r/PrintConfig.cpp:925 +msgid "G-code flavor" +msgstr "G-code-variant" + +#: src/libslic3r/PrintConfig.cpp:926 +msgid "" +"Some G/M-code commands, including temperature control and others, are not " +"universal. Set this option to your printer's firmware to get a compatible " +"output. The \"No extrusion\" flavor prevents PrusaSlicer from exporting any " +"extrusion value at all." +msgstr "" +"Sommige G- en M-commando's zijn niet universeel. Stel deze optie in om een " +"compatibele uitvoer te krijgen voor uw printer. De 'Geen extrusie'-" +"instelling kan gebruikt worden om te printen zonder materiaal te extruderen." + +#: src/libslic3r/PrintConfig.cpp:949 +msgid "No extrusion" +msgstr "Geen extrusie" + +#: src/libslic3r/PrintConfig.cpp:954 +msgid "Label objects" +msgstr "Label objecten" + +#: src/libslic3r/PrintConfig.cpp:955 +msgid "" +"Enable this to add comments into the G-Code labeling print moves with what " +"object they belong to, which is useful for the Octoprint CancelObject " +"plugin. This settings is NOT compatible with Single Extruder Multi Material " +"setup and Wipe into Object / Wipe into Infill." +msgstr "" +"Schakel dit in om opmerkingen in de G-code toe te voegen voor bewegingen die " +"behoren tot een object. Dit is handig voor de OctoPrint CancelObject-plugin. " +"Deze instelling is NIET compatibel met een multi-materialsetup met één " +"extruder en 'Afvegen in object' en 'Afvegen in vulling'." + +#: src/libslic3r/PrintConfig.cpp:962 +msgid "High extruder current on filament swap" +msgstr "Hoge stroomsterkte bij extruder voor filamentwissel" + +#: src/libslic3r/PrintConfig.cpp:963 +msgid "" +"It may be beneficial to increase the extruder motor current during the " +"filament exchange sequence to allow for rapid ramming feed rates and to " +"overcome resistance when loading a filament with an ugly shaped tip." +msgstr "" +"Het kan nuttig zijn om de stroomsterkte van de extrudermotor te verhogen " +"tijdens het uitvoeren van de filamentwisseling om snelle ramming mogelijk te " +"maken en om weerstand te overwinnen tijdens het laden van filament met een " +"misvormde kop." + +#: src/libslic3r/PrintConfig.cpp:971 +msgid "" +"This is the acceleration your printer will use for infill. Set zero to " +"disable acceleration control for infill." +msgstr "" +"Deze acceleratie zal uw printer gebruiken voor de vulling. Als dit is " +"ingesteld op 0, wordt de acceleratiecontrole uitgeschakeld." + +#: src/libslic3r/PrintConfig.cpp:979 +msgid "Combine infill every" +msgstr "Combineer vulling elke" + +#: src/libslic3r/PrintConfig.cpp:981 +msgid "" +"This feature allows to combine infill and speed up your print by extruding " +"thicker infill layers while preserving thin perimeters, thus accuracy." +msgstr "" +"Deze optie staat vullingslagen combineren toe voor een snellere print door " +"de vullingslagen stapsgewijs dikker te maken, terwijl de laagdikte van " +"perimeters behouden wordt." + +#: src/libslic3r/PrintConfig.cpp:984 +msgid "Combine infill every n layers" +msgstr "Combineer vulling elke n lagen" + +#: src/libslic3r/PrintConfig.cpp:990 +msgid "Infill extruder" +msgstr "Vullingsextruder" + +#: src/libslic3r/PrintConfig.cpp:992 +msgid "The extruder to use when printing infill." +msgstr "De extruder die gebruikt wordt voor het printen van de vulling." + +#: src/libslic3r/PrintConfig.cpp:1000 +msgid "" +"Set this to a non-zero value to set a manual extrusion width for infill. If " +"left zero, default extrusion width will be used if set, otherwise 1.125 x " +"nozzle diameter will be used. You may want to use fatter extrudates to speed " +"up the infill and make your parts stronger. If expressed as percentage (for " +"example 90%) it will be computed over layer height." +msgstr "" +"Stel dit in op een niet-nulwaarde om handmatige extrusiebreedte in te " +"stellen. Als die op 0 blijft staan, zal PrusaSlicer de breedte instellen op " +"1,125x de nozzlediameter. Mogelijk wilt u de vulling wat sneller laten " +"printen en het onderdeel sterker maken met deze optie. Als dit is uitgedrukt " +"als percentage, wordt dit berekend over de laagdikte." + +#: src/libslic3r/PrintConfig.cpp:1010 +msgid "Infill before perimeters" +msgstr "Vulling vóór perimeters" + +#: src/libslic3r/PrintConfig.cpp:1011 +msgid "" +"This option will switch the print order of perimeters and infill, making the " +"latter first." +msgstr "" +"Deze optie verandert de printvolgorde van perimeters en vulling; de " +"laatstgenoemde eerst." + +#: src/libslic3r/PrintConfig.cpp:1016 +msgid "Only infill where needed" +msgstr "Alleen vulling waar nodig" + +#: src/libslic3r/PrintConfig.cpp:1018 +msgid "" +"This option will limit infill to the areas actually needed for supporting " +"ceilings (it will act as internal support material). If enabled, slows down " +"the G-code generation due to the multiple checks involved." +msgstr "" +"Deze optie beperkt de vulling tot gebieden waar dit nodig is voor " +"ondersteuning van bovenvlakken (het fungeert als inwendig support). Let op: " +"deze optie vertraagt de G-code-generatie." + +#: src/libslic3r/PrintConfig.cpp:1025 +msgid "Infill/perimeters overlap" +msgstr "Overlapping van vulling/perimeters" + +#: src/libslic3r/PrintConfig.cpp:1027 +msgid "" +"This setting applies an additional overlap between infill and perimeters for " +"better bonding. Theoretically this shouldn't be needed, but backlash might " +"cause gaps. If expressed as percentage (example: 15%) it is calculated over " +"perimeter extrusion width." +msgstr "" +"Deze instelling zorgt voor een extra overlapping tussen vulling en " +"perimeters voor een betere hechting. Theoretisch gezien is dit niet nodig, " +"maar terugslag kan zorgen voor gaten. Als dit is uitgedrukt als percentage, " +"wordt dit berekend over de extrusiebreedte van de perimeters." + +#: src/libslic3r/PrintConfig.cpp:1038 +msgid "Speed for printing the internal fill. Set to zero for auto." +msgstr "" +"Printsnelheid voor vulling. Als dit ingesteld is op 0, wordt de snelheid " +"automatisch berekend." + +#: src/libslic3r/PrintConfig.cpp:1046 +msgid "Inherits profile" +msgstr "Afgeleid profiel" + +#: src/libslic3r/PrintConfig.cpp:1047 +msgid "Name of the profile, from which this profile inherits." +msgstr "Profielnaam waar profiel op is gebaseerd." + +#: src/libslic3r/PrintConfig.cpp:1060 +msgid "Interface shells" +msgstr "Interfaceshells" + +#: src/libslic3r/PrintConfig.cpp:1061 +msgid "" +"Force the generation of solid shells between adjacent materials/volumes. " +"Useful for multi-extruder prints with translucent materials or manual " +"soluble support material." +msgstr "" +"Forceer de generatie van dichte shells tussen aangrenzende materialen/" +"volumes. Dit is handig voor multi-extruderprints met transparante materialen " +"of handmatig oplosbaar support." + +#: src/libslic3r/PrintConfig.cpp:1070 +msgid "" +"This custom code is inserted at every layer change, right after the Z move " +"and before the extruder moves to the first layer point. Note that you can " +"use placeholder variables for all Slic3r settings as well as [layer_num] and " +"[layer_z]." +msgstr "" +"Deze custom code wordt ingevoegd bij elke laagwisseling, direct na de Z-" +"beweging en voor de extruder naar het volgende punt beweegt. Hier kunt u " +"variabelen gebruiken voor alle instellingen zoals 'layer_num' en 'layer_z'." + +#: src/libslic3r/PrintConfig.cpp:1081 +msgid "Supports remaining times" +msgstr "Ondersteunt resterende tijd" + +#: src/libslic3r/PrintConfig.cpp:1082 +msgid "" +"Emit M73 P[percent printed] R[remaining time in minutes] at 1 minute " +"intervals into the G-code to let the firmware show accurate remaining time. " +"As of now only the Prusa i3 MK3 firmware recognizes M73. Also the i3 MK3 " +"firmware supports M73 Qxx Sxx for the silent mode." +msgstr "" +"Zet M73 P[geprint percentage] R[resterende tijd in minuten] na elke minuut " +"in de G-code om de firmware de exacte resterende tijd te laten weten. Vanaf " +"nu herkent de Prusa i3 MK3 de M73-commando's. Ook ondersteunt de i3 MK3 " +"firmware M73 Qxx Sxx voor de stille modus." + +#: src/libslic3r/PrintConfig.cpp:1090 +msgid "Supports stealth mode" +msgstr "Ondersteunt stille modus" + +#: src/libslic3r/PrintConfig.cpp:1091 +msgid "The firmware supports stealth mode" +msgstr "De firmware ondersteunt stille modus" + +#: src/libslic3r/PrintConfig.cpp:1115 +msgid "Maximum feedrate X" +msgstr "Maximale voedingssnelheid van de X-as" + +#: src/libslic3r/PrintConfig.cpp:1116 +msgid "Maximum feedrate Y" +msgstr "Maximale voedingssnelheid van de Y-as" + +#: src/libslic3r/PrintConfig.cpp:1117 +msgid "Maximum feedrate Z" +msgstr "Maximale voedingssnelheid van de Z-as" + +#: src/libslic3r/PrintConfig.cpp:1118 +msgid "Maximum feedrate E" +msgstr "Maximale extrusievoedingssnelheid" + +#: src/libslic3r/PrintConfig.cpp:1121 +msgid "Maximum feedrate of the X axis" +msgstr "Maximale voedingssnelheid van de X-as" + +#: src/libslic3r/PrintConfig.cpp:1122 +msgid "Maximum feedrate of the Y axis" +msgstr "Maximale voedingssnelheid van de Y-as" + +#: src/libslic3r/PrintConfig.cpp:1123 +msgid "Maximum feedrate of the Z axis" +msgstr "Maximale voedingssnelheid van de Z-as" + +#: src/libslic3r/PrintConfig.cpp:1124 +msgid "Maximum feedrate of the E axis" +msgstr "Maximale extrusievoedingssnelheid" + +#: src/libslic3r/PrintConfig.cpp:1133 +msgid "Maximum acceleration X" +msgstr "Maximale acceleratie X" + +#: src/libslic3r/PrintConfig.cpp:1134 +msgid "Maximum acceleration Y" +msgstr "Maximale acceleratie Y" + +#: src/libslic3r/PrintConfig.cpp:1135 +msgid "Maximum acceleration Z" +msgstr "Maximale acceleratie Z" + +#: src/libslic3r/PrintConfig.cpp:1136 +msgid "Maximum acceleration E" +msgstr "Maximale acceleratie E" + +#: src/libslic3r/PrintConfig.cpp:1139 +msgid "Maximum acceleration of the X axis" +msgstr "Maximale acceleratie van de X-as" + +#: src/libslic3r/PrintConfig.cpp:1140 +msgid "Maximum acceleration of the Y axis" +msgstr "Maximale acceleratie van de Y-as" + +#: src/libslic3r/PrintConfig.cpp:1141 +msgid "Maximum acceleration of the Z axis" +msgstr "Maximale acceleratie van de Z-as" + +#: src/libslic3r/PrintConfig.cpp:1142 +msgid "Maximum acceleration of the E axis" +msgstr "Maximale extrusie-acceleratie" + +#: src/libslic3r/PrintConfig.cpp:1151 +msgid "Maximum jerk X" +msgstr "Maximale ruk X" + +#: src/libslic3r/PrintConfig.cpp:1152 +msgid "Maximum jerk Y" +msgstr "Maximale ruk Y" + +#: src/libslic3r/PrintConfig.cpp:1153 +msgid "Maximum jerk Z" +msgstr "Maximale ruk Z" + +#: src/libslic3r/PrintConfig.cpp:1154 +msgid "Maximum jerk E" +msgstr "Maximale ruk E" + +#: src/libslic3r/PrintConfig.cpp:1157 +msgid "Maximum jerk of the X axis" +msgstr "Maximale ruk van de X-as" + +#: src/libslic3r/PrintConfig.cpp:1158 +msgid "Maximum jerk of the Y axis" +msgstr "Maximale ruk van de Y-as" + +#: src/libslic3r/PrintConfig.cpp:1159 +msgid "Maximum jerk of the Z axis" +msgstr "Maximale ruk van de Z-as" + +#: src/libslic3r/PrintConfig.cpp:1160 +msgid "Maximum jerk of the E axis" +msgstr "Maximale extrusie-ruk" + +#: src/libslic3r/PrintConfig.cpp:1171 +msgid "Minimum feedrate when extruding" +msgstr "Minimale voedingssnelheid tijdens extruderen" + +#: src/libslic3r/PrintConfig.cpp:1173 +msgid "Minimum feedrate when extruding (M205 S)" +msgstr "Minimale voedingssnelheid tijdens extruderen (M205 S)" + +#: src/libslic3r/PrintConfig.cpp:1182 +msgid "Minimum travel feedrate" +msgstr "Minimale voedingssnelheid voor bewegingen" + +#: src/libslic3r/PrintConfig.cpp:1184 +msgid "Minimum travel feedrate (M205 T)" +msgstr "Minimale voedingssnelheid voor bewegingen (M205 T)" + +#: src/libslic3r/PrintConfig.cpp:1193 +msgid "Maximum acceleration when extruding" +msgstr "Maximale acceleratie tijdens extruderen" + +#: src/libslic3r/PrintConfig.cpp:1195 +msgid "Maximum acceleration when extruding (M204 S)" +msgstr "Maximale acceleratie tijdens extruderen (M204 S)" + +#: src/libslic3r/PrintConfig.cpp:1204 +msgid "Maximum acceleration when retracting" +msgstr "Maximale acceleratie tijdens retracten" + +#: src/libslic3r/PrintConfig.cpp:1206 +msgid "Maximum acceleration when retracting (M204 T)" +msgstr "Maximale acceleratie tijdens retracten (M204 T)" + +#: src/libslic3r/PrintConfig.cpp:1214 src/libslic3r/PrintConfig.cpp:1223 +msgid "Max" +msgstr "Max" + +#: src/libslic3r/PrintConfig.cpp:1215 +msgid "This setting represents the maximum speed of your fan." +msgstr "Deze instelling gaat over de maximale snelheid van uw ventilator." + +#: src/libslic3r/PrintConfig.cpp:1224 +#, no-c-format +msgid "" +"This is the highest printable layer height for this extruder, used to cap " +"the variable layer height and support layer height. Maximum recommended " +"layer height is 75% of the extrusion width to achieve reasonable inter-layer " +"adhesion. If set to 0, layer height is limited to 75% of the nozzle diameter." +msgstr "" +"Dit is de hoogst printbare laagdikte voor deze extruder en wordt gebruikt " +"voor de variabele laagdikte en supportlaagdikte. De maximaal aanbevolen " +"laagdikte is 75% van de extrusiebreedte om nog een redelijke laaghechting te " +"krijgen. Als dit ingesteld is op 0, wordt de waarde op 75% van de " +"nozzlediameter genomen." + +#: src/libslic3r/PrintConfig.cpp:1234 +msgid "Max print speed" +msgstr "Maximale printsnelheid" + +#: src/libslic3r/PrintConfig.cpp:1235 +msgid "" +"When setting other speed settings to 0 Slic3r will autocalculate the optimal " +"speed in order to keep constant extruder pressure. This experimental setting " +"is used to set the highest print speed you want to allow." +msgstr "" +"Als de alle snelheidsinstellingen op 0 staan, berekent PrusaSlicer " +"automatisch de optimale snelheid voor een constante extrusiedruk. Deze " +"experimentele instelling wordt gebruikt voor de hoogste printsnelheid die u " +"toestaat." + +#: src/libslic3r/PrintConfig.cpp:1245 +msgid "" +"This experimental setting is used to set the maximum volumetric speed your " +"extruder supports." +msgstr "" +"Deze experimentele instelling wordt gebruikt voor de maximale volumetrische " +"snelheid van de extruder." + +#: src/libslic3r/PrintConfig.cpp:1254 +msgid "Max volumetric slope positive" +msgstr "Maximale volumetrische stijging" + +#: src/libslic3r/PrintConfig.cpp:1255 src/libslic3r/PrintConfig.cpp:1266 +msgid "" +"This experimental setting is used to limit the speed of change in extrusion " +"rate. A value of 1.8 mm³/s² ensures, that a change from the extrusion rate " +"of 1.8 mm³/s (0.45mm extrusion width, 0.2mm extrusion height, feedrate 20 mm/" +"s) to 5.4 mm³/s (feedrate 60 mm/s) will take at least 2 seconds." +msgstr "" +"Deze experimentele instelling wordt gebruikt om de snelheidsveranderingen te " +"limiteren. Een waarde van 1.8mm³/s² zorgt voor een extrusieratio van 1.8mm³/" +"s (0,45mm extrusiebreedte, 0,2mm laagdikte, 20mm/s voedingssnelheid) tot " +"5.4mm³/s (60mm/s voedingssnelheid). Dit duurt ten minste 2sec." + +#: src/libslic3r/PrintConfig.cpp:1259 src/libslic3r/PrintConfig.cpp:1270 +msgid "mm³/s²" +msgstr "mm³/s²" + +#: src/libslic3r/PrintConfig.cpp:1265 +msgid "Max volumetric slope negative" +msgstr "Maximale volumetrische daling" + +#: src/libslic3r/PrintConfig.cpp:1277 src/libslic3r/PrintConfig.cpp:1286 +msgid "Min" +msgstr "Min" + +#: src/libslic3r/PrintConfig.cpp:1278 +msgid "This setting represents the minimum PWM your fan needs to work." +msgstr "" +"Deze instelling geeft de minimale snelheid van uw ventilator aan waarbij de " +"ventilator draait." + +#: src/libslic3r/PrintConfig.cpp:1287 +msgid "" +"This is the lowest printable layer height for this extruder and limits the " +"resolution for variable layer height. Typical values are between 0.05 mm and " +"0.1 mm." +msgstr "" +"Dit is de kleinst printbare laagdikte voor deze extruder en limiteert de " +"resolutie voor variabele laagdikte. Typische waarden zijn tussen 0,05 en 0,1 " +"mm." + +#: src/libslic3r/PrintConfig.cpp:1295 +msgid "Min print speed" +msgstr "Minimale printsnelheid" + +#: src/libslic3r/PrintConfig.cpp:1296 +msgid "Slic3r will not scale speed down below this speed." +msgstr "" +"PrusaSlicer zal de printsnelheid niet verlagen tot onder deze snelheid." + +#: src/libslic3r/PrintConfig.cpp:1303 +msgid "Minimal filament extrusion length" +msgstr "Minimale extrusielengte" + +#: src/libslic3r/PrintConfig.cpp:1304 +msgid "" +"Generate no less than the number of skirt loops required to consume the " +"specified amount of filament on the bottom layer. For multi-extruder " +"machines, this minimum applies to each extruder." +msgstr "" +"Genereer meer dan dit aantal skirtlijnen dat nodig is om de aangegeven " +"hoeveelheid filament op de eerste laag te verbruiken. Voor multi-" +"extruderprinters is dit het minimum voor elke extruder." + +#: src/libslic3r/PrintConfig.cpp:1313 +msgid "Configuration notes" +msgstr "Configuratie-opmerkingen" + +#: src/libslic3r/PrintConfig.cpp:1314 +msgid "" +"You can put here your personal notes. This text will be added to the G-code " +"header comments." +msgstr "" +"Hier kunt u eigen opmerkingen plaatsen. Deze tekst wordt bovenin de G-code " +"toegevoegd." + +#: src/libslic3r/PrintConfig.cpp:1324 +msgid "" +"This is the diameter of your extruder nozzle (for example: 0.5, 0.35 etc.)" +msgstr "Dit is de diameter van uw extruder-nozzle (bijvoorbeeld 0.4)" + +#: src/libslic3r/PrintConfig.cpp:1329 +msgid "Host Type" +msgstr "Hosttype" + +#: src/libslic3r/PrintConfig.cpp:1330 +msgid "" +"Slic3r can upload G-code files to a printer host. This field must contain " +"the kind of the host." +msgstr "" +"PrusaSlicer kan gcode-bestanden uploaden naar een printerhost. Dit veld moet " +"het type host bevatten." + +#: src/libslic3r/PrintConfig.cpp:1343 +msgid "Only retract when crossing perimeters" +msgstr "Alleen retracten bij kruisende perimeters" + +#: src/libslic3r/PrintConfig.cpp:1344 +msgid "" +"Disables retraction when the travel path does not exceed the upper layer's " +"perimeters (and thus any ooze will be probably invisible)." +msgstr "" +"Schakelt retracten uit als de bewegingsroute de perimeters van de bovenste " +"laag niet overschrijdt (en maakt eventueel druipen dus onzichtbaar)." + +#: src/libslic3r/PrintConfig.cpp:1351 +msgid "" +"This option will drop the temperature of the inactive extruders to prevent " +"oozing. It will enable a tall skirt automatically and move extruders outside " +"such skirt when changing temperatures." +msgstr "" +"Deze optie verlaagt de temperatuur van de inactieve extruders om druipen te " +"voorkomen. Het staat een smalle skirt automatisch toe en beweegt extruders " +"buiten zo'n skirt als de temperatuur verandert." + +#: src/libslic3r/PrintConfig.cpp:1358 +msgid "Output filename format" +msgstr "Formaat van bestandsnaam" + +#: src/libslic3r/PrintConfig.cpp:1359 +msgid "" +"You can use all configuration options as variables inside this template. For " +"example: [layer_height], [fill_density] etc. You can also use [timestamp], " +"[year], [month], [day], [hour], [minute], [second], [version], " +"[input_filename], [input_filename_base]." +msgstr "" +"U kunt alle instellingen gebruiken in deze template. U kunt hier ook andere " +"variabelen gebruiken, zoals 'layer_height', 'fill_density', 'timestamp', " +"'year', 'month', 'day', 'hour', 'minute', 'second', 'version', " +"'input_filename', 'input_filename_base', etc." + +#: src/libslic3r/PrintConfig.cpp:1368 +msgid "Detect bridging perimeters" +msgstr "Detecteer brugperimeters" + +#: src/libslic3r/PrintConfig.cpp:1370 +msgid "" +"Experimental option to adjust flow for overhangs (bridge flow will be used), " +"to apply bridge speed to them and enable fan." +msgstr "" +"Experimentele optie om het debiet voor overhanging aan te passen. Het debiet " +"voor bruggen wordt aangehouden, evenals de printsnelheid en de koeling." + +#: src/libslic3r/PrintConfig.cpp:1376 +msgid "Filament parking position" +msgstr "Filament parkeerpositie" + +#: src/libslic3r/PrintConfig.cpp:1377 +msgid "" +"Distance of the extruder tip from the position where the filament is parked " +"when unloaded. This should match the value in printer firmware." +msgstr "" +"Afstand van de nozzlepunt tot de positie waar het filament wordt geparkeerd " +"wanneer dat niet geladen is. Deze moet overeenkomen met de waarde in de " +"firmware." + +#: src/libslic3r/PrintConfig.cpp:1385 +msgid "Extra loading distance" +msgstr "Extra laadafstand" + +#: src/libslic3r/PrintConfig.cpp:1386 +msgid "" +"When set to zero, the distance the filament is moved from parking position " +"during load is exactly the same as it was moved back during unload. When " +"positive, it is loaded further, if negative, the loading move is shorter " +"than unloading." +msgstr "" +"Als dit ingesteld is op 0, zal de afstand die het filament tijdens het laden " +"uit de parkeerpositie even groot zijn als wanneer het filament " +"teruggetrokken wordt. Als de waarde positief is, zal het verder geladen " +"worden. Als het negatief is, is de laadafstand dus korter." + +#: src/libslic3r/PrintConfig.cpp:1394 src/libslic3r/PrintConfig.cpp:1412 +#: src/libslic3r/PrintConfig.cpp:1425 src/libslic3r/PrintConfig.cpp:1435 +msgid "Perimeters" +msgstr "Perimeters" + +#: src/libslic3r/PrintConfig.cpp:1395 +msgid "" +"This is the acceleration your printer will use for perimeters. A high value " +"like 9000 usually gives good results if your hardware is up to the job. Set " +"zero to disable acceleration control for perimeters." +msgstr "" +"Deze acceleratie zal uw printer gebruiken voor perimeters. Als dit ingesteld " +"is op 0, worden acceleratie-instellingen voor perimeters uitgezet." + +#: src/libslic3r/PrintConfig.cpp:1403 +msgid "Perimeter extruder" +msgstr "Perimeterextruder" + +#: src/libslic3r/PrintConfig.cpp:1405 +msgid "" +"The extruder to use when printing perimeters and brim. First extruder is 1." +msgstr "" +"De extruder die gebruikt wordt voor het printen van perimeters en de brim." + +#: src/libslic3r/PrintConfig.cpp:1414 +msgid "" +"Set this to a non-zero value to set a manual extrusion width for perimeters. " +"You may want to use thinner extrudates to get more accurate surfaces. If " +"left zero, default extrusion width will be used if set, otherwise 1.125 x " +"nozzle diameter will be used. If expressed as percentage (for example 200%) " +"it will be computed over layer height." +msgstr "" +"Stel in op een niet-nulwaarde om te werken met manuele extrusiebreedte voor " +"de perimeters. Dit kan gebruikt worden voor nauwkeurigere details. Als dit " +"op 0 blijft staan, zal PrusaSlicer de breedte instellen op 1,125x de " +"nozzlediameter. Als dit is uitgedrukt als percentage, wordt dit berekend " +"over de laagdikte." + +#: src/libslic3r/PrintConfig.cpp:1427 +msgid "" +"Speed for perimeters (contours, aka vertical shells). Set to zero for auto." +msgstr "" +"Snelheid voor de perimeters (contouren, ook wel bekend als verticale " +"shells). Als dit ingesteld is op 0, wordt de snelheid automatisch " +"gedetecteerd." + +#: src/libslic3r/PrintConfig.cpp:1437 +msgid "" +"This option sets the number of perimeters to generate for each layer. Note " +"that Slic3r may increase this number automatically when it detects sloping " +"surfaces which benefit from a higher number of perimeters if the Extra " +"Perimeters option is enabled." +msgstr "" +"Instelling voor het te genereren aantal perimeters per laag. PrusaSlicer kan " +"dit aantal verhogen als het schuine vlakken detecteert die gebruik maken van " +"een hoger aantal perimeters als de optie voor extra perimeters is " +"ingeschakeld." + +#: src/libslic3r/PrintConfig.cpp:1441 +msgid "(minimum)" +msgstr "(minimum)" + +#: src/libslic3r/PrintConfig.cpp:1449 +msgid "" +"If you want to process the output G-code through custom scripts, just list " +"their absolute paths here. Separate multiple scripts with a semicolon. " +"Scripts will be passed the absolute path to the G-code file as the first " +"argument, and they can access the Slic3r config settings by reading " +"environment variables." +msgstr "" +"Als u de output-G-code via custom scripts wilt verwerken, hoeft u alleen de " +"paden hier te plaatsen. Scheid meerdere scripts met een puntkomma. Scripts " +"krijgen als eerste argument het pad naar het gcode-bestand. Ze hebben ook " +"toegang tot de configuratie-instellingen door het lezen van variabelen." + +#: src/libslic3r/PrintConfig.cpp:1461 +msgid "Printer type" +msgstr "Printertype" + +#: src/libslic3r/PrintConfig.cpp:1462 +msgid "Type of the printer." +msgstr "Type van de printer." + +#: src/libslic3r/PrintConfig.cpp:1467 +msgid "Printer notes" +msgstr "Printeropmerkingen" + +#: src/libslic3r/PrintConfig.cpp:1468 +msgid "You can put your notes regarding the printer here." +msgstr "Hier kunnen opmerkingen over de printer geplaatst worden." + +#: src/libslic3r/PrintConfig.cpp:1476 +msgid "Printer vendor" +msgstr "Printerleverancier" + +#: src/libslic3r/PrintConfig.cpp:1477 +msgid "Name of the printer vendor." +msgstr "Naam van de printerleverancier." + +#: src/libslic3r/PrintConfig.cpp:1482 +msgid "Printer variant" +msgstr "Printervariant" + +#: src/libslic3r/PrintConfig.cpp:1483 +msgid "" +"Name of the printer variant. For example, the printer variants may be " +"differentiated by a nozzle diameter." +msgstr "" +"Naam van de printervariant. De nozzlediameter kan bijvoorbeeld afwijken voor " +"verschillende varianten." + +#: src/libslic3r/PrintConfig.cpp:1496 +msgid "Raft layers" +msgstr "Raftlagen" + +#: src/libslic3r/PrintConfig.cpp:1498 +msgid "" +"The object will be raised by this number of layers, and support material " +"will be generated under it." +msgstr "" +"Het object wordt verhoogd met dit aantal lagen. Support wordt onder het " +"object gegenereerd." + +#: src/libslic3r/PrintConfig.cpp:1506 +msgid "Resolution" +msgstr "Resolutie" + +#: src/libslic3r/PrintConfig.cpp:1507 +msgid "" +"Minimum detail resolution, used to simplify the input file for speeding up " +"the slicing job and reducing memory usage. High-resolution models often " +"carry more detail than printers can render. Set to zero to disable any " +"simplification and use full resolution from input." +msgstr "" +"Minimale resolutie van details. Dit wordt gebruikt om het geïmporteerde " +"bestand sneller te slicen, evenals de grootte van de G-code. Modellen met " +"een hoge resolutie vragen meer van een printer dan mogelijk. Als dit " +"ingesteld is op 0, wordt simplificatie uitgeschakeld." + +#: src/libslic3r/PrintConfig.cpp:1517 +msgid "Minimum travel after retraction" +msgstr "Minimale beweging na retracten" + +#: src/libslic3r/PrintConfig.cpp:1518 +msgid "" +"Retraction is not triggered when travel moves are shorter than this length." +msgstr "" +"Retracten is niet geactiveerd als bewegingen korter zijn dan de hier " +"ingevoerde lengte." + +#: src/libslic3r/PrintConfig.cpp:1524 +msgid "Retract amount before wipe" +msgstr "Retracthoeveelheid voor het afvegen" + +#: src/libslic3r/PrintConfig.cpp:1525 +msgid "" +"With bowden extruders, it may be wise to do some amount of quick retract " +"before doing the wipe movement." +msgstr "" +"Met Bowden-extruders is het verstandig om een aantal maal snel te retracten " +"voor het afvegen." + +#: src/libslic3r/PrintConfig.cpp:1532 +msgid "Retract on layer change" +msgstr "Retracten bij laagwisselingen" + +#: src/libslic3r/PrintConfig.cpp:1533 +msgid "This flag enforces a retraction whenever a Z move is done." +msgstr "Dit vinkje geeft aan of wordt teruggetrokken bij een Z-beweging." + +#: src/libslic3r/PrintConfig.cpp:1538 src/libslic3r/PrintConfig.cpp:1546 +msgid "Length" +msgstr "Lengte" + +#: src/libslic3r/PrintConfig.cpp:1539 +msgid "Retraction Length" +msgstr "Retractielengte" + +#: src/libslic3r/PrintConfig.cpp:1540 +msgid "" +"When retraction is triggered, filament is pulled back by the specified " +"amount (the length is measured on raw filament, before it enters the " +"extruder)." +msgstr "" +"Als retracten is geactiveerd, wordt filament teruggetrokken op de ingestelde " +"waarde (filamentlengte voor het de extruder in gaat)." + +#: src/libslic3r/PrintConfig.cpp:1542 src/libslic3r/PrintConfig.cpp:1551 +msgid "mm (zero to disable)" +msgstr "mm (stel in op 0 om uit te schakelen)" + +#: src/libslic3r/PrintConfig.cpp:1547 +msgid "Retraction Length (Toolchange)" +msgstr "Retractielengte (toolwissel)" + +#: src/libslic3r/PrintConfig.cpp:1548 +msgid "" +"When retraction is triggered before changing tool, filament is pulled back " +"by the specified amount (the length is measured on raw filament, before it " +"enters the extruder)." +msgstr "" +"Als retracten is geactiveerd voor toolwisseling wordt filament " +"teruggetrokken op de ingestelde waarde (filamentlengte voor het de extruder " +"in gaat)." + +#: src/libslic3r/PrintConfig.cpp:1556 +msgid "Lift Z" +msgstr "Beweeg Z omhoog" + +#: src/libslic3r/PrintConfig.cpp:1557 +msgid "" +"If you set this to a positive value, Z is quickly raised every time a " +"retraction is triggered. When using multiple extruders, only the setting for " +"the first extruder will be considered." +msgstr "" +"Als u dit instelt op een positieve waarde, beweegt de nozzle telkens " +"enigszins omhoog bij het retracten. Als meerdere extruders worden gebruikt, " +"wordt alleen de instelling van de eerste extruder aangehouden." + +#: src/libslic3r/PrintConfig.cpp:1564 +msgid "Above Z" +msgstr "Boven Z" + +#: src/libslic3r/PrintConfig.cpp:1565 +msgid "Only lift Z above" +msgstr "Beweeg Z alleen omhoog boven" + +#: src/libslic3r/PrintConfig.cpp:1566 +msgid "" +"If you set this to a positive value, Z lift will only take place above the " +"specified absolute Z. You can tune this setting for skipping lift on the " +"first layers." +msgstr "" +"Als dit ingesteld is op een positieve waarde, zal de nozzle alleen boven de " +"ingestelde waarde omhoog bewegen voor het retracten. Deze kan aangepast " +"worden om warping te voorkomen bij de eerste lagen." + +#: src/libslic3r/PrintConfig.cpp:1573 +msgid "Below Z" +msgstr "Onder Z" + +#: src/libslic3r/PrintConfig.cpp:1574 +msgid "Only lift Z below" +msgstr "Beweeg Z alleen omhoog onder" + +#: src/libslic3r/PrintConfig.cpp:1575 +msgid "" +"If you set this to a positive value, Z lift will only take place below the " +"specified absolute Z. You can tune this setting for limiting lift to the " +"first layers." +msgstr "" +"Als dit ingesteld is op een positieve waarde, zal de nozzle alleen onder de " +"ingestelde waarde omhoog bewegen bij het retracten." + +#: src/libslic3r/PrintConfig.cpp:1583 src/libslic3r/PrintConfig.cpp:1591 +msgid "Extra length on restart" +msgstr "Extra lengte bij een herstart" + +#: src/libslic3r/PrintConfig.cpp:1584 +msgid "" +"When the retraction is compensated after the travel move, the extruder will " +"push this additional amount of filament. This setting is rarely needed." +msgstr "" +"Als retracten wordt gecompenseerd na een beweging, wordt deze extra " +"hoeveelheid filament geëxtrudeerd. Deze instelling is zelden van toepassing." + +#: src/libslic3r/PrintConfig.cpp:1592 +msgid "" +"When the retraction is compensated after changing tool, the extruder will " +"push this additional amount of filament." +msgstr "" +"Als retracten wordt gecompenseerd na een toolwisseling, wordt deze extra " +"hoeveelheid filament geëxtrudeerd." + +#: src/libslic3r/PrintConfig.cpp:1599 src/libslic3r/PrintConfig.cpp:1600 +msgid "Retraction Speed" +msgstr "Retractiesnelheid" + +#: src/libslic3r/PrintConfig.cpp:1601 +msgid "The speed for retractions (it only applies to the extruder motor)." +msgstr "De snelheid voor retracties (geldt alleen voor de extrudermotor)." + +#: src/libslic3r/PrintConfig.cpp:1607 src/libslic3r/PrintConfig.cpp:1608 +msgid "Deretraction Speed" +msgstr "Deretractiesnelheid" + +#: src/libslic3r/PrintConfig.cpp:1609 +msgid "" +"The speed for loading of a filament into extruder after retraction (it only " +"applies to the extruder motor). If left to zero, the retraction speed is " +"used." +msgstr "" +"De laadsnelheid van filament in de extruder na het retracten (geldt alleen " +"voor de extrudermotor). Als dit ingesteld is op 0, wordt de " +"retractiesnelheid gebruikt." + +#: src/libslic3r/PrintConfig.cpp:1616 +msgid "Seam position" +msgstr "Naadpositie" + +#: src/libslic3r/PrintConfig.cpp:1618 +msgid "Position of perimeters starting points." +msgstr "Startpuntpositie van perimeters." + +#: src/libslic3r/PrintConfig.cpp:1624 +msgid "Random" +msgstr "Willekeurig" + +#: src/libslic3r/PrintConfig.cpp:1625 +msgid "Nearest" +msgstr "Dichstbijzijnd" + +#: src/libslic3r/PrintConfig.cpp:1626 +msgid "Aligned" +msgstr "Uitgelijnd" + +#: src/libslic3r/PrintConfig.cpp:1634 +msgid "Direction" +msgstr "Richting" + +#: src/libslic3r/PrintConfig.cpp:1636 +msgid "Preferred direction of the seam" +msgstr "Richtingsvoorkeur voor de naad" + +#: src/libslic3r/PrintConfig.cpp:1637 +msgid "Seam preferred direction" +msgstr "Richtingsvoorkeur voor de naad" + +#: src/libslic3r/PrintConfig.cpp:1644 +msgid "Jitter" +msgstr "Jitter" + +#: src/libslic3r/PrintConfig.cpp:1646 +msgid "Seam preferred direction jitter" +msgstr "Voorkeursrichting voor de naad - jitter" + +#: src/libslic3r/PrintConfig.cpp:1647 +msgid "Preferred direction of the seam - jitter" +msgstr "Voorkeursrichting voor de naad - jitter" + +#: src/libslic3r/PrintConfig.cpp:1657 +msgid "USB/serial port for printer connection." +msgstr "USB/seriële poort voor verbinding met de printer." + +#: src/libslic3r/PrintConfig.cpp:1664 +msgid "Serial port speed" +msgstr "Snelheid van de seriële poort" + +#: src/libslic3r/PrintConfig.cpp:1665 +msgid "Speed (baud) of USB/serial port for printer connection." +msgstr "" +"Snelheid (baud) van de USB/seriële poort voor de verbinding met de printer." + +#: src/libslic3r/PrintConfig.cpp:1674 +msgid "Distance from object" +msgstr "Afstand vanaf het object" + +#: src/libslic3r/PrintConfig.cpp:1675 +msgid "" +"Distance between skirt and object(s). Set this to zero to attach the skirt " +"to the object(s) and get a brim for better adhesion." +msgstr "" +"Afstand tussen skirt en object. Als dit ingesteld is op 0, wordt de skirt " +"aan het object vastgemaakt; het fungeert dan als brim." + +#: src/libslic3r/PrintConfig.cpp:1682 +msgid "Skirt height" +msgstr "Skirthoogte" + +#: src/libslic3r/PrintConfig.cpp:1683 +msgid "" +"Height of skirt expressed in layers. Set this to a tall value to use skirt " +"as a shield against drafts." +msgstr "" +"Hoogte van de skirt uitgedrukt in het aantal lagen. Stel in op een hoge " +"waarde om te gebruiken als afscherming tegen tocht." + +#: src/libslic3r/PrintConfig.cpp:1690 +msgid "Loops (minimum)" +msgstr "Rondjes (minimaal)" + +#: src/libslic3r/PrintConfig.cpp:1691 +msgid "Skirt Loops" +msgstr "Rondjes voor de skirt" + +#: src/libslic3r/PrintConfig.cpp:1692 +msgid "" +"Number of loops for the skirt. If the Minimum Extrusion Length option is " +"set, the number of loops might be greater than the one configured here. Set " +"this to zero to disable skirt completely." +msgstr "" +"Het aantal rondjes van de skirt. Als de 'minimale extrusielengte'-optie is " +"ingesteld kan dit aantal rondjes groter zijn dan hier is ingesteld. Als dit " +"ingesteld is op 0, wordt de skirt uitgeschakeld." + +#: src/libslic3r/PrintConfig.cpp:1700 +msgid "Slow down if layer print time is below" +msgstr "Vertraag bij een kortere laagprinttijd dan" + +#: src/libslic3r/PrintConfig.cpp:1701 +msgid "" +"If layer print time is estimated below this number of seconds, print moves " +"speed will be scaled down to extend duration to this value." +msgstr "" +"Als de laagprinttijd wordt berekend onder dit aantal seconden, wordt de " +"printsnelheid verlaagd om de laagprinttijd te verlengen." + +#: src/libslic3r/PrintConfig.cpp:1711 +msgid "Small perimeters" +msgstr "Smalle perimeters" + +#: src/libslic3r/PrintConfig.cpp:1713 +msgid "" +"This separate setting will affect the speed of perimeters having radius <= " +"6.5mm (usually holes). If expressed as percentage (for example: 80%) it will " +"be calculated on the perimeters speed setting above. Set to zero for auto." +msgstr "" +"Deze instelling heeft effect op de snelheid van perimeters met een radius <= " +"6.5mm. Als dit is uitgedrukt als percentage, wordt deze genomen over de " +"snelheid van de perimeters. Als dit ingesteld is op 0, wordt een " +"automatische snelheid genomen." + +#: src/libslic3r/PrintConfig.cpp:1723 +msgid "Solid infill threshold area" +msgstr "Dichte vulling bij oppervlak" + +#: src/libslic3r/PrintConfig.cpp:1725 +msgid "" +"Force solid infill for regions having a smaller area than the specified " +"threshold." +msgstr "" +"Forceer dichte vulling voor delen met een kleiner doorsnee-oppervlak dan de " +"hier ingestelde waarde." + +#: src/libslic3r/PrintConfig.cpp:1726 +msgid "mm²" +msgstr "mm²" + +#: src/libslic3r/PrintConfig.cpp:1732 +msgid "Solid infill extruder" +msgstr "Extruder voor dichte vulling" + +#: src/libslic3r/PrintConfig.cpp:1734 +msgid "The extruder to use when printing solid infill." +msgstr "De extruder die gebruikt wordt voor het printen van dichte vullingen." + +#: src/libslic3r/PrintConfig.cpp:1740 +msgid "Solid infill every" +msgstr "Dichte vulling elke" + +#: src/libslic3r/PrintConfig.cpp:1742 +msgid "" +"This feature allows to force a solid layer every given number of layers. " +"Zero to disable. You can set this to any value (for example 9999); Slic3r " +"will automatically choose the maximum possible number of layers to combine " +"according to nozzle diameter and layer height." +msgstr "" +"Deze optie staat het genereren van een dichte laag tussen het ingestelde " +"aantal lagen toe. Stel in op 0 om dit uit te schakelen. Stel dit in op een " +"waarde; PrusaSlicer zal dan automatisch het maximaal aantal lagen kiezen om " +"te combineren op basis van de nozzlediameter en de laagdikte." + +#: src/libslic3r/PrintConfig.cpp:1754 +msgid "" +"Set this to a non-zero value to set a manual extrusion width for infill for " +"solid surfaces. If left zero, default extrusion width will be used if set, " +"otherwise 1.125 x nozzle diameter will be used. If expressed as percentage " +"(for example 90%) it will be computed over layer height." +msgstr "" +"Stel in op een niet-nulwaarde om te werken met manuele extrusiebreedte voor " +"de vulling van dichte lagen. Als die op 0 blijft staan, zal PrusaSlicer de " +"breedte instellen op 1,125x de nozzlediameter. Als dit is uitgedrukt als " +"percentage, wordt dit berekend over de laagdikte." + +#: src/libslic3r/PrintConfig.cpp:1765 +msgid "" +"Speed for printing solid regions (top/bottom/internal horizontal shells). " +"This can be expressed as a percentage (for example: 80%) over the default " +"infill speed above. Set to zero for auto." +msgstr "" +"Printsnelheid voor dichte delen. Als dit is uitgedrukt als percentage, wordt " +"dit berekend over de standaard vullingssnelheid. Als dit ingesteld is op 0, " +"worden automatische waarden genomen." + +#: src/libslic3r/PrintConfig.cpp:1777 +msgid "Number of solid layers to generate on top and bottom surfaces." +msgstr "Aantal te genereren dichte lagen voor boven- en ondervlakken." + +#: src/libslic3r/PrintConfig.cpp:1783 +msgid "Spiral vase" +msgstr "Spiraalmodus" + +#: src/libslic3r/PrintConfig.cpp:1784 +msgid "" +"This feature will raise Z gradually while printing a single-walled object in " +"order to remove any visible seam. This option requires a single perimeter, " +"no infill, no top solid layers and no support material. You can still set " +"any number of bottom solid layers as well as skirt/brim loops. It won't work " +"when printing more than an object." +msgstr "" +"Deze optie zorgt dat Z stapsgewijs omhoog gaat als een object met een enkele " +"perimeter geprint wordt om een naadlijn te voorkomen. Voor deze optie is een " +"enkele perimeter nodig; vulling, bovenlagen en support zijn niet mogelijk. " +"Bodemlagen zijn wel mogelijk, evenals een skirt en brim. Werkt niet bij het " +"printen van meerdere objecten tegelijk." + +#: src/libslic3r/PrintConfig.cpp:1792 +msgid "Temperature variation" +msgstr "Temperatuurverschil" + +#: src/libslic3r/PrintConfig.cpp:1793 +msgid "" +"Temperature difference to be applied when an extruder is not active. Enables " +"a full-height \"sacrificial\" skirt on which the nozzles are periodically " +"wiped." +msgstr "" +"Temperatuurverschil dat wordt toegepast als een extruder niet actief is. Dit " +"genereert een afveegblok waarop de nozzle wordt schoongeveegd." + +#: src/libslic3r/PrintConfig.cpp:1803 +msgid "" +"This start procedure is inserted at the beginning, after bed has reached the " +"target temperature and extruder just started heating, and before extruder " +"has finished heating. If PrusaSlicer detects M104 or M190 in your custom " +"codes, such commands will not be prepended automatically so you're free to " +"customize the order of heating commands and other custom actions. Note that " +"you can use placeholder variables for all PrusaSlicer settings, so you can " +"put a \"M109 S[first_layer_temperature]\" command wherever you want." +msgstr "" +"Deze startprocedure wordt aan het begin ingevoegd, nadat het bed de gewenste " +"temperatuur heeft bereikt, de extruder is begonnen met verwarmen en de " +"extruder klaar is met verwarmen. Als PrusaSlicer M104 of M190 detecteert in " +"uw custom codes, zullen dergelijke commando's niet automatisch worden " +"voorbereid, zodat u vrij bent om de volgorde van de verwarmingscommando's en " +"andere aangepaste acties aan te passen. Merk op dat u voor alle PrusaSlicer-" +"instellingen variabelen kunt gebruiken." + +#: src/libslic3r/PrintConfig.cpp:1818 +msgid "" +"This start procedure is inserted at the beginning, after any printer start " +"gcode (and after any toolchange to this filament in case of multi-material " +"printers). This is used to override settings for a specific filament. If " +"PrusaSlicer detects M104, M109, M140 or M190 in your custom codes, such " +"commands will not be prepended automatically so you're free to customize the " +"order of heating commands and other custom actions. Note that you can use " +"placeholder variables for all PrusaSlicer settings, so you can put a \"M109 " +"S[first_layer_temperature]\" command wherever you want. If you have multiple " +"extruders, the gcode is processed in extruder order." +msgstr "" +"Deze startprocedure wordt aan het begin ingevoegd, na een eventuele " +"printerstart G-code (en na een eventuele toolwissel op dit filament in het " +"geval van multi-materialprinters). Dit wordt gebruikt om de instellingen " +"voor een specifieke filament te overschrijven. Als PrusaSlicer M104, M109, " +"M140 of M190 detecteert in de custom codes, zullen dergelijke commando's " +"niet automatisch worden voorgeprogrammeerd, zodat u vrij bent om de volgorde " +"van de verwarmingscommando's en andere aangepaste acties aan te passen. Merk " +"op dat u variabelen kunt gebruiken voor alle PrusaSlicer-instellingen. Als u " +"meerdere extruders hebt, wordt de G-code in de volgorde van de extruders " +"verwerkt." + +#: src/libslic3r/PrintConfig.cpp:1834 +msgid "Single Extruder Multi Material" +msgstr "Multi-material met één extruder" + +#: src/libslic3r/PrintConfig.cpp:1835 +msgid "The printer multiplexes filaments into a single hot end." +msgstr "De printer mengt filament in een enkele extruder." + +#: src/libslic3r/PrintConfig.cpp:1840 +msgid "Prime all printing extruders" +msgstr "Veeg alle printextruders af" + +#: src/libslic3r/PrintConfig.cpp:1841 +msgid "" +"If enabled, all printing extruders will be primed at the front edge of the " +"print bed at the start of the print." +msgstr "" +"Alle extruders worden afgeveegd aan de voorzijde van het printbed aan het " +"begin van de print als dit aanstaat." + +#: src/libslic3r/PrintConfig.cpp:1846 +msgid "No sparse layers (EXPERIMENTAL)" +msgstr "Geen smalle lagen (experimenteel)" + +#: src/libslic3r/PrintConfig.cpp:1847 +msgid "" +"If enabled, the wipe tower will not be printed on layers with no " +"toolchanges. On layers with a toolchange, extruder will travel downward to " +"print the wipe tower. User is responsible for ensuring there is no collision " +"with the print." +msgstr "" +"Het afveegblok wordt niet geprint bij lagen zonder toolwisselingen als dit " +"is ingeschakeld. Op lagen met een toolwissel zal de extruder neerwaarts " +"bewegen naar het afveegblok. De gebruiker is verantwoordelijk voor eventuele " +"botsingen met de print." + +#: src/libslic3r/PrintConfig.cpp:1854 +msgid "Generate support material" +msgstr "Genereer support" + +#: src/libslic3r/PrintConfig.cpp:1856 +msgid "Enable support material generation." +msgstr "Sta de generatie van support toe." + +#: src/libslic3r/PrintConfig.cpp:1860 +msgid "Auto generated supports" +msgstr "Automatisch gegenereerd support" + +#: src/libslic3r/PrintConfig.cpp:1862 +msgid "" +"If checked, supports will be generated automatically based on the overhang " +"threshold value. If unchecked, supports will be generated inside the " +"\"Support Enforcer\" volumes only." +msgstr "" +"Support wordt automatisch gegenereerd als dit aan staat. Als dit niet " +"aanstaat zal support alleen bij supportforceringen gegenereerd worden." + +#: src/libslic3r/PrintConfig.cpp:1868 +msgid "XY separation between an object and its support" +msgstr "Horizontale ruimte tussen object en support" + +#: src/libslic3r/PrintConfig.cpp:1870 +msgid "" +"XY separation between an object and its support. If expressed as percentage " +"(for example 50%), it will be calculated over external perimeter width." +msgstr "" +"Horizontale ruimte tussen object en support. Als dit is uitgedrukt als " +"percentage, wordt deze berekend over de breedte van de buitenste perimeter." + +#: src/libslic3r/PrintConfig.cpp:1880 +msgid "Pattern angle" +msgstr "Patroonhoek" + +#: src/libslic3r/PrintConfig.cpp:1882 +msgid "" +"Use this setting to rotate the support material pattern on the horizontal " +"plane." +msgstr "Gebruik deze instelling om het patroon van het support te draaien." + +#: src/libslic3r/PrintConfig.cpp:1892 src/libslic3r/PrintConfig.cpp:2644 +msgid "" +"Only create support if it lies on a build plate. Don't create support on a " +"print." +msgstr "" +"Genereer alleen support als dit op het bed geplaatst wordt, dus niet op de " +"print zelf." + +#: src/libslic3r/PrintConfig.cpp:1898 +msgid "Contact Z distance" +msgstr "Contact Z-afstand" + +#: src/libslic3r/PrintConfig.cpp:1900 +msgid "" +"The vertical distance between object and support material interface. Setting " +"this to 0 will also prevent Slic3r from using bridge flow and speed for the " +"first object layer." +msgstr "" +"De afstand tussen objecten en het support. Stel in op 0 om te voorkomen dat " +"PrusaSlicer bruginstellingen gebruikt voor de eerste laag boven het " +"supportdak." + +#: src/libslic3r/PrintConfig.cpp:1907 +msgid "0 (soluble)" +msgstr "0 (oplosbaar)" + +#: src/libslic3r/PrintConfig.cpp:1908 +msgid "0.2 (detachable)" +msgstr "0.2 (losbreekbaar)" + +#: src/libslic3r/PrintConfig.cpp:1913 +msgid "Enforce support for the first" +msgstr "Forceer support voor de eerste" + +#: src/libslic3r/PrintConfig.cpp:1915 +msgid "" +"Generate support material for the specified number of layers counting from " +"bottom, regardless of whether normal support material is enabled or not and " +"regardless of any angle threshold. This is useful for getting more adhesion " +"of objects having a very thin or poor footprint on the build plate." +msgstr "" +"Genereer support voor het ingevoerde aantal lagen, tellend vanaf de bodem, " +"ongeacht de plekken waar standaard support al dan niet is toegestaan, " +"waarbij de ingesteld hoek wordt aangehouden. Dit is handig om meer hechting " +"op het bed te verkrijgen bij objecten met een klein contactoppervlak." + +#: src/libslic3r/PrintConfig.cpp:1920 +msgid "Enforce support for the first n layers" +msgstr "Forceer support voor de eerste n lagen" + +#: src/libslic3r/PrintConfig.cpp:1926 +msgid "Support material/raft/skirt extruder" +msgstr "Extruder voor support/raft/skirt" + +#: src/libslic3r/PrintConfig.cpp:1928 +msgid "" +"The extruder to use when printing support material, raft and skirt (1+, 0 to " +"use the current extruder to minimize tool changes)." +msgstr "" +"De extruder die gebruikt wordt voor support, raft en skirt (stel in op 1 of " +"op 0 om de huidige extruder te gebruiken)." + +#: src/libslic3r/PrintConfig.cpp:1937 +msgid "" +"Set this to a non-zero value to set a manual extrusion width for support " +"material. If left zero, default extrusion width will be used if set, " +"otherwise nozzle diameter will be used. If expressed as percentage (for " +"example 90%) it will be computed over layer height." +msgstr "" +"Stel in op een niet-nulwaarde om te werken met manuele extrusiebreedte voor " +"het support. Als die op 0 blijft staan, zal PrusaSlicer de breedte instellen " +"zelf bepalen op basis van de nozzlediameter. Als dit is uitgedrukt als " +"percentage, wordt dit berekend over de laagdikte." + +#: src/libslic3r/PrintConfig.cpp:1946 +msgid "Interface loops" +msgstr "Interface rondjes" + +#: src/libslic3r/PrintConfig.cpp:1948 +msgid "" +"Cover the top contact layer of the supports with loops. Disabled by default." +msgstr "" +"Bedek de bovenste interfacelagen van het support met rondjes. Staat " +"standaard uit." + +#: src/libslic3r/PrintConfig.cpp:1953 +msgid "Support material/raft interface extruder" +msgstr "Extruder voor supportdak en de bovenlaag van de raft" + +#: src/libslic3r/PrintConfig.cpp:1955 +msgid "" +"The extruder to use when printing support material interface (1+, 0 to use " +"the current extruder to minimize tool changes). This affects raft too." +msgstr "" +"De extruder die gebruikt wordt voor de supportinterface (stel in op hoger " +"dan 1 of op 0 om de huidige extruder te gebruiken voor minder " +"toolwisselingen). Dit heeft ook effect op de raft." + +#: src/libslic3r/PrintConfig.cpp:1962 +msgid "Interface layers" +msgstr "Supportinterface-lagen" + +#: src/libslic3r/PrintConfig.cpp:1964 +msgid "" +"Number of interface layers to insert between the object(s) and support " +"material." +msgstr "Aantal interface-lagen tussen het support en het object." + +#: src/libslic3r/PrintConfig.cpp:1971 +msgid "Interface pattern spacing" +msgstr "Tussenafstand voor interface" + +#: src/libslic3r/PrintConfig.cpp:1973 +msgid "Spacing between interface lines. Set zero to get a solid interface." +msgstr "" +"Ruimte tussen lijnen van supportinterface. Als dit ingesteld is op 0, wordt " +"een dicht supportinterface gegenereerd." + +#: src/libslic3r/PrintConfig.cpp:1982 +msgid "" +"Speed for printing support material interface layers. If expressed as " +"percentage (for example 50%) it will be calculated over support material " +"speed." +msgstr "" +"Printsnelheid van supportinterface-lagen. Als dit is uitgedrukt als " +"percentage, wordt dit berekend over de snelheid van het support." + +#: src/libslic3r/PrintConfig.cpp:1991 +msgid "Pattern" +msgstr "Patroon" + +#: src/libslic3r/PrintConfig.cpp:1993 +msgid "Pattern used to generate support material." +msgstr "Patroon dat gebruikt wordt voor het support." + +#: src/libslic3r/PrintConfig.cpp:1999 +msgid "Rectilinear grid" +msgstr "Rechtlijnig raster" + +#: src/libslic3r/PrintConfig.cpp:2005 +msgid "Pattern spacing" +msgstr "Tussenafstand van het patroon" + +#: src/libslic3r/PrintConfig.cpp:2007 +msgid "Spacing between support material lines." +msgstr "Afstand tussen supportlijnen." + +#: src/libslic3r/PrintConfig.cpp:2016 +msgid "Speed for printing support material." +msgstr "Printsnelheid van support." + +#: src/libslic3r/PrintConfig.cpp:2023 +msgid "Synchronize with object layers" +msgstr "Synchroniseer met objectlagen" + +#: src/libslic3r/PrintConfig.cpp:2025 +msgid "" +"Synchronize support layers with the object print layers. This is useful with " +"multi-material printers, where the extruder switch is expensive." +msgstr "" +"Synchroniseer de supportlagen met de objectlagen. Dit is handig voor multi-" +"materialprinters waar een toolwissel duur is." + +#: src/libslic3r/PrintConfig.cpp:2031 +msgid "Overhang threshold" +msgstr "Maximale overhanghoek" + +#: src/libslic3r/PrintConfig.cpp:2033 +msgid "" +"Support material will not be generated for overhangs whose slope angle (90° " +"= vertical) is above the given threshold. In other words, this value " +"represent the most horizontal slope (measured from the horizontal plane) " +"that you can print without support material. Set to zero for automatic " +"detection (recommended)." +msgstr "" +"Support wordt niet gegenereerd als de overhanghoek boven de gegeven waarde " +"ligt. In andere woorden; deze waarde geeft de hoek met het bed aan die " +"geprint moet worden met support. Als dit ingesteld is op 0, wordt dit " +"automatisch gedetecteerd (aanbevolen)." + +#: src/libslic3r/PrintConfig.cpp:2045 +msgid "With sheath around the support" +msgstr "Met schild rond het support" + +#: src/libslic3r/PrintConfig.cpp:2047 +msgid "" +"Add a sheath (a single perimeter line) around the base support. This makes " +"the support more reliable, but also more difficult to remove." +msgstr "" +"Voeg een schild (één perimeterlijn) rondom het support toe. Dit maakt het " +"support betrouwbaarder maar ook moeilijker te verwijderen." + +#: src/libslic3r/PrintConfig.cpp:2054 +msgid "" +"Extruder temperature for layers after the first one. Set this to zero to " +"disable temperature control commands in the output." +msgstr "" +"Extrudertemperatuur voor lager direct na de eerste laag. Als dit ingesteld " +"is op 0, voorkomt dit een verschil in de output." + +#: src/libslic3r/PrintConfig.cpp:2062 +msgid "Detect thin walls" +msgstr "Detecteer dunne wanden" + +#: src/libslic3r/PrintConfig.cpp:2064 +msgid "" +"Detect single-width walls (parts where two extrusions don't fit and we need " +"to collapse them into a single trace)." +msgstr "" +"Detecteer éénlijnige wanden (delen waar 2 extrusielijnen niet passen en dit " +"geprint moet worden met 1 lijn)." + +#: src/libslic3r/PrintConfig.cpp:2070 +msgid "Threads" +msgstr "Meerdere processen" + +#: src/libslic3r/PrintConfig.cpp:2071 +msgid "" +"Threads are used to parallelize long-running tasks. Optimal threads number " +"is slightly above the number of available cores/processors." +msgstr "" +"Meerdere processen worden gebruikt om langdurige commando's parallel te " +"draaien. Het optimaal aantal processen is vlak boven het aanwezige aantal " +"kernen/processoren." + +#: src/libslic3r/PrintConfig.cpp:2083 +msgid "" +"This custom code is inserted before every toolchange. Placeholder variables " +"for all PrusaSlicer settings as well as {previous_extruder} and " +"{next_extruder} can be used. When a tool-changing command which changes to " +"the correct extruder is included (such as T{next_extruder}), PrusaSlicer " +"will emit no other such command. It is therefore possible to script custom " +"behaviour both before and after the toolchange." +msgstr "" +"Deze custom code wordt ingevoegd voor elke toolwissel. Zowel variabelen van " +"alle PrusaSlicer-instellingen als 'previous_extruder' en 'next_extruder' " +"kunnen gebruikt worden. Als een toolwissel-commando (bijvoorbeeld " +"'T[next_extruder]') is ingevoegd, zal PrusaSlicer niet nog een dergelijk " +"commando invoegen. Het is daarom mogelijk om voor én na de toolwissel een " +"custom script te draaien." + +#: src/libslic3r/PrintConfig.cpp:2096 +msgid "" +"Set this to a non-zero value to set a manual extrusion width for infill for " +"top surfaces. You may want to use thinner extrudates to fill all narrow " +"regions and get a smoother finish. If left zero, default extrusion width " +"will be used if set, otherwise nozzle diameter will be used. If expressed as " +"percentage (for example 90%) it will be computed over layer height." +msgstr "" +"Stel in op een niet-nulwaarde om te werken met manuele extrusiebreedte voor " +"de vulling van bovenvlakken. Dit kan gebruikt worden voor een dunner " +"extrudaat in smalle gebieden voor een gladdere afwerking. Als dit is " +"uitgedrukt als percentage, wordt dit berekend over de laagdikte." + +#: src/libslic3r/PrintConfig.cpp:2108 +msgid "" +"Speed for printing top solid layers (it only applies to the uppermost " +"external layers and not to their internal solid layers). You may want to " +"slow down this to get a nicer surface finish. This can be expressed as a " +"percentage (for example: 80%) over the solid infill speed above. Set to zero " +"for auto." +msgstr "" +"Printsnelheid voor dichte toplagen (alleen de buitenste zichtbare laag). Als " +"deze waarde lager wordt gezet, resulteert dit in een gladder oppervlak. Als " +"dit is uitgedrukt als percentage, wordt dit berekend over de " +"vullingssnelheid. Als dit ingesteld is op 0, wordt een automatische snelheid " +"genomen." + +#: src/libslic3r/PrintConfig.cpp:2123 +msgid "Number of solid layers to generate on top surfaces." +msgstr "Aantal te genereren dichte lagen voor bovenvlakken." + +#: src/libslic3r/PrintConfig.cpp:2124 +msgid "Top solid layers" +msgstr "Bovenste dichte vulling" + +#: src/libslic3r/PrintConfig.cpp:2130 +msgid "Speed for travel moves (jumps between distant extrusion points)." +msgstr "Bewegingssnelheid als niet geëxtrudeerd wordt." + +#: src/libslic3r/PrintConfig.cpp:2138 +msgid "Use firmware retraction" +msgstr "Gebruik de firmware-retractie" + +#: src/libslic3r/PrintConfig.cpp:2139 +msgid "" +"This experimental setting uses G10 and G11 commands to have the firmware " +"handle the retraction. This is only supported in recent Marlin." +msgstr "" +"Deze experimentele instelling gebruikt G10 en G11 commando's voor het " +"retracten in de firmware. Dit wordt alleen ondersteunt bij de recente Marlin-" +"variant." + +#: src/libslic3r/PrintConfig.cpp:2145 +msgid "Use relative E distances" +msgstr "Gebruik relatieve E-waarden" + +#: src/libslic3r/PrintConfig.cpp:2146 +msgid "" +"If your firmware requires relative E values, check this, otherwise leave it " +"unchecked. Most firmwares use absolute values." +msgstr "" +"Als uw firmware relatieve extrusiewaarden nodig heeft, vink dit dan aan. " +"Laat het ander uit staan. De meeste firmware gebruiken absolute waarden." + +#: src/libslic3r/PrintConfig.cpp:2152 +msgid "Use volumetric E" +msgstr "Gebruik volumetrische E-waarden" + +#: src/libslic3r/PrintConfig.cpp:2153 +msgid "" +"This experimental setting uses outputs the E values in cubic millimeters " +"instead of linear millimeters. If your firmware doesn't already know " +"filament diameter(s), you can put commands like 'M200 D[filament_diameter_0] " +"T0' in your start G-code in order to turn volumetric mode on and use the " +"filament diameter associated to the filament selected in Slic3r. This is " +"only supported in recent Marlin." +msgstr "" +"Deze experimentele instelling gebruikt E-waarden in kubieke millimeters in " +"plaats van lineaire millimeters. Als uw firmware nog niet weet wat de " +"filamentdiameter is, kunt u een commando zoals 'M200 D[filament_diameter_0] " +"T0' in de start G-code invoegen om de volumetrische modus te gebruiken. Deze " +"variabele gebruikt de filamentdiameter zoals ingevoerd bij de " +"filamentinstellingen. Dit wordt alleen ondersteund in de recente Marlin-" +"variant." + +#: src/libslic3r/PrintConfig.cpp:2163 +msgid "Enable variable layer height feature" +msgstr "Sta variabele laagdikte toe" + +#: src/libslic3r/PrintConfig.cpp:2164 +msgid "" +"Some printers or printer setups may have difficulties printing with a " +"variable layer height. Enabled by default." +msgstr "" +"Sommige printers of printersetups kunnen niet printen met een variabele " +"laagdikte. Staat standaard aan." + +#: src/libslic3r/PrintConfig.cpp:2170 +msgid "Wipe while retracting" +msgstr "Veeg af bij het retracten" + +#: src/libslic3r/PrintConfig.cpp:2171 +msgid "" +"This flag will move the nozzle while retracting to minimize the possible " +"blob on leaky extruders." +msgstr "" +"Als u dit aanvinkt beweegt de nozzle tijdens het retracten om een blob of " +"lekkende extruders tegen te gaan." + +#: src/libslic3r/PrintConfig.cpp:2178 +msgid "" +"Multi material printers may need to prime or purge extruders on tool " +"changes. Extrude the excess material into the wipe tower." +msgstr "" +"Multi-materialprinters moeten afvegen bij toolwisselingen. Extrudeer het " +"overtollige materiaal op het afveegblok." + +#: src/libslic3r/PrintConfig.cpp:2184 +msgid "Purging volumes - load/unload volumes" +msgstr "Afveegvolume - laad/ontlaad volumes" + +#: src/libslic3r/PrintConfig.cpp:2185 +msgid "" +"This vector saves required volumes to change from/to each tool used on the " +"wipe tower. These values are used to simplify creation of the full purging " +"volumes below." +msgstr "" +"Deze vector bespaart de benodigde volumes om van/naar elke extruder dat op " +"het afveegblok wordt gebruikt te wisselen. Deze waarden worden gebruikt om " +"het creëren van de onderstaande volledige reinigingsvolumes te " +"vereenvoudigen." + +#: src/libslic3r/PrintConfig.cpp:2191 +msgid "Purging volumes - matrix" +msgstr "Afveegvolume - matrix" + +#: src/libslic3r/PrintConfig.cpp:2192 +msgid "" +"This matrix describes volumes (in cubic milimetres) required to purge the " +"new filament on the wipe tower for any given pair of tools." +msgstr "" +"Deze matrix beschrijft volume (in mm³) dat is vereist om nieuw filament af " +"te vegen aan het afveegblok voor elk paar van extruders." + +#: src/libslic3r/PrintConfig.cpp:2201 +msgid "Position X" +msgstr "X-positie" + +#: src/libslic3r/PrintConfig.cpp:2202 +msgid "X coordinate of the left front corner of a wipe tower" +msgstr "X-coördinaat van de linkervoorhoek van het afveegblok" + +#: src/libslic3r/PrintConfig.cpp:2208 +msgid "Position Y" +msgstr "Y-positie" + +#: src/libslic3r/PrintConfig.cpp:2209 +msgid "Y coordinate of the left front corner of a wipe tower" +msgstr "Y-coördinaat van de linkervoorhoek van het afveegblok" + +#: src/libslic3r/PrintConfig.cpp:2216 +msgid "Width of a wipe tower" +msgstr "Breedte van het afveegblok" + +#: src/libslic3r/PrintConfig.cpp:2222 +msgid "Wipe tower rotation angle" +msgstr "Rotatie van het afveegblok" + +#: src/libslic3r/PrintConfig.cpp:2223 +msgid "Wipe tower rotation angle with respect to x-axis." +msgstr "Rotatie van het afveegblok ten opzichte van de X-as." + +#: src/libslic3r/PrintConfig.cpp:2230 +msgid "Wipe into this object's infill" +msgstr "Afvegen in de vulling van het object" + +#: src/libslic3r/PrintConfig.cpp:2231 +msgid "" +"Purging after toolchange will done inside this object's infills. This lowers " +"the amount of waste but may result in longer print time due to additional " +"travel moves." +msgstr "" +"Het afvegen na de toolwissel wordt gedaan in de vulling van het object. Dit " +"reduceert de hoeveelheid afval, maar kan leiden tot een langere printtijd." + +#: src/libslic3r/PrintConfig.cpp:2238 +msgid "Wipe into this object" +msgstr "Afvegen in dit object" + +#: src/libslic3r/PrintConfig.cpp:2239 +msgid "" +"Object will be used to purge the nozzle after a toolchange to save material " +"that would otherwise end up in the wipe tower and decrease print time. " +"Colours of the objects will be mixed as a result." +msgstr "" +"Het object wordt gebruikt om de nozzle af te vegen bij een toolwissel om " +"materiaal dat anders in het afveegblok gebruikt wordt te besparen. Kleuren " +"kunnen dan gemengd worden." + +#: src/libslic3r/PrintConfig.cpp:2245 +msgid "Maximal bridging distance" +msgstr "Maximale brugafstand" + +#: src/libslic3r/PrintConfig.cpp:2246 +msgid "Maximal distance between supports on sparse infill sections." +msgstr "Maximale afstand tussen support op dunne vullingsdelen." + +#: src/libslic3r/PrintConfig.cpp:2252 +msgid "XY Size Compensation" +msgstr "Compensatie voor X- en Y-grootte" + +#: src/libslic3r/PrintConfig.cpp:2254 +msgid "" +"The object will be grown/shrunk in the XY plane by the configured value " +"(negative = inwards, positive = outwards). This might be useful for fine-" +"tuning hole sizes." +msgstr "" +"Het object wordt in horizontale richting verbreed/versmald in de ingestelde " +"waarde (negatief = naar binnen, positief = naar buiten). Dit kan handig zijn " +"voor het verfijnen van gaten." + +#: src/libslic3r/PrintConfig.cpp:2262 +msgid "Z offset" +msgstr "Z-hoogte" + +#: src/libslic3r/PrintConfig.cpp:2263 +msgid "" +"This value will be added (or subtracted) from all the Z coordinates in the " +"output G-code. It is used to compensate for bad Z endstop position: for " +"example, if your endstop zero actually leaves the nozzle 0.3mm far from the " +"print bed, set this to -0.3 (or fix your endstop)." +msgstr "" +"Deze waarde wordt toegevoegd (of afgetrokken) van alle Z-coördinaten in de G-" +"code. Het wordt gebruikt voor een slechte Z-eindstop positie. Als de Z-" +"eindstop bijvoorbeeld een waarde gebruikt die 0.3mm van het printbed is, kan " +"dit ingesteld worden op -0.3mm." + +#: src/libslic3r/PrintConfig.cpp:2330 +msgid "Display width" +msgstr "Schermbreedte" + +#: src/libslic3r/PrintConfig.cpp:2331 +msgid "Width of the display" +msgstr "Breedte van het scherm" + +#: src/libslic3r/PrintConfig.cpp:2336 +msgid "Display height" +msgstr "Schermhoogte" + +#: src/libslic3r/PrintConfig.cpp:2337 +msgid "Height of the display" +msgstr "Hoogte van het scherm" + +#: src/libslic3r/PrintConfig.cpp:2342 +msgid "Number of pixels in" +msgstr "Aantal pixels" + +#: src/libslic3r/PrintConfig.cpp:2344 +msgid "Number of pixels in X" +msgstr "Aantal pixels in de breedte" + +#: src/libslic3r/PrintConfig.cpp:2350 +msgid "Number of pixels in Y" +msgstr "Aantal pixels in de hoogte" + +#: src/libslic3r/PrintConfig.cpp:2355 +msgid "Display horizontal mirroring" +msgstr "Scherm horizontaal spiegelen" + +#: src/libslic3r/PrintConfig.cpp:2356 +msgid "Mirror horizontally" +msgstr "Spiegel horizontaal" + +#: src/libslic3r/PrintConfig.cpp:2357 +msgid "Enable horizontal mirroring of output images" +msgstr "Horizontaal spiegelen" + +#: src/libslic3r/PrintConfig.cpp:2362 +msgid "Display vertical mirroring" +msgstr "Scherm verticaal spiegelen" + +#: src/libslic3r/PrintConfig.cpp:2363 +msgid "Mirror vertically" +msgstr "Verticaal spiegelen" + +#: src/libslic3r/PrintConfig.cpp:2364 +msgid "Enable vertical mirroring of output images" +msgstr "Verticaal spiegelen" + +#: src/libslic3r/PrintConfig.cpp:2369 +msgid "Display orientation" +msgstr "Schermoriëntatie" + +#: src/libslic3r/PrintConfig.cpp:2370 +msgid "" +"Set the actual LCD display orientation inside the SLA printer. Portrait mode " +"will flip the meaning of display width and height parameters and the output " +"images will be rotated by 90 degrees." +msgstr "" +"Stel de werkelijke oriëntatie van het LCD-scherm van de SLA-printer in. " +"Staande modus zal de breedte- en hoogteparameters omwisselen en de output " +"wordt 90 graden gedraaid." + +#: src/libslic3r/PrintConfig.cpp:2376 +msgid "Landscape" +msgstr "Liggend" + +#: src/libslic3r/PrintConfig.cpp:2377 +msgid "Portrait" +msgstr "Staand" + +#: src/libslic3r/PrintConfig.cpp:2382 +msgid "Fast" +msgstr "Snel" + +#: src/libslic3r/PrintConfig.cpp:2383 +msgid "Fast tilt" +msgstr "Snelle kanteling" + +#: src/libslic3r/PrintConfig.cpp:2384 +msgid "Time of the fast tilt" +msgstr "Tijd van de snelle kanteling" + +#: src/libslic3r/PrintConfig.cpp:2391 +msgid "Slow" +msgstr "Langzaam" + +#: src/libslic3r/PrintConfig.cpp:2392 +msgid "Slow tilt" +msgstr "Langzaam kantelen" + +#: src/libslic3r/PrintConfig.cpp:2393 +msgid "Time of the slow tilt" +msgstr "Tijd van de langzame kanteling" + +#: src/libslic3r/PrintConfig.cpp:2400 +msgid "Area fill" +msgstr "Vulgebied" + +#: src/libslic3r/PrintConfig.cpp:2401 +msgid "" +"The percentage of the bed area. \n" +"If the print area exceeds the specified value, \n" +"then a slow tilt will be used, otherwise - a fast tilt" +msgstr "" +"Percentage van het printbed. \n" +"Als het printgebied buiten een specifieke waarde valt \n" +"wordt een korte kanteling gebruikt, anders een snelle kanteling" + +#: src/libslic3r/PrintConfig.cpp:2408 src/libslic3r/PrintConfig.cpp:2409 +#: src/libslic3r/PrintConfig.cpp:2410 +msgid "Printer scaling correction" +msgstr "Verschalingscorrectie voor printer" + +#: src/libslic3r/PrintConfig.cpp:2416 src/libslic3r/PrintConfig.cpp:2417 +msgid "Printer absolute correction" +msgstr "Absolute correctie voor printer" + +#: src/libslic3r/PrintConfig.cpp:2418 +msgid "" +"Will inflate or deflate the sliced 2D polygons according to the sign of the " +"correction." +msgstr "" +"Zal de geslicede veelhoeken uitrekken of laten krimpen, afhankelijk van de " +"correctiewaarde." + +#: src/libslic3r/PrintConfig.cpp:2424 src/libslic3r/PrintConfig.cpp:2425 +msgid "Printer gamma correction" +msgstr "Gammacorrectie voor printer" + +#: src/libslic3r/PrintConfig.cpp:2426 +msgid "" +"This will apply a gamma correction to the rasterized 2D polygons. A gamma " +"value of zero means thresholding with the threshold in the middle. This " +"behaviour eliminates antialiasing without losing holes in polygons." +msgstr "" +"Dit zorgt voor een gammacorrectie voor de veelhoeken. Een gammawaarde van 0 " +"betekent een waarde die in het midden ligt. Dit gedrag elimineert anti-" +"aliasing zonder dat gaten in de veelhoeken verloren gaan." + +#: src/libslic3r/PrintConfig.cpp:2438 src/libslic3r/PrintConfig.cpp:2439 +msgid "SLA material type" +msgstr "SLA-materiaaltype" + +#: src/libslic3r/PrintConfig.cpp:2450 src/libslic3r/PrintConfig.cpp:2451 +msgid "Initial layer height" +msgstr "Laagdikte eerste laag" + +#: src/libslic3r/PrintConfig.cpp:2457 src/libslic3r/PrintConfig.cpp:2458 +msgid "Bottle volume" +msgstr "Flesinhoud (volume)" + +#: src/libslic3r/PrintConfig.cpp:2459 +msgid "ml" +msgstr "ml" + +#: src/libslic3r/PrintConfig.cpp:2464 src/libslic3r/PrintConfig.cpp:2465 +msgid "Bottle weight" +msgstr "Flesinhoud (gewicht)" + +#: src/libslic3r/PrintConfig.cpp:2466 +msgid "kg" +msgstr "kg" + +#: src/libslic3r/PrintConfig.cpp:2473 +msgid "g/ml" +msgstr "g/ml" + +#: src/libslic3r/PrintConfig.cpp:2480 +msgid "money/bottle" +msgstr "€/fles" + +#: src/libslic3r/PrintConfig.cpp:2485 +msgid "Faded layers" +msgstr "Transitielagen" + +#: src/libslic3r/PrintConfig.cpp:2486 +msgid "" +"Number of the layers needed for the exposure time fade from initial exposure " +"time to the exposure time" +msgstr "" +"Aantal lagen waarin de initiële belichtingstijd stapsgewijs wordt " +"teruggebracht naar de standaard belichtingstijd" + +#: src/libslic3r/PrintConfig.cpp:2493 src/libslic3r/PrintConfig.cpp:2494 +msgid "Minimum exposure time" +msgstr "Minimale belichtingstijd" + +#: src/libslic3r/PrintConfig.cpp:2501 src/libslic3r/PrintConfig.cpp:2502 +msgid "Maximum exposure time" +msgstr "Maximale belichtingstijd" + +#: src/libslic3r/PrintConfig.cpp:2509 src/libslic3r/PrintConfig.cpp:2510 +msgid "Exposure time" +msgstr "Belichtingstijd" + +#: src/libslic3r/PrintConfig.cpp:2516 src/libslic3r/PrintConfig.cpp:2517 +msgid "Minimum initial exposure time" +msgstr "Minimale initiële belichtingstijd" + +#: src/libslic3r/PrintConfig.cpp:2524 src/libslic3r/PrintConfig.cpp:2525 +msgid "Maximum initial exposure time" +msgstr "Maximale initiële belichtingstijd" + +#: src/libslic3r/PrintConfig.cpp:2532 src/libslic3r/PrintConfig.cpp:2533 +msgid "Initial exposure time" +msgstr "Initiële belichtingstijd" + +#: src/libslic3r/PrintConfig.cpp:2539 src/libslic3r/PrintConfig.cpp:2540 +msgid "Correction for expansion" +msgstr "Vergrotingscorrectie" + +#: src/libslic3r/PrintConfig.cpp:2546 +msgid "SLA print material notes" +msgstr "SLA-printmateriaal opmerkingen" + +#: src/libslic3r/PrintConfig.cpp:2547 +msgid "You can put your notes regarding the SLA print material here." +msgstr "U kunt hier opmerkingen plaatsen wat betreft het SLA-materiaal." + +#: src/libslic3r/PrintConfig.cpp:2559 src/libslic3r/PrintConfig.cpp:2570 +msgid "Default SLA material profile" +msgstr "Standaard SLA-materiaalprofiel" + +#: src/libslic3r/PrintConfig.cpp:2581 +msgid "Generate supports" +msgstr "Genereer support" + +#: src/libslic3r/PrintConfig.cpp:2583 +msgid "Generate supports for the models" +msgstr "Genereer support voor de modellen" + +#: src/libslic3r/PrintConfig.cpp:2588 +msgid "Support head front diameter" +msgstr "Supportkopdiameter" + +#: src/libslic3r/PrintConfig.cpp:2590 +msgid "Diameter of the pointing side of the head" +msgstr "Diameter van de puntige zijde van de kop" + +#: src/libslic3r/PrintConfig.cpp:2597 +msgid "Support head penetration" +msgstr "Supportkopinsteek" + +#: src/libslic3r/PrintConfig.cpp:2599 +msgid "How much the pinhead has to penetrate the model surface" +msgstr "Hoe ver de supportkop in het model moet steken" + +#: src/libslic3r/PrintConfig.cpp:2606 +msgid "Support head width" +msgstr "Supportkopbreedte" + +#: src/libslic3r/PrintConfig.cpp:2608 +msgid "Width from the back sphere center to the front sphere center" +msgstr "Centerafstand van de achterste tot de voorste bol" + +#: src/libslic3r/PrintConfig.cpp:2616 +msgid "Support pillar diameter" +msgstr "Supportpijler - diameter" + +#: src/libslic3r/PrintConfig.cpp:2618 +msgid "Diameter in mm of the support pillars" +msgstr "Diameter van de supportpijlers (in mm)" + +#: src/libslic3r/PrintConfig.cpp:2626 +msgid "Support pillar connection mode" +msgstr "Supportpijler - verbindingsmodus" + +#: src/libslic3r/PrintConfig.cpp:2627 +msgid "" +"Controls the bridge type between two neighboring pillars. Can be zig-zag, " +"cross (double zig-zag) or dynamic which will automatically switch between " +"the first two depending on the distance of the two pillars." +msgstr "" +"Geeft het type brug tussen twee aangrenzende pijlers aan. Dit kan zigzag, " +"kruisend (dubbele zigzag) of dynamisch zijn. Dynamisch houdt in dat wordt " +"geschakeld tussen de eerste twee, afhankelijk van de pijlerafstand." + +#: src/libslic3r/PrintConfig.cpp:2635 +msgid "Zig-Zag" +msgstr "Zigzag" + +#: src/libslic3r/PrintConfig.cpp:2636 +msgid "Cross" +msgstr "Kruisend" + +#: src/libslic3r/PrintConfig.cpp:2637 +msgid "Dynamic" +msgstr "Dynamisch" + +#: src/libslic3r/PrintConfig.cpp:2649 +msgid "Pillar widening factor" +msgstr "Pijlervergrotingsfactor" + +#: src/libslic3r/PrintConfig.cpp:2651 +msgid "" +"Merging bridges or pillars into another pillars can increase the radius. " +"Zero means no increase, one means full increase." +msgstr "" +"Bruggen of pijlers samenvoegen met andere pijlers kan de radius vergroten. 0 " +"betekent geen vergroting, 1 betekent volle vergroting." + +#: src/libslic3r/PrintConfig.cpp:2660 +msgid "Support base diameter" +msgstr "Supportbasis - diameter" + +#: src/libslic3r/PrintConfig.cpp:2662 +msgid "Diameter in mm of the pillar base" +msgstr "Diameter van de pijlerbasis (in mm)" + +#: src/libslic3r/PrintConfig.cpp:2670 +msgid "Support base height" +msgstr "Supportbasis - hoogte" + +#: src/libslic3r/PrintConfig.cpp:2672 +msgid "The height of the pillar base cone" +msgstr "Hoogte van de pijlerbasiskegel" + +#: src/libslic3r/PrintConfig.cpp:2679 +msgid "Support base safety distance" +msgstr "Supportbasis - veilige afstand" + +#: src/libslic3r/PrintConfig.cpp:2682 +msgid "" +"The minimum distance of the pillar base from the model in mm. Makes sense in " +"zero elevation mode where a gap according to this parameter is inserted " +"between the model and the pad." +msgstr "" +"Minimale afstand tussen pijlerbasis en model (in mm). Dit is handig bij de " +"modus zonder verhoging waar een gat volgens deze parameter wordt ingevoegd " +"tussen het model en de basisplaat." + +#: src/libslic3r/PrintConfig.cpp:2692 +msgid "Critical angle" +msgstr "Kritische hoek" + +#: src/libslic3r/PrintConfig.cpp:2694 +msgid "The default angle for connecting support sticks and junctions." +msgstr "De standaardhoek voor de verbinding van supporttakken en kruisingen." + +#: src/libslic3r/PrintConfig.cpp:2702 +msgid "Max bridge length" +msgstr "Maximale bruglengte" + +#: src/libslic3r/PrintConfig.cpp:2704 +msgid "The max length of a bridge" +msgstr "Maximale bruglengte" + +#: src/libslic3r/PrintConfig.cpp:2711 +msgid "Max pillar linking distance" +msgstr "Maximale pijler-verbindafstand" + +#: src/libslic3r/PrintConfig.cpp:2713 +msgid "" +"The max distance of two pillars to get linked with each other. A zero value " +"will prohibit pillar cascading." +msgstr "" +"Maximale verbindingsafstand van twee pijlers. Een waarde van 0 schakelt aan " +"elkaar verbonden pijlers uit." + +#: src/libslic3r/PrintConfig.cpp:2721 +msgid "Object elevation" +msgstr "Objectverhoging" + +#: src/libslic3r/PrintConfig.cpp:2723 +msgid "" +"How much the supports should lift up the supported object. If \"Pad around " +"object\" is enabled, this value is ignored." +msgstr "" +"Hoe veel het support omhoog moet bewegen op het ondersteunde object. Als " +"'Basisplaat rondom object' is ingeschakeld wordt deze waarde genegeerd." + +#: src/libslic3r/PrintConfig.cpp:2734 +msgid "This is a relative measure of support points density." +msgstr "Relatieve waarde van de dichtheid van supportpunten." + +#: src/libslic3r/PrintConfig.cpp:2740 +msgid "Minimal distance of the support points" +msgstr "Minimale supportpuntafstand" + +#: src/libslic3r/PrintConfig.cpp:2742 +msgid "No support points will be placed closer than this threshold." +msgstr "Minimale afstand tussen supportpunten." + +#: src/libslic3r/PrintConfig.cpp:2748 +msgid "Use pad" +msgstr "Gebruik basisplaat" + +#: src/libslic3r/PrintConfig.cpp:2750 +msgid "Add a pad underneath the supported model" +msgstr "Voeg een basisplaat toe onder het model met support" + +#: src/libslic3r/PrintConfig.cpp:2755 +msgid "Pad wall thickness" +msgstr "Basisplaat - wanddikte" + +#: src/libslic3r/PrintConfig.cpp:2757 +msgid "The thickness of the pad and its optional cavity walls." +msgstr "Dikte van de basisplaat en optionele wanden." + +#: src/libslic3r/PrintConfig.cpp:2765 +msgid "Pad wall height" +msgstr "Basisplaat - wandhoogte" + +#: src/libslic3r/PrintConfig.cpp:2766 +msgid "" +"Defines the pad cavity depth. Set to zero to disable the cavity. Be careful " +"when enabling this feature, as some resins may produce an extreme suction " +"effect inside the cavity, which makes peeling the print off the vat foil " +"difficult." +msgstr "" +"Geeft de basisplaat-wandhoogte aan. Als dit ingesteld is op 0, wordt dit " +"uitgeschakeld. Wees voorzichtig met het aanzetten van deze optie, omdat " +"sommige resins een sterk zuigeffect in de holte produceren, wat het afpellen " +"van de print van het folie lastig kan maken." + +#: src/libslic3r/PrintConfig.cpp:2779 +msgid "Pad brim size" +msgstr "Basisplaat - expansie" + +#: src/libslic3r/PrintConfig.cpp:2780 +msgid "How far should the pad extend around the contained geometry" +msgstr "Hoe ver de basisplaat moet uitsteken buiten de geometrie" + +#: src/libslic3r/PrintConfig.cpp:2790 +msgid "Max merge distance" +msgstr "Maximale combineerafstand" + +#: src/libslic3r/PrintConfig.cpp:2792 +msgid "" +"Some objects can get along with a few smaller pads instead of a single big " +"one. This parameter defines how far the center of two smaller pads should " +"be. If theyare closer, they will get merged into one pad." +msgstr "" +"Sommige objecten werken beter met een aantal kleinere basisplaten in plaats " +"van één grote. Deze parameter bepaalt hoe ver de tussenafstand van de " +"kleinere basisplaten mogen zijn." + +#: src/libslic3r/PrintConfig.cpp:2812 +msgid "Pad wall slope" +msgstr "Basisplaat - zijhoek" + +#: src/libslic3r/PrintConfig.cpp:2814 +msgid "" +"The slope of the pad wall relative to the bed plane. 90 degrees means " +"straight walls." +msgstr "" +"Hoek van de basisplaatzijde ten opzichte van het bed. 90 graden betekent een " +"rechte zijkant." + +#: src/libslic3r/PrintConfig.cpp:2823 +msgid "Pad around object" +msgstr "Basisplaat rondom object" + +#: src/libslic3r/PrintConfig.cpp:2825 +msgid "Create pad around object and ignore the support elevation" +msgstr "Genereer basisplaat rondom object en schakel objectverhoging uit" + +#: src/libslic3r/PrintConfig.cpp:2830 +msgid "Pad around object everywhere" +msgstr "Overal basisplaat rondom object" + +#: src/libslic3r/PrintConfig.cpp:2832 +msgid "Force pad around object everywhere" +msgstr "Forceer basisplaat overal rondom het object" + +#: src/libslic3r/PrintConfig.cpp:2837 +msgid "Pad object gap" +msgstr "Basisplaat - gat" + +#: src/libslic3r/PrintConfig.cpp:2839 +msgid "" +"The gap between the object bottom and the generated pad in zero elevation " +"mode." +msgstr "" +"Het gat tussen de onderkant van het object en de gegenereerde basisplaat in " +"de modus zonder verhoging." + +#: src/libslic3r/PrintConfig.cpp:2848 +msgid "Pad object connector stride" +msgstr "Basisplaat - verbindingstakafstand" + +#: src/libslic3r/PrintConfig.cpp:2850 +msgid "" +"Distance between two connector sticks which connect the object and the " +"generated pad." +msgstr "" +"Afstand tussen twee verbindingstakken die het object verbinden aan de " +"basisplaat." + +#: src/libslic3r/PrintConfig.cpp:2857 +msgid "Pad object connector width" +msgstr "Basisplaat - verbindingstakbreedte" + +#: src/libslic3r/PrintConfig.cpp:2859 +msgid "" +"Width of the connector sticks which connect the object and the generated pad." +msgstr "" +"Breedte van de verbindingstakken die het object en de basisplaat met elkaar " +"verbinden." + +#: src/libslic3r/PrintConfig.cpp:2866 +msgid "Pad object connector penetration" +msgstr "Basisplaat - Verbindingstakinsteek" + +#: src/libslic3r/PrintConfig.cpp:2869 +msgid "How much should the tiny connectors penetrate into the model body." +msgstr "Hoe ver de verbindingstakken in het model steken." + +#: src/libslic3r/PrintConfig.cpp:3247 +msgid "Export OBJ" +msgstr "Exporteer OBJ" + +#: src/libslic3r/PrintConfig.cpp:3248 +msgid "Export the model(s) as OBJ." +msgstr "Exporteer de modellen als OBJ-bestand." + +#: src/libslic3r/PrintConfig.cpp:3259 +msgid "Export SLA" +msgstr "Exporteer SLA" + +#: src/libslic3r/PrintConfig.cpp:3260 +msgid "Slice the model and export SLA printing layers as PNG." +msgstr "Slice het model en exporteer SLA-printlagen als PNG-bestanden." + +#: src/libslic3r/PrintConfig.cpp:3265 +msgid "Export 3MF" +msgstr "Exporteer 3MF" + +#: src/libslic3r/PrintConfig.cpp:3266 +msgid "Export the model(s) as 3MF." +msgstr "Exporteer de modellen als 3MF-bestanden." + +#: src/libslic3r/PrintConfig.cpp:3270 +msgid "Export AMF" +msgstr "Exporteer AMF" + +#: src/libslic3r/PrintConfig.cpp:3271 +msgid "Export the model(s) as AMF." +msgstr "Exporteer de modellen als AMF-bestanden." + +#: src/libslic3r/PrintConfig.cpp:3275 +msgid "Export STL" +msgstr "Exporteer STL" + +#: src/libslic3r/PrintConfig.cpp:3276 +msgid "Export the model(s) as STL." +msgstr "Exporteer de modellen als STL-bestand." + +#: src/libslic3r/PrintConfig.cpp:3281 +msgid "Slice the model and export toolpaths as G-code." +msgstr "Slice het model en exporteer de bewegingen als gcode-bestand." + +#: src/libslic3r/PrintConfig.cpp:3286 +msgid "Slice" +msgstr "Slice" + +#: src/libslic3r/PrintConfig.cpp:3287 +msgid "" +"Slice the model as FFF or SLA based on the printer_technology configuration " +"value." +msgstr "" +"Slice het model als FDM of SLA, gebaseerd op de 'printer_technology'-" +"configuratiewaarde." + +#: src/libslic3r/PrintConfig.cpp:3292 +msgid "Help" +msgstr "Help" + +#: src/libslic3r/PrintConfig.cpp:3293 +msgid "Show this help." +msgstr "Toon deze hulp zien." + +#: src/libslic3r/PrintConfig.cpp:3298 +msgid "Help (FFF options)" +msgstr "Help (FDM-opties)" + +#: src/libslic3r/PrintConfig.cpp:3299 +msgid "Show the full list of print/G-code configuration options." +msgstr "Toon de volledige lijst van print- of G-code-configuratie-opties." + +#: src/libslic3r/PrintConfig.cpp:3303 +msgid "Help (SLA options)" +msgstr "Help (SLA opties)" + +#: src/libslic3r/PrintConfig.cpp:3304 +msgid "Show the full list of SLA print configuration options." +msgstr "Toon de volledige lijst van SLA-printconfiguratie-opties." + +#: src/libslic3r/PrintConfig.cpp:3308 +msgid "Output Model Info" +msgstr "Output model-info" + +#: src/libslic3r/PrintConfig.cpp:3309 +msgid "Write information about the model to the console." +msgstr "Schrijf informatie over het model naar de console." + +#: src/libslic3r/PrintConfig.cpp:3313 +msgid "Save config file" +msgstr "Sla configuratiebestand op" + +#: src/libslic3r/PrintConfig.cpp:3314 +msgid "Save configuration to the specified file." +msgstr "Sla configuratie op in aangegeven bestand." + +#: src/libslic3r/PrintConfig.cpp:3324 +msgid "Align XY" +msgstr "XY uitlijnen" + +#: src/libslic3r/PrintConfig.cpp:3325 +msgid "Align the model to the given point." +msgstr "Lijn de modellen uit op het gegeven punt." + +#: src/libslic3r/PrintConfig.cpp:3330 +msgid "Cut model at the given Z." +msgstr "Snijdt model op de ingestelde hoogte." + +#: src/libslic3r/PrintConfig.cpp:3351 +msgid "Center" +msgstr "Centreer" + +#: src/libslic3r/PrintConfig.cpp:3352 +msgid "Center the print around the given center." +msgstr "Centreer de print op het middelpunt." + +#: src/libslic3r/PrintConfig.cpp:3356 +msgid "Don't arrange" +msgstr "Niet schikken" + +#: src/libslic3r/PrintConfig.cpp:3357 +msgid "" +"Do not rearrange the given models before merging and keep their original XY " +"coordinates." +msgstr "" +"Herschik de modellen niet voor het samenvoegen en behoudt de originele X- en " +"Y-coördinaten." + +#: src/libslic3r/PrintConfig.cpp:3360 +msgid "Duplicate" +msgstr "Dupliceer" + +#: src/libslic3r/PrintConfig.cpp:3361 +msgid "Multiply copies by this factor." +msgstr "Meerdere kopieën van dit aantal." + +#: src/libslic3r/PrintConfig.cpp:3365 +msgid "Duplicate by grid" +msgstr "Dupliceer in raster" + +#: src/libslic3r/PrintConfig.cpp:3366 +msgid "Multiply copies by creating a grid." +msgstr "Meerdere kopieën in raster." + +#: src/libslic3r/PrintConfig.cpp:3369 +msgid "Merge" +msgstr "Samenvoegen" + +#: src/libslic3r/PrintConfig.cpp:3370 +msgid "" +"Arrange the supplied models in a plate and merge them in a single model in " +"order to perform actions once." +msgstr "" +"Schik de toegevoegde modellen en combineer ze tot één model om eenmalig " +"acties uit te voeren." + +#: src/libslic3r/PrintConfig.cpp:3375 +msgid "" +"Try to repair any non-manifold meshes (this option is implicitly added " +"whenever we need to slice the model to perform the requested action)." +msgstr "" +"Probeer alle niet-gesloten meshes te repareren (deze optie is impliciet " +"toegevoegd om, wanneer dat nodig is, onmogelijke modellen toch te slicen)." + +#: src/libslic3r/PrintConfig.cpp:3379 +msgid "Rotation angle around the Z axis in degrees." +msgstr "Rotatiehoek rond de Z-as in graden." + +#: src/libslic3r/PrintConfig.cpp:3383 +msgid "Rotate around X" +msgstr "Draai over de X-as" + +#: src/libslic3r/PrintConfig.cpp:3384 +msgid "Rotation angle around the X axis in degrees." +msgstr "Rotatiehoek rond de X-as in graden." + +#: src/libslic3r/PrintConfig.cpp:3388 +msgid "Rotate around Y" +msgstr "Draai over de Y-as" + +#: src/libslic3r/PrintConfig.cpp:3389 +msgid "Rotation angle around the Y axis in degrees." +msgstr "Rotatiehoek rond de Y-as in graden." + +#: src/libslic3r/PrintConfig.cpp:3394 +msgid "Scaling factor or percentage." +msgstr "Schalingsfactor of percentage." + +#: src/libslic3r/PrintConfig.cpp:3399 +msgid "" +"Detect unconnected parts in the given model(s) and split them into separate " +"objects." +msgstr "" +"Detecteer niet-verbonden onderdelen in het model en deel ze op in " +"verschillende objecten." + +#: src/libslic3r/PrintConfig.cpp:3402 +msgid "Scale to Fit" +msgstr "Verschaal naar passing" + +#: src/libslic3r/PrintConfig.cpp:3403 +msgid "Scale to fit the given volume." +msgstr "Verschaal naar passing in het gegeven volume." + +#: src/libslic3r/PrintConfig.cpp:3412 +msgid "Ignore non-existent config files" +msgstr "Negeer niet-bestaande configuratiebestanden" + +#: src/libslic3r/PrintConfig.cpp:3413 +msgid "Do not fail if a file supplied to --load does not exist." +msgstr "Geef geen fout als een bestand om te laden niet bestaat." + +#: src/libslic3r/PrintConfig.cpp:3416 +msgid "Load config file" +msgstr "Laad configuratiebestand" + +#: src/libslic3r/PrintConfig.cpp:3417 +msgid "" +"Load configuration from the specified file. It can be used more than once to " +"load options from multiple files." +msgstr "" +"Laad configuratie uit een specifiek bestand. Dit kan meerdere keren gebruikt " +"worden om instellingen uit meerdere bestanden te laden." + +#: src/libslic3r/PrintConfig.cpp:3420 +msgid "Output File" +msgstr "Outputbestand" + +#: src/libslic3r/PrintConfig.cpp:3421 +msgid "" +"The file where the output will be written (if not specified, it will be " +"based on the input file)." +msgstr "" +"Het bestand waaroverheen wordt geschreven (als dit niet aangegeven is, wort " +"dit gebaseerd op het inputbestand)." + +#: src/libslic3r/PrintConfig.cpp:3431 +msgid "Data directory" +msgstr "Bestandslocatie voor de data" + +#: src/libslic3r/PrintConfig.cpp:3432 +msgid "" +"Load and store settings at the given directory. This is useful for " +"maintaining different profiles or including configurations from a network " +"storage." +msgstr "" +"Laad fabrieksinstellingen en sla op. Dit is handig voor het onderhouden van " +"verschillende profielen of het opnemen van configuraties van een " +"netwerkopslag." + +#: src/libslic3r/PrintConfig.cpp:3435 +msgid "Logging level" +msgstr "Logboekniveau" + +#: src/libslic3r/PrintConfig.cpp:3436 +msgid "" +"Messages with severity lower or eqal to the loglevel will be printed out. 0:" +"trace, 1:debug, 2:info, 3:warning, 4:error, 5:fatal" +msgstr "" +"Berichten die lager of gelijk zijn aan het logboekniveau worden geprint. 0:" +"traceer, 1:foutopsporing, 2:info, 3:waarschuwing, 4:fout, 5:fatale fout" + +#: src/libslic3r/PrintConfig.cpp:3441 +msgid "Render with a software renderer" +msgstr "Render met software-renderer" + +#: src/libslic3r/PrintConfig.cpp:3442 +msgid "" +"Render with a software renderer. The bundled MESA software renderer is " +"loaded instead of the default OpenGL driver." +msgstr "" +"Render met software-renderer. De meegeleverde MESA-software-renderer is " +"geladen in plaats van het standaard OpenGL stuurprogramma." + +#: src/libslic3r/PrintObject.cpp:106 +msgid "Processing triangulated mesh" +msgstr "Mesh aan het verwerken" + +#: src/libslic3r/PrintObject.cpp:150 +msgid "Generating perimeters" +msgstr "Perimeters genereren" + +#: src/libslic3r/PrintObject.cpp:260 +msgid "Preparing infill" +msgstr "Vulling voorbereiden" + +#: src/libslic3r/PrintObject.cpp:400 +msgid "Generating support material" +msgstr "Support genereren" + +#: src/libslic3r/GCode/PreviewData.cpp:362 +msgid "Height (mm)" +msgstr "Hoogte (mm)" + +#: src/libslic3r/GCode/PreviewData.cpp:364 +msgid "Width (mm)" +msgstr "Breedte (mm)" + +#: src/libslic3r/GCode/PreviewData.cpp:366 +msgid "Speed (mm/s)" +msgstr "Snelheid (mm/s)" + +#: src/libslic3r/GCode/PreviewData.cpp:368 +msgid "Fan Speed (%)" +msgstr "Ventilatorsnelheid (%)" + +#: src/libslic3r/GCode/PreviewData.cpp:370 +msgid "Volumetric flow rate (mm³/s)" +msgstr "Volumetrisch debiet (mm³/s)" diff --git a/resources/localization/pl/PrusaSlicer.mo b/resources/localization/pl/PrusaSlicer.mo index dd6f77378a..89565631ed 100644 Binary files a/resources/localization/pl/PrusaSlicer.mo and b/resources/localization/pl/PrusaSlicer.mo differ diff --git a/resources/localization/pt_br/PrusaSlicer.mo b/resources/localization/pt_br/PrusaSlicer.mo index a2f3e9789d..0c01dd31a2 100644 Binary files a/resources/localization/pt_br/PrusaSlicer.mo and b/resources/localization/pt_br/PrusaSlicer.mo differ diff --git a/resources/profiles/Creality.idx b/resources/profiles/Creality.idx new file mode 100644 index 0000000000..9ac64e8cf1 --- /dev/null +++ b/resources/profiles/Creality.idx @@ -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 diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 181bb4f4ed..ddb0437758 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -5,7 +5,7 @@ name = Creality # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 0.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. @@ -422,4 +422,4 @@ retract_before_wipe = 70% default_print_profile = 0.20mm NORMAL default_filament_profile = Creality PLA start_gcode = G90 ; 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 ; home all\nG1 Z2 F240\nG1 X2 Y10 F3000\nG1 Z0.28 F240\nG92 E0.0\nG1 Y190 E15.0 F1500.0 ; intro line\nG1 X2.3 F5000\nG1 Y10 E30 F1200.0 ; intro line\nG92 E0.0 -end_gcode = M104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+10, max_print_height)} F600{endif} ; Move print head up\nG1 X0 Y200 F3000 ; present print\nM84 X Y E ; disable motors \ No newline at end of file +end_gcode = M104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+10, max_print_height)} F600{endif} ; Move print head up\nG1 X0 Y200 F3000 ; present print\nM84 X Y E ; disable motors diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index c451bb8bf4..d063303d92 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -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 diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 4f5c0482ba..cc791097d2 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -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 diff --git a/sandboxes/CMakeLists.txt b/sandboxes/CMakeLists.txt index 91d6ca225e..a2bd13bb0b 100644 --- a/sandboxes/CMakeLists.txt +++ b/sandboxes/CMakeLists.txt @@ -1,3 +1,4 @@ #add_subdirectory(slasupporttree) #add_subdirectory(openvdb) add_subdirectory(meshboolean) +add_subdirectory(opencsg) diff --git a/sandboxes/meshboolean/CMakeLists.txt b/sandboxes/meshboolean/CMakeLists.txt index 17e876573b..55fb42fd14 100644 --- a/sandboxes/meshboolean/CMakeLists.txt +++ b/sandboxes/meshboolean/CMakeLists.txt @@ -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) diff --git a/sandboxes/meshboolean/MeshBoolean.cpp b/sandboxes/meshboolean/MeshBoolean.cpp index 1aaa4d2b8b..392d907074 100644 --- a/sandboxes/meshboolean/MeshBoolean.cpp +++ b/sandboxes/meshboolean/MeshBoolean.cpp @@ -1,85 +1,44 @@ -#include -#undef PI -#include -//#undef IGL_STATIC_LIBRARY -#include - -#include #include +#include -#include +#include +#include +#include +#include +#include + +#include -#include #include -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> MapMatrixXfUnaligned; - typedef Eigen::Map> MapMatrixXiUnaligned; - - Eigen::MatrixXd V = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), mesh.its.vertices.size(), 3).cast(); - 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 " << 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; } diff --git a/sandboxes/opencsg/CMakeLists.txt b/sandboxes/opencsg/CMakeLists.txt new file mode 100644 index 0000000000..ec1f4cae91 --- /dev/null +++ b/sandboxes/opencsg/CMakeLists.txt @@ -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 + ) diff --git a/sandboxes/opencsg/Engine.cpp b/sandboxes/opencsg/Engine.cpp new file mode 100644 index 0000000000..f110b23c5c --- /dev/null +++ b/sandboxes/opencsg/Engine.cpp @@ -0,0 +1,498 @@ +#include "Engine.hpp" +#include +#include + +#include + +#include + +#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 &&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 CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh) +{ + auto p = std::make_shared(); + p->load_mesh(mesh); + primitives.emplace_back(p); + primitives_free.emplace_back(p.get()); + return p; +} + +shptr CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh, + OpenCSG::Operation o, + unsigned c) +{ + auto p = std::make_shared(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(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()); + 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(); + 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 diff --git a/sandboxes/opencsg/Engine.hpp b/sandboxes/opencsg/Engine.hpp new file mode 100644 index 0000000000..fc76c1b313 --- /dev/null +++ b/sandboxes/opencsg/Engine.hpp @@ -0,0 +1,493 @@ +#ifndef SLIC3R_OCSG_EXMP_ENGINE_HPP +#define SLIC3R_OCSG_EXMP_ENGINE_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace Slic3r { + +class SLAPrint; + +namespace GL { + +// Simple shorthands for smart pointers +template using shptr = std::shared_ptr; +template using uqptr = std::unique_ptr; +template using wkptr = std::weak_ptr; + +template> using vector = std::vector; + +// remove empty weak pointers from a vector +template inline void cleanup(vector> &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 +inline void call(F &&f, vector> &listeners, Args&&... args) { + for (auto &l : listeners) + if (auto p = l.lock()) ((p.get())->*f)(std::forward(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> 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) + { + 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 vertices_and_normals_interleaved; + vector triangle_indices; + vector 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> 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 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 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 &&print); + const SLAPrint * get_print() const { return m_print.get(); } + + BoundingBoxf3 get_bounding_box() const; + + void add_listener(shptr listener) + { + m_listeners.emplace_back(listener); + cleanup(m_listeners); + } + +private: + vector> 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 m_camera; + FpsCounter m_fps_counter; + +public: + + explicit Display(shptr camera = nullptr) + : m_camera(camera ? camera : std::make_shared()) + {} + + ~Display() override; + + shptr get_camera() const { return m_camera; } + shptr get_camera() { return m_camera; } + void set_camera(shptr 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 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> primitives; + vector primitives_free; + vector primitives_csg; + + void clear(); + + shptr add_mesh(const TriangleMesh &mesh); + shptr 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, + 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 m_scene; + vector> m_displays; + + // Call a method of Camera on all the cameras of the attached displays + template + void call_cameras(F &&f, Args&&... args) { + for (wkptr &l : m_displays) + if (auto disp = l.lock()) if (auto cam = disp->get_camera()) + (cam.get()->*f)(std::forward(args)...); + } + +public: + + // Set the scene that will be controlled. + void set_scene(shptr scene) + { + m_scene = scene; + m_scene->add_listener(shared_from_this()); + } + + const Scene * get_scene() const { return m_scene.get(); } + + void add_display(shptr 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 diff --git a/sandboxes/opencsg/ShaderCSGDisplay.cpp b/sandboxes/opencsg/ShaderCSGDisplay.cpp new file mode 100644 index 0000000000..8ceb234be0 --- /dev/null +++ b/sandboxes/opencsg/ShaderCSGDisplay.cpp @@ -0,0 +1,68 @@ +#include "ShaderCSGDisplay.hpp" +#include "libslic3r/SLAPrint.hpp" +#include + +namespace Slic3r { namespace GL { + +void ShaderCSGDisplay::add_mesh(const TriangleMesh &mesh) +{ + auto v = std::make_shared(); + 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(); + 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 diff --git a/sandboxes/opencsg/ShaderCSGDisplay.hpp b/sandboxes/opencsg/ShaderCSGDisplay.hpp new file mode 100644 index 0000000000..bf0c3a4240 --- /dev/null +++ b/sandboxes/opencsg/ShaderCSGDisplay.hpp @@ -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> m_volumes; + + void add_mesh(const TriangleMesh &mesh); +public: + + void render_scene() override; + + void on_scene_updated(const Scene &scene) override; +}; + +}} + +#endif // SHADERCSGDISPLAY_HPP diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp new file mode 100644 index 0000000000..adf9cc457f --- /dev/null +++ b/sandboxes/opencsg/main.cpp @@ -0,0 +1,734 @@ +#include +#include +#include + +#include "Engine.hpp" +#include "ShaderCSGDisplay.hpp" + +#include + +#include +// For compilers that support precompilation, includes "wx/wx.h". +#include +#ifndef WX_PRECOMP +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#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 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 m_display; + +public: + + template + Canvas(Args &&...args): wxGLCanvas(std::forward(args)...) {} + + shptr get_display() const { return m_display; } + + void set_display(shptr 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 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 m_scene; // Model + shptr m_canvas; // Views store + shptr m_ocsgdisplay; // View + shptr m_shadercsg_display; // Another view + shptr m_ctl; // Controller + + // Add a status bar with progress indication. + shptr 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 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 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(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 CSG_ALGS = {"Auto", "Goldfeather", "SCS", "EnricoShader"}; +static const std::vector CSG_DEPTH = {"Off", "OcclusionQuery", "On"}; +static const std::vector CSG_OPT = { "Default", "ForceOn", "On", "Off" }; + +inline long get_idx(const wxString &a, const std::vector &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(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(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(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(); + m_ctl = std::make_shared(); + m_ctl->set_scene(m_scene); + + m_canvas = std::make_shared(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 &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(); + 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() {} diff --git a/sandboxes/openvdb/CMakeLists.txt b/sandboxes/openvdb/CMakeLists.txt index 184452e833..c32d6c8d63 100644 --- a/sandboxes/openvdb/CMakeLists.txt +++ b/sandboxes/openvdb/CMakeLists.txt @@ -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() + diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 28aadc0458..048aea8869 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -162,10 +162,26 @@ 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; - fff_print_config.apply(m_print_config, true); -// sla_print_config.apply(m_print_config, true); - + 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); + 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; for (auto const &opt_key : m_transforms) { @@ -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 &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) diff --git a/src/libigl/igl/copyleft/cgal/SelfIntersectMesh.h b/src/libigl/igl/copyleft/cgal/SelfIntersectMesh.h index 5a70fc39e7..39a4e1a7e3 100644 --- a/src/libigl/igl/copyleft/cgal/SelfIntersectMesh.h +++ b/src/libigl/igl/copyleft/cgal/SelfIntersectMesh.h @@ -635,13 +635,30 @@ inline bool igl::copyleft::cgal::SelfIntersectMesh< { using namespace std; + 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 - if( - A.supporting_plane() != B.supporting_plane() && - A.supporting_plane() != B.supporting_plane().opposite()) - { + 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)) { @@ -936,4 +941,4 @@ inline void igl::copyleft::cgal::SelfIntersectMesh< //process_chunk(0, candidate_triangle_pairs.size()); } -#endif +#endif \ No newline at end of file diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 52b5e60f17..841acdb421 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -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 () diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index bcd0172780..6806f4f540 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -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 &tokens, t_config_option_keys* extra, t_config_option_keys* keys) { std::vector args; diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 9b04ae0269..1258cc3f13 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -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 + 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(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 + 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(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(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(opt); } - template - const TYPE* option(const t_config_option_key &opt_key) const - { return const_cast(this)->option(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(this)->option_throw(opt_key, false); } - template TYPE* option_throw(const t_config_option_key &opt_key, bool create = false) { @@ -1561,10 +1594,6 @@ public: return static_cast(opt); } - template - const TYPE* option_throw(const t_config_option_key &opt_key) const - { return const_cast(this)->option_throw(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(this->option(opt_key, create)); } template const T* opt(const t_config_option_key &opt_key) const { return dynamic_cast(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. diff --git a/src/libslic3r/CustomGCode.cpp b/src/libslic3r/CustomGCode.cpp new file mode 100644 index 0000000000..7c505c9789 --- /dev/null +++ b/src/libslic3r/CustomGCode.cpp @@ -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("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& 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 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> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders) +{ + std::vector> 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(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder)); + } + return custom_tool_changes; +} + +} // namespace CustomGCode + +} // namespace Slic3r diff --git a/src/libslic3r/CustomGCode.hpp b/src/libslic3r/CustomGCode.hpp new file mode 100644 index 0000000000..e54599ca64 --- /dev/null +++ b/src/libslic3r/CustomGCode.hpp @@ -0,0 +1,86 @@ +#ifndef slic3r_CustomGCode_hpp_ +#define slic3r_CustomGCode_hpp_ + +#include +#include + +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 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 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> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders); + +} // namespace CustomGCode + +} // namespace Slic3r + + + +#endif /* slic3r_CustomGCode_hpp_ */ diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index f9c470450c..4a89660447 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -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 diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index 7c0dfcce5b..4aad3603fc 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -327,6 +327,7 @@ extern BoundingBox get_extents_rotated(const ExPolygons &polygons, double angle) extern std::vector get_extents_vector(const ExPolygons &polygons); extern bool remove_sticks(ExPolygon &poly); +extern void keep_largest_contour_only(ExPolygons &polygons); extern std::list expoly_to_polypartition_input(const ExPolygons &expp); extern std::list expoly_to_polypartition_input(const ExPolygon &ex); diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index 2c5c64fe73..b367be0226 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -1,12 +1,18 @@ #include "Flow.hpp" +#include "I18N.hpp" #include "Print.hpp" #include #include +#include + +// 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("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("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(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("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(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)); diff --git a/src/libslic3r/Flow.hpp b/src/libslic3r/Flow.hpp index 51cc4da9d4..aad189775f 100644 --- a/src/libslic3r/Flow.hpp +++ b/src/libslic3r/Flow.hpp @@ -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); diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 14177ed88d..efeb237003 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -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> IdToLayerHeightsProfileMap; typedef std::map IdToLayerConfigRangesMap; typedef std::map> IdToSlaSupportPointsMap; + typedef std::map> 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 @@ -683,6 +692,11 @@ namespace Slic3r { model_object->sla_support_points = obj_sla_support_points->second; model_object->sla_points_status = sla::PointsStatus::UserModified; } + + IdToSlaDrainHolesMap::iterator obj_drain_holes = m_sla_drain_holes.find(object.second + 1); + if (obj_drain_holes != m_sla_drain_holes.end() && !obj_drain_holes->second.empty()) { + model_object->sla_drain_holes = 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 } @@ -1022,6 +1037,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 objects; + boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off); + + // Info on format versioning - see 3mf.hpp + int version = 0; + std::string key("drain_holes_format_version="); + if (!objects.empty() && objects[0].find(key) != std::string::npos) { + objects[0].erase(objects[0].begin(), objects[0].begin() + long(key.size())); // removes the string + version = std::stoi(objects[0]); + objects.erase(objects.begin()); // pop the header + } + + for (const std::string& object : objects) + { + std::vector object_data; + boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off); + + if (object_data.size() != 2) + { + add_error("Error while reading object data"); + continue; + } + + std::vector object_data_id; + boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off); + if (object_data_id.size() != 2) + { + add_error("Error while reading object id"); + continue; + } + + int object_id = std::atoi(object_data_id[1].c_str()); + if (object_id == 0) + { + add_error("Found invalid object id"); + continue; + } + + IdToSlaDrainHolesMap::iterator object_item = m_sla_drain_holes.find(object_id); + if (object_item != m_sla_drain_holes.end()) + { + add_error("Found duplicated SLA drain holes"); + continue; + } + + std::vector object_data_points; + boost::split(object_data_points, object_data[1], boost::is_any_of(" "), boost::token_compress_off); + + sla::DrainHoles sla_drain_holes; + + if (version == 1) { + for (unsigned int i=0; i(".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 (".extruder" ); std::string color = tree.get (".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 BuildItemsList; typedef std::map 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) @@ -2017,6 +2099,14 @@ namespace Slic3r { boost::filesystem::remove(filename); return false; } + + if (!_add_sla_drain_holes_file_to_archive(archive, model)) + { + close_zip_writer(&archive); + boost::filesystem::remove(filename); + return false; + } + // Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml"). // All custom gcode per height of whole Model are stored here @@ -2474,6 +2564,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(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) { @@ -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 @@ -2623,7 +2753,13 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv code_tree.put(".gcode" , code.gcode ); code_tree.put(".extruder" , code.extruder ); code_tree.put(".color" , code.color ); - } + } + + pt::ptree& mode_tree = main_tree.add("mode", ""); + // store mode of a custom_gcode_per_print_z + mode_tree.put(".value", model.custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode : + model.custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode : + CustomGCode::MultiExtruderMode); if (!tree.empty()) { @@ -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(); diff --git a/src/libslic3r/Format/3mf.hpp b/src/libslic3r/Format/3mf.hpp index c28716fffc..8ca1ebde85 100644 --- a/src/libslic3r/Format/3mf.hpp +++ b/src/libslic3r/Format/3mf.hpp @@ -19,6 +19,10 @@ namespace Slic3r { enum { support_points_format_version = 1 }; + + enum { + drain_holes_format_version = 1 + }; class Model; class DynamicPrintConfig; @@ -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 diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 7041956bad..2dc4ea0ba5 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -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,12 +310,18 @@ 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) { - node_type_new = NODE_TYPE_GCODE_PER_HEIGHT; - m_value[0] = get_attribute(atts, "height"); - m_value[1] = get_attribute(atts, "gcode"); - m_value[2] = get_attribute(atts, "extruder"); - m_value[3] = get_attribute(atts, "color"); + 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, "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: @@ -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 << "\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 << " " << input_file << "\n"; -#else - stream << " " << xml_escape(volume->source.input_file) << "\n"; -#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF stream << " " << volume->source.object_idx << "\n"; stream << " " << volume->source.volume_idx << "\n"; stream << " " << volume->source.mesh_offset(0) << "\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(".print_z" , code.print_z ); code_tree.put(".gcode" , code.gcode ); code_tree.put(".extruder" , code.extruder ); code_tree.put(".color" , code.color ); } + pt::ptree& mode_tree = main_tree.add("mode", ""); + // store mode of a custom_gcode_per_print_z + mode_tree.put(".value", + model->custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode : + model->custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? + CustomGCode::MultiAsSingleMode : CustomGCode::MultiExtruderMode); + if (!tree.empty()) { std::ostringstream oss; @@ -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, ">\n \n <", ">\n<"); stream << out << "\n"; diff --git a/src/libslic3r/Format/AMF.hpp b/src/libslic3r/Format/AMF.hpp index 7404e1e901..3e33d4aa37 100644 --- a/src/libslic3r/Format/AMF.hpp +++ b/src/libslic3r/Format/AMF.hpp @@ -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 diff --git a/src/libslic3r/Format/objparser.cpp b/src/libslic3r/Format/objparser.cpp index 28f2fdbbd5..8c1a53459d 100644 --- a/src/libslic3r/Format/objparser.cpp +++ b/src/libslic3r/Format/objparser.cpp @@ -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 bool savevector(FILE *pFile, const std::vector &v) { diff --git a/src/libslic3r/Format/objparser.hpp b/src/libslic3r/Format/objparser.hpp index 5fc25e297b..5f3f010e4a 100644 --- a/src/libslic3r/Format/objparser.hpp +++ b/src/libslic3r/Format/objparser.hpp @@ -3,6 +3,7 @@ #include #include +#include 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); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index e09dc54fe8..6717e961d1 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -8,6 +8,7 @@ #include "GCode/WipeTower.hpp" #include "ShortestPath.hpp" #include "Utils.hpp" +#include "libslic3r.h" #include #include @@ -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 sort_object_instances_by_max_z(const Print &print) +{ + std::vector 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 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 sort_object_instances_by_model_order(const Print& print) +{ + // Build up map from ModelInstance* to PrintInstance* + std::vector> 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 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 print_object_instances_ordering; + std::vector::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(-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,72 +1367,64 @@ 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 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. - tool_ordering = ToolOrdering(object, final_extruder_id); - unsigned int new_extruder_id = tool_ordering.first_extruder(); - if (new_extruder_id == (unsigned int)-1) - // Skip this object. - continue; - initial_extruder_id = new_extruder_id; - final_extruder_id = tool_ordering.last_extruder(); - assert(final_extruder_id != (unsigned int)-1); - } - print.throw_if_canceled(); - this->set_origin(unscale(copy)); - 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. - m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer - m_avoid_crossing_perimeters.use_external_mp_once = true; - _write(file, this->retract()); - _write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object")); - m_enable_cooling_markers = true; - // Disable motion planner when traveling to first object point. - m_avoid_crossing_perimeters.disable_once = true; - // Ff we are printing the bottom layer of an object, and we have already finished - // another one, set first layer temperatures. This happens before the Z move - // is triggered, so machine has more time to reach such temperatures. - m_placeholder_parser.set("current_object_idx", int(finished_objects)); - std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id); - // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. - this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false); - this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false); - _writeln(file, between_objects_gcode); - } - // Reset the cooling buffer internal state (the current position, feed rate, accelerations). - m_cooling_buffer->reset(); - m_cooling_buffer->set_current_extruder(initial_extruder_id); - // Pair the object layers with the support layers by z, extrude them. - std::vector layers_to_print = collect_layers_to_print(object); - for (const LayerToPrint <p : layers_to_print) { - std::vector 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()); - print.throw_if_canceled(); - } -#ifdef HAS_PRESSURE_EQUALIZER - if (m_pressure_equalizer) - _write(file, m_pressure_equalizer->process("", true)); -#endif /* HAS_PRESSURE_EQUALIZER */ - ++ finished_objects; - // 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; + 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) + // Skip this object. + continue; + initial_extruder_id = new_extruder_id; + final_extruder_id = tool_ordering.last_extruder(); + assert(final_extruder_id != (unsigned int)-1); } + print.throw_if_canceled(); + 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. + m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer + m_avoid_crossing_perimeters.use_external_mp_once = true; + _write(file, this->retract()); + _write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object")); + m_enable_cooling_markers = true; + // Disable motion planner when traveling to first object point. + m_avoid_crossing_perimeters.disable_once = true; + // Ff we are printing the bottom layer of an object, and we have already finished + // another one, set first layer temperatures. This happens before the Z move + // is triggered, so machine has more time to reach such temperatures. + m_placeholder_parser.set("current_object_idx", int(finished_objects)); + std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id); + // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. + this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false); + this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false); + _writeln(file, between_objects_gcode); + } + // Reset the cooling buffer internal state (the current position, feed rate, accelerations). + m_cooling_buffer->reset(); + m_cooling_buffer->set_current_extruder(initial_extruder_id); + // Pair the object layers with the support layers by z, extrude them. + std::vector layers_to_print = collect_layers_to_print(object); + for (const LayerToPrint <p : layers_to_print) { + std::vector lrs; + lrs.emplace_back(std::move(ltp)); + 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 + if (m_pressure_equalizer) + _write(file, m_pressure_equalizer->process("", true)); +#endif /* HAS_PRESSURE_EQUALIZER */ + ++ finished_objects; + // 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> 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>> layers_to_print = collect_layers_to_print(print); @@ -1691,12 +1726,12 @@ inline std::vector& object_islands_by_extruder( } std::vector GCode::sort_print_object_instances( - std::vector &objects_by_extruder, - const std::vector &layers, + std::vector &objects_by_extruder, + const std::vector &layers, // Ordering must be defined for normal (non-sequential print). - const std::vector> *ordering, + const std::vector *ordering, // For sequential print, the instance of the object to be printing has to be defined. - const size_t single_object_instance_idx) + const size_t single_object_instance_idx) { std::vector out; @@ -1723,13 +1758,13 @@ std::vector GCode::sort_print_object_instances( if (! sorted.empty()) { const Print &print = *sorted.front().first->print(); out.reserve(sorted.size()); - for (const std::pair &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 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; @@ -1751,31 +1786,39 @@ namespace ProcessLayer // Extruder switches are processed by LayerTools, they should be filtered out. assert(custom_gcode->gcode != ToolChangeCode); - const std::string &custom_code = custom_gcode->gcode; + const std::string &custom_code = custom_gcode->gcode; + bool color_change = custom_code == ColorChangeCode; + bool tool_change = custom_code == ToolChangeCode; + // Tool Change is applied as Color Change for a single extruder printer only. + assert(! tool_change || single_extruder_printer); + std::string pause_print_msg; 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 + // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count + 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,7 +1879,9 @@ namespace Skirt { const Print &print, const std::vector &layers, const LayerTools &layer_tools, - // Heights (print_z) at which the skirt has already been extruded. + // First non-empty support layer. + const SupportLayer *support_layer, + // Heights (print_z) at which the skirt has already been extruded. std::vector &skirt_done) { // Extrude skirt at the print_z of the raft layers and normal object layers @@ -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(0, print.config().skirts.value); @@ -1873,16 +1918,16 @@ namespace Skirt { // and performing the extruder specific extrusions together. void GCode::process_layer( // Write into the output file. - FILE *file, - const Print &print, + FILE *file, + const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. - const std::vector &layers, - const LayerTools &layer_tools, + const std::vector &layers, + const LayerTools &layer_tools, // Pairs of PrintObject index and its instance index. - const std::vector> *ordering, + const std::vector *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) + const size_t single_object_instance_idx) { assert(! layers.empty()); // assert(! layer_tools.extruders.empty()); @@ -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> by_extruder; @@ -2084,16 +2129,16 @@ void GCode::process_layer( // Let's recover vector of extruder overrides: const WipingExtrusions::ExtruderPerCopy *entity_overrides = nullptr; + 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(); + } + printing_extruders.clear(); 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(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->copies().size()); + entity_overrides = const_cast(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); + printing_extruders.emplace_back(correct_extruder_id); } else { printing_extruders.reserve(entity_overrides->size()); for (int extruder : *entity_overrides) @@ -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(- extruder - 1)); + Slic3r::sort_remove_duplicates(printing_extruders); } - 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 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; } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 8ddf2db61d..e3da956c20 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -231,7 +231,7 @@ private: const std::vector &layers, const LayerTools &layer_tools, // Pairs of PrintObject index and its instance index. - const std::vector> *ordering, + const std::vector *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 &layers, // Ordering must be defined for normal (non-sequential print). - const std::vector> *ordering, + const std::vector *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 sort_object_instances_by_model_order(const Print& print); + } #endif diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp index 1fedcf3f03..7a8271e300 100644 --- a/src/libslic3r/GCode/PrintExtents.cpp +++ b/src/libslic3r/GCode/PrintExtents.cpp @@ -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); } } diff --git a/src/libslic3r/GCode/SpiralVase.cpp b/src/libslic3r/GCode/SpiralVase.cpp index a4ae42b318..a3c190069e 100644 --- a/src/libslic3r/GCode/SpiralVase.cpp +++ b/src/libslic3r/GCode/SpiralVase.cpp @@ -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()) { diff --git a/src/libslic3r/GCode/SpiralVase.hpp b/src/libslic3r/GCode/SpiralVase.hpp index e35ca640c3..496c1425c5 100644 --- a/src/libslic3r/GCode/SpiralVase.hpp +++ b/src/libslic3r/GCode/SpiralVase.hpp @@ -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; }; } diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 3e01e2594c..db7c58a9ce 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -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> 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; - std::vector extruder_printing_above(num_extruders, false); - auto custom_gcode_it = custom_gcode_per_print_z.gcodes.rbegin(); + 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 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) { diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index 08dd726563..de0460f1b0 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -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); diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 36127cad77..3ef325aef1 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -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))); diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp index bd997a0470..496b992d8f 100644 --- a/src/libslic3r/GCodeTimeEstimator.hpp +++ b/src/libslic3r/GCodeTimeEstimator.hpp @@ -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); diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 4c53048dc9..38a1c3ebee 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -1,4 +1,5 @@ #include "GCodeWriter.hpp" +#include "CustomGCode.hpp" #include #include #include diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index abeaf00242..3a57c8bd29 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -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; diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index b7725d11da..d66aa8f013 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -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. diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index f4f0d6a5de..67a1acb09e 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -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) diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index 63ff6fb09d..3402d2c856 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -252,46 +252,6 @@ template struct remove_cvref template using remove_cvref_t = typename remove_cvref::type; -template using DefaultContainer = std::vector; - -/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html -template class Container = DefaultContainer> -inline Container> linspace(const T &start, - const T &stop, - const I &n) -{ - Container> 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 Container = DefaultContainer> -inline Container> grid(const T &start, - const T &stop, - const T &stride) -{ - Container> - 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 using enable_if_t = typename std::enable_if::type; @@ -392,6 +352,56 @@ inline IntegerOnly> reserve_vector(I capacity) return ret; } +/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html +template +inline std::vector linspace_vector(const ArithmeticOnly &start, + const T &stop, + const IntegerOnly &n) +{ + std::vector 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 +inline std::array, N> linspace_array(const T &start, const T &stop) +{ + std::array 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 +inline std::vector> grid(const T &start, + const T &stop, + const T &stride) +{ + std::vector 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 diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp new file mode 100644 index 0000000000..6c5418fd4e --- /dev/null +++ b/src/libslic3r/MeshBoolean.cpp @@ -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 +#undef L + +// CGAL headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Slic3r { +namespace MeshBoolean { + +using MapMatrixXfUnaligned = Eigen::Map>; +using MapMatrixXiUnaligned = Eigen::Map>; + +TriangleMesh eigen_to_triangle_mesh(const EigenMesh &emesh) +{ + auto &VC = emesh.first; auto &FC = emesh.second; + + Pointf3s points(size_t(VC.rows())); + std::vector 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(); + + 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; +using _EpecMesh = CGAL::Surface_mesh; + +struct CGALMesh { _EpicMesh m; }; + +// ///////////////////////////////////////////////////////////////////////////// +// Converions from and to CGAL mesh +// ///////////////////////////////////////////////////////////////////////////// + +template void triangle_mesh_to_cgal(const TriangleMesh &M, _Mesh &out) +{ + using Index3 = std::array; + + if (M.empty()) return; + + std::vector points; + std::vector 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(); + 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 cvt; + auto iv = cvt(v); + return {iv.x(), iv.y(), iv.z()}; +} + +template TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh) +{ + Pointf3s points; + std::vector 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(v); + facets.emplace_back(trface); + } + + TriangleMesh out{points, facets}; + out.require_shared_vertices(); + return out; +} + +std::unique_ptr triangle_mesh_to_cgal(const TriangleMesh &M) +{ + std::unique_ptr 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 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 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 diff --git a/src/libslic3r/MeshBoolean.hpp b/src/libslic3r/MeshBoolean.hpp new file mode 100644 index 0000000000..ce17a13286 --- /dev/null +++ b/src/libslic3r/MeshBoolean.hpp @@ -0,0 +1,49 @@ +#ifndef libslic3r_MeshBoolean_hpp_ +#define libslic3r_MeshBoolean_hpp_ + +#include +#include + +#include +#include + +namespace Slic3r { + +namespace MeshBoolean { + +using EigenMesh = std::pair; + +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 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_ diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index d5a40e79fd..b3a18f26bb 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -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,10 +907,8 @@ 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. @@ -1162,7 +1167,9 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b TriangleMesh mesh(volume->mesh()); 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 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> custom_tool_changes(const Model &model, size_t num_extruders) -{ - std::vector> 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(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& custom_gcode_per_print_z, DynamicPrintConfig* config) -{ - auto *colorprint_heights = config->option("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& 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) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index fa5d8f1556..2fbd584610 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -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 #include @@ -198,10 +200,13 @@ 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_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; + 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 @@ -372,7 +377,7 @@ private: template void serialize(Archive &ar) { ar(cereal::base_class(this)); Internal::StaticSerializationWrapper 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(offs(X))); set_offset(Y, unscale(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 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 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> 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& custom_gcode_per_print_z, DynamicPrintConfig* config); #ifndef NDEBUG // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp new file mode 100644 index 0000000000..c30052036e --- /dev/null +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -0,0 +1,135 @@ +#define NOMINMAX +#include "OpenVDBUtils.hpp" +#include +#include +#include + +//#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(); + 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( + 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( + Contour3DDataAdapter{mesh}, tr, exteriorBandWidth, interiorBandWidth, + flags); +} + +template +sla::Contour3D _volumeToMesh(const Grid &grid, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) +{ + openvdb::initialize(); + + std::vector points; + std::vector triangles; + std::vector 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 diff --git a/src/libslic3r/OpenVDBUtils.hpp b/src/libslic3r/OpenVDBUtils.hpp new file mode 100644 index 0000000000..c493845a1c --- /dev/null +++ b/src/libslic3r/OpenVDBUtils.hpp @@ -0,0 +1,45 @@ +#ifndef OPENVDBUTILS_HPP +#define OPENVDBUTILS_HPP + +#include +#include +#include +#include + +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(); } +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 diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 450fff3515..3cd91dafe4 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -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? diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index db3134ded5..527d82b4cb 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1,4 +1,5 @@ #include "PlaceholderParser.hpp" +#include "Flow.hpp" #include #include #include @@ -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(opt_old)->value == config_new.get_abs_value(opt_key) : - *opt_new == *opt_old; + return opt_old != nullptr && *opt_new == *opt_old; } std::vector 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 &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 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(ctx->current_extruder_id))); + } else if (! static_cast(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(opt_parent), *ctx, static_cast(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(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); } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 1cb16b4de6..d5a1fa178d 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -24,8 +24,7 @@ #include #include -//! 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::vectorcopies().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); } + Transform3d 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 print_objects_from_model_object(const ModelObject &model_object) +static std::vector print_objects_from_model_object(const ModelObject &model_object) { - std::set trafos; - PrintInstances trafo; - trafo.copies.assign(1, Point()); + std::set 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(*it).copies.emplace_back(trafo.copies.front()); + // Search or insert a trafo. + auto it = trafos.emplace(trafo).first; + const_cast(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift }); } - return std::vector(trafos.begin(), trafos.end()); + return std::vector(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 &va, const std::vector &vb) +static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector &va, const std::vector &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(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; - 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); - } + // 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 new_print_instances = print_objects_from_model_object(*model_object); + std::vector 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(*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 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 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()) - 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 object_height; - for (const PrintObject *object : m_objects) - object_height.insert(object_height.end(), object->copies().size(), object->size(2)); - std::sort(object_height.begin(), object_height.end()); - // Ignore the tallest *copy* (this is why we repeat height for all of them): - // it will be printed as last one so its height doesn't matter. - object_height.pop_back(); - if (! object_height.empty() && object_height.back() > scale_(m_config.extruder_clearance_height.value)) - return L("Some objects are too tall and cannot be printed without extruder collisions."); - } - } // end if (m_config.complete_objects) + if (! sequential_print_horizontal_clearance_valid(*this)) + return L("Some objects are too close; your extruder will collide with them."); + if (! sequential_print_vertical_clearance_valid(*this)) + return L("Some objects are too tall and cannot be printed without extruder collisions."); + } 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; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 53a6f97b98..7b326472e4 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -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 LayerPtrs; typedef std::vector 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 PrintInstances; + class PrintObject : public PrintObjectBaseWithState { 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>> 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) { @@ -126,9 +143,9 @@ public: // This is the *total* layer count (including support layers) // this value is not supposed to be compared with Layer::id // since they have different semantics. - size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); } - size_t layer_count() const { return m_layers.size(); } - void clear_layers(); + size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); } + size_t layer_count() const { return m_layers.size(); } + void clear_layers(); const Layer* get_layer(int idx) const { return m_layers[idx]; } Layer* get_layer(int idx) { return m_layers[idx]; } // Get a layer exactly at print_z. @@ -177,17 +194,16 @@ public: std::vector slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); } std::vector 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 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 slice_region(size_t region_id, const std::vector &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 slice_region(size_t region_id, const std::vector &z, SlicingMode mode) const; std::vector slice_modifiers(size_t region_id, const std::vector &z) const; - std::vector slice_volumes(const std::vector &z, const std::vector &volumes) const; - std::vector slice_volume(const std::vector &z, const ModelVolume &volume) const; - std::vector slice_volume(const std::vector &z, const std::vector &ranges, const ModelVolume &volume) const; + std::vector slice_volumes(const std::vector &z, SlicingMode mode, const std::vector &volumes) const; + std::vector slice_volume(const std::vector &z, SlicingMode mode, const ModelVolume &volume) const; + std::vector slice_volume(const std::vector &z, const std::vector &ranges, SlicingMode mode, const ModelVolume &volume) const; }; struct WipeTowerData @@ -325,7 +346,7 @@ private: // Prevents erroneous use by other classes. typedef PrintBaseWithState 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 &opt_keys); diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index 55e24178de..81affe04d4 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -64,8 +64,8 @@ std::string PrintBase::output_filename(const std::string &format, const std::str boost::filesystem::path filename = format.empty() ? cfg.opt_string("input_filename_base") + default_ext : this->placeholder_parser().process(format, 0, &cfg); - if (filename.extension().empty()) - filename = boost::filesystem::change_extension(filename, default_ext); + if (filename.extension().empty()) + filename = boost::filesystem::change_extension(filename, default_ext); return filename.string(); } catch (std::runtime_error &err) { throw std::runtime_error(L("Failed processing of the output_filename_format template.") + "\n" + err.what()); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 9fe50d82eb..36d56c4c10 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -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) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 6d70f54082..c854feafc8 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -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::ge return keys_map; } -/* -template<> inline const t_config_enum_values& ConfigOptionEnum::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::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 @@ -1084,6 +1067,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); } }; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8a59b6c3bf..4ddcaedc8b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -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(); - } - - 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); - } + // 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(); + } + + // 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(); + + 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 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 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_new; if (interface_shells) - surfaces_new.assign(m_layers.size(), Surfaces()); + surfaces_new.assign(num_layers, Surfaces()); tbb::parallel_for( - tbb::blocked_range(0, m_layers.size()), + tbb::blocked_range(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& 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(0, m_layers.size()), - [this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range& range) { + [this, idx_region, interface_shells](const tbb::blocked_range& 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 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 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(0, m_layers.size(), grain_size), + tbb::blocked_range(0, num_layers, grain_size), [this, &cache_top_botom_regions](const tbb::blocked_range& 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(0, m_layers.size(), grain_size), + tbb::blocked_range(0, num_layers, grain_size), [this, idx_region, &cache_top_botom_regions](const tbb::blocked_range& 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(0, m_layers.size(), grain_size), - [this, idx_region, n_extra_top_layers, n_extra_bottom_layers, &cache_top_botom_regions] + tbb::blocked_range(0, num_layers, grain_size), + [this, idx_region, &cache_top_botom_regions] (const tbb::blocked_range& 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) { @@ -1058,8 +1096,9 @@ void PrintObject::discover_vertical_shells() ++ debug_idx; #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - Layer *layer = m_layers[idx_layer]; - LayerRegion *layerm = layer->m_regions[idx_region]; + 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()) { - holes = intersection(holes, cache.holes); - } - size_t n_shell_old = shell.size(); - if (n > int(idx_layer)) - // Collect top surfaces. - polygons_append(shell, cache.top_surfaces); - else if (n < int(idx_layer)) - // Collect bottom and bottom bridge surfaces. - 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); - } + 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); + if (! cache.top_surfaces.empty()) { + polygons_append(shell, cache.top_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. + 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(this->size(2)), this->object_extruders()); + this->print()->config(), m_config, unscale(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 &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 &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_by_layer = this->slice_region(region_id, slice_zs); + std::vector 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 &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 PrintObject::slice_region(size_t region_id, const std::vector &z) const +std::vector PrintObject::slice_region(size_t region_id, const std::vector &z, SlicingMode mode) const { std::vector volumes; if (region_id < this->region_volumes.size()) { @@ -1865,7 +1922,7 @@ std::vector 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 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 merge; @@ -1933,7 +1990,7 @@ std::vector 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 this_slices = this->slice_volume(slice_zs, ranges, *model_volume); + std::vector 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 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 PrintObject::slice_volumes(const std::vector &z, const std::vector &volumes) const +std::vector PrintObject::slice_volumes(const std::vector &z, SlicingMode mode, const std::vector &volumes) const { std::vector layers; if (! volumes.empty()) { @@ -1997,7 +2054,7 @@ std::vector PrintObject::slice_volumes(const std::vector &z, if (mesh.stl.stats.number_of_facets > 0) { mesh.transform(m_trafo, true); // apply XY shift - mesh.translate(- unscale(m_copies_shift(0)), - unscale(m_copies_shift(1)), 0); + mesh.translate(- unscale(m_center_offset.x()), - unscale(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 PrintObject::slice_volumes(const std::vector &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 PrintObject::slice_volume(const std::vector &z, const ModelVolume &volume) const +std::vector PrintObject::slice_volume(const std::vector &z, SlicingMode mode, const ModelVolume &volume) const { std::vector layers; if (! z.empty()) { @@ -2027,7 +2084,7 @@ std::vector PrintObject::slice_volume(const std::vector &z, c if (mesh.stl.stats.number_of_facets > 0) { mesh.transform(m_trafo, true); // apply XY shift - mesh.translate(- unscale(m_copies_shift(0)), - unscale(m_copies_shift(1)), 0); + mesh.translate(- unscale(m_center_offset.x()), - unscale(m_center_offset.y()), 0); // perform actual slicing TriangleMeshSlicer mslicer; const Print *print = this->print(); @@ -2035,7 +2092,7 @@ std::vector PrintObject::slice_volume(const std::vector &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 PrintObject::slice_volume(const std::vector &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 PrintObject::slice_volume(const std::vector &z, const std::vector &ranges, const ModelVolume &volume) const +std::vector PrintObject::slice_volume(const std::vector &z, const std::vector &ranges, SlicingMode mode, const ModelVolume &volume) const { std::vector 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 z_filtered; std::vector> n_filtered; @@ -2065,7 +2122,7 @@ std::vector PrintObject::slice_volume(const std::vector &z, c n_filtered.emplace_back(std::make_pair(first, i)); } if (! n_filtered.empty()) { - std::vector layers = this->slice_volume(z_filtered, volume); + std::vector layers = this->slice_volume(z_filtered, mode, volume); out.assign(z.size(), ExPolygons()); i = 0; for (const std::pair &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; } } diff --git a/src/libslic3r/SLA/SLABoostAdapter.hpp b/src/libslic3r/SLA/BoostAdapter.hpp similarity index 97% rename from src/libslic3r/SLA/SLABoostAdapter.hpp rename to src/libslic3r/SLA/BoostAdapter.hpp index 1e9daf4613..b7b3c63a6c 100644 --- a/src/libslic3r/SLA/SLABoostAdapter.hpp +++ b/src/libslic3r/SLA/BoostAdapter.hpp @@ -1,7 +1,7 @@ -#ifndef SLABOOSTADAPTER_HPP -#define SLABOOSTADAPTER_HPP +#ifndef SLA_BOOSTADAPTER_HPP +#define SLA_BOOSTADAPTER_HPP -#include "SLA/SLABoilerPlate.hpp" +#include #include namespace boost { diff --git a/src/libslic3r/SLA/Clustering.hpp b/src/libslic3r/SLA/Clustering.hpp new file mode 100644 index 0000000000..1b0d47d953 --- /dev/null +++ b/src/libslic3r/SLA/Clustering.hpp @@ -0,0 +1,30 @@ +#ifndef SLA_CLUSTERING_HPP +#define SLA_CLUSTERING_HPP + +#include +#include +#include + +namespace Slic3r { namespace sla { + +using ClusterEl = std::vector; +using ClusteredPoints = std::vector; + +// Clustering a set of points by the given distance. +ClusteredPoints cluster(const std::vector& indices, + std::function pointfn, + double dist, + unsigned max_points); + +ClusteredPoints cluster(const PointSet& points, + double dist, + unsigned max_points); + +ClusteredPoints cluster( + const std::vector& indices, + std::function pointfn, + std::function predicate, + unsigned max_points); + +}} +#endif // CLUSTERING_HPP diff --git a/src/libslic3r/SLA/Common.cpp b/src/libslic3r/SLA/Common.cpp new file mode 100644 index 0000000000..d2aac18fda --- /dev/null +++ b/src/libslic3r/SLA/Common.cpp @@ -0,0 +1,769 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// 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 +#include "boost/geometry/index/rtree.hpp" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4244) +#pragma warning(disable: 4267) +#endif +#include +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include + +#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 +PointIndex::query(std::function fn) const +{ + namespace bgi = boost::geometry::index; + + std::vector ret; + m_impl->m_store.query(bgi::satisfies(fn), std::back_inserter(ret)); + return ret; +} + +std::vector PointIndex::nearest(const Vec3d &el, unsigned k = 1) const +{ + namespace bgi = boost::geometry::index; + std::vector 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 fn) +{ + for(auto& el : m_impl->m_store) fn(el); +} + +void PointIndex::foreach(std::function fn) const +{ + for(const auto &el : m_impl->m_store) fn(el); +} + +/* ************************************************************************** + * BoxIndex implementation + * ************************************************************************** */ + +class BoxIndex::Impl { +public: + using BoostIndex = boost::geometry::index:: + rtree /* ? */>; + + 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 BoxIndex::query(const BoundingBox &qrbb, + BoxIndex::QueryType qt) +{ + namespace bgi = boost::geometry::index; + + std::vector 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 fn) +{ + for(auto& el : m_impl->m_store) fn(el); +} + + +/* **************************************************************************** + * EigenMesh3D implementation + * ****************************************************************************/ + +class EigenMesh3D::AABBImpl: public igl::AABB { +public: +#ifdef SLIC3R_SLA_NEEDS_WINDTREE + igl::WindingNumberAABB 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(); + V.block<1, 3>(3 * i + 1, 0) = facet.vertex[1].cast(); + V.block<1, 3>(3 * i + 2, 0) = facet.vertex[2].cast(); + 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 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::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::query_ray_hits(const Vec3d &s, const Vec3d &dir) const +{ + std::vector outs; + std::vector 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& 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 hole_isects; + hole_isects.reserve(m_holes.size()); + + auto sf = s.cast(); + auto dirf = dir.cast(); + + // Collect hits on all holes, preserve information about entry/exit + for (const sla::DrainHole& hole : m_holes) { + std::array, 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 pp = p; + Eigen::Matrix 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; + + auto line = Line3D::Through(e1, e2); + double d = line.distance(p); + return std::abs(d) < eps; +} + +template 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 thr, // throw on cancel + const std::vector& pt_indices) +{ + if (points.rows() == 0 || mesh.V().rows() == 0 || mesh.F().rows() == 0) + return {}; + + std::vector 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 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 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( + const Index3D &, const PointIndexEl &)> qfn) +{ + using Elems = std::vector; + + // Recursive function for visiting all the points in a given distance to + // each other + std::function group = + [&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster) + { + for(auto& p : pts) { + std::vector 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 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 distance_queryfn(const Index3D& sindex, + const PointIndexEl& p, + double dist, + unsigned max_points) +{ + std::vector 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& indices, + std::function 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& indices, + std::function pointfn, + std::function 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 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 diff --git a/src/libslic3r/SLA/Common.hpp b/src/libslic3r/SLA/Common.hpp new file mode 100644 index 0000000000..e1c5930e2a --- /dev/null +++ b/src/libslic3r/SLA/Common.hpp @@ -0,0 +1,32 @@ +#ifndef SLA_COMMON_HPP +#define SLA_COMMON_HPP + +#include +#include +#include +#include +#include + +//#include "SLASpatIndex.hpp" + +//#include +//#include + +// #define SLIC3R_SLA_NEEDS_WINDTREE + +namespace Slic3r { + +// Typedefs from Point.hpp +typedef Eigen::Matrix Vec3f; +typedef Eigen::Matrix Vec3d; +typedef Eigen::Matrix Vec4i; + +namespace sla { + +using PointSet = Eigen::MatrixXd; + +} // namespace sla +} // namespace Slic3r + + +#endif // SLASUPPORTTREE_HPP diff --git a/src/libslic3r/SLA/ConcaveHull.cpp b/src/libslic3r/SLA/ConcaveHull.cpp index dff0617216..d3c0d10224 100644 --- a/src/libslic3r/SLA/ConcaveHull.cpp +++ b/src/libslic3r/SLA/ConcaveHull.cpp @@ -1,7 +1,9 @@ -#include "ConcaveHull.hpp" +#include +#include + #include #include -#include "SLASpatIndex.hpp" + #include namespace Slic3r { @@ -40,9 +42,9 @@ 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, - coord_t delta, - ClipperLib::JoinType jointype) +static ClipperLib::Paths fast_offset(const ClipperLib::Paths &paths, + coord_t delta, + ClipperLib::JoinType jointype) { using ClipperLib::ClipperOffset; using ClipperLib::etClosedPolygon; @@ -73,7 +75,7 @@ Points ConcaveHull::calculate_centroids() const Points centroids = reserve_vector(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; } diff --git a/src/libslic3r/SLA/ConcaveHull.hpp b/src/libslic3r/SLA/ConcaveHull.hpp index 94e16d77cc..dccdafd18c 100644 --- a/src/libslic3r/SLA/ConcaveHull.hpp +++ b/src/libslic3r/SLA/ConcaveHull.hpp @@ -1,5 +1,5 @@ -#ifndef CONCAVEHULL_HPP -#define CONCAVEHULL_HPP +#ifndef SLA_CONCAVEHULL_HPP +#define SLA_CONCAVEHULL_HPP #include diff --git a/src/libslic3r/SLA/SLAConcurrency.hpp b/src/libslic3r/SLA/Concurrency.hpp similarity index 96% rename from src/libslic3r/SLA/SLAConcurrency.hpp rename to src/libslic3r/SLA/Concurrency.hpp index 4beb2aead1..8620c67b19 100644 --- a/src/libslic3r/SLA/SLAConcurrency.hpp +++ b/src/libslic3r/SLA/Concurrency.hpp @@ -1,5 +1,5 @@ -#ifndef SLACONCURRENCY_H -#define SLACONCURRENCY_H +#ifndef SLA_CONCURRENCY_H +#define SLA_CONCURRENCY_H #include #include diff --git a/src/libslic3r/SLA/Contour3D.cpp b/src/libslic3r/SLA/Contour3D.cpp new file mode 100644 index 0000000000..e39672b8bd --- /dev/null +++ b/src/libslic3r/SLA/Contour3D.cpp @@ -0,0 +1,149 @@ +#include +#include + +#include + +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()); + + 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()); + + 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()); + + 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 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 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 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 diff --git a/src/libslic3r/SLA/Contour3D.hpp b/src/libslic3r/SLA/Contour3D.hpp new file mode 100644 index 0000000000..295612f19b --- /dev/null +++ b/src/libslic3r/SLA/Contour3D.hpp @@ -0,0 +1,45 @@ +#ifndef SLA_CONTOUR3D_HPP +#define SLA_CONTOUR3D_HPP + +#include + +#include + +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 points; + std::vector faces3; + std::vector 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 diff --git a/src/libslic3r/SLA/EigenMesh3D.hpp b/src/libslic3r/SLA/EigenMesh3D.hpp new file mode 100644 index 0000000000..e8b869bb46 --- /dev/null +++ b/src/libslic3r/SLA/EigenMesh3D.hpp @@ -0,0 +1,160 @@ +#ifndef SLA_EIGENMESH3D_H +#define SLA_EIGENMESH3D_H + +#include +#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 m_aabb; + + // This holds a copy of holes in the mesh. Initialized externally + // by load_mesh setter. + std::vector 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::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& 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 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& 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 throw_on_cancel = [](){}, + const std::vector& selected_points = {}); + +}} // namespace Slic3r::sla + +#endif // EIGENMESH3D_H diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp new file mode 100644 index 0000000000..c4a616d93a --- /dev/null +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -0,0 +1,277 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +//! macro used to mark string used at localization, +//! return same string +#define L(s) Slic3r::I18N::translate(s) + +namespace Slic3r { +namespace sla { + +template> +inline void _scale(S s, TriangleMesh &m) { m.scale(float(s)); } + +template> +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 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( + _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()); + for(auto& p : hole.points) p = q * p + pos.cast(); + + 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 plane(normal, pos); + float dist = plane.signedDistance(pt); + if (dist < float(EPSILON) || dist > height) + return false; + + Eigen::ParametrizedLine 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, 2>& out) + const +{ + assert(is_approx(normal.norm(), 1.f)); + const Eigen::ParametrizedLine 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 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(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(); + ++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(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 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(); + 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 & obj_slices, + const std::vector &slicegrid, + float closing_radius, + const sla::DrainHoles & holes, + std::function 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 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 diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp new file mode 100644 index 0000000000..cc7d310eae --- /dev/null +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -0,0 +1,75 @@ +#ifndef SLA_HOLLOWING_HPP +#define SLA_HOLLOWING_HPP + +#include +#include +#include +#include + +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, 2>& out) const; + + Contour3D to_mesh() const; + + template inline void serialize(Archive &ar) + { + ar(pos, normal, radius, height); + } + + static constexpr size_t steps = 32; +}; + +using DrainHoles = std::vector; + +std::unique_ptr generate_interior(const TriangleMesh &mesh, + const HollowingConfig & = {}, + const JobController &ctl = {}); + +void cut_drainholes(std::vector & obj_slices, + const std::vector &slicegrid, + float closing_radius, + const sla::DrainHoles & holes, + std::function thr); + +} +} + +#endif // HOLLOWINGFILTER_H diff --git a/src/libslic3r/SLA/JobController.hpp b/src/libslic3r/SLA/JobController.hpp new file mode 100644 index 0000000000..3baa3d12d1 --- /dev/null +++ b/src/libslic3r/SLA/JobController.hpp @@ -0,0 +1,31 @@ +#ifndef SLA_JOBCONTROLLER_HPP +#define SLA_JOBCONTROLLER_HPP + +#include + +namespace Slic3r { namespace sla { + +/// A Control structure for the support calculation. Consists of the status +/// indicator callback and the stop condition predicate. +struct JobController +{ + using StatusFn = std::function; + using StopCond = std::function; + using CancelFn = std::function; + + // This will signal the status of the calculation to the front-end + StatusFn statuscb = [](unsigned, const std::string&){}; + + // Returns true if the calculation should be aborted. + StopCond stopcondition = [](){ return false; }; + + // Similar to cancel callback. This should check the stop condition and + // if true, throw an appropriate exception. (TriangleMeshSlicer needs this) + // consider it a hard abort. stopcondition is permits the algorithm to + // terminate itself + CancelFn cancelfn = [](){}; +}; + +}} // namespace Slic3r::sla + +#endif // JOBCONTROLLER_HPP diff --git a/src/libslic3r/SLA/SLAPad.cpp b/src/libslic3r/SLA/Pad.cpp similarity index 98% rename from src/libslic3r/SLA/SLAPad.cpp rename to src/libslic3r/SLA/Pad.cpp index 6f0db8e6c7..cf17867583 100644 --- a/src/libslic3r/SLA/SLAPad.cpp +++ b/src/libslic3r/SLA/Pad.cpp @@ -1,10 +1,12 @@ -#include "SLAPad.hpp" -#include "SLABoilerPlate.hpp" -#include "SLASpatIndex.hpp" +#include +#include +#include +#include +#include + #include "ConcaveHull.hpp" #include "boost/log/trivial.hpp" -#include "SLABoostAdapter.hpp" #include "ClipperUtils.hpp" #include "Tesselate.hpp" #include "MTUtils.hpp" @@ -69,7 +71,7 @@ Contour3D walls( // Shorthand for the vertex arrays auto& upts = upper.points, &lpts = lower.points; - auto& rpts = ret.points; auto& ind = ret.indices; + auto& rpts = ret.points; auto& ind = ret.faces3; // If the Z levels are flipped, or the offset difference is negative, we // will interpret that as the triangles normals should be inverted. @@ -636,7 +638,7 @@ void pad_blueprint(const TriangleMesh & mesh, TriangleMeshSlicer slicer(&mesh); auto out = reserve_vector(heights.size()); - slicer.slice(heights, 0.f, &out, thrfn); + slicer.slice(heights, SlicingMode::Regular, 0.f, &out, thrfn); size_t count = 0; for(auto& o : out) count += o.size(); @@ -676,7 +678,7 @@ void create_pad(const ExPolygons &sup_blueprint, ThrowOnCancel thr) { Contour3D t = create_pad_geometry(sup_blueprint, model_blueprint, cfg, thr); - out.merge(mesh(std::move(t))); + out.merge(to_triangle_mesh(std::move(t))); } std::string PadConfig::validate() const diff --git a/src/libslic3r/SLA/SLAPad.hpp b/src/libslic3r/SLA/Pad.hpp similarity index 98% rename from src/libslic3r/SLA/SLAPad.hpp rename to src/libslic3r/SLA/Pad.hpp index 4abcdd281e..61eec2dd1e 100644 --- a/src/libslic3r/SLA/SLAPad.hpp +++ b/src/libslic3r/SLA/Pad.hpp @@ -1,5 +1,5 @@ -#ifndef SLABASEPOOL_HPP -#define SLABASEPOOL_HPP +#ifndef SLA_PAD_HPP +#define SLA_PAD_HPP #include #include diff --git a/src/libslic3r/SLA/SLARaster.cpp b/src/libslic3r/SLA/Raster.cpp similarity index 99% rename from src/libslic3r/SLA/SLARaster.cpp rename to src/libslic3r/SLA/Raster.cpp index 091cadd231..9b7f1db7d9 100644 --- a/src/libslic3r/SLA/SLARaster.cpp +++ b/src/libslic3r/SLA/Raster.cpp @@ -3,7 +3,7 @@ #include -#include "SLARaster.hpp" +#include #include "libslic3r/ExPolygon.hpp" #include "libslic3r/MTUtils.hpp" #include diff --git a/src/libslic3r/SLA/SLARaster.hpp b/src/libslic3r/SLA/Raster.hpp similarity index 98% rename from src/libslic3r/SLA/SLARaster.hpp rename to src/libslic3r/SLA/Raster.hpp index b3d73536bb..4d76b32909 100644 --- a/src/libslic3r/SLA/SLARaster.hpp +++ b/src/libslic3r/SLA/Raster.hpp @@ -1,5 +1,5 @@ -#ifndef SLARASTER_HPP -#define SLARASTER_HPP +#ifndef SLA_RASTER_HPP +#define SLA_RASTER_HPP #include #include diff --git a/src/libslic3r/SLA/SLARasterWriter.cpp b/src/libslic3r/SLA/RasterWriter.cpp similarity index 69% rename from src/libslic3r/SLA/SLARasterWriter.cpp rename to src/libslic3r/SLA/RasterWriter.cpp index 6ac86827ef..13aef7d8a3 100644 --- a/src/libslic3r/SLA/SLARasterWriter.cpp +++ b/src/libslic3r/SLA/RasterWriter.cpp @@ -1,6 +1,10 @@ -#include "SLARasterWriter.hpp" -#include "libslic3r/Zipper.hpp" -#include "libslic3r/Time.hpp" +#include + +#include + +#include "libslic3r/PrintConfig.hpp" +#include +#include #include "ExPolygon.hpp" #include @@ -10,14 +14,16 @@ namespace Slic3r { namespace sla { -std::string RasterWriter::createIniContent(const std::string& projectname) const +void RasterWriter::write_ini(const std::map &m, std::string &ini) +{ + for (auto ¶m : m) ini += param.first + " = " + param.second + "\n"; +} + +std::string RasterWriter::create_ini_content(const std::string& projectname) const { std::string out("action = print\njobDir = "); out += projectname + "\n"; - - for (auto ¶m : m_config) - out += param.first + " = " + param.second + "\n"; - + write_ini(m_config, out); return out; } @@ -51,7 +57,12 @@ void RasterWriter::save(Zipper &zipper, const std::string &prjname) zipper.add_entry("config.ini"); - zipper << createIniContent(project); + zipper << create_ini_content(project); + + zipper.add_entry("prusaslicer.ini"); + std::string prusaslicer_ini; + write_ini(m_slicer_config, prusaslicer_ini); + zipper << prusaslicer_ini; for(unsigned i = 0; i < m_layers_rst.size(); i++) { @@ -87,6 +98,29 @@ std::string get_cfg_value(const DynamicPrintConfig &cfg, const std::string &key) return ret; } +void append_full_config(const DynamicPrintConfig &cfg, std::map &keys) +{ + using namespace std::literals::string_view_literals; + + // Sorted list of config keys, which shall not be stored into the ini. + static constexpr auto banned_keys = { + "compatible_printers"sv, + "compatible_prints"sv, + "print_host"sv, + "printhost_apikey"sv, + "printhost_cafile"sv + }; + + assert(std::is_sorted(banned_keys.begin(), banned_keys.end())); + auto is_banned = [](const std::string &key) { + return std::binary_search(banned_keys.begin(), banned_keys.end(), key); + }; + + for (const std::string &key : cfg.keys()) + if (! is_banned(key) && ! cfg.option(key)->is_nil()) + keys[key] = cfg.opt_serialize(key); +} + } // namespace void RasterWriter::set_config(const DynamicPrintConfig &cfg) @@ -99,9 +133,9 @@ void RasterWriter::set_config(const DynamicPrintConfig &cfg) m_config["printerVariant"] = get_cfg_value(cfg, "printer_variant"); m_config["printerProfile"] = get_cfg_value(cfg, "printer_settings_id"); m_config["printProfile"] = get_cfg_value(cfg, "sla_print_settings_id"); - m_config["fileCreationTimestamp"] = Utils::utc_timestamp(); m_config["prusaSlicerVersion"] = SLIC3R_BUILD_ID; + append_full_config(cfg, m_slicer_config); } void RasterWriter::set_statistics(const PrintStatistics &stats) diff --git a/src/libslic3r/SLA/SLARasterWriter.hpp b/src/libslic3r/SLA/RasterWriter.hpp similarity index 89% rename from src/libslic3r/SLA/SLARasterWriter.hpp rename to src/libslic3r/SLA/RasterWriter.hpp index 93a315c821..75162893de 100644 --- a/src/libslic3r/SLA/SLARasterWriter.hpp +++ b/src/libslic3r/SLA/RasterWriter.hpp @@ -1,5 +1,5 @@ -#ifndef SLARASTERWRITER_HPP -#define SLARASTERWRITER_HPP +#ifndef SLA_RASTERWRITER_HPP +#define SLA_RASTERWRITER_HPP // For png export of the sliced model #include @@ -9,12 +9,14 @@ #include #include -#include "libslic3r/PrintConfig.hpp" +#include +#include -#include "SLARaster.hpp" -#include "libslic3r/Zipper.hpp" +namespace Slic3r { -namespace Slic3r { namespace sla { +class DynamicPrintConfig; + +namespace sla { // API to write the zipped sla output layers and metadata. // Implementation uses PNG raster output. @@ -64,8 +66,10 @@ private: double m_gamma; std::map m_config; + std::map m_slicer_config; - std::string createIniContent(const std::string& projectname) const; + static void write_ini(const std::map &m, std::string &ini); + std::string create_ini_content(const std::string& projectname) const; public: @@ -116,7 +120,7 @@ public: void save(Zipper &zipper, const std::string &prjname = ""); void set_statistics(const PrintStatistics &statistics); - + void set_config(const DynamicPrintConfig &cfg); }; diff --git a/src/libslic3r/SLA/SLARotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp similarity index 97% rename from src/libslic3r/SLA/SLARotfinder.cpp rename to src/libslic3r/SLA/Rotfinder.cpp index 2e64059d68..9ec9837e2a 100644 --- a/src/libslic3r/SLA/SLARotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -2,9 +2,9 @@ #include #include -#include "SLABoilerPlate.hpp" -#include "SLARotfinder.hpp" -#include "SLASupportTree.hpp" +#include +#include +#include #include "Model.hpp" namespace Slic3r { diff --git a/src/libslic3r/SLA/SLARotfinder.hpp b/src/libslic3r/SLA/Rotfinder.hpp similarity index 95% rename from src/libslic3r/SLA/SLARotfinder.hpp rename to src/libslic3r/SLA/Rotfinder.hpp index b8cec859e2..4469f9731d 100644 --- a/src/libslic3r/SLA/SLARotfinder.hpp +++ b/src/libslic3r/SLA/Rotfinder.hpp @@ -1,5 +1,5 @@ -#ifndef SLAROTFINDER_HPP -#define SLAROTFINDER_HPP +#ifndef SLA_ROTFINDER_HPP +#define SLA_ROTFINDER_HPP #include #include diff --git a/src/libslic3r/SLA/SLABoilerPlate.hpp b/src/libslic3r/SLA/SLABoilerPlate.hpp deleted file mode 100644 index d7ce26bb2b..0000000000 --- a/src/libslic3r/SLA/SLABoilerPlate.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef SLABOILERPLATE_HPP -#define SLABOILERPLATE_HPP - -#include -#include -#include - -#include -#include - -#include "SLACommon.hpp" -#include "SLASpatIndex.hpp" - -namespace Slic3r { -namespace sla { - -/// Intermediate struct for a 3D mesh -struct Contour3D { - Pointf3s points; - std::vector indices; - - Contour3D& merge(const Contour3D& ctr) - { - auto s3 = coord_t(points.size()); - auto s = indices.size(); - - points.insert(points.end(), ctr.points.begin(), ctr.points.end()); - indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end()); - - for(size_t n = s; n < indices.size(); n++) { - auto& idx = indices[n]; idx.x() += s3; idx.y() += s3; idx.z() += s3; - } - - return *this; - } - - Contour3D& merge(const Pointf3s& triangles) - { - const size_t offs = points.size(); - points.insert(points.end(), triangles.begin(), triangles.end()); - indices.reserve(indices.size() + points.size() / 3); - - for(int i = int(offs); i < int(points.size()); i += 3) - indices.emplace_back(i, i + 1, i + 2); - - return *this; - } - - // Write the index triangle structure to OBJ file for debugging purposes. - void to_obj(std::ostream& stream) - { - for(auto& p : points) { - stream << "v " << p.transpose() << "\n"; - } - - for(auto& f : indices) { - stream << "f " << (f + Vec3i(1, 1, 1)).transpose() << "\n"; - } - } -}; - -using ClusterEl = std::vector; -using ClusteredPoints = std::vector; - -// Clustering a set of points by the given distance. -ClusteredPoints cluster(const std::vector& indices, - std::function pointfn, - double dist, - unsigned max_points); - -ClusteredPoints cluster(const PointSet& points, - double dist, - unsigned max_points); - -ClusteredPoints cluster( - const std::vector& indices, - std::function pointfn, - std::function predicate, - unsigned max_points); - - -// 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& mesh, - double eps = 0.05, // min distance from edges - std::function throw_on_cancel = [](){}, - const std::vector& selected_points = {}); - -/// Mesh from an existing contour. -inline TriangleMesh mesh(const Contour3D& ctour) { - return {ctour.points, ctour.indices}; -} - -/// Mesh from an evaporating 3D contour -inline TriangleMesh mesh(Contour3D&& ctour) { - return {std::move(ctour.points), std::move(ctour.indices)}; -} - -} -} - -#endif // SLABOILERPLATE_HPP diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp deleted file mode 100644 index 97b4596761..0000000000 --- a/src/libslic3r/SLA/SLACommon.hpp +++ /dev/null @@ -1,187 +0,0 @@ -#ifndef SLACOMMON_HPP -#define SLACOMMON_HPP - -#include -#include -#include - -// #define SLIC3R_SLA_NEEDS_WINDTREE - -namespace Slic3r { - -// Typedefs from Point.hpp -typedef Eigen::Matrix Vec3f; -typedef Eigen::Matrix Vec3d; - -class TriangleMesh; - -namespace sla { - -// An enum to keep track of where the current points on the ModelObject came from. -enum class PointsStatus { - NoPoints, // No points were generated so far. - Generating, // The autogeneration algorithm triggered, but not yet finished. - AutoGenerated, // Points were autogenerated (i.e. copied from the backend). - UserModified // User has done some edits. -}; - -struct SupportPoint -{ - Vec3f pos; - float head_front_radius; - bool is_new_island; - - SupportPoint() - : pos(Vec3f::Zero()), head_front_radius(0.f), is_new_island(false) - {} - - SupportPoint(float pos_x, - float pos_y, - float pos_z, - float head_radius, - bool new_island) - : pos(pos_x, pos_y, pos_z) - , head_front_radius(head_radius) - , is_new_island(new_island) - {} - - SupportPoint(Vec3f position, float head_radius, bool new_island) - : pos(position) - , head_front_radius(head_radius) - , is_new_island(new_island) - {} - - SupportPoint(Eigen::Matrix data) - : pos(data(0), data(1), data(2)) - , head_front_radius(data(3)) - , is_new_island(data(4) != 0.f) - {} - - bool operator==(const SupportPoint &sp) const - { - return (pos == sp.pos) && head_front_radius == sp.head_front_radius && - is_new_island == sp.is_new_island; - } - bool operator!=(const SupportPoint &sp) const { return !(sp == (*this)); } - - template void serialize(Archive &ar) - { - ar(pos, head_front_radius, is_new_island); - } -}; - -using SupportPoints = std::vector; - -/// An index-triangle structure for libIGL functions. Also serves as an -/// alternative (raw) input format for the SLASupportTree -class EigenMesh3D { - class AABBImpl; - - Eigen::MatrixXd m_V; - Eigen::MatrixXi m_F; - double m_ground_level = 0, m_gnd_offset = 0; - - std::unique_ptr m_aabb; -public: - - EigenMesh3D(const TriangleMesh&); - EigenMesh3D(const EigenMesh3D& other); - EigenMesh3D& operator=(const EigenMesh3D&); - - ~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 { - double m_t = std::nan(""); - int m_face_id = -1; - const EigenMesh3D *m_mesh = nullptr; - Vec3d m_dir; - Vec3d m_source; - 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 can create a placeholder object which is invalid (not created - // by a query_ray_hit call) but the distance can be preset to - // a specific value for distinguishing the placeholder. - inline hit_result(double val = std::nan("")): m_t(val) {} - - inline double distance() const { return m_t; } - inline const Vec3d& direction() const { return m_dir; } - inline Vec3d position() const { return m_source + m_dir * m_t; } - inline int face() const { return m_face_id; } - inline bool is_valid() const { return m_mesh != nullptr; } - - // Hit_result can decay into a double as the hit distance. - inline operator double() const { return distance(); } - - inline Vec3d normal() const { - if(m_face_id < 0 || !is_valid()) return {}; - auto trindex = m_mesh->m_F.row(m_face_id); - const Vec3d& p1 = m_mesh->V().row(trindex(0)); - const Vec3d& p2 = m_mesh->V().row(trindex(1)); - const Vec3d& p3 = m_mesh->V().row(trindex(2)); - Eigen::Vector3d U = p2 - p1; - Eigen::Vector3d V = p3 - p1; - return U.cross(V).normalized(); - } - - inline bool is_inside() { - return m_face_id >= 0 && normal().dot(m_dir) > 0; - } - }; - - // 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; - - 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); - } -}; - -using PointSet = Eigen::MatrixXd; - -} // namespace sla -} // namespace Slic3r - - -#endif // SLASUPPORTTREE_HPP diff --git a/src/libslic3r/SLA/SLASpatIndex.hpp b/src/libslic3r/SLA/SpatIndex.hpp similarity index 97% rename from src/libslic3r/SLA/SLASpatIndex.hpp rename to src/libslic3r/SLA/SpatIndex.hpp index 20b6fcd589..2955cdcdf6 100644 --- a/src/libslic3r/SLA/SLASpatIndex.hpp +++ b/src/libslic3r/SLA/SpatIndex.hpp @@ -1,5 +1,5 @@ -#ifndef SPATINDEX_HPP -#define SPATINDEX_HPP +#ifndef SLA_SPATINDEX_HPP +#define SLA_SPATINDEX_HPP #include #include diff --git a/src/libslic3r/SLA/SupportPoint.hpp b/src/libslic3r/SLA/SupportPoint.hpp new file mode 100644 index 0000000000..202a614c32 --- /dev/null +++ b/src/libslic3r/SLA/SupportPoint.hpp @@ -0,0 +1,69 @@ +#ifndef SLA_SUPPORTPOINT_HPP +#define SLA_SUPPORTPOINT_HPP + +#include +#include +#include + +namespace Slic3r { namespace sla { + +// An enum to keep track of where the current points on the ModelObject came from. +enum class PointsStatus { + NoPoints, // No points were generated so far. + Generating, // The autogeneration algorithm triggered, but not yet finished. + AutoGenerated, // Points were autogenerated (i.e. copied from the backend). + UserModified // User has done some edits. +}; + +struct SupportPoint +{ + Vec3f pos; + float head_front_radius; + bool is_new_island; + + SupportPoint() + : pos(Vec3f::Zero()), head_front_radius(0.f), is_new_island(false) + {} + + SupportPoint(float pos_x, + float pos_y, + float pos_z, + float head_radius, + bool new_island) + : pos(pos_x, pos_y, pos_z) + , head_front_radius(head_radius) + , is_new_island(new_island) + {} + + SupportPoint(Vec3f position, float head_radius, bool new_island) + : pos(position) + , head_front_radius(head_radius) + , is_new_island(new_island) + {} + + SupportPoint(Eigen::Matrix data) + : pos(data(0), data(1), data(2)) + , head_front_radius(data(3)) + , is_new_island(data(4) != 0.f) + {} + + bool operator==(const SupportPoint &sp) const + { + float rdiff = std::abs(head_front_radius - sp.head_front_radius); + return (pos == sp.pos) && rdiff < float(EPSILON) && + is_new_island == sp.is_new_island; + } + + bool operator!=(const SupportPoint &sp) const { return !(sp == (*this)); } + + template void serialize(Archive &ar) + { + ar(pos, head_front_radius, is_new_island); + } +}; + +using SupportPoints = std::vector; + +}} // namespace Slic3r::sla + +#endif // SUPPORTPOINT_HPP diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp similarity index 89% rename from src/libslic3r/SLA/SLAAutoSupports.cpp rename to src/libslic3r/SLA/SupportPointGenerator.cpp index 65f5901431..78c2ced356 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -3,7 +3,7 @@ #include -#include "SLAAutoSupports.hpp" +#include "SupportPointGenerator.hpp" #include "Model.hpp" #include "ExPolygon.hpp" #include "SVG.hpp" @@ -18,7 +18,7 @@ namespace Slic3r { namespace sla { -/*float SLAAutoSupports::approximate_geodesic_distance(const Vec3d& p1, const Vec3d& p2, Vec3d& n1, Vec3d& n2) +/*float SupportPointGenerator::approximate_geodesic_distance(const Vec3d& p1, const Vec3d& p2, Vec3d& n1, Vec3d& n2) { n1.normalize(); n2.normalize(); @@ -36,7 +36,7 @@ namespace sla { } -float SLAAutoSupports::get_required_density(float angle) const +float SupportPointGenerator::get_required_density(float angle) const { // calculation would be density_0 * cos(angle). To provide one more degree of freedom, we will scale the angle // to get the user-set density for 45 deg. So it ends up as density_0 * cos(K * angle). @@ -44,27 +44,45 @@ float SLAAutoSupports::get_required_density(float angle) const return std::max(0.f, float(m_config.density_at_horizontal * cos(K*angle))); } -float SLAAutoSupports::distance_limit(float angle) const +float SupportPointGenerator::distance_limit(float angle) const { return 1./(2.4*get_required_density(angle)); }*/ -SLAAutoSupports::SLAAutoSupports(const sla::EigenMesh3D & emesh, - const std::vector &slices, - const std::vector & heights, - const Config & config, - std::function throw_on_cancel, - std::function statusfn) +SupportPointGenerator::SupportPointGenerator( + const sla::EigenMesh3D &emesh, + const std::vector &slices, + const std::vector & heights, + const Config & config, + std::function throw_on_cancel, + std::function statusfn) + : SupportPointGenerator(emesh, config, throw_on_cancel, statusfn) +{ + std::random_device rd; + m_rng.seed(rd()); + execute(slices, heights); +} + +SupportPointGenerator::SupportPointGenerator( + const EigenMesh3D &emesh, + const SupportPointGenerator::Config &config, + std::function throw_on_cancel, + std::function statusfn) : m_config(config) , m_emesh(emesh) , m_throw_on_cancel(throw_on_cancel) , m_statusfn(statusfn) +{ +} + +void SupportPointGenerator::execute(const std::vector &slices, + const std::vector & heights) { process(slices, heights); project_onto_mesh(m_output); } -void SLAAutoSupports::project_onto_mesh(std::vector& points) const +void SupportPointGenerator::project_onto_mesh(std::vector& points) const { // The function makes sure that all the points are really exactly placed on the mesh. @@ -77,36 +95,29 @@ void SLAAutoSupports::project_onto_mesh(std::vector& points) m_throw_on_cancel(); Vec3f& p = points[point_id].pos; // Project the point upward and downward and choose the closer intersection with the mesh. - //bool up = igl::ray_mesh_intersect(p.cast(), Vec3f(0., 0., 1.), m_V, m_F, hit_up); - //bool down = igl::ray_mesh_intersect(p.cast(), Vec3f(0., 0., -1.), m_V, m_F, hit_down); - sla::EigenMesh3D::hit_result hit_up = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., 1.)); sla::EigenMesh3D::hit_result hit_down = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., -1.)); - bool up = hit_up.face() != -1; - bool down = hit_down.face() != -1; + bool up = hit_up.is_hit(); + bool down = hit_down.is_hit(); if (!up && !down) continue; sla::EigenMesh3D::hit_result& hit = (!down || (hit_up.distance() < hit_down.distance())) ? hit_up : hit_down; - //int fid = hit.face(); - //Vec3f bc(1-hit.u-hit.v, hit.u, hit.v); - //p = (bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2))).cast(); - p = p + (hit.distance() * hit.direction()).cast(); } }); } -static std::vector make_layers( +static std::vector make_layers( const std::vector& slices, const std::vector& heights, std::function throw_on_cancel) { assert(slices.size() == heights.size()); // Allocate empty layers. - std::vector layers; + std::vector layers; layers.reserve(slices.size()); for (size_t i = 0; i < slices.size(); ++ i) layers.emplace_back(i, heights[i]); @@ -122,7 +133,7 @@ static std::vector make_layers( if ((layer_id % 8) == 0) // Don't call the following function too often as it flushes CPU write caches due to synchronization primitves. throw_on_cancel(); - SLAAutoSupports::MyLayer &layer = layers[layer_id]; + SupportPointGenerator::MyLayer &layer = layers[layer_id]; const ExPolygons &islands = slices[layer_id]; //FIXME WTF? const float height = (layer_id>2 ? heights[layer_id-3] : heights[0]-(heights[1]-heights[0])); @@ -143,8 +154,8 @@ static std::vector make_layers( if ((layer_id % 2) == 0) // Don't call the following function too often as it flushes CPU write caches due to synchronization primitves. throw_on_cancel(); - SLAAutoSupports::MyLayer &layer_above = layers[layer_id]; - SLAAutoSupports::MyLayer &layer_below = layers[layer_id - 1]; + SupportPointGenerator::MyLayer &layer_above = layers[layer_id]; + SupportPointGenerator::MyLayer &layer_below = layers[layer_id - 1]; //FIXME WTF? const float layer_height = (layer_id!=0 ? heights[layer_id]-heights[layer_id-1] : heights[0]); const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports @@ -152,8 +163,8 @@ static std::vector make_layers( const float slope_angle = 75.f * (float(M_PI)/180.f); // smaller number - less supports const float slope_offset = float(scale_(layer_height / std::tan(slope_angle))); //FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands. - for (SLAAutoSupports::Structure &top : layer_above.islands) { - for (SLAAutoSupports::Structure &bottom : layer_below.islands) { + for (SupportPointGenerator::Structure &top : layer_above.islands) { + for (SupportPointGenerator::Structure &bottom : layer_below.islands) { float overlap_area = top.overlap_area(bottom); if (overlap_area > 0) { top.islands_below.emplace_back(&bottom, overlap_area); @@ -191,13 +202,13 @@ static std::vector make_layers( return layers; } -void SLAAutoSupports::process(const std::vector& slices, const std::vector& heights) +void SupportPointGenerator::process(const std::vector& slices, const std::vector& heights) { -#ifdef SLA_AUTOSUPPORTS_DEBUG +#ifdef SLA_SUPPORTPOINTGEN_DEBUG std::vector> islands; -#endif /* SLA_AUTOSUPPORTS_DEBUG */ +#endif /* SLA_SUPPORTPOINTGEN_DEBUG */ - std::vector layers = make_layers(slices, heights, m_throw_on_cancel); + std::vector layers = make_layers(slices, heights, m_throw_on_cancel); PointGrid3D point_grid; point_grid.cell_size = Vec3f(10.f, 10.f, 10.f); @@ -206,8 +217,8 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: double status = 0; for (unsigned int layer_id = 0; layer_id < layers.size(); ++ layer_id) { - SLAAutoSupports::MyLayer *layer_top = &layers[layer_id]; - SLAAutoSupports::MyLayer *layer_bottom = (layer_id > 0) ? &layers[layer_id - 1] : nullptr; + SupportPointGenerator::MyLayer *layer_top = &layers[layer_id]; + SupportPointGenerator::MyLayer *layer_bottom = (layer_id > 0) ? &layers[layer_id - 1] : nullptr; std::vector support_force_bottom; if (layer_bottom != nullptr) { support_force_bottom.assign(layer_bottom->islands.size(), 0.f); @@ -263,13 +274,13 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: status += increment; m_statusfn(int(std::round(status))); -#ifdef SLA_AUTOSUPPORTS_DEBUG +#ifdef SLA_SUPPORTPOINTGEN_DEBUG /*std::string layer_num_str = std::string((i<10 ? "0" : "")) + std::string((i<100 ? "0" : "")) + std::to_string(i); output_expolygons(expolys_top, "top" + layer_num_str + ".svg"); output_expolygons(diff, "diff" + layer_num_str + ".svg"); if (!islands.empty()) output_expolygons(islands, "islands" + layer_num_str + ".svg");*/ -#endif /* SLA_AUTOSUPPORTS_DEBUG */ +#endif /* SLA_SUPPORTPOINTGEN_DEBUG */ } } @@ -449,7 +460,7 @@ static inline std::vector poisson_disk_from_samples(const std::vector raw_samples = sample_expolygon_with_boundary(islands, samples_per_mm2, 5.f / poisson_radius, rng); + + std::vector raw_samples = sample_expolygon_with_boundary(islands, samples_per_mm2, 5.f / poisson_radius, m_rng); std::vector poisson_samples; for (size_t iter = 0; iter < 4; ++ iter) { poisson_samples = poisson_disk_from_samples(raw_samples, poisson_radius, @@ -488,7 +498,7 @@ void SLAAutoSupports::uniformly_cover(const ExPolygons& islands, Structure& stru min_spacing = std::max(m_config.minimal_distance, min_spacing * coeff); } -#ifdef SLA_AUTOSUPPORTS_DEBUG +#ifdef SLA_SUPPORTPOINTGEN_DEBUG { static int irun = 0; Slic3r::SVG svg(debug_out_path("SLA_supports-uniformly_cover-%d.svg", irun ++), get_extents(islands)); @@ -503,7 +513,7 @@ void SLAAutoSupports::uniformly_cover(const ExPolygons& islands, Structure& stru // assert(! poisson_samples.empty()); if (poisson_samples_target < poisson_samples.size()) { - std::shuffle(poisson_samples.begin(), poisson_samples.end(), rng); + std::shuffle(poisson_samples.begin(), poisson_samples.end(), m_rng); poisson_samples.erase(poisson_samples.begin() + poisson_samples_target, poisson_samples.end()); } for (const Vec2f &pt : poisson_samples) { @@ -528,8 +538,8 @@ void remove_bottom_points(std::vector &pts, double gnd_lvl, double pts.erase(endit, pts.end()); } -#ifdef SLA_AUTOSUPPORTS_DEBUG -void SLAAutoSupports::output_structures(const std::vector& structures) +#ifdef SLA_SUPPORTPOINTGEN_DEBUG +void SupportPointGenerator::output_structures(const std::vector& structures) { for (unsigned int i=0 ; i& structures } } -void SLAAutoSupports::output_expolygons(const ExPolygons& expolys, const std::string &filename) +void SupportPointGenerator::output_expolygons(const ExPolygons& expolys, const std::string &filename) { BoundingBox bb(Point(-30000000, -30000000), Point(30000000, 30000000)); Slic3r::SVG svg_cummulative(filename, bb); diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SupportPointGenerator.hpp similarity index 66% rename from src/libslic3r/SLA/SLAAutoSupports.hpp rename to src/libslic3r/SLA/SupportPointGenerator.hpp index d2f50f0a46..738fc57303 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SupportPointGenerator.hpp @@ -1,43 +1,50 @@ -#ifndef SLAAUTOSUPPORTS_HPP_ -#define SLAAUTOSUPPORTS_HPP_ +#ifndef SLA_SUPPORTPOINTGENERATOR_HPP +#define SLA_SUPPORTPOINTGENERATOR_HPP + +#include + +#include +#include +#include #include #include #include -#include #include -// #define SLA_AUTOSUPPORTS_DEBUG +// #define SLA_SUPPORTPOINTGEN_DEBUG -namespace Slic3r { -namespace sla { +namespace Slic3r { namespace sla { -class SLAAutoSupports { +class SupportPointGenerator { public: struct Config { - float density_relative {1.f}; - float minimal_distance {1.f}; - float head_diameter {0.4f}; - /////////////// - inline float support_force() const { return 7.7f / density_relative; } // a force one point can support (arbitrary force unit) - inline float tear_pressure() const { return 1.f; } // pressure that the display exerts (the force unit per mm2) - }; - - SLAAutoSupports(const sla::EigenMesh3D& emesh, const std::vector& slices, - const std::vector& heights, const Config& config, std::function throw_on_cancel, std::function statusfn); + float density_relative {1.f}; + float minimal_distance {1.f}; + float head_diameter {0.4f}; + /////////////// + inline float support_force() const { return 7.7f / density_relative; } // a force one point can support (arbitrary force unit) + inline float tear_pressure() const { return 1.f; } // pressure that the display exerts (the force unit per mm2) + }; + + SupportPointGenerator(const EigenMesh3D& emesh, const std::vector& slices, + const std::vector& heights, const Config& config, std::function throw_on_cancel, std::function statusfn); + + SupportPointGenerator(const EigenMesh3D& emesh, const Config& config, std::function throw_on_cancel, std::function statusfn); + + const std::vector& output() const { return m_output; } + std::vector& output() { return m_output; } + + struct MyLayer; - const std::vector& output() { return m_output; } - - struct MyLayer; - struct Structure { Structure(MyLayer &layer, const ExPolygon& poly, const BoundingBox &bbox, const Vec2f ¢roid, float area, float h) : layer(&layer), polygon(&poly), bbox(bbox), centroid(centroid), area(area), height(h) -#ifdef SLA_AUTOSUPPORTS_DEBUG +#ifdef SLA_SUPPORTPOINTGEN_DEBUG , unique_id(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch())) -#endif /* SLA_AUTOSUPPORTS_DEBUG */ - {} +#endif /* SLA_SUPPORTPOINTGEN_DEBUG */ + {} MyLayer *layer; const ExPolygon* polygon = nullptr; const BoundingBox bbox; @@ -49,24 +56,24 @@ public: float supports_force_this_layer = 0.f; float supports_force_inherited = 0.f; float supports_force_total() const { return this->supports_force_this_layer + this->supports_force_inherited; } -#ifdef SLA_AUTOSUPPORTS_DEBUG +#ifdef SLA_SUPPORTPOINTGEN_DEBUG std::chrono::milliseconds unique_id; -#endif /* SLA_AUTOSUPPORTS_DEBUG */ - +#endif /* SLA_SUPPORTPOINTGEN_DEBUG */ + struct Link { - Link(Structure *island, float overlap_area) : island(island), overlap_area(overlap_area) {} + Link(Structure *island, float overlap_area) : island(island), overlap_area(overlap_area) {} Structure *island; float overlap_area; }; #ifdef NDEBUG - // In release mode, use the optimized container. + // In release mode, use the optimized container. boost::container::small_vector islands_above; boost::container::small_vector islands_below; #else - // In debug mode, use the standard vector, which is well handled by debugger visualizer. - std::vector islands_above; - std::vector islands_below; + // In debug mode, use the standard vector, which is well handled by debugger visualizer. + std::vector islands_above; + std::vector islands_below; #endif // Overhangs, that are dangling considerably. ExPolygons dangling_areas; @@ -74,16 +81,16 @@ public: ExPolygons overhangs; // Overhangs, where the surface must slope. ExPolygons overhangs_slopes; - float overhangs_area; - + float overhangs_area = 0.f; + bool overlaps(const Structure &rhs) const { return this->bbox.overlap(rhs.bbox) && (this->polygon->overlaps(*rhs.polygon) || rhs.polygon->overlaps(*this->polygon)); } float overlap_area(const Structure &rhs) const { double out = 0.; if (this->bbox.overlap(rhs.bbox)) { - Polygons polys = intersection(to_polygons(*this->polygon), to_polygons(*rhs.polygon), false); - for (const Polygon &poly : polys) + Polygons polys = intersection(to_polygons(*this->polygon), to_polygons(*rhs.polygon), false); + for (const Polygon &poly : polys) out += poly.area(); } return float(out); @@ -96,13 +103,13 @@ public: } Polygons polygons_below() const { size_t cnt = 0; - for (const Link &below : this->islands_below) + for (const Link &below : this->islands_below) cnt += 1 + below.island->polygon->holes.size(); Polygons out; out.reserve(cnt); - for (const Link &below : this->islands_below) { + for (const Link &below : this->islands_below) { out.emplace_back(below.island->polygon->contour); - append(out, below.island->polygon->holes); + append(out, below.island->polygon->holes); } return out; } @@ -116,19 +123,19 @@ public: // Positive deficit of the supports. If negative, this area is well supported. If positive, more supports need to be added. float support_force_deficit(const float tear_pressure) const { return this->area * tear_pressure - this->supports_force_total(); } }; - + struct MyLayer { - MyLayer(const size_t layer_id, coordf_t print_z) : layer_id(layer_id), print_z(print_z) {} + MyLayer(const size_t layer_id, coordf_t print_z) : layer_id(layer_id), print_z(print_z) {} size_t layer_id; coordf_t print_z; std::vector islands; }; - + struct RichSupportPoint { Vec3f position; Structure *island; }; - + struct PointGrid3D { struct GridHash { std::size_t operator()(const Vec3i &cell_id) const { @@ -136,23 +143,23 @@ public: } }; typedef std::unordered_multimap Grid; - + Vec3f cell_size; Grid grid; - + Vec3i cell_id(const Vec3f &pos) { return Vec3i(int(floor(pos.x() / cell_size.x())), int(floor(pos.y() / cell_size.y())), int(floor(pos.z() / cell_size.z()))); } - + void insert(const Vec2f &pos, Structure *island) { RichSupportPoint pt; - pt.position = Vec3f(pos.x(), pos.y(), float(island->layer->print_z)); + pt.position = Vec3f(pos.x(), pos.y(), float(island->layer->print_z)); pt.island = island; grid.emplace(cell_id(pt.position), pt); } - + bool collides_with(const Vec2f &pos, Structure *island, float radius) { Vec3f pos3d(pos.x(), pos.y(), float(island->layer->print_z)); Vec3i cell = cell_id(pos3d); @@ -170,41 +177,45 @@ public: } return false; } - + private: bool collides_with(const Vec3f &pos, float radius, Grid::const_iterator it_begin, Grid::const_iterator it_end) { for (Grid::const_iterator it = it_begin; it != it_end; ++ it) { - float dist2 = (it->second.position - pos).squaredNorm(); + float dist2 = (it->second.position - pos).squaredNorm(); if (dist2 < radius * radius) return true; } return false; } }; - + + void execute(const std::vector &slices, + const std::vector & heights); + + void seed(std::mt19937::result_type s) { m_rng.seed(s); } private: - std::vector m_output; - - SLAAutoSupports::Config m_config; - + std::vector m_output; + + SupportPointGenerator::Config m_config; + void process(const std::vector& slices, const std::vector& heights); void uniformly_cover(const ExPolygons& islands, Structure& structure, PointGrid3D &grid3d, bool is_new_island = false, bool just_one = false); - void project_onto_mesh(std::vector& points) const; + void project_onto_mesh(std::vector& points) const; -#ifdef SLA_AUTOSUPPORTS_DEBUG +#ifdef SLA_SUPPORTPOINTGEN_DEBUG static void output_expolygons(const ExPolygons& expolys, const std::string &filename); static void output_structures(const std::vector &structures); -#endif // SLA_AUTOSUPPORTS_DEBUG - - const sla::EigenMesh3D& m_emesh; +#endif // SLA_SUPPORTPOINTGEN_DEBUG + + const EigenMesh3D& m_emesh; std::function m_throw_on_cancel; std::function m_statusfn; + + std::mt19937 m_rng; }; void remove_bottom_points(std::vector &pts, double gnd_lvl, double tolerance); -} // namespace sla -} // namespace Slic3r +}} // namespace Slic3r::sla - -#endif // SLAAUTOSUPPORTS_HPP_ +#endif // SUPPORTPOINTGENERATOR_HPP diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SupportTree.cpp similarity index 90% rename from src/libslic3r/SLA/SLASupportTree.cpp rename to src/libslic3r/SLA/SupportTree.cpp index fea8bf731c..5ee35f9e0a 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SupportTree.cpp @@ -4,10 +4,10 @@ */ #include -#include "SLASupportTree.hpp" -#include "SLABoilerPlate.hpp" -#include "SLASpatIndex.hpp" -#include "SLASupportTreeBuilder.hpp" +#include +#include +#include +#include #include #include @@ -61,7 +61,7 @@ std::vector SupportTree::slice( slices.emplace_back(); TriangleMeshSlicer sup_slicer(&sup_mesh); - sup_slicer.slice(grid, cr, &slices.back(), ctl().cancelfn); + sup_slicer.slice(grid, SlicingMode::Regular, cr, &slices.back(), ctl().cancelfn); } if (!pad_mesh.empty()) { @@ -75,7 +75,7 @@ std::vector SupportTree::slice( std::copy(grid.begin(), maxzit, std::back_inserter(padgrid)); TriangleMeshSlicer pad_slicer(&pad_mesh); - pad_slicer.slice(padgrid, cr, &slices.back(), ctl().cancelfn); + pad_slicer.slice(padgrid, SlicingMode::Regular, cr, &slices.back(), ctl().cancelfn); } size_t len = grid.size(); diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SupportTree.hpp similarity index 83% rename from src/libslic3r/SLA/SLASupportTree.hpp rename to src/libslic3r/SLA/SupportTree.hpp index 322b29251e..acf6c10f5e 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SupportTree.hpp @@ -1,12 +1,15 @@ -#ifndef SLASUPPORTTREE_HPP -#define SLASUPPORTTREE_HPP +#ifndef SLA_SUPPORTTREE_HPP +#define SLA_SUPPORTTREE_HPP #include #include #include -#include "SLACommon.hpp" -#include "SLAPad.hpp" +#include +#include +#include +#include +#include namespace Slic3r { @@ -105,27 +108,6 @@ struct SupportConfig enum class MeshType { Support, Pad }; -/// A Control structure for the support calculation. Consists of the status -/// indicator callback and the stop condition predicate. -struct JobController -{ - using StatusFn = std::function; - using StopCond = std::function; - using CancelFn = std::function; - - // This will signal the status of the calculation to the front-end - StatusFn statuscb = [](unsigned, const std::string&){}; - - // Returns true if the calculation should be aborted. - StopCond stopcondition = [](){ return false; }; - - // Similar to cancel callback. This should check the stop condition and - // if true, throw an appropriate exception. (TriangleMeshSlicer needs this) - // consider it a hard abort. stopcondition is permits the algorithm to - // terminate itself - CancelFn cancelfn = [](){}; -}; - struct SupportableMesh { EigenMesh3D emesh; diff --git a/src/libslic3r/SLA/SLASupportTreeBuilder.cpp b/src/libslic3r/SLA/SupportTreeBuilder.cpp similarity index 96% rename from src/libslic3r/SLA/SLASupportTreeBuilder.cpp rename to src/libslic3r/SLA/SupportTreeBuilder.cpp index 2e0310ed8d..d385e98a29 100644 --- a/src/libslic3r/SLA/SLASupportTreeBuilder.cpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.cpp @@ -1,5 +1,6 @@ -#include "SLASupportTreeBuilder.hpp" -#include "SLASupportTreeBuildsteps.hpp" +#include +#include +#include namespace Slic3r { namespace sla { @@ -12,7 +13,7 @@ Contour3D sphere(double rho, Portion portion, double fa) { if(rho <= 1e-6 && rho >= -1e-6) return ret; auto& vertices = ret.points; - auto& facets = ret.indices; + auto& facets = ret.faces3; // Algorithm: // Add points one-by-one to the sphere grid and form facets using relative @@ -102,7 +103,7 @@ Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp) auto steps = int(ssteps); auto& points = ret.points; - auto& indices = ret.indices; + auto& indices = ret.faces3; points.reserve(2*ssteps); double a = 2*PI/steps; @@ -211,8 +212,8 @@ Head::Head(double r_big_mm, coord_t i1s1 = coord_t(idx1), i1s2 = coord_t(idx2); coord_t i2s1 = i1s1 + 1, i2s2 = i1s2 + 1; - mesh.indices.emplace_back(i1s1, i2s1, i2s2); - mesh.indices.emplace_back(i1s1, i2s2, i1s2); + mesh.faces3.emplace_back(i1s1, i2s1, i2s2); + mesh.faces3.emplace_back(i1s1, i2s2, i1s2); } auto i1s1 = coord_t(s1.points.size()) - coord_t(steps); @@ -220,8 +221,8 @@ Head::Head(double r_big_mm, auto i1s2 = coord_t(s1.points.size()); auto i2s2 = coord_t(s1.points.size()) + coord_t(steps) - 1; - mesh.indices.emplace_back(i2s2, i2s1, i1s1); - mesh.indices.emplace_back(i1s2, i2s2, i1s1); + mesh.faces3.emplace_back(i2s2, i2s1, i1s1); + mesh.faces3.emplace_back(i1s2, i2s2, i1s1); // To simplify further processing, we translate the mesh so that the // last vertex of the pointing sphere (the pinpoint) will be at (0,0,0) @@ -240,7 +241,7 @@ Pillar::Pillar(const Vec3d &jp, const Vec3d &endp, double radius, size_t st): // move the data. Contour3D body = cylinder(radius, height, st, endp); mesh.points.swap(body.points); - mesh.indices.swap(body.indices); + mesh.faces3.swap(body.faces3); } } @@ -275,7 +276,7 @@ Pillar &Pillar::add_base(double baseheight, double radius) base.points.emplace_back(endpt); base.points.emplace_back(ep); - auto& indices = base.indices; + auto& indices = base.faces3; auto hcenter = int(base.points.size() - 1); auto lcenter = int(base.points.size() - 2); auto offs = int(steps); @@ -466,7 +467,7 @@ const TriangleMesh &SupportTreeBuilder::merged_mesh() const return m_meshcache; } - m_meshcache = mesh(merged); + m_meshcache = to_triangle_mesh(merged); // The mesh will be passed by const-pointer to TriangleMeshSlicer, // which will need this. diff --git a/src/libslic3r/SLA/SLASupportTreeBuilder.hpp b/src/libslic3r/SLA/SupportTreeBuilder.hpp similarity index 97% rename from src/libslic3r/SLA/SLASupportTreeBuilder.hpp rename to src/libslic3r/SLA/SupportTreeBuilder.hpp index c0d9f04c0f..90cf417c83 100644 --- a/src/libslic3r/SLA/SLASupportTreeBuilder.hpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.hpp @@ -1,10 +1,11 @@ -#ifndef SUPPORTTREEBUILDER_HPP -#define SUPPORTTREEBUILDER_HPP +#ifndef SLA_SUPPORTTREEBUILDER_HPP +#define SLA_SUPPORTTREEBUILDER_HPP -#include "SLAConcurrency.hpp" -#include "SLABoilerPlate.hpp" -#include "SLASupportTree.hpp" -#include "SLAPad.hpp" +#include +#include +#include +#include +#include #include namespace Slic3r { @@ -73,7 +74,7 @@ Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI), // h: Height // ssteps: how many edges will create the base circle // sp: starting point -Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp = {0,0,0}); +Contour3D cylinder(double r, double h, size_t ssteps = 45, const Vec3d &sp = {0,0,0}); const constexpr long ID_UNSET = -1; diff --git a/src/libslic3r/SLA/SLASupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp similarity index 70% rename from src/libslic3r/SLA/SLASupportTreeBuildsteps.cpp rename to src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 392a963e12..5d60d75136 100644 --- a/src/libslic3r/SLA/SLASupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -1,4 +1,4 @@ -#include "SLASupportTreeBuildsteps.hpp" +#include #include #include @@ -7,8 +7,16 @@ namespace Slic3r { namespace sla { +static const Vec3d DOWN = {0.0, 0.0, -1.0}; + +using libnest2d::opt::initvals; +using libnest2d::opt::bound; +using libnest2d::opt::StopCriteria; +using libnest2d::opt::GeneticOptimizer; +using libnest2d::opt::SubplexOptimizer; + SupportTreeBuildsteps::SupportTreeBuildsteps(SupportTreeBuilder & builder, - const SupportableMesh &sm) + const SupportableMesh &sm) : m_cfg(sm.cfg) , m_mesh(sm.emesh) , m_support_pts(sm.pts) @@ -30,7 +38,7 @@ SupportTreeBuildsteps::SupportTreeBuildsteps(SupportTreeBuilder & builder, } bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, - const SupportableMesh &sm) + const SupportableMesh &sm) { if(sm.pts.empty()) return false; @@ -158,190 +166,182 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, return pc == ABORT; } +// Give points on a 3D ring with given center, radius and orientation +// method based on: +// https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space +template +class PointRing { + std::array m_phis; + + // Two vectors that will be perpendicular to each other and to the + // axis. Values for a(X) and a(Y) are now arbitrary, a(Z) is just a + // placeholder. + // a and b vectors are perpendicular to the ring direction and to each other. + // Together they define the plane where we have to iterate with the + // given angles in the 'm_phis' vector + Vec3d a = {0, 1, 0}, b; + double m_radius = 0.; + + static inline bool constexpr is_one(double val) + { + return std::abs(std::abs(val) - 1) < 1e-20; + } + +public: + + PointRing(const Vec3d &n) + { + m_phis = linspace_array(0., 2 * PI); + + // We have to address the case when the direction vector v (same as + // dir) is coincident with one of the world axes. In this case two of + // its components will be completely zero and one is 1.0. Our method + // becomes dangerous here due to division with zero. Instead, vector + // 'a' can be an element-wise rotated version of 'v' + if(is_one(n(X)) || is_one(n(Y)) || is_one(n(Z))) { + a = {n(Z), n(X), n(Y)}; + b = {n(Y), n(Z), n(X)}; + } + else { + a(Z) = -(n(Y)*a(Y)) / n(Z); a.normalize(); + b = a.cross(n); + } + } + + Vec3d get(size_t idx, const Vec3d src, double r) const + { + double phi = m_phis[idx]; + double sinphi = std::sin(phi); + double cosphi = std::cos(phi); + + double rpscos = r * cosphi; + double rpssin = r * sinphi; + + // Point on the sphere + return {src(X) + rpscos * a(X) + rpssin * b(X), + src(Y) + rpscos * a(Y) + rpssin * b(Y), + src(Z) + rpscos * a(Z) + rpssin * b(Z)}; + } +}; + +template +static Hit min_hit(const C &hits) +{ + auto mit = std::min_element(hits.begin(), hits.end(), + [](const Hit &h1, const Hit &h2) { + return h1.distance() < h2.distance(); + }); + + return *mit; +} + EigenMesh3D::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( const Vec3d &s, const Vec3d &dir, double r_pin, double r_back, double width) { static const size_t SAMPLES = 8; - // method based on: - // https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space + // Move away slightly from the touching point to avoid raycasting on the + // inner surface of the mesh. + + const double& sd = m_cfg.safety_distance_mm; + + auto& m = m_mesh; + using HitResult = EigenMesh3D::hit_result; + + // Hit results + std::array hits; + + struct Rings { + double rpin; + double rback; + Vec3d spin; + Vec3d sback; + PointRing ring; + + Vec3d backring(size_t idx) { return ring.get(idx, sback, rback); } + Vec3d pinring(size_t idx) { return ring.get(idx, spin, rpin); } + } rings {r_pin + sd, r_back + sd, s, s + width * dir, dir}; // We will shoot multiple rays from the head pinpoint in the direction // of the pinhead robe (side) surface. The result will be the smallest // hit distance. - // Move away slightly from the touching point to avoid raycasting on the - // inner surface of the mesh. - Vec3d v = dir; // Our direction (axis) - Vec3d c = s + width * dir; - const double& sd = m_cfg.safety_distance_mm; + ccr::enumerate(hits.begin(), hits.end(), + [&m, &rings, sd](HitResult &hit, size_t i) { - // Two vectors that will be perpendicular to each other and to the - // axis. Values for a(X) and a(Y) are now arbitrary, a(Z) is just a - // placeholder. - Vec3d a(0, 1, 0), b; + // Point on the circle on the pin sphere + Vec3d ps = rings.pinring(i); + // This is the point on the circle on the back sphere + Vec3d p = rings.backring(i); + + // Point ps is not on mesh but can be inside or + // outside as well. This would cause many problems + // with ray-casting. To detect the position we will + // use the ray-casting result (which has an is_inside + // predicate). - // The portions of the circle (the head-back circle) for which we will - // shoot rays. - std::array phis; - for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size(); + Vec3d n = (p - ps).normalized(); + auto q = m.query_ray_hit(ps + sd * n, n); - auto& m = m_mesh; - using HitResult = EigenMesh3D::hit_result; + if (q.is_inside()) { // the hit is inside the model + if (q.distance() > rings.rpin) { + // If we are inside the model and the hit + // distance is bigger than our pin circle + // diameter, it probably indicates that the + // support point was already inside the + // model, or there is really no space + // around the point. We will assign a zero + // hit distance to these cases which will + // enforce the function return value to be + // an invalid ray with zero hit distance. + // (see min_element at the end) + hit = HitResult(0.0); + } else { + // re-cast the ray from the outside of the + // object. The starting point has an offset + // of 2*safety_distance because the + // original ray has also had an offset + auto q2 = m.query_ray_hit(ps + (q.distance() + 2 * sd) * n, n); + hit = q2; + } + } else + hit = q; + }); - // Hit results - std::array hits; - - // We have to address the case when the direction vector v (same as - // dir) is coincident with one of the world axes. In this case two of - // its components will be completely zero and one is 1.0. Our method - // becomes dangerous here due to division with zero. Instead, vector - // 'a' can be an element-wise rotated version of 'v' - auto chk1 = [] (double val) { - return std::abs(std::abs(val) - 1) < 1e-20; - }; - - if(chk1(v(X)) || chk1(v(Y)) || chk1(v(Z))) { - a = {v(Z), v(X), v(Y)}; - b = {v(Y), v(Z), v(X)}; - } - else { - a(Z) = -(v(Y)*a(Y)) / v(Z); a.normalize(); - b = a.cross(v); - } - - // Now a and b vectors are perpendicular to v and to each other. - // Together they define the plane where we have to iterate with the - // given angles in the 'phis' vector - ccr::enumerate( - phis.begin(), phis.end(), - [&hits, &m, sd, r_pin, r_back, s, a, b, c](double phi, size_t i) { - double sinphi = std::sin(phi); - double cosphi = std::cos(phi); - - // Let's have a safety coefficient for the radiuses. - double rpscos = (sd + r_pin) * cosphi; - double rpssin = (sd + r_pin) * sinphi; - double rpbcos = (sd + r_back) * cosphi; - double rpbsin = (sd + r_back) * sinphi; - - // Point on the circle on the pin sphere - Vec3d ps(s(X) + rpscos * a(X) + rpssin * b(X), - s(Y) + rpscos * a(Y) + rpssin * b(Y), - s(Z) + rpscos * a(Z) + rpssin * b(Z)); - - // Point ps is not on mesh but can be inside or - // outside as well. This would cause many problems - // with ray-casting. To detect the position we will - // use the ray-casting result (which has an is_inside - // predicate). - - // This is the point on the circle on the back sphere - Vec3d p(c(X) + rpbcos * a(X) + rpbsin * b(X), - c(Y) + rpbcos * a(Y) + rpbsin * b(Y), - c(Z) + rpbcos * a(Z) + rpbsin * b(Z)); - - Vec3d n = (p - ps).normalized(); - auto q = m.query_ray_hit(ps + sd * n, n); - - if (q.is_inside()) { // the hit is inside the model - if (q.distance() > r_pin + sd) { - // If we are inside the model and the hit - // distance is bigger than our pin circle - // diameter, it probably indicates that the - // support point was already inside the - // model, or there is really no space - // around the point. We will assign a zero - // hit distance to these cases which will - // enforce the function return value to be - // an invalid ray with zero hit distance. - // (see min_element at the end) - hits[i] = HitResult(0.0); - } else { - // re-cast the ray from the outside of the - // object. The starting point has an offset - // of 2*safety_distance because the - // original ray has also had an offset - auto q2 = m.query_ray_hit( - ps + (q.distance() + 2 * sd) * n, n); - hits[i] = q2; - } - } else - hits[i] = q; - }); - - auto mit = std::min_element(hits.begin(), hits.end()); - - return *mit; + return min_hit(hits); } EigenMesh3D::hit_result SupportTreeBuildsteps::bridge_mesh_intersect( - const Vec3d &s, const Vec3d &dir, double r, bool ins_check) + const Vec3d &src, const Vec3d &dir, double r, bool ins_check) { static const size_t SAMPLES = 8; + PointRing ring{dir}; - // helper vector calculations - Vec3d a(0, 1, 0), b; - const double& sd = m_cfg.safety_distance_mm; - - // INFO: for explanation of the method used here, see the previous - // method's comments. - - auto chk1 = [] (double val) { - return std::abs(std::abs(val) - 1) < 1e-20; - }; - - if(chk1(dir(X)) || chk1(dir(Y)) || chk1(dir(Z))) { - a = {dir(Z), dir(X), dir(Y)}; - b = {dir(Y), dir(Z), dir(X)}; - } - else { - a(Z) = -(dir(Y)*a(Y)) / dir(Z); a.normalize(); - b = a.cross(dir); - } - - // circle portions - std::array phis; - for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size(); - - auto& m = m_mesh; - using HitResult = EigenMesh3D::hit_result; + using Hit = EigenMesh3D::hit_result; // Hit results - std::array hits; + std::array hits; - ccr::enumerate( - phis.begin(), phis.end(), - [&m, a, b, sd, dir, r, s, ins_check, &hits] (double phi, size_t i) { - double sinphi = std::sin(phi); - double cosphi = std::cos(phi); - - // Let's have a safety coefficient for the radiuses. - double rcos = (sd + r) * cosphi; - double rsin = (sd + r) * sinphi; - - // Point on the circle on the pin sphere - Vec3d p (s(X) + rcos * a(X) + rsin * b(X), - s(Y) + rcos * a(Y) + rsin * b(Y), - s(Z) + rcos * a(Z) + rsin * b(Z)); - - auto hr = m.query_ray_hit(p + sd*dir, dir); - - if(ins_check && hr.is_inside()) { - if(hr.distance() > 2 * r + sd) hits[i] = HitResult(0.0); - else { - // re-cast the ray from the outside of the object - auto hr2 = - m.query_ray_hit(p + (hr.distance() + 2*sd)*dir, dir); - - hits[i] = hr2; - } - } else hits[i] = hr; - }); + ccr::enumerate(hits.begin(), hits.end(), + [this, r, src, ins_check, &ring, dir] (Hit &hit, size_t i) { + + const double sd = m_cfg.safety_distance_mm; + + // Point on the circle on the pin sphere + Vec3d p = ring.get(i, src, r + sd); + + auto hr = m_mesh.query_ray_hit(p + sd * dir, dir); + + if(ins_check && hr.is_inside()) { + if(hr.distance() > 2 * r + sd) hit = Hit(0.0); + else { + // re-cast the ray from the outside of the object + hit = m_mesh.query_ray_hit(p + (hr.distance() + 2 * sd) * dir, dir); + } + } else hit = hr; + }); - auto mit = std::min_element(hits.begin(), hits.end()); - - return *mit; + return min_hit(hits); } bool SupportTreeBuildsteps::interconnect(const Pillar &pillar, @@ -411,7 +411,7 @@ bool SupportTreeBuildsteps::interconnect(const Pillar &pillar, // TODO: This is a workaround to not have a faulty last bridge while(ej(Z) >= eupper(Z) /*endz*/) { - if(bridge_mesh_intersect(sj, dirv(sj, ej), pillar.r) >= bridge_distance) + if(bridge_mesh_distance(sj, dirv(sj, ej), pillar.r) >= bridge_distance) { m_builder.add_crossbridge(sj, ej, pillar.r); was_connected = true; @@ -422,7 +422,7 @@ bool SupportTreeBuildsteps::interconnect(const Pillar &pillar, Vec3d sjback(ej(X), ej(Y), sj(Z)); Vec3d ejback(sj(X), sj(Y), ej(Z)); if (sjback(Z) <= slower(Z) && ejback(Z) >= eupper(Z) && - bridge_mesh_intersect(sjback, dirv(sjback, ejback), + bridge_mesh_distance(sjback, dirv(sjback, ejback), pillar.r) >= bridge_distance) { // need to check collision for the cross stick m_builder.add_crossbridge(sjback, ejback, pillar.r); @@ -479,7 +479,7 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head, bridgestart(Z) -= zdiff; touchjp(Z) = Zdown; - double t = bridge_mesh_intersect(headjp, {0,0,-1}, r); + double t = bridge_mesh_distance(headjp, DOWN, r); // We can't insert a pillar under the source head to connect // with the nearby pillar's starting junction @@ -497,8 +497,7 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head, double minz = m_builder.ground_level + 2 * m_cfg.head_width_mm; if(bridgeend(Z) < minz) return false; - double t = bridge_mesh_intersect(bridgestart, - dirv(bridgestart, bridgeend), r); + double t = bridge_mesh_distance(bridgestart, dirv(bridgestart, bridgeend), r); // Cannot insert the bridge. (further search might not worth the hassle) if(t < distance(bridgestart, bridgeend)) return false; @@ -560,9 +559,7 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, double radius, long head_id) { - // People were killed for this number (seriously) - static const double SQR2 = std::sqrt(2.0); - static const Vec3d DOWN = {0.0, 0.0, -1.0}; + const double SLOPE = 1. / std::cos(m_cfg.bridge_slope); double gndlvl = m_builder.ground_level; Vec3d endp = {jp(X), jp(Y), gndlvl}; @@ -573,38 +570,47 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, bool can_add_base = true; bool normal_mode = true; + // If in zero elevation mode and the pillar is too close to the model body, + // the support pillar can not be placed in the gap between the model and + // the pad, and the pillar bases must not touch the model body either. + // To solve this, a corrector bridge is inserted between the starting point + // (jp) and the new pillar. if (m_cfg.object_elevation_mm < EPSILON && (dist = std::sqrt(m_mesh.squared_distance(endp))) < min_dist) { // Get the distance from the mesh. This can be later optimized // to get the distance in 2D plane because we are dealing with // the ground level only. + + normal_mode = false; + + // The min distance needed to move away from the model in XY plane. + double current_d = min_dist - dist; + double current_bride_d = SLOPE * current_d; + + // get a suitable direction for the corrector bridge. It is the + // original sourcedir's azimuth but the polar angle is saturated to the + // configured bridge slope. + auto [polar, azimuth] = dir_to_spheric(sourcedir); + polar = PI - m_cfg.bridge_slope; + auto dir = spheric_to_dir(polar, azimuth).normalized(); - normal_mode = false; - double mind = min_dist - dist; - double azimuth = std::atan2(sourcedir(Y), sourcedir(X)); - double sinpolar = std::sin(PI - m_cfg.bridge_slope); - double cospolar = std::cos(PI - m_cfg.bridge_slope); - double cosazm = std::cos(azimuth); - double sinazm = std::sin(azimuth); - - auto dir = Vec3d(cosazm * sinpolar, sinazm * sinpolar, cospolar) - .normalized(); - - using namespace libnest2d::opt; StopCriteria scr; scr.stop_score = min_dist; SubplexOptimizer solver(scr); + // Search for a distance along the corrector bridge to move the endpoint + // sufficiently away form the model body. The first few optimization + // cycles should succeed here. auto result = solver.optimize_max( [this, dir, jp, gndlvl](double mv) { - Vec3d endpt = jp + SQR2 * mv * dir; + Vec3d endpt = jp + mv * dir; endpt(Z) = gndlvl; return std::sqrt(m_mesh.squared_distance(endpt)); }, - initvals(mind), bound(0.0, 2 * min_dist)); + initvals(current_bride_d), + bound(0.0, m_cfg.max_bridge_length_mm - current_bride_d)); - mind = std::get<0>(result.optimum); - endp = jp + SQR2 * mind * dir; + endp = jp + std::get<0>(result.optimum) * dir; Vec3d pgnd = {endp(X), endp(Y), gndlvl}; can_add_base = result.score > min_dist; @@ -618,12 +624,12 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, }; // We have to check if the bridge is feasible. - if (bridge_mesh_intersect(jp, dir, radius) < (endp - jp).norm()) + if (bridge_mesh_distance(jp, dir, radius) < (endp - jp).norm()) abort_in_shame(); else { // If the new endpoint is below ground, do not make a pillar if (endp(Z) < gndlvl) - endp = endp - SQR2 * (gndlvl - endp(Z)) * dir; // back off + endp = endp - SLOPE * (gndlvl - endp(Z)) * dir; // back off else { auto hit = bridge_mesh_intersect(endp, DOWN, radius); @@ -685,11 +691,6 @@ void SupportTreeBuildsteps::filter() // not be enough space for the pinhead. Filtering is applied for // these reasons. - using libnest2d::opt::bound; - using libnest2d::opt::initvals; - using libnest2d::opt::GeneticOptimizer; - using libnest2d::opt::StopCriteria; - ccr::SpinningMutex mutex; auto addfn = [&mutex](PtIndices &container, unsigned val) { std::lock_guard lk(mutex); @@ -708,10 +709,7 @@ void SupportTreeBuildsteps::filter() // (Quaternion::FromTwoVectors) and apply the rotation to the // arrow head. - double z = n(2); - double r = 1.0; // for normalized vector - double polar = std::acos(z / r); - double azimuth = std::atan2(n(1), n(0)); + auto [polar, azimuth] = dir_to_spheric(n); // skip if the tilt is not sane if(polar >= PI - m_cfg.normal_cutoff_angle) { @@ -729,9 +727,7 @@ void SupportTreeBuildsteps::filter() double pin_r = double(m_support_pts[fidx].head_front_radius); // Reassemble the now corrected normal - auto nn = Vec3d(std::cos(azimuth) * std::sin(polar), - std::sin(azimuth) * std::sin(polar), - std::cos(polar)).normalized(); + auto nn = spheric_to_dir(polar, azimuth).normalized(); // check available distance EigenMesh3D::hit_result t @@ -757,28 +753,23 @@ void SupportTreeBuildsteps::filter() auto oresult = solver.optimize_max( [this, pin_r, w, hp](double plr, double azm) { - auto dir = Vec3d(std::cos(azm) * std::sin(plr), - std::sin(azm) * std::sin(plr), - std::cos(plr)).normalized(); + auto dir = spheric_to_dir(plr, azm).normalized(); - double score = pinhead_mesh_intersect( + double score = pinhead_mesh_distance( hp, dir, pin_r, m_cfg.head_back_radius_mm, w); return score; }, initvals(polar, azimuth), // start with what we have - bound(3 * PI / 4, - PI), // Must not exceed the tilt limit + bound(3 * PI / 4, PI), // Must not exceed the tilt limit bound(-PI, PI) // azimuth can be a full search ); if(oresult.score > w) { polar = std::get<0>(oresult.optimum); azimuth = std::get<1>(oresult.optimum); - nn = Vec3d(std::cos(azimuth) * std::sin(polar), - std::sin(azimuth) * std::sin(polar), - std::cos(polar)).normalized(); - t = oresult.score; + nn = spheric_to_dir(polar, azimuth).normalized(); + t = EigenMesh3D::hit_result(oresult.score); } } @@ -837,16 +828,17 @@ void SupportTreeBuildsteps::classify() m_thr(); auto& head = m_builder.head(i); - Vec3d n(0, 0, -1); double r = head.r_back_mm; Vec3d headjp = head.junction_point(); // collision check - auto hit = bridge_mesh_intersect(headjp, n, r); + auto hit = bridge_mesh_intersect(headjp, DOWN, r); if(std::isinf(hit.distance())) ground_head_indices.emplace_back(i); else if(m_cfg.ground_facing_only) head.invalidate(); - else m_iheads_onmodel.emplace_back(std::make_pair(i, hit)); + else m_iheads_onmodel.emplace_back(i); + + m_head_to_ground_scans[i] = hit; } // We want to search for clusters of points that are far enough @@ -893,13 +885,14 @@ void SupportTreeBuildsteps::routing_to_ground() // get the current cluster centroid auto & thr = m_thr; const auto &points = m_points; - long lcid = cluster_centroid( + + long lcid = cluster_centroid( cl, [&points](size_t idx) { return points.row(long(idx)); }, [thr](const Vec3d &p1, const Vec3d &p2) { thr(); return distance(Vec2d(p1(X), p1(Y)), Vec2d(p2(X), p2(Y))); }); - + assert(lcid >= 0); unsigned hid = cl[size_t(lcid)]; // Head ID @@ -944,192 +937,138 @@ void SupportTreeBuildsteps::routing_to_ground() } } +bool SupportTreeBuildsteps::connect_to_ground(Head &head, const Vec3d &dir) +{ + auto hjp = head.junction_point(); + double r = head.r_back_mm; + double t = bridge_mesh_distance(hjp, dir, head.r_back_mm); + double d = 0, tdown = 0; + t = std::min(t, m_cfg.max_bridge_length_mm); + + while (d < t && !std::isinf(tdown = bridge_mesh_distance(hjp + d * dir, DOWN, r))) + d += r; + + if(!std::isinf(tdown)) return false; + + Vec3d endp = hjp + d * dir; + m_builder.add_bridge(head.id, endp); + m_builder.add_junction(endp, head.r_back_mm); + + this->create_ground_pillar(endp, dir, head.r_back_mm); + + return true; +} + +bool SupportTreeBuildsteps::connect_to_ground(Head &head) +{ + if (connect_to_ground(head, head.dir)) return true; + + // Optimize bridge direction: + // Straight path failed so we will try to search for a suitable + // direction out of the cavity. + auto [polar, azimuth] = dir_to_spheric(head.dir); + + StopCriteria stc; + stc.max_iterations = m_cfg.optimizer_max_iterations; + stc.relative_score_difference = m_cfg.optimizer_rel_score_diff; + stc.stop_score = 1e6; + GeneticOptimizer solver(stc); + solver.seed(0); // we want deterministic behavior + + double r_back = head.r_back_mm; + Vec3d hjp = head.junction_point(); + auto oresult = solver.optimize_max( + [this, hjp, r_back](double plr, double azm) { + Vec3d n = spheric_to_dir(plr, azm).normalized(); + return bridge_mesh_distance(hjp, n, r_back); + }, + initvals(polar, azimuth), // let's start with what we have + bound(3*PI/4, PI), // Must not exceed the slope limit + bound(-PI, PI) // azimuth can be a full range search + ); + + Vec3d bridgedir = spheric_to_dir(oresult.optimum).normalized(); + return connect_to_ground(head, bridgedir); +} + +bool SupportTreeBuildsteps::connect_to_model_body(Head &head) +{ + if (head.id <= ID_UNSET) return false; + + auto it = m_head_to_ground_scans.find(unsigned(head.id)); + if (it == m_head_to_ground_scans.end()) return false; + + auto &hit = it->second; + Vec3d hjp = head.junction_point(); + double zangle = std::asin(hit.direction()(Z)); + zangle = std::max(zangle, PI/4); + double h = std::sin(zangle) * head.fullwidth(); + + // The width of the tail head that we would like to have... + h = std::min(hit.distance() - head.r_back_mm, h); + + if(h <= 0.) return false; + + Vec3d endp{hjp(X), hjp(Y), hjp(Z) - hit.distance() + h}; + auto center_hit = m_mesh.query_ray_hit(hjp, DOWN); + + double hitdiff = center_hit.distance() - hit.distance(); + Vec3d hitp = std::abs(hitdiff) < 2*head.r_back_mm? + center_hit.position() : hit.position(); + + head.transform(); + + long pillar_id = m_builder.add_pillar(head.id, endp, head.r_back_mm); + Pillar &pill = m_builder.pillar(pillar_id); + + Vec3d taildir = endp - hitp; + double dist = distance(endp, hitp) + m_cfg.head_penetration_mm; + double w = dist - 2 * head.r_pin_mm - head.r_back_mm; + + if (w < 0.) { + BOOST_LOG_TRIVIAL(error) << "Pinhead width is negative!"; + w = 0.; + } + + Head tailhead(head.r_back_mm, head.r_pin_mm, w, + m_cfg.head_penetration_mm, taildir, hitp); + + tailhead.transform(); + pill.base = tailhead.mesh; + + m_pillar_index.guarded_insert(pill.endpoint(), pill.id); + + return true; +} + void SupportTreeBuildsteps::routing_to_model() { // We need to check if there is an easy way out to the bed surface. // If it can be routed there with a bridge shorter than // min_bridge_distance. - - // First we want to index the available pillars. The best is to connect - // these points to the available pillars - - auto routedown = [this](Head& head, const Vec3d& dir, double dist) - { - head.transform(); - Vec3d endp = head.junction_point() + dist * dir; - m_builder.add_bridge(head.id, endp); - m_builder.add_junction(endp, head.r_back_mm); - - this->create_ground_pillar(endp, dir, head.r_back_mm); - }; - - std::vector modelpillars; - ccr::SpinningMutex mutex; - auto onmodelfn = - [this, routedown, &modelpillars, &mutex] - (const std::pair &el, size_t) - { + ccr::enumerate(m_iheads_onmodel.begin(), m_iheads_onmodel.end(), + [this] (const unsigned idx, size_t) { m_thr(); - unsigned idx = el.first; - EigenMesh3D::hit_result hit = el.second; auto& head = m_builder.head(idx); - Vec3d hjp = head.junction_point(); - // ///////////////////////////////////////////////////////////////// // Search nearby pillar - // ///////////////////////////////////////////////////////////////// - if(search_pillar_and_connect(head)) { head.transform(); return; } - // ///////////////////////////////////////////////////////////////// - // Try straight path - // ///////////////////////////////////////////////////////////////// - // Cannot connect to nearby pillar. We will try to search for // a route to the ground. + if(connect_to_ground(head)) { head.transform(); return; } - double t = bridge_mesh_intersect(hjp, head.dir, head.r_back_mm); - double d = 0, tdown = 0; - Vec3d dirdown(0.0, 0.0, -1.0); - - t = std::min(t, m_cfg.max_bridge_length_mm); - - while(d < t && !std::isinf(tdown = bridge_mesh_intersect( - hjp + d*head.dir, - dirdown, head.r_back_mm))) { - d += head.r_back_mm; - } - - if(std::isinf(tdown)) { // we heave found a route to the ground - routedown(head, head.dir, d); return; - } - - // ///////////////////////////////////////////////////////////////// - // Optimize bridge direction - // ///////////////////////////////////////////////////////////////// - - // Straight path failed so we will try to search for a suitable - // direction out of the cavity. - - // Get the spherical representation of the normal. its easier to - // work with. - double z = head.dir(Z); - double r = 1.0; // for normalized vector - double polar = std::acos(z / r); - double azimuth = std::atan2(head.dir(Y), head.dir(X)); - - using libnest2d::opt::bound; - using libnest2d::opt::initvals; - using libnest2d::opt::GeneticOptimizer; - using libnest2d::opt::StopCriteria; - - StopCriteria stc; - stc.max_iterations = m_cfg.optimizer_max_iterations; - stc.relative_score_difference = m_cfg.optimizer_rel_score_diff; - stc.stop_score = 1e6; - GeneticOptimizer solver(stc); - solver.seed(0); // we want deterministic behavior - - double r_back = head.r_back_mm; - - auto oresult = solver.optimize_max( - [this, hjp, r_back](double plr, double azm) - { - Vec3d n = Vec3d(std::cos(azm) * std::sin(plr), - std::sin(azm) * std::sin(plr), - std::cos(plr)).normalized(); - return bridge_mesh_intersect(hjp, n, r_back); - }, - initvals(polar, azimuth), // let's start with what we have - bound(3*PI/4, PI), // Must not exceed the slope limit - bound(-PI, PI) // azimuth can be a full range search - ); - - d = 0; t = oresult.score; - - polar = std::get<0>(oresult.optimum); - azimuth = std::get<1>(oresult.optimum); - Vec3d bridgedir = Vec3d(std::cos(azimuth) * std::sin(polar), - std::sin(azimuth) * std::sin(polar), - std::cos(polar)).normalized(); - - t = std::min(t, m_cfg.max_bridge_length_mm); - - while(d < t && !std::isinf(tdown = bridge_mesh_intersect( - hjp + d*bridgedir, - dirdown, - head.r_back_mm))) { - d += head.r_back_mm; - } - - if(std::isinf(tdown)) { // we heave found a route to the ground - routedown(head, bridgedir, d); return; - } - - // ///////////////////////////////////////////////////////////////// - // Route to model body - // ///////////////////////////////////////////////////////////////// - - double zangle = std::asin(hit.direction()(Z)); - zangle = std::max(zangle, PI/4); - double h = std::sin(zangle) * head.fullwidth(); - - // The width of the tail head that we would like to have... - h = std::min(hit.distance() - head.r_back_mm, h); - - if(h > 0) { - Vec3d endp{hjp(X), hjp(Y), hjp(Z) - hit.distance() + h}; - auto center_hit = m_mesh.query_ray_hit(hjp, dirdown); - - double hitdiff = center_hit.distance() - hit.distance(); - Vec3d hitp = std::abs(hitdiff) < 2*head.r_back_mm? - center_hit.position() : hit.position(); - - head.transform(); - - long pillar_id = m_builder.add_pillar(head.id, endp, head.r_back_mm); - Pillar &pill = m_builder.pillar(pillar_id); - - Vec3d taildir = endp - hitp; - double dist = distance(endp, hitp) + m_cfg.head_penetration_mm; - double w = dist - 2 * head.r_pin_mm - head.r_back_mm; - - if (w < 0.) { - BOOST_LOG_TRIVIAL(error) << "Pinhead width is negative!"; - w = 0.; - } - - Head tailhead(head.r_back_mm, - head.r_pin_mm, - w, - m_cfg.head_penetration_mm, - taildir, - hitp); - - tailhead.transform(); - pill.base = tailhead.mesh; - - // Experimental: add the pillar to the index for cascading - std::lock_guard lk(mutex); - modelpillars.emplace_back(unsigned(pill.id)); - return; - } + // No route to the ground, so connect to the model body as a last resort + if (connect_to_model_body(head)) { return; } // We have failed to route this head. BOOST_LOG_TRIVIAL(warning) - << "Failed to route model facing support point." - << " ID: " << idx; + << "Failed to route model facing support point. ID: " << idx; + head.invalidate(); - }; - - ccr::enumerate(m_iheads_onmodel.begin(), m_iheads_onmodel.end(), onmodelfn); - - for(auto pillid : modelpillars) { - auto& pillar = m_builder.pillar(pillid); - m_pillar_index.insert(pillar.endpoint(), pillid); - } + }); } void SupportTreeBuildsteps::interconnect_pillars() @@ -1280,7 +1219,8 @@ void SupportTreeBuildsteps::interconnect_pillars() spts[n] = s; // Check the path vertically down - auto hr = bridge_mesh_intersect(s, {0, 0, -1}, pillar().r); + Vec3d check_from = s + Vec3d{0., 0., pillar().r}; + auto hr = bridge_mesh_intersect(check_from, DOWN, pillar().r); Vec3d gndsp{s(X), s(Y), gnd}; // If the path is clear, check for pillar base collisions @@ -1310,9 +1250,8 @@ void SupportTreeBuildsteps::interconnect_pillars() m_pillar_index.insert(pp.endpoint(), unsigned(pp.id)); m_builder.add_junction(s, pillar().r); - double t = bridge_mesh_intersect(pillarsp, - dirv(pillarsp, s), - pillar().r); + double t = bridge_mesh_distance(pillarsp, dirv(pillarsp, s), + pillar().r); if (distance(pillarsp, s) < t) m_builder.add_bridge(pillarsp, s, pillar().r); @@ -1360,12 +1299,11 @@ void SupportTreeBuildsteps::routing_headless() Vec3d n = m_support_nmls.row(i); // mesh outward normal Vec3d sp = sph - n * HWIDTH_MM; // stick head start point - Vec3d dir = {0, 0, -1}; Vec3d sj = sp + R * n; // stick start point // This is only for checking - double idist = bridge_mesh_intersect(sph, dir, R, true); - double realdist = ray_mesh_intersect(sj, dir); + double idist = bridge_mesh_distance(sph, DOWN, R, true); + double realdist = ray_mesh_intersect(sj, DOWN).distance(); double dist = realdist; if (std::isinf(dist)) dist = sph(Z) - m_builder.ground_level; @@ -1378,7 +1316,7 @@ void SupportTreeBuildsteps::routing_headless() } bool use_endball = !std::isinf(realdist); - Vec3d ej = sj + (dist + HWIDTH_MM) * dir; + Vec3d ej = sj + (dist + HWIDTH_MM) * DOWN ; m_builder.add_compact_bridge(sp, ej, n, R, use_endball); } } diff --git a/src/libslic3r/SLA/SLASupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp similarity index 84% rename from src/libslic3r/SLA/SLASupportTreeBuildsteps.hpp rename to src/libslic3r/SLA/SupportTreeBuildsteps.hpp index e953cc1361..cfe78fe97a 100644 --- a/src/libslic3r/SLA/SLASupportTreeBuildsteps.hpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp @@ -3,7 +3,8 @@ #include -#include "SLASupportTreeBuilder.hpp" +#include +#include namespace Slic3r { namespace sla { @@ -19,6 +20,32 @@ inline Vec2d to_vec2(const Vec3d& v3) { return {v3(X), v3(Y)}; } +inline std::pair dir_to_spheric(const Vec3d &n, double norm = 1.) +{ + double z = n.z(); + double r = norm; + double polar = std::acos(z / r); + double azimuth = std::atan2(n(1), n(0)); + return {polar, azimuth}; +} + +inline Vec3d spheric_to_dir(double polar, double azimuth) +{ + return {std::cos(azimuth) * std::sin(polar), + std::sin(azimuth) * std::sin(polar), std::cos(polar)}; +} + +inline Vec3d spheric_to_dir(const std::tuple &v) +{ + auto [plr, azm] = v; + return spheric_to_dir(plr, azm); +} + +inline Vec3d spheric_to_dir(const std::pair &v) +{ + return spheric_to_dir(v.first, v.second); +} + // This function returns the position of the centroid in the input 'clust' // vector of point indices. template @@ -150,10 +177,10 @@ class SupportTreeBuildsteps { using PtIndices = std::vector; PtIndices m_iheads; // support points with pinhead + PtIndices m_iheads_onmodel; PtIndices m_iheadless; // headless support points - - // supp. pts. connecting to model: point index and the ray hit data - std::vector> m_iheads_onmodel; + + std::map m_head_to_ground_scans; // normals for support points from model faces. PointSet m_support_nmls; @@ -179,10 +206,10 @@ class SupportTreeBuildsteps { // When bridging heads to pillars... TODO: find a cleaner solution ccr::BlockingMutex m_bridge_mutex; - inline double ray_mesh_intersect(const Vec3d& s, - const Vec3d& dir) + inline EigenMesh3D::hit_result ray_mesh_intersect(const Vec3d& s, + const Vec3d& dir) { - return m_mesh.query_ray_hit(s, dir).distance(); + return m_mesh.query_ray_hit(s, dir); } // This function will test if a future pinhead would not collide with the @@ -202,6 +229,11 @@ class SupportTreeBuildsteps { double r_pin, double r_back, double width); + + template + inline double pinhead_mesh_distance(Args&&...args) { + return pinhead_mesh_intersect(std::forward(args)...).distance(); + } // Checking bridge (pillar and stick as well) intersection with the model. // If the function is used for headless sticks, the ins_check parameter @@ -216,21 +248,40 @@ class SupportTreeBuildsteps { const Vec3d& dir, double r, bool ins_check = false); + + template + inline double bridge_mesh_distance(Args&&...args) { + return bridge_mesh_intersect(std::forward(args)...).distance(); + } // Helper function for interconnecting two pillars with zig-zag bridges. bool interconnect(const Pillar& pillar, const Pillar& nextpillar); // For connecting a head to a nearby pillar. bool connect_to_nearpillar(const Head& head, long nearpillar_id); - + + // Find route for a head to the ground. Inserts additional bridge from the + // head to the pillar if cannot create pillar directly. + // The optional dir parameter is the direction of the bridge which is the + // direction of the pinhead if omitted. + bool connect_to_ground(Head& head, const Vec3d &dir); + inline bool connect_to_ground(Head& head); + + bool connect_to_model_body(Head &head); + bool search_pillar_and_connect(const Head& head); - + // This is a proxy function for pillar creation which will mind the gap // between the pad and the model bottom in zero elevation mode. + // jp is the starting junction point which needs to be routed down. + // sourcedir is the allowed direction of an optional bridge between the + // jp junction and the final pillar. void create_ground_pillar(const Vec3d &jp, const Vec3d &sourcedir, double radius, long head_id = ID_UNSET); + + public: SupportTreeBuildsteps(SupportTreeBuilder & builder, const SupportableMesh &sm); diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SupportTreeIGL.cpp similarity index 91% rename from src/libslic3r/SLA/SLASupportTreeIGL.cpp rename to src/libslic3r/SLA/SupportTreeIGL.cpp index 05f8b19842..ea2be6be11 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SupportTreeIGL.cpp @@ -1,6 +1,6 @@ #include #include "SLA/SLASupportTree.hpp" -#include "SLA/SLABoilerPlate.hpp" +#include "SLA/SLACommon.hpp" #include "SLA/SLASpatIndex.hpp" // Workaround: IGL signed_distance.h will define PI in the igl namespace. @@ -228,6 +228,26 @@ 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; @@ -252,6 +272,31 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const return ret; } +std::vector +EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const +{ + std::vector outs; + std::vector 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; }); + + // 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_face_id = hit.id; + } + + return outs; +} + #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; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index d9f8b7b6db..06c4f687b9 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1,7 +1,6 @@ #include "SLAPrint.hpp" -#include "SLA/SLASupportTree.hpp" -#include "SLA/SLAPad.hpp" -#include "SLA/SLAAutoSupports.hpp" +#include "SLAPrintSteps.hpp" + #include "ClipperUtils.hpp" #include "Geometry.hpp" #include "MTUtils.hpp" @@ -13,9 +12,6 @@ #include #include -// For geometry algorithms with native Clipper types (no copies and conversions) -#include - // #define SLAPRINT_DO_BENCHMARK #ifdef SLAPRINT_DO_BENCHMARK @@ -32,65 +28,86 @@ namespace Slic3r { -class SLAPrintObject::SupportData : public sla::SupportableMesh + +bool is_zero_elevation(const SLAPrintObjectConfig &c) { -public: - sla::SupportTree::UPtr support_tree_ptr; // the supports - std::vector support_slices; // sliced supports + return c.pad_enable.getBool() && c.pad_around_object.getBool(); +} + +// Compile the argument for support creation from the static print config. +sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) +{ + sla::SupportConfig scfg; - inline SupportData(const TriangleMesh &t): sla::SupportableMesh{t, {}, {}} {} + scfg.enabled = c.supports_enable.getBool(); + scfg.head_front_radius_mm = 0.5*c.support_head_front_diameter.getFloat(); + scfg.head_back_radius_mm = 0.5*c.support_pillar_diameter.getFloat(); + scfg.head_penetration_mm = c.support_head_penetration.getFloat(); + scfg.head_width_mm = c.support_head_width.getFloat(); + scfg.object_elevation_mm = is_zero_elevation(c) ? + 0. : c.support_object_elevation.getFloat(); + scfg.bridge_slope = c.support_critical_angle.getFloat() * PI / 180.0 ; + scfg.max_bridge_length_mm = c.support_max_bridge_length.getFloat(); + scfg.max_pillar_link_distance_mm = c.support_max_pillar_link_distance.getFloat(); + switch(c.support_pillar_connection_mode.getInt()) { + case slapcmZigZag: + scfg.pillar_connection_mode = sla::PillarConnectionMode::zigzag; break; + case slapcmCross: + scfg.pillar_connection_mode = sla::PillarConnectionMode::cross; break; + case slapcmDynamic: + scfg.pillar_connection_mode = sla::PillarConnectionMode::dynamic; break; + } + scfg.ground_facing_only = c.support_buildplate_only.getBool(); + scfg.pillar_widening_factor = c.support_pillar_widening_factor.getFloat(); + scfg.base_radius_mm = 0.5*c.support_base_diameter.getFloat(); + scfg.base_height_mm = c.support_base_height.getFloat(); + scfg.pillar_base_safety_distance_mm = + c.support_base_safety_distance.getFloat() < EPSILON ? + scfg.safety_distance_mm : c.support_base_safety_distance.getFloat(); - sla::SupportTree::UPtr &create_support_tree(const sla::JobController &ctl) - { - support_tree_ptr = sla::SupportTree::create(*this, ctl); - return support_tree_ptr; + return scfg; +} + +sla::PadConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c) +{ + sla::PadConfig::EmbedObject ret; + + ret.enabled = is_zero_elevation(c); + + if(ret.enabled) { + ret.everywhere = c.pad_around_object_everywhere.getBool(); + ret.object_gap_mm = c.pad_object_gap.getFloat(); + ret.stick_width_mm = c.pad_object_connector_width.getFloat(); + ret.stick_stride_mm = c.pad_object_connector_stride.getFloat(); + ret.stick_penetration_mm = c.pad_object_connector_penetration + .getFloat(); } -}; + + return ret; +} -namespace { - -// should add up to 100 (%) -const std::array OBJ_STEP_LEVELS = +sla::PadConfig make_pad_cfg(const SLAPrintObjectConfig& c) { - 30, // slaposObjectSlice, - 20, // slaposSupportPoints, - 10, // slaposSupportTree, - 10, // slaposPad, - 30, // slaposSliceSupports, -}; + sla::PadConfig pcfg; + + pcfg.wall_thickness_mm = c.pad_wall_thickness.getFloat(); + pcfg.wall_slope = c.pad_wall_slope.getFloat() * PI / 180.0; + + pcfg.max_merge_dist_mm = c.pad_max_merge_distance.getFloat(); + pcfg.wall_height_mm = c.pad_wall_height.getFloat(); + pcfg.brim_size_mm = c.pad_brim_size.getFloat(); + + // set builtin pad implicitly ON + pcfg.embed_object = builtin_pad_cfg(c); + + return pcfg; +} -// Object step to status label. The labels are localized at the time of calling, thus supporting language switching. -std::string OBJ_STEP_LABELS(size_t idx) +bool validate_pad(const TriangleMesh &pad, const sla::PadConfig &pcfg) { - switch (idx) { - case slaposObjectSlice: return L("Slicing model"); - case slaposSupportPoints: return L("Generating support points"); - case slaposSupportTree: return L("Generating support tree"); - case slaposPad: return L("Generating pad"); - case slaposSliceSupports: return L("Slicing supports"); - default:; - } - assert(false); return "Out of bounds!"; -}; - -// Should also add up to 100 (%) -const std::array PRINT_STEP_LEVELS = -{ - 10, // slapsMergeSlicesAndEval - 90, // slapsRasterize -}; - -// Print step to status label. The labels are localized at the time of calling, thus supporting language switching. -std::string PRINT_STEP_LABELS(size_t idx) -{ - switch (idx) { - case slapsMergeSlicesAndEval: return L("Merging slices and calculating statistics"); - case slapsRasterize: return L("Rasterizing layers"); - default:; - } - assert(false); return "Out of bounds!"; -}; - + // An empty pad can only be created if embed_object mode is enabled + // and the pad is not forced everywhere + return !pad.empty() || (pcfg.embed_object.enabled && !pcfg.embed_object.everywhere); } void SLAPrint::clear() @@ -105,10 +122,10 @@ void SLAPrint::clear() } // Transformation without rotation around Z and without a shift by X and Y. -static Transform3d sla_trafo(const SLAPrint& p, const ModelObject &model_object) +Transform3d SLAPrint::sla_trafo(const ModelObject &model_object) const { - Vec3d corr = p.relative_correction(); + Vec3d corr = this->relative_correction(); ModelInstance &model_instance = *model_object.instances.front(); Vec3d offset = model_instance.get_offset(); @@ -359,7 +376,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con bool sla_trafo_differs = model_object.instances.empty() != model_object_new.instances.empty() || (! model_object.instances.empty() && - (! sla_trafo(*this, model_object).isApprox(sla_trafo(*this, model_object_new)) || + (! sla_trafo(model_object).isApprox(sla_trafo(model_object_new)) || model_object.instances.front()->is_left_handed() != model_object_new.instances.front()->is_left_handed())); if (model_parts_differ || sla_trafo_differs) { // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects. @@ -397,6 +414,13 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con model_object.sla_support_points = model_object_new.sla_support_points; } model_object.sla_points_status = model_object_new.sla_points_status; + + // Invalidate hollowing if drain holes have changed + if (model_object.sla_drain_holes != model_object_new.sla_drain_holes) + { + model_object.sla_drain_holes = model_object_new.sla_drain_holes; + update_apply_status(it_print_object_status->print_object->invalidate_step(slaposDrillHoles)); + } // Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step. model_object.name = model_object_new.name; @@ -429,7 +453,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con // FIXME: this invalidates the transformed mesh in SLAPrintObject // which is expensive to calculate (especially the raw_mesh() call) - print_object->set_trafo(sla_trafo(*this, model_object), model_object.instances.front()->is_left_handed()); + print_object->set_trafo(sla_trafo(model_object), model_object.instances.front()->is_left_handed()); print_object->set_instances(std::move(new_instances)); @@ -576,87 +600,6 @@ std::string SLAPrint::output_filename(const std::string &filename_base) const return this->PrintBase::output_filename(m_print_config.output_filename_format.value, ".sl1", filename_base, &config); } -namespace { - -bool is_zero_elevation(const SLAPrintObjectConfig &c) { - return c.pad_enable.getBool() && c.pad_around_object.getBool(); -} - -// Compile the argument for support creation from the static print config. -sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) { - sla::SupportConfig scfg; - - scfg.enabled = c.supports_enable.getBool(); - scfg.head_front_radius_mm = 0.5*c.support_head_front_diameter.getFloat(); - scfg.head_back_radius_mm = 0.5*c.support_pillar_diameter.getFloat(); - scfg.head_penetration_mm = c.support_head_penetration.getFloat(); - scfg.head_width_mm = c.support_head_width.getFloat(); - scfg.object_elevation_mm = is_zero_elevation(c) ? - 0. : c.support_object_elevation.getFloat(); - scfg.bridge_slope = c.support_critical_angle.getFloat() * PI / 180.0 ; - scfg.max_bridge_length_mm = c.support_max_bridge_length.getFloat(); - scfg.max_pillar_link_distance_mm = c.support_max_pillar_link_distance.getFloat(); - switch(c.support_pillar_connection_mode.getInt()) { - case slapcmZigZag: - scfg.pillar_connection_mode = sla::PillarConnectionMode::zigzag; break; - case slapcmCross: - scfg.pillar_connection_mode = sla::PillarConnectionMode::cross; break; - case slapcmDynamic: - scfg.pillar_connection_mode = sla::PillarConnectionMode::dynamic; break; - } - scfg.ground_facing_only = c.support_buildplate_only.getBool(); - scfg.pillar_widening_factor = c.support_pillar_widening_factor.getFloat(); - scfg.base_radius_mm = 0.5*c.support_base_diameter.getFloat(); - scfg.base_height_mm = c.support_base_height.getFloat(); - scfg.pillar_base_safety_distance_mm = - c.support_base_safety_distance.getFloat() < EPSILON ? - scfg.safety_distance_mm : c.support_base_safety_distance.getFloat(); - - return scfg; -} - -sla::PadConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c) { - sla::PadConfig::EmbedObject ret; - - ret.enabled = is_zero_elevation(c); - - if(ret.enabled) { - ret.everywhere = c.pad_around_object_everywhere.getBool(); - ret.object_gap_mm = c.pad_object_gap.getFloat(); - ret.stick_width_mm = c.pad_object_connector_width.getFloat(); - ret.stick_stride_mm = c.pad_object_connector_stride.getFloat(); - ret.stick_penetration_mm = c.pad_object_connector_penetration - .getFloat(); - } - - return ret; -} - -sla::PadConfig make_pad_cfg(const SLAPrintObjectConfig& c) { - sla::PadConfig pcfg; - - pcfg.wall_thickness_mm = c.pad_wall_thickness.getFloat(); - pcfg.wall_slope = c.pad_wall_slope.getFloat() * PI / 180.0; - - pcfg.max_merge_dist_mm = c.pad_max_merge_distance.getFloat(); - pcfg.wall_height_mm = c.pad_wall_height.getFloat(); - pcfg.brim_size_mm = c.pad_brim_size.getFloat(); - - // set builtin pad implicitly ON - pcfg.embed_object = builtin_pad_cfg(c); - - return pcfg; -} - -bool validate_pad(const TriangleMesh &pad, const sla::PadConfig &pcfg) -{ - // An empty pad can only be created if embed_object mode is enabled - // and the pad is not forced everywhere - return !pad.empty() || (pcfg.embed_object.enabled && !pcfg.embed_object.everywhere); -} - -} - std::string SLAPrint::validate() const { for(SLAPrintObject * po : m_objects) { @@ -726,751 +669,16 @@ bool SLAPrint::invalidate_step(SLAPrintStep step) void SLAPrint::process() { - using namespace sla; - using ExPolygon = Slic3r::ExPolygon; - if(m_objects.empty()) return; // Assumption: at this point the print objects should be populated only with // the model objects we have to process and the instances are also filtered - - // shortcut to initial layer height - double ilhd = m_material_config.initial_layer_height.getFloat(); - auto ilh = float(ilhd); - - coord_t ilhs = scaled(ilhd); - const size_t objcount = m_objects.size(); - - static const unsigned min_objstatus = 0; // where the per object operations start - static const unsigned max_objstatus = 50; // where the per object operations end - - // the coefficient that multiplies the per object status values which - // are set up for <0, 100>. They need to be scaled into the whole process - const double ostepd = (max_objstatus - min_objstatus) / (objcount * 100.0); - - // The slicing will be performed on an imaginary 1D grid which starts from - // the bottom of the bounding box created around the supported model. So - // the first layer which is usually thicker will be part of the supports - // not the model geometry. Exception is when the model is not in the air - // (elevation is zero) and no pad creation was requested. In this case the - // model geometry starts on the ground level and the initial layer is part - // of it. In any case, the model and the supports have to be sliced in the - // same imaginary grid (the height vector argument to TriangleMeshSlicer). - - // Slicing the model object. This method is oversimplified and needs to - // be compared with the fff slicing algorithm for verification - auto slice_model = [this, ilhs, ilh](SLAPrintObject& po) { - const TriangleMesh& mesh = po.transformed_mesh(); - - // We need to prepare the slice index... - - double lhd = m_objects.front()->m_config.layer_height.getFloat(); - float lh = float(lhd); - coord_t lhs = scaled(lhd); - auto && bb3d = mesh.bounding_box(); - double minZ = bb3d.min(Z) - po.get_elevation(); - double maxZ = bb3d.max(Z); - auto minZf = float(minZ); - coord_t minZs = scaled(minZ); - coord_t maxZs = scaled(maxZ); - - po.m_slice_index.clear(); - - size_t cap = size_t(1 + (maxZs - minZs - ilhs) / lhs); - po.m_slice_index.reserve(cap); - - po.m_slice_index.emplace_back(minZs + ilhs, minZf + ilh / 2.f, ilh); - - for(coord_t h = minZs + ilhs + lhs; h <= maxZs; h += lhs) - po.m_slice_index.emplace_back(h, unscaled(h) - lh / 2.f, lh); - - // Just get the first record that is from the model: - auto slindex_it = - po.closest_slice_record(po.m_slice_index, float(bb3d.min(Z))); - - if(slindex_it == po.m_slice_index.end()) - //TRN To be shown at the status bar on SLA slicing error. - throw std::runtime_error( - L("Slicing had to be stopped due to an internal error: " - "Inconsistent slice index.")); - - po.m_model_height_levels.clear(); - po.m_model_height_levels.reserve(po.m_slice_index.size()); - for(auto it = slindex_it; it != po.m_slice_index.end(); ++it) - po.m_model_height_levels.emplace_back(it->slice_level()); - - TriangleMeshSlicer slicer(&mesh); - - po.m_model_slices.clear(); - slicer.slice(po.m_model_height_levels, - float(po.config().slice_closing_radius.value), - &po.m_model_slices, - [this](){ throw_if_canceled(); }); - - auto mit = slindex_it; - double doffs = m_printer_config.absolute_correction.getFloat(); - coord_t clpr_offs = scaled(doffs); - for(size_t id = 0; - id < po.m_model_slices.size() && mit != po.m_slice_index.end(); - id++) - { - // We apply the printer correction offset here. - if(clpr_offs != 0) - po.m_model_slices[id] = - offset_ex(po.m_model_slices[id], float(clpr_offs)); - - mit->set_model_slice_idx(po, id); ++mit; - } - - if(po.m_config.supports_enable.getBool() || - po.m_config.pad_enable.getBool()) - { - po.m_supportdata.reset( - new SLAPrintObject::SupportData(po.transformed_mesh()) ); - } - }; - - // In this step we check the slices, identify island and cover them with - // support points. Then we sprinkle the rest of the mesh. - auto support_points = [this, ostepd](SLAPrintObject& po) { - // If supports are disabled, we can skip the model scan. - if(!po.m_config.supports_enable.getBool()) return; - - if (!po.m_supportdata) - po.m_supportdata.reset( - new SLAPrintObject::SupportData(po.transformed_mesh())); - - const ModelObject& mo = *po.m_model_object; - - BOOST_LOG_TRIVIAL(debug) << "Support point count " - << mo.sla_support_points.size(); - - // Unless the user modified the points or we already did the calculation, we will do - // the autoplacement. Otherwise we will just blindly copy the frontend data - // into the backend cache. - if (mo.sla_points_status != sla::PointsStatus::UserModified) { - - // Hypothetical use of the slice index: - // auto bb = po.transformed_mesh().bounding_box(); - // auto range = po.get_slice_records(bb.min(Z)); - // std::vector heights; heights.reserve(range.size()); - // for(auto& record : range) heights.emplace_back(record.slice_level()); - - // calculate heights of slices (slices are calculated already) - const std::vector& heights = po.m_model_height_levels; - - this->throw_if_canceled(); - SLAAutoSupports::Config config; - const SLAPrintObjectConfig& cfg = po.config(); - - // the density config value is in percents: - config.density_relative = float(cfg.support_points_density_relative / 100.f); - config.minimal_distance = float(cfg.support_points_minimal_distance); - config.head_diameter = float(cfg.support_head_front_diameter); - - // scaling for the sub operations - double d = ostepd * OBJ_STEP_LEVELS[slaposSupportPoints] / 100.0; - double init = m_report_status.status(); - - auto statuscb = [this, d, init](unsigned st) - { - double current = init + st * d; - if(std::round(m_report_status.status()) < std::round(current)) - m_report_status(*this, current, - OBJ_STEP_LABELS(slaposSupportPoints)); - - }; - - // Construction of this object does the calculation. - this->throw_if_canceled(); - SLAAutoSupports auto_supports(po.m_supportdata->emesh, - po.get_model_slices(), - heights, - config, - [this]() { throw_if_canceled(); }, - statuscb); - - // Now let's extract the result. - const std::vector& points = auto_supports.output(); - this->throw_if_canceled(); - po.m_supportdata->pts = points; - - BOOST_LOG_TRIVIAL(debug) << "Automatic support points: " - << po.m_supportdata->pts.size(); - - // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass - // the update status to GLGizmoSlaSupports - m_report_status(*this, - -1, - L("Generating support points"), - SlicingStatus::RELOAD_SLA_SUPPORT_POINTS); - } - else { - // There are either some points on the front-end, or the user - // removed them on purpose. No calculation will be done. - po.m_supportdata->pts = po.transformed_support_points(); - } - - // If the zero elevation mode is engaged, we have to filter out all the - // points that are on the bottom of the object - if (is_zero_elevation(po.config())) { - double tolerance = po.config().pad_enable.getBool() - ? po.m_config.pad_wall_thickness.getFloat() - : po.m_config.support_base_height.getFloat(); - - remove_bottom_points(po.m_supportdata->pts, - po.m_supportdata->emesh.ground_level(), - tolerance); - } - }; - - // In this step we create the supports - auto support_tree = [this, ostepd](SLAPrintObject& po) - { - if(!po.m_supportdata) return; - - sla::PadConfig pcfg = make_pad_cfg(po.m_config); - - if (pcfg.embed_object) - po.m_supportdata->emesh.ground_level_offset(pcfg.wall_thickness_mm); - - po.m_supportdata->cfg = make_support_cfg(po.m_config); - - // scaling for the sub operations - double d = ostepd * OBJ_STEP_LEVELS[slaposSupportTree] / 100.0; - double init = m_report_status.status(); - JobController ctl; - - ctl.statuscb = [this, d, init](unsigned st, const std::string &logmsg) { - double current = init + st * d; - if (std::round(m_report_status.status()) < std::round(current)) - m_report_status(*this, current, - OBJ_STEP_LABELS(slaposSupportTree), - SlicingStatus::DEFAULT, logmsg); - }; - ctl.stopcondition = [this]() { return canceled(); }; - ctl.cancelfn = [this]() { throw_if_canceled(); }; - - po.m_supportdata->create_support_tree(ctl); - - if (!po.m_config.supports_enable.getBool()) return; - - throw_if_canceled(); - - // Create the unified mesh - auto rc = SlicingStatus::RELOAD_SCENE; - - // This is to prevent "Done." being displayed during merged_mesh() - m_report_status(*this, -1, L("Visualizing supports")); - - BOOST_LOG_TRIVIAL(debug) << "Processed support point count " - << po.m_supportdata->pts.size(); - - // Check the mesh for later troubleshooting. - if(po.support_mesh().empty()) - BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty"; - - m_report_status(*this, -1, L("Visualizing supports"), rc); - }; - - // This step generates the sla base pad - auto generate_pad = [this](SLAPrintObject& po) { - // this step can only go after the support tree has been created - // and before the supports had been sliced. (or the slicing has to be - // repeated) - - if(po.m_config.pad_enable.getBool()) - { - // Get the distilled pad configuration from the config - sla::PadConfig pcfg = make_pad_cfg(po.m_config); - - ExPolygons bp; // This will store the base plate of the pad. - double pad_h = pcfg.full_height(); - const TriangleMesh &trmesh = po.transformed_mesh(); - - // This call can get pretty time consuming - auto thrfn = [this](){ throw_if_canceled(); }; - - if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) { - // No support (thus no elevation) or zero elevation mode - // we sometimes call it "builtin pad" is enabled so we will - // get a sample from the bottom of the mesh and use it for pad - // creation. - sla::pad_blueprint(trmesh, bp, float(pad_h), - float(po.m_config.layer_height.getFloat()), - thrfn); - } - - po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg); - auto &pad_mesh = po.m_supportdata->support_tree_ptr->retrieve_mesh(MeshType::Pad); - - if (!validate_pad(pad_mesh, pcfg)) - throw std::runtime_error( - L("No pad can be generated for this model with the " - "current configuration")); - - } else if(po.m_supportdata && po.m_supportdata->support_tree_ptr) { - po.m_supportdata->support_tree_ptr->remove_pad(); - } - - po.throw_if_canceled(); - auto rc = SlicingStatus::RELOAD_SCENE; - m_report_status(*this, -1, L("Visualizing supports"), rc); - }; - - // Slicing the support geometries similarly to the model slicing procedure. - // If the pad had been added previously (see step "base_pool" than it will - // be part of the slices) - auto slice_supports = [this](SLAPrintObject& po) { - auto& sd = po.m_supportdata; - - if(sd) sd->support_slices.clear(); - - // Don't bother if no supports and no pad is present. - if (!po.m_config.supports_enable.getBool() && - !po.m_config.pad_enable.getBool()) - return; - - if(sd && sd->support_tree_ptr) { - - std::vector heights; heights.reserve(po.m_slice_index.size()); - - for(auto& rec : po.m_slice_index) { - heights.emplace_back(rec.slice_level()); - } - - sd->support_slices = sd->support_tree_ptr->slice( - heights, float(po.config().slice_closing_radius.value)); - } - - double doffs = m_printer_config.absolute_correction.getFloat(); - coord_t clpr_offs = scaled(doffs); - for(size_t i = 0; - i < sd->support_slices.size() && i < po.m_slice_index.size(); - ++i) - { - // We apply the printer correction offset here. - if(clpr_offs != 0) - sd->support_slices[i] = - offset_ex(sd->support_slices[i], float(clpr_offs)); - - po.m_slice_index[i].set_support_slice_idx(po, i); - } - - // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update - // status to the 3D preview to load the SLA slices. - m_report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW); - }; - - // Merging the slices from all the print objects into one slice grid and - // calculating print statistics from the merge result. - auto merge_slices_and_eval_stats = [this, ilhs]() { - - // clear the rasterizer input - m_printer_input.clear(); - - size_t mx = 0; - for(SLAPrintObject * o : m_objects) { - if(auto m = o->get_slice_index().size() > mx) mx = m; - } - - m_printer_input.reserve(mx); - - auto eps = coord_t(SCALED_EPSILON); - - for(SLAPrintObject * o : m_objects) { - coord_t gndlvl = o->get_slice_index().front().print_level() - ilhs; - - for(const SliceRecord& slicerecord : o->get_slice_index()) { - coord_t lvlid = slicerecord.print_level() - gndlvl; - - // Neat trick to round the layer levels to the grid. - lvlid = eps * (lvlid / eps); - - auto it = std::lower_bound(m_printer_input.begin(), - m_printer_input.end(), - PrintLayer(lvlid)); - - if(it == m_printer_input.end() || it->level() != lvlid) - it = m_printer_input.insert(it, PrintLayer(lvlid)); - - - it->add(slicerecord); - } - } - - m_print_statistics.clear(); - - using ClipperPoint = ClipperLib::IntPoint; - using ClipperPolygon = ClipperLib::Polygon; // see clipper_polygon.hpp in libnest2d - using ClipperPolygons = std::vector; - namespace sl = libnest2d::shapelike; // For algorithms - - // Set up custom union and diff functions for clipper polygons - auto polyunion = [] (const ClipperPolygons& subjects) - { - ClipperLib::Clipper clipper; - - bool closed = true; - - for(auto& path : subjects) { - clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); - clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); - } - - auto mode = ClipperLib::pftPositive; - - return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode); - }; - - auto polydiff = [](const ClipperPolygons& subjects, const ClipperPolygons& clips) - { - ClipperLib::Clipper clipper; - - bool closed = true; - - for(auto& path : subjects) { - clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); - clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); - } - - for(auto& path : clips) { - clipper.AddPath(path.Contour, ClipperLib::ptClip, closed); - clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed); - } - - auto mode = ClipperLib::pftPositive; - - return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode); - }; - - // libnest calculates positive area for clockwise polygons, Slic3r is in counter-clockwise - auto areafn = [](const ClipperPolygon& poly) { return - sl::area(poly); }; - - const double area_fill = m_printer_config.area_fill.getFloat()*0.01;// 0.5 (50%); - const double fast_tilt = m_printer_config.fast_tilt_time.getFloat();// 5.0; - const double slow_tilt = m_printer_config.slow_tilt_time.getFloat();// 8.0; - - const double init_exp_time = m_material_config.initial_exposure_time.getFloat(); - const double exp_time = m_material_config.exposure_time.getFloat(); - - const int fade_layers_cnt = m_default_object_config.faded_layers.getInt();// 10 // [3;20] - - const auto width = scaled(m_printer_config.display_width.getFloat()); - const auto height = scaled(m_printer_config.display_height.getFloat()); - const double display_area = width*height; - - // get polygons for all instances in the object - auto get_all_polygons = - [](const ExPolygons& input_polygons, - const std::vector& instances, - bool is_lefthanded) - { - ClipperPolygons polygons; - polygons.reserve(input_polygons.size() * instances.size()); - - for (const ExPolygon& polygon : input_polygons) { - if(polygon.contour.empty()) continue; - - for (size_t i = 0; i < instances.size(); ++i) - { - ClipperPolygon poly; - - // We need to reverse if is_lefthanded is true but - bool needreverse = is_lefthanded; - - // should be a move - poly.Contour.reserve(polygon.contour.size() + 1); - - auto& cntr = polygon.contour.points; - if(needreverse) - for(auto it = cntr.rbegin(); it != cntr.rend(); ++it) - poly.Contour.emplace_back(it->x(), it->y()); - else - for(auto& p : cntr) - poly.Contour.emplace_back(p.x(), p.y()); - - for(auto& h : polygon.holes) { - poly.Holes.emplace_back(); - auto& hole = poly.Holes.back(); - hole.reserve(h.points.size() + 1); - - if(needreverse) - for(auto it = h.points.rbegin(); it != h.points.rend(); ++it) - hole.emplace_back(it->x(), it->y()); - else - for(auto& p : h.points) - hole.emplace_back(p.x(), p.y()); - } - - if(is_lefthanded) { - for(auto& p : poly.Contour) p.X = -p.X; - for(auto& h : poly.Holes) for(auto& p : h) p.X = -p.X; - } - - sl::rotate(poly, double(instances[i].rotation)); - sl::translate(poly, ClipperPoint{instances[i].shift(X), - instances[i].shift(Y)}); - - polygons.emplace_back(std::move(poly)); - } - } - return polygons; - }; - - double supports_volume(0.0); - double models_volume(0.0); - - double estim_time(0.0); - - size_t slow_layers = 0; - size_t fast_layers = 0; - - const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1); - double fade_layer_time = init_exp_time; - - SpinMutex mutex; - using Lock = std::lock_guard; - - // Going to parallel: - auto printlayerfn = [this, - // functions and read only vars - get_all_polygons, polyunion, polydiff, areafn, - area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time, - - // write vars - &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers, - &fast_layers, &fade_layer_time](size_t sliced_layer_cnt) - { - PrintLayer& layer = m_printer_input[sliced_layer_cnt]; - - // vector of slice record references - auto& slicerecord_references = layer.slices(); - - if(slicerecord_references.empty()) return; - - // Layer height should match for all object slices for a given level. - const auto l_height = double(slicerecord_references.front().get().layer_height()); - - // Calculation of the consumed material - - ClipperPolygons model_polygons; - ClipperPolygons supports_polygons; - - size_t c = std::accumulate(layer.slices().begin(), - layer.slices().end(), - size_t(0), - [](size_t a, const SliceRecord &sr) { - return a + sr.get_slice(soModel) - .size(); - }); - - model_polygons.reserve(c); - - c = std::accumulate(layer.slices().begin(), - layer.slices().end(), - size_t(0), - [](size_t a, const SliceRecord &sr) { - return a + sr.get_slice(soModel).size(); - }); - - supports_polygons.reserve(c); - - for(const SliceRecord& record : layer.slices()) { - const SLAPrintObject *po = record.print_obj(); - - const ExPolygons &modelslices = record.get_slice(soModel); - - bool is_lefth = record.print_obj()->is_left_handed(); - if (!modelslices.empty()) { - ClipperPolygons v = get_all_polygons(modelslices, po->instances(), is_lefth); - for(ClipperPolygon& p_tmp : v) model_polygons.emplace_back(std::move(p_tmp)); - } - - const ExPolygons &supportslices = record.get_slice(soSupport); - - if (!supportslices.empty()) { - ClipperPolygons v = get_all_polygons(supportslices, po->instances(), is_lefth); - for(ClipperPolygon& p_tmp : v) supports_polygons.emplace_back(std::move(p_tmp)); - } - } - - model_polygons = polyunion(model_polygons); - double layer_model_area = 0; - for (const ClipperPolygon& polygon : model_polygons) - layer_model_area += areafn(polygon); - - if (layer_model_area < 0 || layer_model_area > 0) { - Lock lck(mutex); models_volume += layer_model_area * l_height; - } - - if(!supports_polygons.empty()) { - if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons); - else supports_polygons = polydiff(supports_polygons, model_polygons); - // allegedly, union of subject is done withing the diff according to the pftPositive polyFillType - } - - double layer_support_area = 0; - for (const ClipperPolygon& polygon : supports_polygons) - layer_support_area += areafn(polygon); - - if (layer_support_area < 0 || layer_support_area > 0) { - Lock lck(mutex); supports_volume += layer_support_area * l_height; - } - - // Here we can save the expensively calculated polygons for printing - ClipperPolygons trslices; - trslices.reserve(model_polygons.size() + supports_polygons.size()); - for(ClipperPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly)); - for(ClipperPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly)); - - layer.transformed_slices(polyunion(trslices)); - - // Calculation of the slow and fast layers to the future controlling those values on FW - - const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill; - const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt; - - { Lock lck(mutex); - if (is_fast_layer) - fast_layers++; - else - slow_layers++; - - - // Calculation of the printing time - - if (sliced_layer_cnt < 3) - estim_time += init_exp_time; - else if (fade_layer_time > exp_time) - { - fade_layer_time -= delta_fade_time; - estim_time += fade_layer_time; - } - else - estim_time += exp_time; - - estim_time += tilt_time; - } - }; - - // sequential version for debugging: - // for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i); - tbb::parallel_for(0, m_printer_input.size(), printlayerfn); - - auto SCALING2 = SCALING_FACTOR * SCALING_FACTOR; - m_print_statistics.support_used_material = supports_volume * SCALING2; - m_print_statistics.objects_used_material = models_volume * SCALING2; - - // Estimated printing time - // A layers count o the highest object - if (m_printer_input.size() == 0) - m_print_statistics.estimated_print_time = std::nan(""); - else - m_print_statistics.estimated_print_time = estim_time; - - m_print_statistics.fast_layers_count = fast_layers; - m_print_statistics.slow_layers_count = slow_layers; - -#if ENABLE_THUMBNAIL_GENERATOR - // second argument set to -3 to differentiate it from the same call made into slice_supports() - m_report_status(*this, -3, "", SlicingStatus::RELOAD_SLA_PREVIEW); -#else - m_report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW); -#endif // ENABLE_THUMBNAIL_GENERATOR - }; - - // Rasterizing the model objects, and their supports - auto rasterize = [this]() { - if(canceled()) return; - - // Set up the printer, allocate space for all the layers - sla::RasterWriter &printer = init_printer(); - - auto lvlcnt = unsigned(m_printer_input.size()); - printer.layers(lvlcnt); - - // coefficient to map the rasterization state (0-99) to the allocated - // portion (slot) of the process state - double sd = (100 - max_objstatus) / 100.0; - - // slot is the portion of 100% that is realted to rasterization - unsigned slot = PRINT_STEP_LEVELS[slapsRasterize]; - - // pst: previous state - double pst = m_report_status.status(); - - double increment = (slot * sd) / m_printer_input.size(); - double dstatus = m_report_status.status(); - - SpinMutex slck; - - // procedure to process one height level. This will run in parallel - auto lvlfn = - [this, &slck, &printer, increment, &dstatus, &pst] - (unsigned level_id) - { - if(canceled()) return; - - PrintLayer& printlayer = m_printer_input[level_id]; - - // Switch to the appropriate layer in the printer - printer.begin_layer(level_id); - - for(const ClipperLib::Polygon& poly : printlayer.transformed_slices()) - printer.draw_polygon(poly, level_id); - - // Finish the layer for later saving it. - printer.finish_layer(level_id); - - // Status indication guarded with the spinlock - { - std::lock_guard lck(slck); - dstatus += increment; - double st = std::round(dstatus); - if(st > pst) { - m_report_status(*this, st, - PRINT_STEP_LABELS(slapsRasterize)); - pst = st; - } - } - }; - - // last minute escape - if(canceled()) return; - - // Sequential version (for testing) - // for(unsigned l = 0; l < lvlcnt; ++l) lvlfn(l); - - // Print all the layers in parallel - tbb::parallel_for(0, lvlcnt, lvlfn); - - // Set statistics values to the printer - sla::RasterWriter::PrintStatistics stats; - stats.used_material = (m_print_statistics.objects_used_material + - m_print_statistics.support_used_material) / - 1000; - - int num_fade = m_default_object_config.faded_layers.getInt(); - stats.num_fade = num_fade >= 0 ? size_t(num_fade) : size_t(0); - stats.num_fast = m_print_statistics.fast_layers_count; - stats.num_slow = m_print_statistics.slow_layers_count; - stats.estimated_print_time_s = m_print_statistics.estimated_print_time; - - m_printer->set_statistics(stats); - }; - - using slaposFn = std::function; - using slapsFn = std::function; - - slaposFn pobj_program[] = - { - slice_model, support_points, support_tree, generate_pad, slice_supports - }; + + Steps printsteps{this}; // We want to first process all objects... std::vector level1_obj_steps = { - slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad + slaposHollowing, slaposDrillHoles, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad }; // and then slice all supports to allow preview to be displayed ASAP @@ -1478,10 +686,9 @@ void SLAPrint::process() slaposSliceSupports }; - slapsFn print_program[] = { merge_slices_and_eval_stats, rasterize }; SLAPrintStep print_steps[] = { slapsMergeSlicesAndEval, slapsRasterize }; - - double st = min_objstatus; + + double st = Steps::min_objstatus; BOOST_LOG_TRIVIAL(info) << "Start slicing process."; @@ -1496,10 +703,10 @@ void SLAPrint::process() std::array step_times {}; auto apply_steps_on_objects = - [this, &st, ostepd, &pobj_program, &step_times, &bench] + [this, &st, &printsteps, &step_times, &bench] (const std::vector &steps) { - unsigned incr = 0; + double incr = 0; for (SLAPrintObject *po : m_objects) { for (SLAPrintObjectStep step : steps) { @@ -1509,19 +716,19 @@ void SLAPrint::process() // throws the canceled signal. throw_if_canceled(); - st += incr * ostepd; + st += incr; if (po->m_stepmask[step] && po->set_started(step)) { - m_report_status(*this, st, OBJ_STEP_LABELS(step)); + m_report_status(*this, st, printsteps.label(step)); bench.start(); - pobj_program[step](*po); + printsteps.execute(step, *po); bench.stop(); step_times[step] += bench.getElapsedSec(); throw_if_canceled(); po->set_done(step); } - incr = OBJ_STEP_LEVELS[step]; + incr = printsteps.progressrange(step); } } }; @@ -1531,23 +738,22 @@ void SLAPrint::process() // this would disable the rasterization step // std::fill(m_stepmask.begin(), m_stepmask.end(), false); - - double pstd = (100 - max_objstatus) / 100.0; - st = max_objstatus; + + st = Steps::max_objstatus; for(SLAPrintStep currentstep : print_steps) { throw_if_canceled(); if (m_stepmask[currentstep] && set_started(currentstep)) { - m_report_status(*this, st, PRINT_STEP_LABELS(currentstep)); + m_report_status(*this, st, printsteps.label(currentstep)); bench.start(); - print_program[currentstep](); + printsteps.execute(currentstep); bench.stop(); step_times[slaposCount + currentstep] += bench.getElapsedSec(); throw_if_canceled(); set_done(currentstep); } - st += PRINT_STEP_LEVELS[currentstep] * pstd; + st += printsteps.progressrange(currentstep); } // If everything vent well @@ -1556,10 +762,10 @@ void SLAPrint::process() #ifdef SLAPRINT_DO_BENCHMARK std::string csvbenchstr; for (size_t i = 0; i < size_t(slaposCount); ++i) - csvbenchstr += OBJ_STEP_LABELS(i) + ";"; + csvbenchstr += printsteps.label(SLAPrintObjectStep(i)) + ";"; for (size_t i = 0; i < size_t(slapsCount); ++i) - csvbenchstr += PRINT_STEP_LABELS(i) + ";"; + csvbenchstr += printsteps.label(SLAPrintStep(i)) + ";"; csvbenchstr += "\n"; for (double t : step_times) csvbenchstr += std::to_string(t) + ";"; @@ -1711,7 +917,14 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector steps; bool invalidated = false; for (const t_config_option_key &opt_key : opt_keys) { - if ( opt_key == "layer_height" + if ( opt_key == "hollowing_enable" + || opt_key == "hollowing_min_thickness" + || opt_key == "hollowing_quality" + || opt_key == "hollowing_closing_distance" + ) { + steps.emplace_back(slaposHollowing); + } else if ( + opt_key == "layer_height" || opt_key == "faded_layers" || opt_key == "pad_enable" || opt_key == "pad_wall_thickness" @@ -1769,8 +982,14 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step) { bool invalidated = Inherited::invalidate_step(step); // propagate to dependent steps - if (step == slaposObjectSlice) { + if (step == slaposHollowing) { invalidated |= this->invalidate_all_steps(); + } else if (step == slaposDrillHoles) { + invalidated |= this->invalidate_steps({ slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports }); + invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); + } else if (step == slaposObjectSlice) { + invalidated |= this->invalidate_steps({ slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports }); + invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); } else if (step == slaposSupportPoints) { invalidated |= this->invalidate_steps({ slaposSupportTree, slaposPad, slaposSliceSupports }); invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); @@ -1876,14 +1095,14 @@ const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const const std::vector& v = o == soModel? m_po->get_model_slices() : m_po->get_support_slices(); - if(idx >= v.size()) return EMPTY_SLICE; - return idx >= v.size() ? EMPTY_SLICE : v[idx]; } bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const { switch (step) { + case slaposDrillHoles: + return m_hollowing_data && !m_hollowing_data->hollow_mesh_with_holes.empty(); case slaposSupportTree: return ! this->support_mesh().empty(); case slaposPad: @@ -1900,6 +1119,10 @@ TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const return this->support_mesh(); case slaposPad: return this->pad_mesh(); + case slaposDrillHoles: + if (m_hollowing_data) + return m_hollowing_data->hollow_mesh_with_holes; + [[fallthrough]]; default: return TriangleMesh(); } @@ -1925,6 +1148,14 @@ const TriangleMesh& SLAPrintObject::pad_mesh() const return EMPTY_MESH; } +const TriangleMesh &SLAPrintObject::hollowed_interior_mesh() const +{ + if (m_hollowing_data && m_config.hollowing_enable.getBool()) + return m_hollowing_data->interior; + + return EMPTY_MESH; +} + const TriangleMesh &SLAPrintObject::transformed_mesh() const { // we need to transform the raw mesh... // currently all the instances share the same x and y rotation and scaling @@ -1938,21 +1169,36 @@ const TriangleMesh &SLAPrintObject::transformed_mesh() const { return m_transformed_rmesh.get(); } -std::vector SLAPrintObject::transformed_support_points() const +sla::SupportPoints SLAPrintObject::transformed_support_points() const { assert(m_model_object != nullptr); - std::vector& spts = m_model_object->sla_support_points; + auto spts = m_model_object->sla_support_points; + auto tr = trafo().cast(); + for (sla::SupportPoint& suppt : spts) { + suppt.pos = tr * suppt.pos; + } + + return spts; +} - // this could be cached as well - std::vector ret; - ret.reserve(spts.size()); +sla::DrainHoles SLAPrintObject::transformed_drainhole_points() const +{ + assert(m_model_object != nullptr); + auto pts = m_model_object->sla_drain_holes; + auto tr = trafo().cast(); + auto sc = m_model_object->instances.front()->get_scaling_factor().cast(); + for (sla::DrainHole &hl : pts) { + hl.pos = tr * hl.pos; + hl.normal = tr * hl.normal - tr.translation(); - for(sla::SupportPoint& sp : spts) { - Vec3f transformed_pos = trafo().cast() * sp.pos; - ret.emplace_back(transformed_pos, sp.head_front_radius, sp.is_new_island); + // The normal scales as a covector (and we must also + // undo the damage already done). + hl.normal = Vec3f(hl.normal(0)/(sc(0)*sc(0)), + hl.normal(1)/(sc(1)*sc(1)), + hl.normal(2)/(sc(2)*sc(2))); } - return ret; + return pts; } DynamicConfig SLAPrintStatistics::config() const diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 8f386d407f..c9f5198db8 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -3,8 +3,8 @@ #include #include "PrintBase.hpp" -//#include "PrintExport.hpp" -#include "SLA/SLARasterWriter.hpp" +#include "SLA/RasterWriter.hpp" +#include "SLA/SupportTree.hpp" #include "Point.hpp" #include "MTUtils.hpp" #include "Zipper.hpp" @@ -19,6 +19,8 @@ enum SLAPrintStep : unsigned int { }; enum SLAPrintObjectStep : unsigned int { + slaposHollowing, + slaposDrillHoles, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, @@ -73,13 +75,23 @@ public: // Support mesh is only valid if this->is_step_done(slaposSupportTree) is true. const TriangleMesh& support_mesh() const; // Get a pad mesh centered around origin in XY, and with zero rotation around Z applied. - // Support mesh is only valid if this->is_step_done(slaposBasePool) is true. + // Support mesh is only valid if this->is_step_done(slaposPad) is true. const TriangleMesh& pad_mesh() const; + + // Ready after this->is_step_done(slaposDrillHoles) is true + const TriangleMesh& hollowed_interior_mesh() const; + + // Get the mesh that is going to be printed with all the modifications + // like hollowing and drilled holes. + const TriangleMesh & get_mesh_to_print() const { + return (m_hollowing_data && is_step_done(slaposDrillHoles)) ? m_hollowing_data->hollow_mesh_with_holes : transformed_mesh(); + } // This will return the transformed mesh which is cached const TriangleMesh& transformed_mesh() const; - std::vector transformed_support_points() const; + sla::SupportPoints transformed_support_points() const; + sla::DrainHoles transformed_drainhole_points() const; // Get the needed Z elevation for the model geometry if supports should be // displayed. This Z offset should also be applied to the support @@ -126,9 +138,9 @@ public: // Returns the current layer height float layer_height() const { return m_height; } - bool is_valid() const { return ! std::isnan(m_slice_z); } + bool is_valid() const { return m_po && ! std::isnan(m_slice_z); } - const SLAPrintObject* print_obj() const { assert(m_po); return m_po; } + const SLAPrintObject* print_obj() const { return m_po; } // Methods for setting the indices into the slice vectors. void set_model_slice_idx(const SLAPrintObject &po, size_t id) { @@ -287,9 +299,35 @@ private: // Caching the transformed (m_trafo) raw mesh of the object mutable CachedObject m_transformed_rmesh; - - class SupportData; + + class SupportData : public sla::SupportableMesh + { + public: + sla::SupportTree::UPtr support_tree_ptr; // the supports + std::vector support_slices; // sliced supports + + inline SupportData(const TriangleMesh &t) + : sla::SupportableMesh{t, {}, {}} + {} + + sla::SupportTree::UPtr &create_support_tree(const sla::JobController &ctl) + { + support_tree_ptr = sla::SupportTree::create(*this, ctl); + return support_tree_ptr; + } + }; + std::unique_ptr m_supportdata; + + class HollowingData + { + public: + + TriangleMesh interior; + mutable TriangleMesh hollow_mesh_with_holes; // caching the complete hollowed mesh + }; + + std::unique_ptr m_hollowing_data; }; using PrintObjects = std::vector; @@ -339,7 +377,9 @@ class SLAPrint : public PrintBaseWithState { private: // Prevents erroneous use by other classes. typedef PrintBaseWithState Inherited; - + + class Steps; // See SLAPrintSteps.cpp + public: SLAPrint(): m_stepmask(slapsCount, true) {} @@ -381,6 +421,9 @@ public: // Extracted value from the configuration objects Vec3d relative_correction() const; + // Return sla tansformation for a given model_object + Transform3d sla_trafo(const ModelObject &model_object) const; + std::string output_filename(const std::string &filename_base = std::string()) const override; const SLAPrintStatistics& print_statistics() const { return m_print_statistics; } @@ -401,8 +444,8 @@ public: template void transformed_slices(Container&& c) { m_transformed_slices = std::forward(c); } - - friend void SLAPrint::process(); + + friend class SLAPrint::Steps; public: @@ -478,6 +521,19 @@ private: friend SLAPrintObject; }; +// Helper functions: + +bool is_zero_elevation(const SLAPrintObjectConfig &c); + +sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c); + +sla::PadConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c); + +sla::PadConfig make_pad_cfg(const SLAPrintObjectConfig& c); + +bool validate_pad(const TriangleMesh &pad, const sla::PadConfig &pcfg); + + } // namespace Slic3r #endif /* slic3r_SLAPrint_hpp_ */ diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp new file mode 100644 index 0000000000..c8550e2fe3 --- /dev/null +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -0,0 +1,916 @@ +#include +#include + +// Need the cylinder method for the the drainholes in hollowing step +#include + +#include +#include +#include + +#include + +// For geometry algorithms with native Clipper types (no copies and conversions) +#include + +#include + +#include "I18N.hpp" + +//! macro used to mark string used at localization, +//! return same string +#define L(s) Slic3r::I18N::translate(s) + +namespace Slic3r { + +namespace { + +const std::array OBJ_STEP_LEVELS = { + 10, // slaposHollowing, + 10, // slaposDrillHoles + 10, // slaposObjectSlice, + 20, // slaposSupportPoints, + 10, // slaposSupportTree, + 10, // slaposPad, + 30, // slaposSliceSupports, +}; + +std::string OBJ_STEP_LABELS(size_t idx) +{ + switch (idx) { + case slaposHollowing: return L("Hollowing model"); + case slaposDrillHoles: return L("Drilling holes into model."); + case slaposObjectSlice: return L("Slicing model"); + case slaposSupportPoints: return L("Generating support points"); + case slaposSupportTree: return L("Generating support tree"); + case slaposPad: return L("Generating pad"); + case slaposSliceSupports: return L("Slicing supports"); + default:; + } + assert(false); + return "Out of bounds!"; +} + +const std::array PRINT_STEP_LEVELS = { + 10, // slapsMergeSlicesAndEval + 90, // slapsRasterize +}; + +std::string PRINT_STEP_LABELS(size_t idx) +{ + switch (idx) { + case slapsMergeSlicesAndEval: return L("Merging slices and calculating statistics"); + case slapsRasterize: return L("Rasterizing layers"); + default:; + } + assert(false); return "Out of bounds!"; +} + +} + +SLAPrint::Steps::Steps(SLAPrint *print) + : m_print{print} + , m_rng{std::random_device{}()} + , objcount{m_print->m_objects.size()} + , ilhd{m_print->m_material_config.initial_layer_height.getFloat()} + , ilh{float(ilhd)} + , ilhs{scaled(ilhd)} + , objectstep_scale{(max_objstatus - min_objstatus) / (objcount * 100.0)} +{} + +void SLAPrint::Steps::hollow_model(SLAPrintObject &po) +{ + po.m_hollowing_data.reset(); + + if (! po.m_config.hollowing_enable.getBool()) { + BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!"; + return; + } + + BOOST_LOG_TRIVIAL(info) << "Performing hollowing step!"; + + double thickness = po.m_config.hollowing_min_thickness.getFloat(); + double quality = po.m_config.hollowing_quality.getFloat(); + double closing_d = po.m_config.hollowing_closing_distance.getFloat(); + sla::HollowingConfig hlwcfg{thickness, quality, closing_d}; + auto meshptr = generate_interior(po.transformed_mesh(), hlwcfg); + + if (meshptr->empty()) + BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!"; + else { + po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); + po.m_hollowing_data->interior = *meshptr; + } +} + +// Drill holes into the hollowed/original mesh. +void SLAPrint::Steps::drill_holes(SLAPrintObject &po) +{ + bool needs_drilling = ! po.m_model_object->sla_drain_holes.empty(); + bool is_hollowed = (po.m_hollowing_data && ! po.m_hollowing_data->interior.empty()); + + if (! is_hollowed && ! needs_drilling) { + // In this case we can dump any data that might have been + // generated on previous runs. + po.m_hollowing_data.reset(); + return; + } + + if (! po.m_hollowing_data) + po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); + + // Hollowing and/or drilling is active, m_hollowing_data is valid. + + // Regenerate hollowed mesh, even if it was there already. It may contain + // holes that are no longer on the frontend. + TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes; + hollowed_mesh = po.transformed_mesh(); + if (! po.m_hollowing_data->interior.empty()) { + hollowed_mesh.merge(po.m_hollowing_data->interior); + hollowed_mesh.require_shared_vertices(); + } + + if (! needs_drilling) { + BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes)."; + return; + } + + BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes."; + sla::DrainHoles drainholes = po.transformed_drainhole_points(); + + std::uniform_real_distribution dist(0., float(EPSILON)); + auto holes_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal({}); + for (sla::DrainHole holept : drainholes) { + holept.normal += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)}; + holept.normal.normalize(); + holept.pos += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)}; + TriangleMesh m = sla::to_triangle_mesh(holept.to_mesh()); + m.require_shared_vertices(); + auto cgal_m = MeshBoolean::cgal::triangle_mesh_to_cgal(m); + MeshBoolean::cgal::plus(*holes_mesh_cgal, *cgal_m); + } + + if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal)) + throw std::runtime_error(L("Too much overlapping holes.")); + + auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh); + + try { + MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal); + hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); + } catch (const std::runtime_error &) { + throw std::runtime_error(L( + "Drilling holes into the mesh failed. " + "This is usually caused by broken model. Try to fix it first.")); + } +} + +// The slicing will be performed on an imaginary 1D grid which starts from +// the bottom of the bounding box created around the supported model. So +// the first layer which is usually thicker will be part of the supports +// not the model geometry. Exception is when the model is not in the air +// (elevation is zero) and no pad creation was requested. In this case the +// model geometry starts on the ground level and the initial layer is part +// of it. In any case, the model and the supports have to be sliced in the +// same imaginary grid (the height vector argument to TriangleMeshSlicer). +void SLAPrint::Steps::slice_model(SLAPrintObject &po) +{ + const TriangleMesh &mesh = po.get_mesh_to_print(); + + // We need to prepare the slice index... + + double lhd = m_print->m_objects.front()->m_config.layer_height.getFloat(); + float lh = float(lhd); + coord_t lhs = scaled(lhd); + auto && bb3d = mesh.bounding_box(); + double minZ = bb3d.min(Z) - po.get_elevation(); + double maxZ = bb3d.max(Z); + auto minZf = float(minZ); + coord_t minZs = scaled(minZ); + coord_t maxZs = scaled(maxZ); + + po.m_slice_index.clear(); + + size_t cap = size_t(1 + (maxZs - minZs - ilhs) / lhs); + po.m_slice_index.reserve(cap); + + po.m_slice_index.emplace_back(minZs + ilhs, minZf + ilh / 2.f, ilh); + + for(coord_t h = minZs + ilhs + lhs; h <= maxZs; h += lhs) + po.m_slice_index.emplace_back(h, unscaled(h) - lh / 2.f, lh); + + // Just get the first record that is from the model: + auto slindex_it = + po.closest_slice_record(po.m_slice_index, float(bb3d.min(Z))); + + if(slindex_it == po.m_slice_index.end()) + //TRN To be shown at the status bar on SLA slicing error. + throw std::runtime_error( + L("Slicing had to be stopped due to an internal error: " + "Inconsistent slice index.")); + + po.m_model_height_levels.clear(); + po.m_model_height_levels.reserve(po.m_slice_index.size()); + for(auto it = slindex_it; it != po.m_slice_index.end(); ++it) + po.m_model_height_levels.emplace_back(it->slice_level()); + + TriangleMeshSlicer slicer(&mesh); + + po.m_model_slices.clear(); + float closing_r = float(po.config().slice_closing_radius.value); + auto thr = [this]() { m_print->throw_if_canceled(); }; + auto &slice_grid = po.m_model_height_levels; + slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &po.m_model_slices, thr); + + if (po.m_hollowing_data && ! po.m_hollowing_data->interior.empty()) { + po.m_hollowing_data->interior.repair(true); + TriangleMeshSlicer interior_slicer(&po.m_hollowing_data->interior); + std::vector interior_slices; + interior_slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &interior_slices, thr); + + sla::ccr::enumerate(interior_slices.begin(), interior_slices.end(), + [&po](const ExPolygons &slice, size_t i) { + po.m_model_slices[i] = + diff_ex(po.m_model_slices[i], slice); + }); + } + + auto mit = slindex_it; + double doffs = m_print->m_printer_config.absolute_correction.getFloat(); + coord_t clpr_offs = scaled(doffs); + for(size_t id = 0; + id < po.m_model_slices.size() && mit != po.m_slice_index.end(); + id++) + { + // We apply the printer correction offset here. + if(clpr_offs != 0) + po.m_model_slices[id] = + offset_ex(po.m_model_slices[id], float(clpr_offs)); + + mit->set_model_slice_idx(po, id); ++mit; + } + + if(po.m_config.supports_enable.getBool() || po.m_config.pad_enable.getBool()) + { + po.m_supportdata.reset(new SLAPrintObject::SupportData(mesh)); + } +} + +// In this step we check the slices, identify island and cover them with +// support points. Then we sprinkle the rest of the mesh. +void SLAPrint::Steps::support_points(SLAPrintObject &po) +{ + // If supports are disabled, we can skip the model scan. + if(!po.m_config.supports_enable.getBool()) return; + + const TriangleMesh &mesh = po.get_mesh_to_print(); + + if (!po.m_supportdata) + po.m_supportdata.reset(new SLAPrintObject::SupportData(mesh)); + + const ModelObject& mo = *po.m_model_object; + + BOOST_LOG_TRIVIAL(debug) << "Support point count " + << mo.sla_support_points.size(); + + // Unless the user modified the points or we already did the calculation, + // we will do the autoplacement. Otherwise we will just blindly copy the + // frontend data into the backend cache. + if (mo.sla_points_status != sla::PointsStatus::UserModified) { + + // calculate heights of slices (slices are calculated already) + const std::vector& heights = po.m_model_height_levels; + + // Tell the mesh where drain holes are. Although the points are + // calculated on slices, the algorithm then raycasts the points + // so they actually lie on the mesh. +// po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points()); + + throw_if_canceled(); + sla::SupportPointGenerator::Config config; + const SLAPrintObjectConfig& cfg = po.config(); + + // the density config value is in percents: + config.density_relative = float(cfg.support_points_density_relative / 100.f); + config.minimal_distance = float(cfg.support_points_minimal_distance); + config.head_diameter = float(cfg.support_head_front_diameter); + + // scaling for the sub operations + double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportPoints] / 100.0; + double init = current_status(); + + auto statuscb = [this, d, init](unsigned st) + { + double current = init + st * d; + if(std::round(current_status()) < std::round(current)) + report_status(current, OBJ_STEP_LABELS(slaposSupportPoints)); + }; + + // Construction of this object does the calculation. + throw_if_canceled(); + sla::SupportPointGenerator auto_supports( + po.m_supportdata->emesh, po.get_model_slices(), heights, config, + [this]() { throw_if_canceled(); }, statuscb); + + // Now let's extract the result. + const std::vector& points = auto_supports.output(); + throw_if_canceled(); + po.m_supportdata->pts = points; + + BOOST_LOG_TRIVIAL(debug) << "Automatic support points: " + << po.m_supportdata->pts.size(); + + // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass + // the update status to GLGizmoSlaSupports + report_status(-1, L("Generating support points"), + SlicingStatus::RELOAD_SLA_SUPPORT_POINTS); + } else { + // There are either some points on the front-end, or the user + // removed them on purpose. No calculation will be done. + po.m_supportdata->pts = po.transformed_support_points(); + } + + // If the zero elevation mode is engaged, we have to filter out all the + // points that are on the bottom of the object + if (is_zero_elevation(po.config())) { + double tolerance = po.config().pad_enable.getBool() ? + po.m_config.pad_wall_thickness.getFloat() : + po.m_config.support_base_height.getFloat(); + + remove_bottom_points(po.m_supportdata->pts, + po.m_supportdata->emesh.ground_level(), + tolerance); + } +} + +void SLAPrint::Steps::support_tree(SLAPrintObject &po) +{ + if(!po.m_supportdata) return; + + sla::PadConfig pcfg = make_pad_cfg(po.m_config); + + if (pcfg.embed_object) + po.m_supportdata->emesh.ground_level_offset(pcfg.wall_thickness_mm); + + po.m_supportdata->cfg = make_support_cfg(po.m_config); +// po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points()); + + // scaling for the sub operations + double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportTree] / 100.0; + double init = current_status(); + sla::JobController ctl; + + ctl.statuscb = [this, d, init](unsigned st, const std::string &logmsg) { + double current = init + st * d; + if (std::round(current_status()) < std::round(current)) + report_status(current, OBJ_STEP_LABELS(slaposSupportTree), + SlicingStatus::DEFAULT, logmsg); + }; + ctl.stopcondition = [this]() { return canceled(); }; + ctl.cancelfn = [this]() { throw_if_canceled(); }; + + po.m_supportdata->create_support_tree(ctl); + + if (!po.m_config.supports_enable.getBool()) return; + + throw_if_canceled(); + + // Create the unified mesh + auto rc = SlicingStatus::RELOAD_SCENE; + + // This is to prevent "Done." being displayed during merged_mesh() + report_status(-1, L("Visualizing supports")); + + BOOST_LOG_TRIVIAL(debug) << "Processed support point count " + << po.m_supportdata->pts.size(); + + // Check the mesh for later troubleshooting. + if(po.support_mesh().empty()) + BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty"; + + report_status(-1, L("Visualizing supports"), rc); +} + +void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { + // this step can only go after the support tree has been created + // and before the supports had been sliced. (or the slicing has to be + // repeated) + + if(po.m_config.pad_enable.getBool()) { + // Get the distilled pad configuration from the config + sla::PadConfig pcfg = make_pad_cfg(po.m_config); + + ExPolygons bp; // This will store the base plate of the pad. + double pad_h = pcfg.full_height(); + const TriangleMesh &trmesh = po.transformed_mesh(); + + if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) { + // No support (thus no elevation) or zero elevation mode + // we sometimes call it "builtin pad" is enabled so we will + // get a sample from the bottom of the mesh and use it for pad + // creation. + sla::pad_blueprint(trmesh, bp, float(pad_h), + float(po.m_config.layer_height.getFloat()), + [this](){ throw_if_canceled(); }); + } + + po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg); + auto &pad_mesh = po.m_supportdata->support_tree_ptr->retrieve_mesh(sla::MeshType::Pad); + + if (!validate_pad(pad_mesh, pcfg)) + throw std::runtime_error( + L("No pad can be generated for this model with the " + "current configuration")); + + } else if(po.m_supportdata && po.m_supportdata->support_tree_ptr) { + po.m_supportdata->support_tree_ptr->remove_pad(); + } + + throw_if_canceled(); + report_status(-1, L("Visualizing supports"), SlicingStatus::RELOAD_SCENE); +} + +// Slicing the support geometries similarly to the model slicing procedure. +// If the pad had been added previously (see step "base_pool" than it will +// be part of the slices) +void SLAPrint::Steps::slice_supports(SLAPrintObject &po) { + auto& sd = po.m_supportdata; + + if(sd) sd->support_slices.clear(); + + // Don't bother if no supports and no pad is present. + if (!po.m_config.supports_enable.getBool() && !po.m_config.pad_enable.getBool()) + return; + + if(sd && sd->support_tree_ptr) { + auto heights = reserve_vector(po.m_slice_index.size()); + + for(auto& rec : po.m_slice_index) heights.emplace_back(rec.slice_level()); + + sd->support_slices = sd->support_tree_ptr->slice( + heights, float(po.config().slice_closing_radius.value)); + } + + double doffs = m_print->m_printer_config.absolute_correction.getFloat(); + coord_t clpr_offs = scaled(doffs); + + for (size_t i = 0; i < sd->support_slices.size() && i < po.m_slice_index.size(); ++i) { + // We apply the printer correction offset here. + if (clpr_offs != 0) + sd->support_slices[i] = offset_ex(sd->support_slices[i], float(clpr_offs)); + + po.m_slice_index[i].set_support_slice_idx(po, i); + } + + // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update + // status to the 3D preview to load the SLA slices. + report_status(-2, "", SlicingStatus::RELOAD_SLA_PREVIEW); +} + +using ClipperPoint = ClipperLib::IntPoint; +using ClipperPolygon = ClipperLib::Polygon; // see clipper_polygon.hpp in libnest2d +using ClipperPolygons = std::vector; + +static ClipperPolygons polyunion(const ClipperPolygons &subjects) +{ + ClipperLib::Clipper clipper; + + bool closed = true; + + for(auto& path : subjects) { + clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); + clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); + } + + auto mode = ClipperLib::pftPositive; + + return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode); +} + +static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPolygons& clips) +{ + ClipperLib::Clipper clipper; + + bool closed = true; + + for(auto& path : subjects) { + clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); + clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); + } + + for(auto& path : clips) { + clipper.AddPath(path.Contour, ClipperLib::ptClip, closed); + clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed); + } + + auto mode = ClipperLib::pftPositive; + + return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode); +} + +// get polygons for all instances in the object +static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o) +{ + namespace sl = libnest2d::sl; + + if (!record.print_obj()) return {}; + + ClipperPolygons polygons; + auto &input_polygons = record.get_slice(o); + auto &instances = record.print_obj()->instances(); + bool is_lefthanded = record.print_obj()->is_left_handed(); + polygons.reserve(input_polygons.size() * instances.size()); + + for (const ExPolygon& polygon : input_polygons) { + if(polygon.contour.empty()) continue; + + for (size_t i = 0; i < instances.size(); ++i) + { + ClipperPolygon poly; + + // We need to reverse if is_lefthanded is true but + bool needreverse = is_lefthanded; + + // should be a move + poly.Contour.reserve(polygon.contour.size() + 1); + + auto& cntr = polygon.contour.points; + if(needreverse) + for(auto it = cntr.rbegin(); it != cntr.rend(); ++it) + poly.Contour.emplace_back(it->x(), it->y()); + else + for(auto& p : cntr) + poly.Contour.emplace_back(p.x(), p.y()); + + for(auto& h : polygon.holes) { + poly.Holes.emplace_back(); + auto& hole = poly.Holes.back(); + hole.reserve(h.points.size() + 1); + + if(needreverse) + for(auto it = h.points.rbegin(); it != h.points.rend(); ++it) + hole.emplace_back(it->x(), it->y()); + else + for(auto& p : h.points) + hole.emplace_back(p.x(), p.y()); + } + + if(is_lefthanded) { + for(auto& p : poly.Contour) p.X = -p.X; + for(auto& h : poly.Holes) for(auto& p : h) p.X = -p.X; + } + + sl::rotate(poly, double(instances[i].rotation)); + sl::translate(poly, ClipperPoint{instances[i].shift.x(), + instances[i].shift.y()}); + + polygons.emplace_back(std::move(poly)); + } + } + + return polygons; +} + +void SLAPrint::Steps::initialize_printer_input() +{ + auto &printer_input = m_print->m_printer_input; + + // clear the rasterizer input + printer_input.clear(); + + size_t mx = 0; + for(SLAPrintObject * o : m_print->m_objects) { + if(auto m = o->get_slice_index().size() > mx) mx = m; + } + + printer_input.reserve(mx); + + auto eps = coord_t(SCALED_EPSILON); + + for(SLAPrintObject * o : m_print->m_objects) { + coord_t gndlvl = o->get_slice_index().front().print_level() - ilhs; + + for(const SliceRecord& slicerecord : o->get_slice_index()) { + if (!slicerecord.is_valid()) + throw std::runtime_error( + L("There are unprintable objects. Try to " + "adjust support settings to make the " + "objects printable.")); + + coord_t lvlid = slicerecord.print_level() - gndlvl; + + // Neat trick to round the layer levels to the grid. + lvlid = eps * (lvlid / eps); + + auto it = std::lower_bound(printer_input.begin(), + printer_input.end(), + PrintLayer(lvlid)); + + if(it == printer_input.end() || it->level() != lvlid) + it = printer_input.insert(it, PrintLayer(lvlid)); + + + it->add(slicerecord); + } + } +} + +// Merging the slices from all the print objects into one slice grid and +// calculating print statistics from the merge result. +void SLAPrint::Steps::merge_slices_and_eval_stats() { + + initialize_printer_input(); + + auto &print_statistics = m_print->m_print_statistics; + auto &printer_config = m_print->m_printer_config; + auto &material_config = m_print->m_material_config; + auto &printer_input = m_print->m_printer_input; + + print_statistics.clear(); + + // libnest calculates positive area for clockwise polygons, Slic3r is in counter-clockwise + auto areafn = [](const ClipperPolygon& poly) { return - libnest2d::sl::area(poly); }; + + const double area_fill = printer_config.area_fill.getFloat()*0.01;// 0.5 (50%); + const double fast_tilt = printer_config.fast_tilt_time.getFloat();// 5.0; + const double slow_tilt = printer_config.slow_tilt_time.getFloat();// 8.0; + + const double init_exp_time = material_config.initial_exposure_time.getFloat(); + const double exp_time = material_config.exposure_time.getFloat(); + + const int fade_layers_cnt = m_print->m_default_object_config.faded_layers.getInt();// 10 // [3;20] + + const auto width = scaled(printer_config.display_width.getFloat()); + const auto height = scaled(printer_config.display_height.getFloat()); + const double display_area = width*height; + + double supports_volume(0.0); + double models_volume(0.0); + + double estim_time(0.0); + + size_t slow_layers = 0; + size_t fast_layers = 0; + + const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1); + double fade_layer_time = init_exp_time; + + sla::ccr::SpinningMutex mutex; + using Lock = std::lock_guard; + + // Going to parallel: + auto printlayerfn = [ + // functions and read only vars + areafn, area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time, + + // write vars + &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers, + &fast_layers, &fade_layer_time](PrintLayer& layer, size_t sliced_layer_cnt) + { + // vector of slice record references + auto& slicerecord_references = layer.slices(); + + if(slicerecord_references.empty()) return; + + // Layer height should match for all object slices for a given level. + const auto l_height = double(slicerecord_references.front().get().layer_height()); + + // Calculation of the consumed material + + ClipperPolygons model_polygons; + ClipperPolygons supports_polygons; + + size_t c = std::accumulate(layer.slices().begin(), + layer.slices().end(), + size_t(0), + [](size_t a, const SliceRecord &sr) { + return a + sr.get_slice(soModel).size(); + }); + + model_polygons.reserve(c); + + c = std::accumulate(layer.slices().begin(), + layer.slices().end(), + size_t(0), + [](size_t a, const SliceRecord &sr) { + return a + sr.get_slice(soModel).size(); + }); + + supports_polygons.reserve(c); + + for(const SliceRecord& record : layer.slices()) { + + ClipperPolygons modelslices = get_all_polygons(record, soModel); + for(ClipperPolygon& p_tmp : modelslices) model_polygons.emplace_back(std::move(p_tmp)); + + ClipperPolygons supportslices = get_all_polygons(record, soSupport); + for(ClipperPolygon& p_tmp : supportslices) supports_polygons.emplace_back(std::move(p_tmp)); + + } + + model_polygons = polyunion(model_polygons); + double layer_model_area = 0; + for (const ClipperPolygon& polygon : model_polygons) + layer_model_area += areafn(polygon); + + if (layer_model_area < 0 || layer_model_area > 0) { + Lock lck(mutex); models_volume += layer_model_area * l_height; + } + + if(!supports_polygons.empty()) { + if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons); + else supports_polygons = polydiff(supports_polygons, model_polygons); + // allegedly, union of subject is done withing the diff according to the pftPositive polyFillType + } + + double layer_support_area = 0; + for (const ClipperPolygon& polygon : supports_polygons) + layer_support_area += areafn(polygon); + + if (layer_support_area < 0 || layer_support_area > 0) { + Lock lck(mutex); supports_volume += layer_support_area * l_height; + } + + // Here we can save the expensively calculated polygons for printing + ClipperPolygons trslices; + trslices.reserve(model_polygons.size() + supports_polygons.size()); + for(ClipperPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly)); + for(ClipperPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly)); + + layer.transformed_slices(polyunion(trslices)); + + // Calculation of the slow and fast layers to the future controlling those values on FW + + const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill; + const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt; + + { Lock lck(mutex); + if (is_fast_layer) + fast_layers++; + else + slow_layers++; + + + // Calculation of the printing time + + if (sliced_layer_cnt < 3) + estim_time += init_exp_time; + else if (fade_layer_time > exp_time) + { + fade_layer_time -= delta_fade_time; + estim_time += fade_layer_time; + } + else + estim_time += exp_time; + + estim_time += tilt_time; + } + }; + + // sequential version for debugging: + // for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i); + sla::ccr::enumerate(printer_input.begin(), printer_input.end(), printlayerfn); + + auto SCALING2 = SCALING_FACTOR * SCALING_FACTOR; + print_statistics.support_used_material = supports_volume * SCALING2; + print_statistics.objects_used_material = models_volume * SCALING2; + + // Estimated printing time + // A layers count o the highest object + if (printer_input.size() == 0) + print_statistics.estimated_print_time = std::nan(""); + else + print_statistics.estimated_print_time = estim_time; + + print_statistics.fast_layers_count = fast_layers; + print_statistics.slow_layers_count = slow_layers; + + report_status(-2, "", SlicingStatus::RELOAD_SLA_PREVIEW); +} + +// Rasterizing the model objects, and their supports +void SLAPrint::Steps::rasterize() +{ + if(canceled()) return; + + auto &print_statistics = m_print->m_print_statistics; + auto &printer_input = m_print->m_printer_input; + + // Set up the printer, allocate space for all the layers + sla::RasterWriter &printer = m_print->init_printer(); + + auto lvlcnt = unsigned(printer_input.size()); + printer.layers(lvlcnt); + + // coefficient to map the rasterization state (0-99) to the allocated + // portion (slot) of the process state + double sd = (100 - max_objstatus) / 100.0; + + // slot is the portion of 100% that is realted to rasterization + unsigned slot = PRINT_STEP_LEVELS[slapsRasterize]; + + // pst: previous state + double pst = current_status(); + + double increment = (slot * sd) / printer_input.size(); + double dstatus = current_status(); + + sla::ccr::SpinningMutex slck; + using Lock = std::lock_guard; + + // procedure to process one height level. This will run in parallel + auto lvlfn = + [this, &slck, &printer, increment, &dstatus, &pst] + (PrintLayer& printlayer, size_t idx) + { + if(canceled()) return; + auto level_id = unsigned(idx); + + // Switch to the appropriate layer in the printer + printer.begin_layer(level_id); + + for(const ClipperLib::Polygon& poly : printlayer.transformed_slices()) + printer.draw_polygon(poly, level_id); + + // Finish the layer for later saving it. + printer.finish_layer(level_id); + + // Status indication guarded with the spinlock + { + Lock lck(slck); + dstatus += increment; + double st = std::round(dstatus); + if(st > pst) { + report_status(st, PRINT_STEP_LABELS(slapsRasterize)); + pst = st; + } + } + }; + + // last minute escape + if(canceled()) return; + + // Sequential version (for testing) + // for(unsigned l = 0; l < lvlcnt; ++l) lvlfn(l); + + // Print all the layers in parallel + sla::ccr::enumerate(printer_input.begin(), printer_input.end(), lvlfn); + + // Set statistics values to the printer + sla::RasterWriter::PrintStatistics stats; + stats.used_material = (print_statistics.objects_used_material + + print_statistics.support_used_material) / 1000; + + int num_fade = m_print->m_default_object_config.faded_layers.getInt(); + stats.num_fade = num_fade >= 0 ? size_t(num_fade) : size_t(0); + stats.num_fast = print_statistics.fast_layers_count; + stats.num_slow = print_statistics.slow_layers_count; + stats.estimated_print_time_s = print_statistics.estimated_print_time; + + printer.set_statistics(stats); +} + +std::string SLAPrint::Steps::label(SLAPrintObjectStep step) +{ + return OBJ_STEP_LABELS(step); +} + +std::string SLAPrint::Steps::label(SLAPrintStep step) +{ + return PRINT_STEP_LABELS(step); +} + +double SLAPrint::Steps::progressrange(SLAPrintObjectStep step) const +{ + return OBJ_STEP_LEVELS[step] * objectstep_scale; +} + +double SLAPrint::Steps::progressrange(SLAPrintStep step) const +{ + return PRINT_STEP_LEVELS[step] * (100 - max_objstatus) / 100.0; +} + +void SLAPrint::Steps::execute(SLAPrintObjectStep step, SLAPrintObject &obj) +{ + switch(step) { + case slaposHollowing: hollow_model(obj); break; + case slaposDrillHoles: drill_holes(obj); break; + case slaposObjectSlice: slice_model(obj); break; + case slaposSupportPoints: support_points(obj); break; + case slaposSupportTree: support_tree(obj); break; + case slaposPad: generate_pad(obj); break; + case slaposSliceSupports: slice_supports(obj); break; + case slaposCount: assert(false); + } +} + +void SLAPrint::Steps::execute(SLAPrintStep step) +{ + switch (step) { + case slapsMergeSlicesAndEval: merge_slices_and_eval_stats(); break; + case slapsRasterize: rasterize(); break; + case slapsCount: assert(false); + } +} + +} diff --git a/src/libslic3r/SLAPrintSteps.hpp b/src/libslic3r/SLAPrintSteps.hpp new file mode 100644 index 0000000000..0418072cf8 --- /dev/null +++ b/src/libslic3r/SLAPrintSteps.hpp @@ -0,0 +1,72 @@ +#ifndef SLAPRINTSTEPS_HPP +#define SLAPRINTSTEPS_HPP + +#include + +#include + +#include +#include + +namespace Slic3r { + +class SLAPrint::Steps +{ +private: + SLAPrint *m_print = nullptr; + std::mt19937 m_rng; + +public: + // where the per object operations start and end + static const constexpr unsigned min_objstatus = 0; + static const constexpr unsigned max_objstatus = 50; + +private: + const size_t objcount; + + // shortcut to initial layer height + const double ilhd; + const float ilh; + const coord_t ilhs; + + // the coefficient that multiplies the per object status values which + // are set up for <0, 100>. They need to be scaled into the whole process + const double objectstep_scale; + + template void report_status(Args&&...args) + { + m_print->m_report_status(*m_print, std::forward(args)...); + } + + double current_status() const { return m_print->m_report_status.status(); } + void throw_if_canceled() const { m_print->throw_if_canceled(); } + bool canceled() const { return m_print->canceled(); } + void initialize_printer_input(); + +public: + Steps(SLAPrint *print); + + void hollow_model(SLAPrintObject &po); + void drill_holes (SLAPrintObject &po); + void slice_model(SLAPrintObject& po); + void support_points(SLAPrintObject& po); + void support_tree(SLAPrintObject& po); + void generate_pad(SLAPrintObject& po); + void slice_supports(SLAPrintObject& po); + + void merge_slices_and_eval_stats(); + void rasterize(); + + void execute(SLAPrintObjectStep step, SLAPrintObject &obj); + void execute(SLAPrintStep step); + + static std::string label(SLAPrintObjectStep step); + static std::string label(SLAPrintStep step); + + double progressrange(SLAPrintObjectStep step) const; + double progressrange(SLAPrintStep step) const; +}; + +} + +#endif // SLAPRINTSTEPS_HPP diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index e9df4c5b5c..0aa897fd77 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -1946,24 +1946,27 @@ ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const Clippe return chain_path_items(points, items); } -std::vector> chain_print_object_instances(const Print &print) +std::vector chain_print_object_instances(const Print &print) { // Order objects using a nearest neighbor search. Points object_reference_points; std::vector> instances; for (size_t i = 0; i < print.objects().size(); ++ i) { const PrintObject &object = *print.objects()[i]; - for (size_t j = 0; j < object.copies().size(); ++ j) { - object_reference_points.emplace_back(object.copy_center(j)); + for (size_t j = 0; j < object.instances().size(); ++ j) { + // Sliced PrintObjects are centered, object.instances()[j].shift is the center of the PrintObject in G-code coordinates. + object_reference_points.emplace_back(object.instances()[j].shift); instances.emplace_back(i, j); } } auto segment_end_point = [&object_reference_points](size_t idx, bool /* first_point */) -> const Point& { return object_reference_points[idx]; }; std::vector> ordered = chain_segments_greedy(segment_end_point, instances.size(), nullptr); - std::vector> out; + std::vector out; out.reserve(instances.size()); - for (auto &segment_and_reversal : ordered) - out.emplace_back(instances[segment_and_reversal.first]); + for (auto &segment_and_reversal : ordered) { + const std::pair &inst = instances[segment_and_reversal.first]; + out.emplace_back(&print.objects()[inst.first]->instances()[inst.second]); + } return out; } diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp index cd342015d7..65d8b7f239 100644 --- a/src/libslic3r/ShortestPath.hpp +++ b/src/libslic3r/ShortestPath.hpp @@ -30,7 +30,8 @@ std::vector chain_clipper_polynodes(const Points &points // Chain instances of print objects by an approximate shortest path. // Returns pairs of PrintObject idx and instance of that PrintObject. class Print; -std::vector> chain_print_object_instances(const Print &print); +struct PrintInstance; +std::vector chain_print_object_instances(const Print &print); } // namespace Slic3r diff --git a/src/libslic3r/SimplifyMesh.cpp b/src/libslic3r/SimplifyMesh.cpp new file mode 100644 index 0000000000..d30ecfec57 --- /dev/null +++ b/src/libslic3r/SimplifyMesh.cpp @@ -0,0 +1,66 @@ +#include "SimplifyMesh.hpp" +#include "SimplifyMeshImpl.hpp" + +namespace SimplifyMesh { + +template<> struct vertex_traits { + using coord_type = float; + using compute_type = double; + + static inline float x(const stl_vertex &v) { return v.x(); } + static inline float& x(stl_vertex &v) { return v.x(); } + + static inline float y(const stl_vertex &v) { return v.y(); } + static inline float& y(stl_vertex &v) { return v.y(); } + + static inline float z(const stl_vertex &v) { return v.z(); } + static inline float& z(stl_vertex &v) { return v.z(); } +}; + +template<> struct mesh_traits { + using vertex_t = stl_vertex; + static size_t face_count(const indexed_triangle_set &m) + { + return m.indices.size(); + } + static size_t vertex_count(const indexed_triangle_set &m) + { + return m.vertices.size(); + } + static vertex_t vertex(const indexed_triangle_set &m, size_t idx) + { + return m.vertices[idx]; + } + static void vertex(indexed_triangle_set &m, size_t idx, const vertex_t &v) + { + m.vertices[idx] = v; + } + static Index3 triangle(const indexed_triangle_set &m, size_t idx) + { + std::array t; + for (size_t i = 0; i < 3; ++i) t[i] = size_t(m.indices[idx](int(i))); + return t; + } + static void triangle(indexed_triangle_set &m, size_t fidx, const Index3 &t) + { + auto &face = m.indices[fidx]; + face(0) = int(t[0]); face(1) = int(t[1]); face(2) = int(t[2]); + } + static void update(indexed_triangle_set &m, size_t vc, size_t fc) + { + m.vertices.resize(vc); + m.indices.resize(fc); + } +}; + +} // namespace SimplifyMesh + +namespace Slic3r { + +void simplify_mesh(indexed_triangle_set &m) +{ + SimplifyMesh::implementation::SimplifiableMesh sm{&m}; + sm.simplify_mesh_lossless(); +} + +} diff --git a/src/libslic3r/SimplifyMesh.hpp b/src/libslic3r/SimplifyMesh.hpp new file mode 100644 index 0000000000..fb3e73d049 --- /dev/null +++ b/src/libslic3r/SimplifyMesh.hpp @@ -0,0 +1,25 @@ +#ifndef MESHSIMPLIFY_HPP +#define MESHSIMPLIFY_HPP + +#include + +#include + +namespace Slic3r { + +void simplify_mesh(indexed_triangle_set &); + +// TODO: (but this can be done with IGL as well) +// void simplify_mesh(indexed_triangle_set &, int face_count, float agressiveness = 0.5f); + +template void simplify_mesh(TriangleMesh &m, Args &&...a) +{ + m.require_shared_vertices(); + simplify_mesh(m.its, std::forward(a)...); + m = TriangleMesh{m.its}; + m.require_shared_vertices(); +} + +} // namespace Slic3r + +#endif // MESHSIMPLIFY_H diff --git a/src/libslic3r/SimplifyMeshImpl.hpp b/src/libslic3r/SimplifyMeshImpl.hpp new file mode 100644 index 0000000000..4b6b0f5cbc --- /dev/null +++ b/src/libslic3r/SimplifyMeshImpl.hpp @@ -0,0 +1,670 @@ +// /////////////////////////////////////////// +// +// Mesh Simplification Tutorial +// +// (C) by Sven Forstmann in 2014 +// +// License : MIT +// http://opensource.org/licenses/MIT +// +// https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification +// +// 5/2016: Chris Rorden created minimal version for OSX/Linux/Windows compile +// https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification/ +// +// libslic3r refactor by tamasmeszaros + +#ifndef SIMPLIFYMESHIMPL_HPP +#define SIMPLIFYMESHIMPL_HPP + +#include +#include +#include +#include +#include + +#ifndef NDEBUG +#include +#include +#endif + +namespace SimplifyMesh { + +using Bary = std::array; +using Index3 = std::array; + +template struct vertex_traits { + using coord_type = typename Vertex::coord_type; + using compute_type = coord_type; + + static coord_type x(const Vertex &v); + static coord_type& x(Vertex &v); + + static coord_type y(const Vertex &v); + static coord_type& y(Vertex &v); + + static coord_type z(const Vertex &v); + static coord_type& z(Vertex &v); +}; + +template struct mesh_traits { + using vertex_t = typename Mesh::vertex_t; + + static size_t face_count(const Mesh &m); + static size_t vertex_count(const Mesh &m); + static vertex_t vertex(const Mesh &m, size_t vertex_idx); + static void vertex(Mesh &m, size_t vertex_idx, const vertex_t &v); + static Index3 triangle(const Mesh &m, size_t face_idx); + static void triangle(Mesh &m, size_t face_idx, const Index3 &t); + static void update(Mesh &m, size_t vertex_count, size_t face_count); +}; + +namespace implementation { + +// A shorter C++14 style form of the enable_if metafunction +template +using enable_if_t = typename std::enable_if::type; + +// Meta predicates for floating, integer and generic arithmetic types +template +using FloatingOnly = enable_if_t::value, O>; + +template +using IntegerOnly = enable_if_t::value, O>; + +template +using ArithmeticOnly = enable_if_t::value, O>; + +template< class T > +struct remove_cvref { + using type = typename std::remove_cv< + typename std::remove_reference::type>::type; +}; + +template< class T > +using remove_cvref_t = typename remove_cvref::type; + +template FloatingOnly is_approx(T val, T ref) { return std::abs(val - ref) < 1e-8; } +template IntegerOnly is_approx(T val, T ref) { val == ref; } + +template class SymetricMatrix { + static const constexpr size_t N = 10; +public: + + explicit SymetricMatrix(ArithmeticOnly c = T()) { std::fill(m, m + N, c); } + + // Make plane + SymetricMatrix(T a, T b, T c, T d) + { + m[0] = a * a; m[1] = a * b; m[2] = a * c; m[3] = a * d; + m[4] = b * b; m[5] = b * c; m[6] = b * d; + m[7] = c * c; m[8] = c * d; + m[9] = d * d; + } + + T operator[](int c) const { return m[c]; } + + // Determinant + T det(int a11, int a12, int a13, + int a21, int a22, int a23, + int a31, int a32, int a33) + { + T det = m[a11] * m[a22] * m[a33] + m[a13] * m[a21] * m[a32] + + m[a12] * m[a23] * m[a31] - m[a13] * m[a22] * m[a31] - + m[a11] * m[a23] * m[a32] - m[a12] * m[a21] * m[a33]; + + return det; + } + + const SymetricMatrix& operator+=(const SymetricMatrix& n) + { + for (size_t i = 0; i < N; ++i) m[i] += n[i]; + return *this; + } + + SymetricMatrix operator+(const SymetricMatrix& n) + { + SymetricMatrix self = *this; + return self += n; + } + + T m[N]; +}; + +template using TCoord = typename vertex_traits>::coord_type; +template using TCompute = typename vertex_traits>::compute_type; +template inline TCoord x(const V &v) { return vertex_traits>::x(v); } +template inline TCoord y(const V &v) { return vertex_traits>::y(v); } +template inline TCoord z(const V &v) { return vertex_traits>::z(v); } +template inline TCoord& x(V &v) { return vertex_traits>::x(v); } +template inline TCoord& y(V &v) { return vertex_traits>::y(v); } +template inline TCoord& z(V &v) { return vertex_traits>::z(v); } +template using TVertex = typename mesh_traits>::vertex_t; +template using TMeshCoord = TCoord>; + +template TCompute dot(const Vertex &v1, const Vertex &v2) +{ + return TCompute(x(v1)) * x(v2) + + TCompute(y(v1)) * y(v2) + + TCompute(z(v1)) * z(v2); +} + +template Vertex cross(const Vertex &a, const Vertex &b) +{ + return Vertex{y(a) * z(b) - z(a) * y(b), + z(a) * x(b) - x(a) * z(b), + x(a) * y(b) - y(a) * x(b)}; +} + +template TCompute lengthsq(const Vertex &v) +{ + return TCompute(x(v)) * x(v) + TCompute(y(v)) * y(v) + + TCompute(z(v)) * z(v); +} + +template void normalize(Vertex &v) +{ + double square = std::sqrt(lengthsq(v)); + x(v) /= square; y(v) /= square; z(v) /= square; +} + +using Bary = std::array; + +template +Bary barycentric(const Vertex &p, const Vertex &a, const Vertex &b, const Vertex &c) +{ + Vertex v0 = (b - a); + Vertex v1 = (c - a); + Vertex v2 = (p - a); + + double d00 = dot(v0, v0); + double d01 = dot(v0, v1); + double d11 = dot(v1, v1); + double d20 = dot(v2, v0); + double d21 = dot(v2, v1); + double denom = d00 * d11 - d01 * d01; + double v = (d11 * d20 - d01 * d21) / denom; + double w = (d00 * d21 - d01 * d20) / denom; + double u = 1.0 - v - w; + + return {u, v, w}; +} + +template class SimplifiableMesh { + Mesh *m_mesh; + + using Vertex = TVertex; + using Coord = TMeshCoord; + using HiPrecison = TCompute>; + using SymMat = SymetricMatrix; + + struct FaceInfo { + size_t idx; + double err[4] = {0.}; + bool deleted = false, dirty = false; + Vertex n; + explicit FaceInfo(size_t id): idx(id) {} + }; + + struct VertexInfo { + size_t idx; + size_t tstart = 0, tcount = 0; + bool border = false; + SymMat q; + explicit VertexInfo(size_t id): idx(id) {} + }; + + struct Ref { size_t face; size_t vertex; }; + + std::vector m_refs; + std::vector m_faceinfo; + std::vector m_vertexinfo; + + void compact_faces(); + void compact(); + + size_t mesh_vcount() const { return mesh_traits::vertex_count(*m_mesh); } + size_t mesh_facecount() const { return mesh_traits::face_count(*m_mesh); } + + size_t vcount() const { return m_vertexinfo.size(); } + + inline Vertex read_vertex(size_t vi) const + { + return mesh_traits::vertex(*m_mesh, vi); + } + + inline Vertex read_vertex(const VertexInfo &vinf) const + { + return read_vertex(vinf.idx); + } + + inline void write_vertex(size_t idx, const Vertex &v) const + { + mesh_traits::vertex(*m_mesh, idx, v); + } + + inline void write_vertex(const VertexInfo &vinf, const Vertex &v) const + { + write_vertex(vinf.idx, v); + } + + inline Index3 read_triangle(size_t fi) const + { + return mesh_traits::triangle(*m_mesh, fi); + } + + inline Index3 read_triangle(const FaceInfo &finf) const + { + return read_triangle(finf.idx); + } + + inline void write_triangle(size_t idx, const Index3 &t) + { + return mesh_traits::triangle(*m_mesh, idx, t); + } + + inline void write_triangle(const FaceInfo &finf, const Index3 &t) + { + return write_triangle(finf.idx, t); + } + + inline std::array triangle_vertices(const Index3 &f) const + { + std::array p; + for (size_t i = 0; i < 3; ++i) p[i] = read_vertex(f[i]); + return p; + } + + // Error between vertex and Quadric + static double vertex_error(const SymMat &q, const Vertex &v) + { + Coord _x = x(v) , _y = y(v), _z = z(v); + return q[0] * _x * _x + 2 * q[1] * _x * _y + 2 * q[2] * _x * _z + + 2 * q[3] * _x + q[4] * _y * _y + 2 * q[5] * _y * _z + + 2 * q[6] * _y + q[7] * _z * _z + 2 * q[8] * _z + q[9]; + } + + // Error for one edge + double calculate_error(size_t id_v1, size_t id_v2, Vertex &p_result); + + void calculate_error(FaceInfo &fi) + { + Vertex p; + Index3 t = read_triangle(fi); + for (size_t j = 0; j < 3; ++j) + fi.err[j] = calculate_error(t[j], t[(j + 1) % 3], p); + + fi.err[3] = std::min(fi.err[0], std::min(fi.err[1], fi.err[2])); + } + + void update_mesh(int iteration); + + // Update triangle connections and edge error after a edge is collapsed + void update_triangles(size_t i, VertexInfo &vi, std::vector &deleted, int &deleted_triangles); + + // Check if a triangle flips when this edge is removed + bool flipped(const Vertex &p, size_t i0, size_t i1, VertexInfo &v0, VertexInfo &v1, std::vector &deleted); + +public: + + explicit SimplifiableMesh(Mesh *m) : m_mesh{m} + { + static_assert( + std::is_arithmetic::value, + "Coordinate type of mesh has to be an arithmetic type!"); + + m_faceinfo.reserve(mesh_traits::face_count(*m)); + m_vertexinfo.reserve(mesh_traits::vertex_count(*m)); + for (size_t i = 0; i < mesh_facecount(); ++i) m_faceinfo.emplace_back(i); + for (size_t i = 0; i < mesh_vcount(); ++i) m_vertexinfo.emplace_back(i); + + } + + template void simplify_mesh_lossless(ProgressFn &&fn); + void simplify_mesh_lossless() { simplify_mesh_lossless([](int){}); } +}; + +template void SimplifiableMesh::compact_faces() +{ + auto it = std::remove_if(m_faceinfo.begin(), m_faceinfo.end(), + [](const FaceInfo &inf) { return inf.deleted; }); + + m_faceinfo.erase(it, m_faceinfo.end()); +} + +template void SimplifiableMesh::compact() +{ + for (auto &vi : m_vertexinfo) vi.tcount = 0; + + compact_faces(); + + for (FaceInfo &fi : m_faceinfo) + for (size_t vidx : read_triangle(fi)) m_vertexinfo[vidx].tcount = 1; + + size_t dst = 0; + for (VertexInfo &vi : m_vertexinfo) { + if (vi.tcount) { + vi.tstart = dst; + write_vertex(dst++, read_vertex(vi)); + } + } + + size_t vertex_count = dst; + + dst = 0; + for (const FaceInfo &fi : m_faceinfo) { + Index3 t = read_triangle(fi); + for (size_t &idx : t) idx = m_vertexinfo[idx].tstart; + write_triangle(dst++, t); + } + + mesh_traits::update(*m_mesh, vertex_count, m_faceinfo.size()); +} + +template +double SimplifiableMesh::calculate_error(size_t id_v1, size_t id_v2, Vertex &p_result) +{ + // compute interpolated vertex + + SymMat q = m_vertexinfo[id_v1].q + m_vertexinfo[id_v2].q; + + bool border = m_vertexinfo[id_v1].border & m_vertexinfo[id_v2].border; + double error = 0; + HiPrecison det = q.det(0, 1, 2, 1, 4, 5, 2, 5, 7); + + if (!is_approx(det, HiPrecison(0)) && !border) + { + // q_delta is invertible + x(p_result) = Coord(-1) / det * q.det(1, 2, 3, 4, 5, 6, 5, 7, 8); // vx = A41/det(q_delta) + y(p_result) = Coord( 1) / det * q.det(0, 2, 3, 1, 5, 6, 2, 7, 8); // vy = A42/det(q_delta) + z(p_result) = Coord(-1) / det * q.det(0, 1, 3, 1, 4, 6, 2, 5, 8); // vz = A43/det(q_delta) + + error = vertex_error(q, p_result); + } else { + // det = 0 -> try to find best result + Vertex p1 = read_vertex(id_v1); + Vertex p2 = read_vertex(id_v2); + Vertex p3 = (p1 + p2) / 2; + double error1 = vertex_error(q, p1); + double error2 = vertex_error(q, p2); + double error3 = vertex_error(q, p3); + error = std::min(error1, std::min(error2, error3)); + + if (is_approx(error1, error)) p_result = p1; + if (is_approx(error2, error)) p_result = p2; + if (is_approx(error3, error)) p_result = p3; + } + + return error; +} + +template void SimplifiableMesh::update_mesh(int iteration) +{ + if (iteration > 0) compact_faces(); + + assert(mesh_vcount() == m_vertexinfo.size()); + + // + // Init Quadrics by Plane & Edge Errors + // + // required at the beginning ( iteration == 0 ) + // recomputing during the simplification is not required, + // but mostly improves the result for closed meshes + // + if (iteration == 0) { + + for (VertexInfo &vinf : m_vertexinfo) vinf.q = SymMat{}; + for (FaceInfo &finf : m_faceinfo) { + Index3 t = read_triangle(finf); + std::array p = triangle_vertices(t); + Vertex n = cross(Vertex(p[1] - p[0]), Vertex(p[2] - p[0])); + normalize(n); + finf.n = n; + + for (size_t fi : t) + m_vertexinfo[fi].q += SymMat(x(n), y(n), z(n), -dot(n, p[0])); + + calculate_error(finf); + } + } + + // Init Reference ID list + for (VertexInfo &vi : m_vertexinfo) { vi.tstart = 0; vi.tcount = 0; } + + for (FaceInfo &fi : m_faceinfo) + for (size_t vidx : read_triangle(fi)) + m_vertexinfo[vidx].tcount++; + + size_t tstart = 0; + for (VertexInfo &vi : m_vertexinfo) { + vi.tstart = tstart; + tstart += vi.tcount; + vi.tcount = 0; + } + + // Write References + m_refs.resize(m_faceinfo.size() * 3); + for (size_t i = 0; i < m_faceinfo.size(); ++i) { + const FaceInfo &fi = m_faceinfo[i]; + Index3 t = read_triangle(fi); + for (size_t j = 0; j < 3; ++j) { + VertexInfo &vi = m_vertexinfo[t[j]]; + + assert(vi.tstart + vi.tcount < m_refs.size()); + + Ref &ref = m_refs[vi.tstart + vi.tcount]; + ref.face = i; + ref.vertex = j; + vi.tcount++; + } + } + + // Identify boundary : vertices[].border=0,1 + if (iteration == 0) { + for (VertexInfo &vi: m_vertexinfo) vi.border = false; + + std::vector vcount, vids; + + for (VertexInfo &vi: m_vertexinfo) { + vcount.clear(); + vids.clear(); + + for(size_t j = 0; j < vi.tcount; ++j) { + assert(vi.tstart + j < m_refs.size()); + FaceInfo &fi = m_faceinfo[m_refs[vi.tstart + j].face]; + Index3 t = read_triangle(fi); + + for (size_t fid : t) { + size_t ofs=0; + while (ofs < vcount.size()) + { + if (vids[ofs] == fid) break; + ofs++; + } + if (ofs == vcount.size()) + { + vcount.emplace_back(1); + vids.emplace_back(fid); + } + else + vcount[ofs]++; + } + } + + for (size_t j = 0; j < vcount.size(); ++j) + if(vcount[j] == 1) m_vertexinfo[vids[j]].border = true; + } + } +} + +template +void SimplifiableMesh::update_triangles(size_t i0, + VertexInfo & vi, + std::vector &deleted, + int &deleted_triangles) +{ + Vertex p; + for (size_t k = 0; k < vi.tcount; ++k) { + assert(vi.tstart + k < m_refs.size()); + + Ref &r = m_refs[vi.tstart + k]; + FaceInfo &fi = m_faceinfo[r.face]; + + if (fi.deleted) continue; + + if (deleted[k]) { + fi.deleted = true; + deleted_triangles++; + continue; + } + + Index3 t = read_triangle(fi); + t[r.vertex] = i0; + write_triangle(fi, t); + + fi.dirty = true; + fi.err[0] = calculate_error(t[0], t[1], p); + fi.err[1] = calculate_error(t[1], t[2], p); + fi.err[2] = calculate_error(t[2], t[0], p); + fi.err[3] = std::min(fi.err[0], std::min(fi.err[1], fi.err[2])); + m_refs.emplace_back(r); + } +} + +template +bool SimplifiableMesh::flipped(const Vertex & p, + size_t /*i0*/, + size_t i1, + VertexInfo & v0, + VertexInfo & /*v1*/, + std::vector &deleted) +{ + for (size_t k = 0; k < v0.tcount; ++k) { + size_t ridx = v0.tstart + k; + assert(ridx < m_refs.size()); + + FaceInfo &fi = m_faceinfo[m_refs[ridx].face]; + if (fi.deleted) continue; + + Index3 t = read_triangle(fi); + int s = m_refs[ridx].vertex; + size_t id1 = t[(s+1) % 3]; + size_t id2 = t[(s+2) % 3]; + + if(id1 == i1 || id2 == i1) // delete ? + { + deleted[k] = true; + continue; + } + + Vertex d1 = read_vertex(id1) - p; + normalize(d1); + Vertex d2 = read_vertex(id2) - p; + normalize(d2); + + if (std::abs(dot(d1, d2)) > 0.999) return true; + + Vertex n = cross(d1, d2); + normalize(n); + + deleted[k] = false; + if (dot(n, fi.n) < 0.2) return true; + } + + return false; +} + +template +template void SimplifiableMesh::simplify_mesh_lossless(Fn &&fn) +{ + // init + for (FaceInfo &fi : m_faceinfo) fi.deleted = false; + + // main iteration loop + int deleted_triangles=0; + std::vector deleted0, deleted1; + + for (int iteration = 0; iteration < 9999; iteration ++) { + // update mesh constantly + update_mesh(iteration); + + // clear dirty flag + for (FaceInfo &fi : m_faceinfo) fi.dirty = false; + + // + // All triangles with edges below the threshold will be removed + // + // The following numbers works well for most models. + // If it does not, try to adjust the 3 parameters + // + double threshold = std::numeric_limits::epsilon(); //1.0E-3 EPS; // Really? (tm) + + fn(iteration); + + for (FaceInfo &fi : m_faceinfo) { + if (fi.err[3] > threshold || fi.deleted || fi.dirty) continue; + + for (size_t j = 0; j < 3; ++j) { + if (fi.err[j] > threshold) continue; + + Index3 t = read_triangle(fi); + size_t i0 = t[j]; + VertexInfo &v0 = m_vertexinfo[i0]; + + size_t i1 = t[(j + 1) % 3]; + VertexInfo &v1 = m_vertexinfo[i1]; + + // Border check + if(v0.border != v1.border) continue; + + // Compute vertex to collapse to + Vertex p; + calculate_error(i0, i1, p); + + deleted0.resize(v0.tcount); // normals temporarily + deleted1.resize(v1.tcount); // normals temporarily + + // don't remove if flipped + if (flipped(p, i0, i1, v0, v1, deleted0)) continue; + if (flipped(p, i1, i0, v1, v0, deleted1)) continue; + + // not flipped, so remove edge + write_vertex(v0, p); + v0.q = v1.q + v0.q; + size_t tstart = m_refs.size(); + + update_triangles(i0, v0, deleted0, deleted_triangles); + update_triangles(i0, v1, deleted1, deleted_triangles); + + assert(m_refs.size() >= tstart); + + size_t tcount = m_refs.size() - tstart; + + if(tcount <= v0.tcount) + { + // save ram + if (tcount) { + auto from = m_refs.begin() + tstart, to = from + tcount; + std::copy(from, to, m_refs.begin() + v0.tstart); + } + } + else + // append + v0.tstart = tstart; + + v0.tcount = tcount; + break; + } + } + + if (deleted_triangles <= 0) break; + deleted_triangles = 0; + } + + compact(); +} + +} // namespace implementation +} // namespace SimplifyMesh + +#endif // SIMPLIFYMESHIMPL_HPP diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 2a32ba5ef4..82d2d19890 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -41,6 +41,23 @@ inline coordf_t max_layer_height_from_nozzle(const PrintConfig &print_config, in return std::max(min_layer_height, (max_layer_height == 0.) ? (0.75 * nozzle_dmr) : max_layer_height); } +// Minimum layer height for the variable layer height algorithm. +coordf_t Slicing::min_layer_height_from_nozzle(const DynamicPrintConfig &print_config, int idx_nozzle) +{ + coordf_t min_layer_height = print_config.opt_float("min_layer_height", idx_nozzle - 1); + return (min_layer_height == 0.) ? MIN_LAYER_HEIGHT_DEFAULT : std::max(MIN_LAYER_HEIGHT, min_layer_height); +} + +// Maximum layer height for the variable layer height algorithm, 3/4 of a nozzle dimaeter by default, +// it should not be smaller than the minimum layer height. +coordf_t Slicing::max_layer_height_from_nozzle(const DynamicPrintConfig &print_config, int idx_nozzle) +{ + coordf_t min_layer_height = min_layer_height_from_nozzle(print_config, idx_nozzle); + coordf_t max_layer_height = print_config.opt_float("max_layer_height", idx_nozzle - 1); + coordf_t nozzle_dmr = print_config.opt_float("nozzle_diameter", idx_nozzle - 1); + return std::max(min_layer_height, (max_layer_height == 0.) ? (0.75 * nozzle_dmr) : max_layer_height); +} + SlicingParameters SlicingParameters::create_from_config( const PrintConfig &print_config, const PrintObjectConfig &object_config, diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 036344b224..95cf6891b2 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -99,7 +99,6 @@ struct SlicingParameters }; static_assert(IsTriviallyCopyable::value, "SlicingParameters class is not POD (and it should be - see constructor)."); - // The two slicing parameters lead to the same layering as long as the variable layer thickness is not in action. inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters &sp2) { @@ -183,7 +182,17 @@ extern int generate_layer_height_texture( const std::vector &layers, void *data, int rows, int cols, bool level_of_detail_2nd_level); -}; // namespace Slic3r +namespace Slicing { + // Minimum layer height for the variable layer height algorithm. Nozzle index is 1 based. + coordf_t min_layer_height_from_nozzle(const DynamicPrintConfig &print_config, int idx_nozzle); + + // Maximum layer height for the variable layer height algorithm, 3/4 of a nozzle dimaeter by default, + // it should not be smaller than the minimum layer height. + // Nozzle index is 1 based. + coordf_t max_layer_height_from_nozzle(const DynamicPrintConfig &print_config, int idx_nozzle); +} // namespace Slicing + +} // namespace Slic3r namespace cereal { diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 4983787861..e5a2f3faee 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -47,21 +47,6 @@ //================== #define ENABLE_2_2_0_BETA1 1 -// Enable using Y axis of 3Dconnexion devices as zoom -#define ENABLE_3DCONNEXION_Y_AS_ZOOM (1 && ENABLE_2_2_0_BETA1) - -// Enable a modified version of the toolbar textures where all the icons are separated by 1 pixel -#define ENABLE_MODIFIED_TOOLBAR_TEXTURES (1 && ENABLE_2_2_0_BETA1) - -// Enable configurable paths export (fullpath or not) to 3mf and amf -#define ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF (1 && ENABLE_2_2_0_BETA1) - -// Enable 6 degrees of freedom camera -#define ENABLE_6DOF_CAMERA (1 && ENABLE_2_2_0_BETA1) - -// Enhance reload from disk to be able to work with 3mf/amf files saved with PrusaSlicer 2.1.0 and earlier -#define ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK (1 && ENABLE_2_2_0_BETA1) - //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ //================== diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 5cd97522d9..ee5e96f3d8 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -70,6 +70,34 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& f stl_get_size(&stl); } +TriangleMesh::TriangleMesh(const indexed_triangle_set &M) +{ + stl.stats.type = inmemory; + + // count facets and allocate memory + stl.stats.number_of_facets = uint32_t(M.indices.size()); + stl.stats.original_num_facets = int(stl.stats.number_of_facets); + stl_allocate(&stl); + + for (uint32_t i = 0; i < stl.stats.number_of_facets; ++ i) { + stl_facet facet; + facet.vertex[0] = M.vertices[size_t(M.indices[i](0))]; + facet.vertex[1] = M.vertices[size_t(M.indices[i](1))]; + facet.vertex[2] = M.vertices[size_t(M.indices[i](2))]; + facet.extra[0] = 0; + facet.extra[1] = 0; + + stl_normal normal; + stl_calculate_normal(normal, &facet); + stl_normalize_vector(normal); + facet.normal = normal; + + stl.facet_start[i] = facet; + } + + stl_get_size(&stl); +} + // #define SLIC3R_TRACE_REPAIR void TriangleMesh::repair(bool update_shared_vertices) @@ -599,7 +627,7 @@ std::vector TriangleMesh::slice(const std::vector &z) std::vector z_f(z.begin(), z.end()); TriangleMeshSlicer mslicer(this); std::vector layers; - mslicer.slice(z_f, 0.0004f, &layers, [](){}); + mslicer.slice(z_f, SlicingMode::Regular, 0.0004f, &layers, [](){}); return layers; } @@ -748,7 +776,7 @@ void TriangleMeshSlicer::set_up_direction(const Vec3f& up) -void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const +void TriangleMeshSlicer::slice(const std::vector &z, SlicingMode mode, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const { BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice"; @@ -803,11 +831,38 @@ void TriangleMeshSlicer::slice(const std::vector &z, std::vectorresize(z.size()); tbb::parallel_for( tbb::blocked_range(0, z.size()), - [&lines, &layers, throw_on_cancel, this](const tbb::blocked_range& range) { + [&lines, &layers, mode, throw_on_cancel, this](const tbb::blocked_range& range) { for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx) { if ((line_idx & 0x0ffff) == 0) throw_on_cancel(); - this->make_loops(lines[line_idx], &(*layers)[line_idx]); + + Polygons &polygons = (*layers)[line_idx]; + this->make_loops(lines[line_idx], &polygons); + + if (! polygons.empty()) { + if (mode == SlicingMode::Positive) { + // Reorient all loops to be CCW. + for (Polygon& p : polygons) + p.make_counter_clockwise(); + } else if (mode == SlicingMode::PositiveLargestContour) { + // Keep just the largest polygon, make it CCW. + double max_area = 0.; + Polygon* max_area_polygon = nullptr; + for (Polygon& p : polygons) { + double a = p.area(); + if (std::abs(a) > std::abs(max_area)) { + max_area = a; + max_area_polygon = &p; + } + } + assert(max_area_polygon != nullptr); + if (max_area < 0.) + max_area_polygon->reverse(); + Polygon p(std::move(*max_area_polygon)); + polygons.clear(); + polygons.emplace_back(std::move(p)); + } + } } } ); @@ -885,22 +940,25 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector &z, const float closing_radius, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const +void TriangleMeshSlicer::slice(const std::vector &z, SlicingMode mode, const float closing_radius, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const { std::vector layers_p; - this->slice(z, &layers_p, throw_on_cancel); + this->slice(z, (mode == SlicingMode::PositiveLargestContour) ? SlicingMode::Positive : mode, &layers_p, throw_on_cancel); BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - start"; layers->resize(z.size()); tbb::parallel_for( tbb::blocked_range(0, z.size()), - [&layers_p, closing_radius, layers, throw_on_cancel, this](const tbb::blocked_range& range) { + [&layers_p, mode, closing_radius, layers, throw_on_cancel, this](const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { #ifdef SLIC3R_TRIANGLEMESH_DEBUG printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]); #endif throw_on_cancel(); - this->make_expolygons(layers_p[layer_id], closing_radius, &(*layers)[layer_id]); + ExPolygons &expolygons = (*layers)[layer_id]; + this->make_expolygons(layers_p[layer_id], closing_radius, &expolygons); + if (mode == SlicingMode::PositiveLargestContour) + keep_largest_contour_only(expolygons); } }); BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end"; diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index ef935455ef..bd872a975a 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -23,6 +23,7 @@ class TriangleMesh public: TriangleMesh() : repaired(false) {} TriangleMesh(const Pointf3s &points, const std::vector &facets); + explicit TriangleMesh(const indexed_triangle_set &M); void clear() { this->stl.clear(); this->its.clear(); this->repaired = false; } bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); } bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); } @@ -161,6 +162,16 @@ public: typedef std::vector IntersectionLines; typedef std::vector IntersectionLinePtrs; +enum class SlicingMode : uint32_t { + // Regular slicing, maintain all contours and their orientation. + Regular, + // Maintain all contours, orient all contours CCW, therefore all holes are being closed. + Positive, + // Orient all contours CCW and keep only the contour with the largest area. + // This mode is useful for slicing complex objects in vase mode. + PositiveLargestContour, +}; + class TriangleMeshSlicer { public: @@ -168,8 +179,8 @@ public: TriangleMeshSlicer() : mesh(nullptr) {} TriangleMeshSlicer(const TriangleMesh* mesh) { this->init(mesh, [](){}); } void init(const TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel); - void slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; - void slice(const std::vector &z, const float closing_radius, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; + void slice(const std::vector &z, SlicingMode mode, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; + void slice(const std::vector &z, SlicingMode mode, const float closing_radius, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; enum FacetSliceType { NoSlice = 0, Slicing = 1, @@ -206,7 +217,7 @@ inline void slice_mesh( { if (mesh.empty()) return; TriangleMeshSlicer slicer(&mesh); - slicer.slice(z, &layers, thr); + slicer.slice(z, SlicingMode::Regular, &layers, thr); } inline void slice_mesh( @@ -218,7 +229,7 @@ inline void slice_mesh( { if (mesh.empty()) return; TriangleMeshSlicer slicer(&mesh); - slicer.slice(z, closing_radius, &layers, thr); + slicer.slice(z, SlicingMode::Regular, closing_radius, &layers, thr); } TriangleMesh make_cube(double x, double y, double z); diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index f91d32d288..c451507779 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -584,15 +584,20 @@ std::string string_printf(const char *format, ...) va_start(args1, format); va_list args2; va_copy(args2, args1); - - size_t needed_size = ::vsnprintf(nullptr, 0, format, args1) + 1; - va_end(args1); - - std::string res(needed_size, '\0'); - ::vsnprintf(&res.front(), res.size(), format, args2); - va_end(args2); - - return res; + + static const size_t INITIAL_LEN = 200; + std::string buffer(INITIAL_LEN, '\0'); + + int bufflen = ::vsnprintf(buffer.data(), INITIAL_LEN - 1, format, args1); + + if (bufflen >= int(INITIAL_LEN)) { + buffer.resize(size_t(bufflen) + 1); + ::vsnprintf(buffer.data(), buffer.size(), format, args2); + } + + buffer.resize(bufflen); + + return buffer; } std::string header_slic3r_generated() diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 751a1fcafe..c8589903e1 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -47,6 +47,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoFlatten.hpp GUI/Gizmos/GLGizmoCut.cpp GUI/Gizmos/GLGizmoCut.hpp + GUI/Gizmos/GLGizmoHollow.cpp + GUI/Gizmos/GLGizmoHollow.hpp GUI/GLSelectionRectangle.cpp GUI/GLSelectionRectangle.hpp GUI/GLTexture.hpp @@ -129,6 +131,7 @@ set(SLIC3R_GUI_SOURCES Utils/Serial.hpp GUI/ConfigWizard.cpp GUI/ConfigWizard.hpp + GUI/ConfigWizard_private.hpp GUI/MsgDialog.cpp GUI/MsgDialog.hpp GUI/UpdateDialogs.cpp @@ -140,8 +143,13 @@ set(SLIC3R_GUI_SOURCES GUI/ProgressStatusBar.cpp GUI/PrintHostDialogs.cpp GUI/PrintHostDialogs.hpp + GUI/Job.hpp GUI/Mouse3DController.cpp GUI/Mouse3DController.hpp + GUI/DoubleSlider.cpp + GUI/DoubleSlider.hpp + GUI/ObjectDataViewModel.cpp + GUI/ObjectDataViewModel.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp @@ -175,7 +183,6 @@ if (APPLE) GUI/RemovableDriveManagerMM.h GUI/Mouse3DHandlerMac.mm ) - #DK FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration) endif () @@ -185,7 +192,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi) -#DK + if(APPLE) target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY}) endif() diff --git a/src/slic3r/Config/Version.cpp b/src/slic3r/Config/Version.cpp index 2104a6eeab..2d036e9f35 100644 --- a/src/slic3r/Config/Version.cpp +++ b/src/slic3r/Config/Version.cpp @@ -66,6 +66,10 @@ bool Version::is_current_slic3r_supported() const return this->is_slic3r_supported(Slic3r::SEMVER); } +bool Version::is_current_slic3r_downgrade() const +{ + return Slic3r::SEMVER < min_slic3r_version; +} #if 0 //TODO: This test should be moved to a unit test, once we have C++ unit tests in place. static int version_test() diff --git a/src/slic3r/Config/Version.hpp b/src/slic3r/Config/Version.hpp index e5f1a2e214..881b856ff1 100644 --- a/src/slic3r/Config/Version.hpp +++ b/src/slic3r/Config/Version.hpp @@ -29,6 +29,7 @@ struct Version bool is_slic3r_supported(const Semver &slicer_version) const; bool is_current_slic3r_supported() const; + bool is_current_slic3r_downgrade() const; }; // Index of vendor specific config bundle versions and Slic3r compatibilities. diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 00b7f2382c..23acd18ef7 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -260,11 +260,7 @@ Point Bed3D::point_projection(const Point& point) const return m_polygon.point_projection(point); } -#if ENABLE_6DOF_CAMERA void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes) const -#else -void Bed3D::render(GLCanvas3D& canvas, float theta, float scale_factor, bool show_axes) const -#endif // ENABLE_6DOF_CAMERA { m_scale_factor = scale_factor; @@ -275,15 +271,9 @@ void Bed3D::render(GLCanvas3D& canvas, float theta, float scale_factor, bool sho switch (m_type) { -#if ENABLE_6DOF_CAMERA case System: { render_system(canvas, bottom); break; } default: case Custom: { render_custom(canvas, bottom); break; } -#else - case System: { render_system(canvas, theta > 90.0f); break; } - default: - case Custom: { render_custom(canvas, theta > 90.0f); break; } -#endif // ENABLE_6DOF_CAMERA } glsafe(::glDisable(GL_DEPTH_TEST)); diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index b1835a919d..3a5f959788 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -107,11 +107,7 @@ public: bool contains(const Point& point) const; Point point_projection(const Point& point) const; -#if ENABLE_6DOF_CAMERA void render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes) const; -#else - void render(GLCanvas3D& canvas, float theta, float scale_factor, bool show_axes) const; -#endif // ENABLE_6DOF_CAMERA private: void calc_bounding_boxes() const; diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index f6b1719dbf..9f36ab5371 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -10,7 +10,7 @@ #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Slicing.hpp" #include "libslic3r/GCode/Analyzer.hpp" -#include "slic3r/GUI/PresetBundle.hpp" +#include "slic3r/GUI/BitmapCache.hpp" #include "libslic3r/Format/STL.hpp" #include "libslic3r/Utils.hpp" @@ -792,14 +792,14 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con for (unsigned int i = 0; i < colors_count; ++i) { const std::string& txt_color = config->opt_string("extruder_colour", i); - if (PresetBundle::parse_color(txt_color, rgb)) + if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) { colors[i].set(txt_color, rgb); } else { const std::string& txt_color = config->opt_string("filament_colour", i); - if (PresetBundle::parse_color(txt_color, rgb)) + if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) colors[i].set(txt_color, rgb); } } diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 5d559c246d..aed907004f 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -313,33 +313,38 @@ public: // Valid geometry_id should always be positive. std::pair geometry_id; // An ID containing the extruder ID (used to select color). - int extruder_id; - // Is this object selected? - bool selected; - // Is this object disabled from selection? - bool disabled; - // Is this object printable? - bool printable; - // Whether or not this volume is active for rendering - bool is_active; - // Whether or not to use this volume when applying zoom_to_volumes() - bool zoom_to_volumes; - // Wheter or not this volume is enabled for outside print volume detection in shader. - bool shader_outside_printer_detection_enabled; - // Wheter or not this volume is outside print volume. - bool is_outside; + int extruder_id; + + // Various boolean flags. + struct { + // Is this object selected? + bool selected : 1; + // Is this object disabled from selection? + bool disabled : 1; + // Is this object printable? + bool printable : 1; + // Whether or not this volume is active for rendering + bool is_active : 1; + // Whether or not to use this volume when applying zoom_to_volumes() + bool zoom_to_volumes : 1; + // Wheter or not this volume is enabled for outside print volume detection in shader. + bool shader_outside_printer_detection_enabled : 1; + // Wheter or not this volume is outside print volume. + bool is_outside : 1; + // Wheter or not this volume has been generated from a modifier + bool is_modifier : 1; + // Wheter or not this volume has been generated from the wipe tower + bool is_wipe_tower : 1; + // Wheter or not this volume has been generated from an extrusion path + bool is_extrusion_path : 1; + // Wheter or not to always render this volume using its own alpha + bool force_transparent : 1; + // Whether or not always use the volume's own color (not using SELECTED/HOVER/DISABLED/OUTSIDE) + bool force_native_color : 1; + }; + // Is mouse or rectangle selection over this object to select/deselect it ? - EHoverState hover; - // Wheter or not this volume has been generated from a modifier - bool is_modifier; - // Wheter or not this volume has been generated from the wipe tower - bool is_wipe_tower; - // Wheter or not this volume has been generated from an extrusion path - bool is_extrusion_path; - // Wheter or not to always render this volume using its own alpha - bool force_transparent; - // Whether or not always use the volume's own color (not using SELECTED/HOVER/DISABLED/OUTSIDE) - bool force_native_color; + EHoverState hover; // Interleaved triangles & normals with indexed triangles & quads. GLIndexedVertexArray indexed_vertex_array; diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index a410f3ad8d..35799e0460 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -61,10 +61,8 @@ void AppConfig::set_defaults() if (get("preset_update").empty()) set("preset_update", "1"); -#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF if (get("export_sources_full_pathnames").empty()) set("export_sources_full_pathnames", "0"); -#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF // remove old 'use_legacy_opengl' parameter from this config, if present if (!get("use_legacy_opengl").empty()) @@ -90,10 +88,8 @@ void AppConfig::set_defaults() if (get("use_perspective_camera").empty()) set("use_perspective_camera", "1"); -#if ENABLE_6DOF_CAMERA if (get("use_free_camera").empty()) set("use_free_camera", "0"); -#endif // ENABLE_6DOF_CAMERA // Remove legacy window positions/sizes erase("", "main_frame_maximized"); @@ -284,11 +280,7 @@ void AppConfig::set_recent_projects(const std::vector& recent_proje } } -#if ENABLE_3DCONNEXION_Y_AS_ZOOM void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone, double zoom_speed) -#else -void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone) -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM { std::string key = std::string("mouse_device:") + name; auto it = m_storage.find(key); @@ -300,9 +292,7 @@ void AppConfig::set_mouse_device(const std::string& name, double translation_spe it->second["translation_deadzone"] = std::to_string(translation_deadzone); it->second["rotation_speed"] = std::to_string(rotation_speed); it->second["rotation_deadzone"] = std::to_string(rotation_deadzone); -#if ENABLE_3DCONNEXION_Y_AS_ZOOM it->second["zoom_speed"] = std::to_string(zoom_speed); -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM } bool AppConfig::get_mouse_device_translation_speed(const std::string& name, double& speed) @@ -365,7 +355,6 @@ bool AppConfig::get_mouse_device_rotation_deadzone(const std::string& name, floa return true; } -#if ENABLE_3DCONNEXION_Y_AS_ZOOM bool AppConfig::get_mouse_device_zoom_speed(const std::string& name, double& speed) { std::string key = std::string("mouse_device:") + name; @@ -380,7 +369,6 @@ bool AppConfig::get_mouse_device_zoom_speed(const std::string& name, double& spe speed = (float)::atof(it_val->second.c_str()); return true; } -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM void AppConfig::update_config_dir(const std::string &dir) { diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp index 32f1c32c85..c49260173f 100644 --- a/src/slic3r/GUI/AppConfig.hpp +++ b/src/slic3r/GUI/AppConfig.hpp @@ -133,18 +133,12 @@ public: std::vector get_recent_projects() const; void set_recent_projects(const std::vector& recent_projects); -#if ENABLE_3DCONNEXION_Y_AS_ZOOM void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone, double zoom_speed); -#else - void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone); -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM bool get_mouse_device_translation_speed(const std::string& name, double& speed); bool get_mouse_device_translation_deadzone(const std::string& name, double& deadzone); bool get_mouse_device_rotation_speed(const std::string& name, float& speed); bool get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone); -#if ENABLE_3DCONNEXION_Y_AS_ZOOM bool get_mouse_device_zoom_speed(const std::string& name, double& speed); -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM static const std::string SECTION_FILAMENTS; static const std::string SECTION_MATERIALS; diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index 7322a88d1c..8627ef4cb6 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -1,6 +1,9 @@ #include "BitmapCache.hpp" #include "libslic3r/Utils.hpp" +#include "../Utils/MacDarkMode.hpp" +#include "GUI.hpp" + #include #if ! defined(WIN32) && ! defined(__APPLE__) @@ -20,6 +23,16 @@ namespace Slic3r { namespace GUI { +BitmapCache::BitmapCache() +{ +#ifdef __APPLE__ + // Note: win->GetContentScaleFactor() is not used anymore here because it tends to + // return bogus results quite often (such as 1.0 on Retina or even 0.0). + // We're using the max scaling factor across all screens because it's very likely to be good enough. + m_scale = mac_max_scaling_factor(); +#endif +} + void BitmapCache::clear() { for (std::pair &bitmap : m_map) @@ -55,6 +68,14 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_ auto it = m_map.find(bitmap_key); if (it == m_map.end()) { bitmap = new wxBitmap(width, height); +#ifdef __APPLE__ + // Contrary to intuition, the `scale` argument isn't "please scale this to such and such" + // but rather "the wxImage is sized for backing scale such and such". + // So, We need to let the Mac OS wxBitmap implementation + // know that the image may already be scaled appropriately for Retina, + // and thereby that it's not supposed to upscale it. + bitmap->CreateScaled(width, height, -1, m_scale); +#endif m_map[bitmap_key] = bitmap; } else { bitmap = it->second; @@ -100,8 +121,13 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *beg size_t width = 0; size_t height = 0; for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { +#ifdef __APPLE__ + width += bmp->GetScaledWidth(); + height = std::max(height, bmp->GetScaledHeight()); +#else width += bmp->GetWidth(); height = std::max(height, bmp->GetHeight()); +#endif } #ifdef BROKEN_ALPHA @@ -167,7 +193,12 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *beg for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { if (bmp->GetWidth() > 0) memDC.DrawBitmap(*bmp, x, 0, true); +#ifdef __APPLE__ + // we should "move" with step equal to non-scaled width + x += bmp->GetScaledWidth(); +#else x += bmp->GetWidth(); +#endif } memDC.SelectObject(wxNullBitmap); return bitmap; @@ -175,7 +206,7 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *beg #endif } -wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, float scale /* = 1.0f */, const bool grayscale/* = false*/) +wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale/* = false*/) { wxImage image(width, height); image.InitAlpha(); @@ -192,7 +223,7 @@ wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned w if (grayscale) image = image.ConvertToGreyscale(m_gs, m_gs, m_gs); - return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image), scale)); + return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image), m_scale)); } wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width, unsigned height, @@ -227,12 +258,12 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width, } wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height, - float scale /* = 1.0f */, const bool grayscale/* = false*/, const bool dark_mode/* = false*/) + const bool grayscale/* = false*/, const bool dark_mode/* = false*/) { std::string bitmap_key = bitmap_name + ( target_height !=0 ? "-h" + std::to_string(target_height) : "-w" + std::to_string(target_width)) - + (scale != 1.0f ? "-s" + std::to_string(scale) : "") + + (m_scale != 1.0f ? "-s" + std::to_string(m_scale) : "") + (grayscale ? "-gs" : ""); /* For the Dark mode of any platform, we should draw icons in respect to OS background @@ -272,7 +303,7 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_ if (image == nullptr) return nullptr; - target_height != 0 ? target_height *= scale : target_width *= scale; + target_height != 0 ? target_height *= m_scale : target_width *= m_scale; float svg_scale = target_height != 0 ? (float)target_height / image->height : target_width != 0 ? @@ -297,11 +328,16 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_ ::nsvgDeleteRasterizer(rast); ::nsvgDelete(image); - return this->insert_raw_rgba(bitmap_key, width, height, data.data(), scale, grayscale); + return this->insert_raw_rgba(bitmap_key, width, height, data.data(), grayscale); } -wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency) +//we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap +wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling/* = false*/) { + double scale = suppress_scaling ? 1.0f : m_scale; + width *= scale; + height *= scale; + wxImage image(width, height); image.InitAlpha(); unsigned char* imgdata = image.GetData(); @@ -312,7 +348,32 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi *imgdata ++ = b; *imgalpha ++ = transparency; } - return wxImage_to_wxBitmap_with_alpha(std::move(image)); + return wxImage_to_wxBitmap_with_alpha(std::move(image), scale); +} + + +static inline int hex_digit_to_int(const char c) +{ + return + (c >= '0' && c <= '9') ? int(c - '0') : + (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : + (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; +} + +bool BitmapCache::parse_color(const std::string& scolor, unsigned char* rgb_out) +{ + rgb_out[0] = rgb_out[1] = rgb_out[2] = 0; + if (scolor.size() != 7 || scolor.front() != '#') + return false; + const char* c = scolor.data() + 1; + for (size_t i = 0; i < 3; ++i) { + int digit1 = hex_digit_to_int(*c++); + int digit2 = hex_digit_to_int(*c++); + if (digit1 == -1 || digit2 == -1) + return false; + rgb_out[i] = (unsigned char)(digit1 * 16 + digit2); + } + return true; } } // namespace GUI diff --git a/src/slic3r/GUI/BitmapCache.hpp b/src/slic3r/GUI/BitmapCache.hpp index 041e7d8920..3f29c2fd62 100644 --- a/src/slic3r/GUI/BitmapCache.hpp +++ b/src/slic3r/GUI/BitmapCache.hpp @@ -1,24 +1,23 @@ #ifndef SLIC3R_GUI_BITMAP_CACHE_HPP #define SLIC3R_GUI_BITMAP_CACHE_HPP +#include +#include + #include #ifndef WX_PRECOMP #include #endif -#include "libslic3r/libslic3r.h" -#include "libslic3r/Config.hpp" - -#include "GUI.hpp" - namespace Slic3r { namespace GUI { class BitmapCache { public: - BitmapCache() {} + BitmapCache(); ~BitmapCache() { clear(); } void clear(); + double scale() { return m_scale; } wxBitmap* find(const std::string &name) { auto it = m_map.find(name); return (it == m_map.end()) ? nullptr : it->second; } const wxBitmap* find(const std::string &name) const { return const_cast(this)->find(name); } @@ -29,20 +28,23 @@ public: wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3); wxBitmap* insert(const std::string &name, const std::vector &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); } wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end); - wxBitmap* insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, float scale = 1.0f, const bool grayscale = false); + wxBitmap* insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale = false); // Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero. wxBitmap* load_png(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false); // Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width. - wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, float scale = 1.0f, const bool grayscale = false, const bool dark_mode = false); + wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false); - static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency); - static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); } - static wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); } + /*static */wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling = false); + /*static */wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3], bool suppress_scaling = false) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE, suppress_scaling); } + /*static */wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); } + + static bool parse_color(const std::string& scolor, unsigned char* rgb_out); private: std::map m_map; - double m_gs = 0.2; // value, used for image.ConvertToGreyscale(m_gs, m_gs, m_gs) + double m_gs = 0.2; // value, used for image.ConvertToGreyscale(m_gs, m_gs, m_gs) + double m_scale = 1.0; // value, used for correct scaling of SVG icons on Retina display }; } // GUI diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index c21947d585..d17ba7cd7c 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -7,15 +7,11 @@ #include "GUI_App.hpp" #include "AppConfig.hpp" #if ENABLE_CAMERA_STATISTICS -#if ENABLE_6DOF_CAMERA #include "Mouse3DController.hpp" -#endif // ENABLE_6DOF_CAMERA #endif // ENABLE_CAMERA_STATISTICS #include -static const float GIMBALL_LOCK_THETA_MAX = 180.0f; - // phi / theta angles to orient the camera. static const float VIEW_DEFAULT[2] = { 45.0f, 45.0f }; static const float VIEW_LEFT[2] = { 90.0f, 90.0f }; @@ -39,40 +35,27 @@ double Camera::FrustrumZMargin = 10.0; double Camera::MaxFovDeg = 60.0; Camera::Camera() -#if ENABLE_6DOF_CAMERA : requires_zoom_to_bed(false) -#else - : phi(45.0f) - , requires_zoom_to_bed(false) - , inverted_phi(false) -#endif // ENABLE_6DOF_CAMERA , m_type(Perspective) , m_target(Vec3d::Zero()) -#if !ENABLE_6DOF_CAMERA - , m_theta(45.0f) -#endif // !ENABLE_6DOF_CAMERA + , m_zenit(45.0f) , m_zoom(1.0) , m_distance(DefaultDistance) , m_gui_scale(1.0) , m_view_matrix(Transform3d::Identity()) , m_projection_matrix(Transform3d::Identity()) { -#if ENABLE_6DOF_CAMERA set_default_orientation(); -#endif // ENABLE_6DOF_CAMERA } std::string Camera::get_type_as_string() const { switch (m_type) { - case Unknown: - return "unknown"; - case Perspective: - return "perspective"; + case Unknown: return "unknown"; + case Perspective: return "perspective"; default: - case Ortho: - return "orthographic"; + case Ortho: return "orthographic"; }; } @@ -88,10 +71,7 @@ void Camera::set_type(EType type) void Camera::set_type(const std::string& type) { - if (type == "1") - set_type(Perspective); - else - set_type(Ortho); + set_type((type == "1") ? Perspective : Ortho); } void Camera::select_next_type() @@ -105,36 +85,9 @@ void Camera::select_next_type() void Camera::set_target(const Vec3d& target) { -#if ENABLE_6DOF_CAMERA translate_world(target - m_target); -#else - BoundingBoxf3 test_box = m_scene_box; - test_box.translate(-m_scene_box.center()); - // We may let this factor be customizable - static const double ScaleFactor = 1.5; - test_box.scale(ScaleFactor); - test_box.translate(m_scene_box.center()); - - m_target(0) = clamp(test_box.min(0), test_box.max(0), target(0)); - m_target(1) = clamp(test_box.min(1), test_box.max(1), target(1)); - m_target(2) = clamp(test_box.min(2), test_box.max(2), target(2)); -#endif // ENABLE_6DOF_CAMERA } -#if !ENABLE_6DOF_CAMERA -void Camera::set_theta(float theta, bool apply_limit) -{ - if (apply_limit) - m_theta = clamp(0.0f, GIMBALL_LOCK_THETA_MAX, theta); - else - { - m_theta = fmod(theta, 360.0f); - if (m_theta < 0.0f) - m_theta += 360.0f; - } -} -#endif // !ENABLE_6DOF_CAMERA - void Camera::update_zoom(double delta_zoom) { set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1)); @@ -151,54 +104,23 @@ void Camera::set_zoom(double zoom) m_zoom = std::min(zoom, max_zoom()); } -#if ENABLE_6DOF_CAMERA void Camera::select_view(const std::string& direction) { if (direction == "iso") set_default_orientation(); else if (direction == "left") - m_view_matrix = look_at(m_target - m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ()); + look_at(m_target - m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ()); else if (direction == "right") - m_view_matrix = look_at(m_target + m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ()); + look_at(m_target + m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ()); else if (direction == "top") - m_view_matrix = look_at(m_target + m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY()); + look_at(m_target + m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY()); else if (direction == "bottom") - m_view_matrix = look_at(m_target - m_distance * Vec3d::UnitZ(), m_target, -Vec3d::UnitY()); + look_at(m_target - m_distance * Vec3d::UnitZ(), m_target, -Vec3d::UnitY()); else if (direction == "front") - m_view_matrix = look_at(m_target - m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ()); + look_at(m_target - m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ()); else if (direction == "rear") - m_view_matrix = look_at(m_target + m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ()); + look_at(m_target + m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ()); } -#else -bool Camera::select_view(const std::string& direction) -{ - const float* dir_vec = nullptr; - - if (direction == "iso") - dir_vec = VIEW_DEFAULT; - else if (direction == "left") - dir_vec = VIEW_LEFT; - else if (direction == "right") - dir_vec = VIEW_RIGHT; - else if (direction == "top") - dir_vec = VIEW_TOP; - else if (direction == "bottom") - dir_vec = VIEW_BOTTOM; - else if (direction == "front") - dir_vec = VIEW_FRONT; - else if (direction == "rear") - dir_vec = VIEW_REAR; - - if (dir_vec != nullptr) - { - phi = dir_vec[0]; - set_theta(dir_vec[1], false); - return true; - } - else - return false; -} -#endif // ENABLE_6DOF_CAMERA double Camera::get_fov() const { @@ -220,92 +142,52 @@ void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) const void Camera::apply_view_matrix() const { -#if !ENABLE_6DOF_CAMERA - double theta_rad = Geometry::deg2rad(-(double)m_theta); - double phi_rad = Geometry::deg2rad((double)phi); - double sin_theta = ::sin(theta_rad); - Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad)); -#endif // !ENABLE_6DOF_CAMERA - glsafe(::glMatrixMode(GL_MODELVIEW)); glsafe(::glLoadIdentity()); - -#if ENABLE_6DOF_CAMERA glsafe(::glMultMatrixd(m_view_matrix.data())); -#else - glsafe(::glRotatef(-m_theta, 1.0f, 0.0f, 0.0f)); // pitch - glsafe(::glRotatef(phi, 0.0f, 0.0f, 1.0f)); // yaw - - glsafe(::glTranslated(-camera_pos(0), -camera_pos(1), -camera_pos(2))); - - glsafe(::glGetDoublev(GL_MODELVIEW_MATRIX, m_view_matrix.data())); -#endif // ENABLE_6DOF_CAMERA } void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double far_z) const { - set_distance(DefaultDistance); - double w = 0.0; double h = 0.0; - while (true) + double old_distance = m_distance; + m_frustrum_zs = calc_tight_frustrum_zs_around(box); + if (m_distance != old_distance) + // the camera has been moved re-apply view matrix + apply_view_matrix(); + + if (near_z > 0.0) + m_frustrum_zs.first = std::max(std::min(m_frustrum_zs.first, near_z), FrustrumMinNearZ); + + if (far_z > 0.0) + m_frustrum_zs.second = std::max(m_frustrum_zs.second, far_z); + + w = 0.5 * (double)m_viewport[2]; + h = 0.5 * (double)m_viewport[3]; + + double inv_zoom = get_inv_zoom(); + w *= inv_zoom; + h *= inv_zoom; + + switch (m_type) { - m_frustrum_zs = calc_tight_frustrum_zs_around(box); - - if (near_z > 0.0) - m_frustrum_zs.first = std::min(m_frustrum_zs.first, near_z); - - if (far_z > 0.0) - m_frustrum_zs.second = std::max(m_frustrum_zs.second, far_z); - - w = 0.5 * (double)m_viewport[2]; - h = 0.5 * (double)m_viewport[3]; - - if (m_zoom != 0.0) - { - double inv_zoom = 1.0 / m_zoom; - w *= inv_zoom; - h *= inv_zoom; - } - - switch (m_type) - { - default: - case Ortho: - { - m_gui_scale = 1.0; - break; - } - case Perspective: - { - // scale near plane to keep w and h constant on the plane at z = m_distance - double scale = m_frustrum_zs.first / m_distance; - w *= scale; - h *= scale; - m_gui_scale = scale; - break; - } - } - - if (m_type == Perspective) - { - double fov_deg = Geometry::rad2deg(2.0 * std::atan(h / m_frustrum_zs.first)); - - // adjust camera distance to keep fov in a limited range - if (fov_deg > MaxFovDeg) - { - double delta_z = h / ::tan(0.5 * Geometry::deg2rad(MaxFovDeg)) - m_frustrum_zs.first; - if (delta_z > 0.001) - set_distance(m_distance + delta_z); - else - break; - } - else - break; - } - else - break; + default: + case Ortho: + { + m_gui_scale = 1.0; + break; + } + case Perspective: + { + // scale near plane to keep w and h constant on the plane at z = m_distance + double scale = m_frustrum_zs.first / m_distance; + w *= scale; + h *= scale; + m_gui_scale = scale; + break; + } } glsafe(::glMatrixMode(GL_PROJECTION)); @@ -331,14 +213,14 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa } #if ENABLE_THUMBNAIL_GENERATOR -void Camera::zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h, double margin_factor) +void Camera::zoom_to_box(const BoundingBoxf3& box, double margin_factor) #else void Camera::zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h) #endif // ENABLE_THUMBNAIL_GENERATOR { // Calculate the zoom factor needed to adjust the view around the given box. #if ENABLE_THUMBNAIL_GENERATOR - double zoom = calc_zoom_to_bounding_box_factor(box, canvas_w, canvas_h, margin_factor); + double zoom = calc_zoom_to_bounding_box_factor(box, margin_factor); #else double zoom = calc_zoom_to_bounding_box_factor(box, canvas_w, canvas_h); #endif // ENABLE_THUMBNAIL_GENERATOR @@ -346,28 +228,20 @@ void Camera::zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h) { m_zoom = zoom; // center view around box center -#if ENABLE_6DOF_CAMERA set_target(box.center()); -#else - m_target = box.center(); -#endif // ENABLE_6DOF_CAMERA } } #if ENABLE_THUMBNAIL_GENERATOR -void Camera::zoom_to_volumes(const GLVolumePtrs& volumes, int canvas_w, int canvas_h, double margin_factor) +void Camera::zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor) { Vec3d center; - double zoom = calc_zoom_to_volumes_factor(volumes, canvas_w, canvas_h, center, margin_factor); + double zoom = calc_zoom_to_volumes_factor(volumes, center, margin_factor); if (zoom > 0.0) { m_zoom = zoom; // center view around the calculated center -#if ENABLE_6DOF_CAMERA set_target(center); -#else - m_target = center; -#endif // ENABLE_6DOF_CAMERA } } #endif // ENABLE_THUMBNAIL_GENERATOR @@ -379,15 +253,15 @@ void Camera::debug_render() const imgui.begin(std::string("Camera statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); std::string type = get_type_as_string(); -#if ENABLE_6DOF_CAMERA if (wxGetApp().plater()->get_mouse3d_controller().is_running() || (wxGetApp().app_config->get("use_free_camera") == "1")) type += "/free"; else type += "/constrained"; -#endif // ENABLE_6DOF_CAMERA + Vec3f position = get_position().cast(); Vec3f target = m_target.cast(); float distance = (float)get_distance(); + float zenit = (float)m_zenit; Vec3f forward = get_dir_forward().cast(); Vec3f right = get_dir_right().cast(); Vec3f up = get_dir_up().cast(); @@ -396,6 +270,7 @@ void Camera::debug_render() const float deltaZ = farZ - nearZ; float zoom = (float)m_zoom; float fov = (float)get_fov(); + std::arrayviewport = get_viewport(); float gui_scale = (float)get_gui_scale(); ImGui::InputText("Type", type.data(), type.length(), ImGuiInputTextFlags_ReadOnly); @@ -404,6 +279,8 @@ void Camera::debug_render() const ImGui::InputFloat3("Target", target.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat("Distance", &distance, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::Separator(); + ImGui::InputFloat("Zenit", &zenit, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); + ImGui::Separator(); ImGui::InputFloat3("Forward", forward.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Right", right.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Up", up.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); @@ -415,12 +292,13 @@ void Camera::debug_render() const ImGui::InputFloat("Zoom", &zoom, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat("Fov", &fov, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::Separator(); + ImGui::InputInt4("Viewport", viewport.data(), ImGuiInputTextFlags_ReadOnly); + ImGui::Separator(); ImGui::InputFloat("GUI scale", &gui_scale, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); imgui.end(); } #endif // ENABLE_CAMERA_STATISTICS -#if ENABLE_6DOF_CAMERA void Camera::translate_world(const Vec3d& displacement) { Vec3d new_target = validate_target(m_target + displacement); @@ -432,12 +310,45 @@ void Camera::translate_world(const Vec3d& displacement) } } -void Camera::rotate_on_sphere(double delta_azimut_rad, double delta_zenit_rad) +void Camera::rotate_on_sphere(double delta_azimut_rad, double delta_zenit_rad, bool apply_limits) { + m_zenit += Geometry::rad2deg(delta_zenit_rad); + if (apply_limits) { + if (m_zenit > 90.0f) { + delta_zenit_rad -= Geometry::deg2rad(m_zenit - 90.0f); + m_zenit = 90.0f; + } + else if (m_zenit < -90.0f) { + delta_zenit_rad -= Geometry::deg2rad(m_zenit + 90.0f); + m_zenit = -90.0f; + } + } + + // FIXME -> The following is a HACK !!! + // When the value of the zenit rotation is large enough, the following call to rotate() shows + // numerical instability introducing some scaling into m_view_matrix (verified by checking + // that the camera space unit vectors are no more unit). + // See also https://dev.prusa3d.com/browse/SPE-1082 + // We split the zenit rotation into a set of smaller rotations which are then applied. + static const double MAX_ALLOWED = Geometry::deg2rad(0.1); + unsigned int zenit_steps_count = 1 + (unsigned int)(std::abs(delta_zenit_rad) / MAX_ALLOWED); + double zenit_step = delta_zenit_rad / (double)zenit_steps_count; + Vec3d target = m_target; translate_world(-target); - m_view_matrix.rotate(Eigen::AngleAxisd(delta_zenit_rad, get_dir_right())); - m_view_matrix.rotate(Eigen::AngleAxisd(delta_azimut_rad, Vec3d::UnitZ())); + + if (zenit_step != 0.0) + { + Vec3d right = get_dir_right(); + for (unsigned int i = 0; i < zenit_steps_count; ++i) + { + m_view_matrix.rotate(Eigen::AngleAxisd(zenit_step, right)); + } + } + + if (delta_azimut_rad != 0.0) + m_view_matrix.rotate(Eigen::AngleAxisd(delta_azimut_rad, Vec3d::UnitZ())); + translate_world(target); } @@ -456,50 +367,57 @@ void Camera::rotate_local_around_pivot(const Vec3d& rotation_rad, const Vec3d& p m_view_matrix.rotate(Eigen::AngleAxisd(rotation_rad(1), get_dir_up())); m_view_matrix.rotate(Eigen::AngleAxisd(rotation_rad(2), get_dir_forward())); translate_world(center); + update_zenit(); } double Camera::min_zoom() const { - return 0.7 * calc_zoom_to_bounding_box_factor(m_scene_box, (int)m_viewport[2], (int)m_viewport[3]); + return 0.7 * calc_zoom_to_bounding_box_factor(m_scene_box); } -#endif // ENABLE_6DOF_CAMERA std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const { std::pair ret; + auto& [near_z, far_z] = ret; - while (true) + // box in eye space + BoundingBoxf3 eye_box = box.transformed(m_view_matrix); + near_z = -eye_box.max(2); + far_z = -eye_box.min(2); + + // apply margin + near_z -= FrustrumZMargin; + far_z += FrustrumZMargin; + + // ensure min size + if (far_z - near_z < FrustrumMinZRange) { - // box in eye space - BoundingBoxf3 eye_box = box.transformed(m_view_matrix); - ret.first = -eye_box.max(2); - ret.second = -eye_box.min(2); + double mid_z = 0.5 * (near_z + far_z); + double half_size = 0.5 * FrustrumMinZRange; + near_z = mid_z - half_size; + far_z = mid_z + half_size; + } - // apply margin - ret.first -= FrustrumZMargin; - ret.second += FrustrumZMargin; - - // ensure min size - if (ret.second - ret.first < FrustrumMinZRange) - { - double mid_z = 0.5 * (ret.first + ret.second); - double half_size = 0.5 * FrustrumMinZRange; - ret.first = mid_z - half_size; - ret.second = mid_z + half_size; - } - - if (ret.first >= FrustrumMinNearZ) - break; - - // ensure min Near Z - set_distance(m_distance + FrustrumMinNearZ - ret.first); + if (near_z < FrustrumMinNearZ) + { + float delta = FrustrumMinNearZ - near_z; + set_distance(m_distance + delta); + near_z += delta; + far_z += delta; + } + else if ((near_z > 2.0 * FrustrumMinNearZ) && (m_distance > DefaultDistance)) + { + float delta = m_distance - DefaultDistance; + set_distance(DefaultDistance); + near_z -= delta; + far_z -= delta; } return ret; } #if ENABLE_THUMBNAIL_GENERATOR -double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h, double margin_factor) const +double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, double margin_factor) const #else double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const #endif // ENABLE_THUMBNAIL_GENERATOR @@ -511,9 +429,6 @@ double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int ca // project the box vertices on a plane perpendicular to the camera forward axis // then calculates the vertices coordinate on this plane along the camera xy axes - // ensure that the view matrix is updated - apply_view_matrix(); - Vec3d right = get_dir_right(); Vec3d up = get_dir_up(); Vec3d forward = get_dir_forward(); @@ -569,11 +484,11 @@ double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int ca dx *= margin_factor; dy *= margin_factor; - return std::min((double)canvas_w / dx, (double)canvas_h / dy); + return std::min((double)m_viewport[2] / dx, (double)m_viewport[3] / dy); } #if ENABLE_THUMBNAIL_GENERATOR -double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, int canvas_w, int canvas_h, Vec3d& center, double margin_factor) const +double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& center, double margin_factor) const { if (volumes.empty()) return -1.0; @@ -581,9 +496,6 @@ double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, int canv // project the volumes vertices on a plane perpendicular to the camera forward axis // then calculates the vertices coordinate on this plane along the camera xy axes - // ensure that the view matrix is updated - apply_view_matrix(); - Vec3d right = get_dir_right(); Vec3d up = get_dir_up(); Vec3d forward = get_dir_forward(); @@ -634,51 +546,55 @@ double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, int canv if ((dx <= 0.0) || (dy <= 0.0)) return -1.0f; - return std::min((double)canvas_w / dx, (double)canvas_h / dy); + return std::min((double)m_viewport[2] / dx, (double)m_viewport[3] / dy); } #endif // ENABLE_THUMBNAIL_GENERATOR void Camera::set_distance(double distance) const { - m_distance = distance; - apply_view_matrix(); + if (m_distance != distance) + { + m_view_matrix.translate((distance - m_distance) * get_dir_forward()); + m_distance = distance; + } } -#if ENABLE_6DOF_CAMERA -Transform3d Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up) const +void Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up) { Vec3d unit_z = (position - target).normalized(); Vec3d unit_x = up.cross(unit_z).normalized(); Vec3d unit_y = unit_z.cross(unit_x).normalized(); - Transform3d matrix; + m_target = target; + Vec3d new_position = m_target + m_distance * unit_z; - matrix(0, 0) = unit_x(0); - matrix(0, 1) = unit_x(1); - matrix(0, 2) = unit_x(2); - matrix(0, 3) = -unit_x.dot(position); + m_view_matrix(0, 0) = unit_x(0); + m_view_matrix(0, 1) = unit_x(1); + m_view_matrix(0, 2) = unit_x(2); + m_view_matrix(0, 3) = -unit_x.dot(new_position); - matrix(1, 0) = unit_y(0); - matrix(1, 1) = unit_y(1); - matrix(1, 2) = unit_y(2); - matrix(1, 3) = -unit_y.dot(position); + m_view_matrix(1, 0) = unit_y(0); + m_view_matrix(1, 1) = unit_y(1); + m_view_matrix(1, 2) = unit_y(2); + m_view_matrix(1, 3) = -unit_y.dot(new_position); - matrix(2, 0) = unit_z(0); - matrix(2, 1) = unit_z(1); - matrix(2, 2) = unit_z(2); - matrix(2, 3) = -unit_z.dot(position); + m_view_matrix(2, 0) = unit_z(0); + m_view_matrix(2, 1) = unit_z(1); + m_view_matrix(2, 2) = unit_z(2); + m_view_matrix(2, 3) = -unit_z.dot(new_position); - matrix(3, 0) = 0.0; - matrix(3, 1) = 0.0; - matrix(3, 2) = 0.0; - matrix(3, 3) = 1.0; + m_view_matrix(3, 0) = 0.0; + m_view_matrix(3, 1) = 0.0; + m_view_matrix(3, 2) = 0.0; + m_view_matrix(3, 3) = 1.0; - return matrix; + update_zenit(); } void Camera::set_default_orientation() { - double theta_rad = Geometry::deg2rad(-45.0); + m_zenit = 45.0f; + double theta_rad = Geometry::deg2rad(-(double)m_zenit); double phi_rad = Geometry::deg2rad(45.0); double sin_theta = ::sin(theta_rad); Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad)); @@ -699,7 +615,11 @@ Vec3d Camera::validate_target(const Vec3d& target) const std::clamp(target(1), test_box.min(1), test_box.max(1)), std::clamp(target(2), test_box.min(2), test_box.max(2))); } -#endif // ENABLE_6DOF_CAMERA + +void Camera::update_zenit() +{ + m_zenit = Geometry::rad2deg(0.5 * M_PI - std::acos(std::clamp(-get_dir_forward().dot(Vec3d::UnitZ()), -1.0, 1.0))); +} } // GUI } // Slic3r diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index 2a43361089..0ddaa8ff38 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -30,29 +30,19 @@ struct Camera Num_types }; -#if !ENABLE_6DOF_CAMERA - float phi; - bool inverted_phi; -#endif // !ENABLE_6DOF_CAMERA bool requires_zoom_to_bed; private: EType m_type; Vec3d m_target; -#if !ENABLE_6DOF_CAMERA - float m_theta; -#endif // !ENABLE_6DOF_CAMERA + float m_zenit; double m_zoom; // Distance between camera position and camera target measured along the camera Z axis mutable double m_distance; mutable double m_gui_scale; mutable std::array m_viewport; -#if ENABLE_6DOF_CAMERA - Transform3d m_view_matrix; -#else mutable Transform3d m_view_matrix; -#endif // ENABLE_6DOF_CAMERA mutable Transform3d m_projection_matrix; mutable std::pair m_frustrum_zs; @@ -71,14 +61,9 @@ public: const Vec3d& get_target() const { return m_target; } void set_target(const Vec3d& target); - double get_distance() const { return m_distance; } + double get_distance() const { return (get_position() - m_target).norm(); } double get_gui_scale() const { return m_gui_scale; } -#if !ENABLE_6DOF_CAMERA - float get_theta() const { return m_theta; } - void set_theta(float theta, bool apply_limit); -#endif // !ENABLE_6DOF_CAMERA - double get_zoom() const { return m_zoom; } double get_inv_zoom() const { assert(m_zoom != 0.0); return 1.0 / m_zoom; } void update_zoom(double delta_zoom); @@ -87,11 +72,7 @@ public: const BoundingBoxf3& get_scene_box() const { return m_scene_box; } void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; } -#if ENABLE_6DOF_CAMERA void select_view(const std::string& direction); -#else - bool select_view(const std::string& direction); -#endif // ENABLE_6DOF_CAMERA const std::array& get_viewport() const { return m_viewport; } const Transform3d& get_view_matrix() const { return m_view_matrix; } @@ -115,8 +96,8 @@ public: void apply_projection(const BoundingBoxf3& box, double near_z = -1.0, double far_z = -1.0) const; #if ENABLE_THUMBNAIL_GENERATOR - void zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h, double margin_factor = DefaultZoomToBoxMarginFactor); - void zoom_to_volumes(const GLVolumePtrs& volumes, int canvas_w, int canvas_h, double margin_factor = DefaultZoomToVolumesMarginFactor); + void zoom_to_box(const BoundingBoxf3& box, double margin_factor = DefaultZoomToBoxMarginFactor); + void zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor = DefaultZoomToVolumesMarginFactor); #else void zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h); #endif // ENABLE_THUMBNAIL_GENERATOR @@ -125,13 +106,13 @@ public: void debug_render() const; #endif // ENABLE_CAMERA_STATISTICS -#if ENABLE_6DOF_CAMERA // translate the camera in world space void translate_world(const Vec3d& displacement); // rotate the camera on a sphere having center == m_target and radius == m_distance // using the given variations of spherical coordinates - void rotate_on_sphere(double delta_azimut_rad, double delta_zenit_rad); + // if apply_limits == true the camera stops rotating when its forward vector is parallel to the world Z axis + void rotate_on_sphere(double delta_azimut_rad, double delta_zenit_rad, bool apply_limits); // rotate the camera around three axes parallel to the camera local axes and passing through m_target void rotate_local_around_target(const Vec3d& rotation_rad); @@ -144,25 +125,23 @@ public: double max_zoom() const { return 100.0; } double min_zoom() const; -#endif // ENABLE_6DOF_CAMERA private: // returns tight values for nearZ and farZ plane around the given bounding box // the camera MUST be outside of the bounding box in eye coordinate of the given box std::pair calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const; #if ENABLE_THUMBNAIL_GENERATOR - double calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h, double margin_factor = DefaultZoomToBoxMarginFactor) const; - double calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, int canvas_w, int canvas_h, Vec3d& center, double margin_factor = DefaultZoomToVolumesMarginFactor) const; + double calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, double margin_factor = DefaultZoomToBoxMarginFactor) const; + double calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& center, double margin_factor = DefaultZoomToVolumesMarginFactor) const; #else double calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const; #endif // ENABLE_THUMBNAIL_GENERATOR void set_distance(double distance) const; -#if ENABLE_6DOF_CAMERA - Transform3d look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up) const; + void look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up); void set_default_orientation(); Vec3d validate_target(const Vec3d& target) const; -#endif // ENABLE_6DOF_CAMERA + void update_zenit(); }; } // GUI diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 966f347618..0d38ee5660 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -70,14 +70,21 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con double fill_density = config->option("fill_density")->value; if (config->opt_bool("spiral_vase") && - !(config->opt_int("perimeters") == 1 && config->opt_int("top_solid_layers") == 0 && - fill_density == 0)) { + ! (config->opt_int("perimeters") == 1 && + config->opt_int("top_solid_layers") == 0 && + fill_density == 0 && + ! config->opt_bool("support_material") && + config->opt_int("support_material_enforce_layers") == 0 && + config->opt_bool("ensure_vertical_shell_thickness") && + ! config->opt_bool("thin_walls"))) + { wxString msg_text = _(L("The Spiral Vase mode requires:\n" "- one perimeter\n" "- no top solid layers\n" "- 0% fill density\n" "- no support material\n" - "- inactive Ensure vertical shell thickness")); + "- Ensure vertical shell thickness enabled\n" + "- Detect thin walls disabled")); if (is_global_config) msg_text += "\n\n" + _(L("Shall I adjust those settings in order to enable Spiral Vase?")); wxMessageDialog dialog(nullptr, msg_text, _(L("Spiral Vase")), @@ -90,7 +97,8 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con new_conf.set_key_value("fill_density", new ConfigOptionPercent(0)); new_conf.set_key_value("support_material", new ConfigOptionBool(false)); new_conf.set_key_value("support_material_enforce_layers", new ConfigOptionInt(0)); - new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(false)); + new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(true)); + new_conf.set_key_value("thin_walls", new ConfigOptionBool(false)); fill_density = 0; } else { @@ -233,22 +241,27 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) "solid_infill_every_layers", "solid_infill_below_area", "infill_extruder" }) toggle_field(el, have_infill); - bool have_solid_infill = config->opt_int("top_solid_layers") > 0 || config->opt_int("bottom_solid_layers") > 0; + bool has_spiral_vase = config->opt_bool("spiral_vase"); + bool has_top_solid_infill = config->opt_int("top_solid_layers") > 0; + bool has_bottom_solid_infill = config->opt_int("bottom_solid_layers") > 0; + bool has_solid_infill = has_top_solid_infill || has_bottom_solid_infill; // solid_infill_extruder uses the same logic as in Print::extruders() for (auto el : { "top_fill_pattern", "bottom_fill_pattern", "infill_first", "solid_infill_extruder", "solid_infill_extrusion_width", "solid_infill_speed" }) - toggle_field(el, have_solid_infill); + toggle_field(el, has_solid_infill); for (auto el : { "fill_angle", "bridge_angle", "infill_extrusion_width", "infill_speed", "bridge_speed" }) - toggle_field(el, have_infill || have_solid_infill); + toggle_field(el, have_infill || has_solid_infill); + + toggle_field("top_solid_min_thickness", ! has_spiral_vase && has_top_solid_infill); + toggle_field("bottom_solid_min_thickness", ! has_spiral_vase && has_bottom_solid_infill); // Gap fill is newly allowed in between perimeter lines even for empty infill (see GH #1476). toggle_field("gap_fill_speed", have_perimeters); - bool have_top_solid_infill = config->opt_int("top_solid_layers") > 0; for (auto el : { "top_infill_extrusion_width", "top_solid_infill_speed" }) - toggle_field(el, have_top_solid_infill); + toggle_field(el, has_top_solid_infill); bool have_default_acceleration = config->opt_float("default_acceleration") > 0; for (auto el : { "perimeter_acceleration", "infill_acceleration", diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 880d1127bb..99353fbd99 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -793,7 +793,6 @@ PageUpdate::PageUpdate(ConfigWizard *parent) box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); }); } -#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF PageReloadFromDisk::PageReloadFromDisk(ConfigWizard* parent) : ConfigWizardPage(parent, _(L("Reload from disk")), _(L("Reload from disk"))) , full_pathnames(false) @@ -808,7 +807,6 @@ PageReloadFromDisk::PageReloadFromDisk(ConfigWizard* parent) box_pathnames->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->full_pathnames = event.IsChecked(); }); } -#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF PageMode::PageMode(ConfigWizard *parent) : ConfigWizardPage(parent, _(L("View mode")), _(L("View mode"))) @@ -1401,9 +1399,7 @@ void ConfigWizard::priv::load_pages() btn_finish->Enable(any_fff_selected || any_sla_selected); index->add_page(page_update); -#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF index->add_page(page_reload_from_disk); -#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF index->add_page(page_mode); index->go_to(former_active); // Will restore the active item/page if possible @@ -1853,10 +1849,7 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese } app_config->set("version_check", page_update->version_check ? "1" : "0"); app_config->set("preset_update", page_update->preset_update ? "1" : "0"); - -#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF app_config->set("export_sources_full_pathnames", page_reload_from_disk->full_pathnames ? "1" : "0"); -#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF page_mode->serialize_mode(app_config); @@ -2001,6 +1994,10 @@ ConfigWizard::ConfigWizard(wxWindow *parent) p->page_msla = new PagePrinters(this, _(L("Prusa MSLA Technology Printers")), "Prusa MSLA", *vendor_prusa, 0, T_SLA); p->add_page(p->page_msla); + // Pages for 3rd party vendors + p->create_3rdparty_pages(); // Needs to be done _before_ creating PageVendors + p->add_page(p->page_vendors = new PageVendors(this)); + p->any_sla_selected = p->check_sla_selected(); p->any_fff_selected = p->check_fff_selected(); @@ -2013,19 +2010,13 @@ ConfigWizard::ConfigWizard(wxWindow *parent) p->add_page(p->page_custom = new PageCustom(this)); p->add_page(p->page_update = new PageUpdate(this)); -#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF p->add_page(p->page_reload_from_disk = new PageReloadFromDisk(this)); -#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF p->add_page(p->page_mode = new PageMode(this)); p->add_page(p->page_firmware = new PageFirmware(this)); p->add_page(p->page_bed = new PageBedShape(this)); p->add_page(p->page_diams = new PageDiameters(this)); p->add_page(p->page_temps = new PageTemperatures(this)); - // Pages for 3rd party vendors - p->create_3rdparty_pages(); // Needs to be done _before_ creating PageVendors - p->add_page(p->page_vendors = new PageVendors(this)); - p->load_pages(); p->index->go_to(size_t{0}); diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 9c14633c9f..e056b0b9e4 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -305,16 +305,12 @@ struct PageUpdate: ConfigWizardPage PageUpdate(ConfigWizard *parent); }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF struct PageReloadFromDisk : ConfigWizardPage { bool full_pathnames; PageReloadFromDisk(ConfigWizard* parent); }; -#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ struct PageMode: ConfigWizardPage { @@ -470,11 +466,7 @@ struct ConfigWizard::priv PageMaterials *page_sla_materials = nullptr; PageCustom *page_custom = nullptr; PageUpdate *page_update = nullptr; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF PageReloadFromDisk *page_reload_from_disk = nullptr; -#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ PageMode *page_mode = nullptr; PageVendors *page_vendors = nullptr; Pages3rdparty pages_3rdparty; diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp new file mode 100644 index 0000000000..a462b88947 --- /dev/null +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -0,0 +1,2125 @@ +#include "wxExtensions.hpp" +#include "libslic3r/GCode/PreviewData.hpp" +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "I18N.hpp" +#include "ExtruderSequenceDialog.hpp" +#include "libslic3r/Print.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "Field.hpp" + +namespace Slic3r { + +using GUI::from_u8; +using GUI::into_u8; + +namespace DoubleSlider { + +wxDEFINE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); + +Control::Control( wxWindow *parent, + wxWindowID id, + int lowerValue, + int higherValue, + int minValue, + int maxValue, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& val, + const wxString& name) : + wxControl(parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE), + m_lower_value(lowerValue), + m_higher_value (higherValue), + m_min_value(minValue), + m_max_value(maxValue), + m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL) +{ +#ifdef __WXOSX__ + is_osx = true; +#endif //__WXOSX__ + if (!is_osx) + SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX + + m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "right_half_circle.png") : ScalableBitmap(this, "thumb_up")); + m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "left_half_circle.png" ) : ScalableBitmap(this, "thumb_down")); + m_thumb_size = m_bmp_thumb_lower.GetBmpSize(); + + m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add"); + m_bmp_add_tick_off = ScalableBitmap(this, "colorchange_add_f"); + m_bmp_del_tick_on = ScalableBitmap(this, "colorchange_del"); + m_bmp_del_tick_off = ScalableBitmap(this, "colorchange_del_f"); + m_tick_icon_dim = m_bmp_add_tick_on.GetBmpWidth(); + + m_bmp_one_layer_lock_on = ScalableBitmap(this, "lock_closed"); + m_bmp_one_layer_lock_off = ScalableBitmap(this, "lock_closed_f"); + m_bmp_one_layer_unlock_on = ScalableBitmap(this, "lock_open"); + m_bmp_one_layer_unlock_off = ScalableBitmap(this, "lock_open_f"); + m_lock_icon_dim = m_bmp_one_layer_lock_on.GetBmpWidth(); + + m_bmp_revert = ScalableBitmap(this, "undo"); + m_revert_icon_dim = m_bmp_revert.GetBmpWidth(); + m_bmp_cog = ScalableBitmap(this, "cog"); + m_cog_icon_dim = m_bmp_cog.GetBmpWidth(); + + m_selection = ssUndef; + m_ticks.set_pause_print_msg(_utf8(L("Place bearings in slots and resume printing"))); + + // slider events + this->Bind(wxEVT_PAINT, &Control::OnPaint, this); + this->Bind(wxEVT_CHAR, &Control::OnChar, this); + this->Bind(wxEVT_LEFT_DOWN, &Control::OnLeftDown, this); + this->Bind(wxEVT_MOTION, &Control::OnMotion, this); + this->Bind(wxEVT_LEFT_UP, &Control::OnLeftUp, this); + this->Bind(wxEVT_MOUSEWHEEL, &Control::OnWheel, this); + this->Bind(wxEVT_ENTER_WINDOW,&Control::OnEnterWin, this); + this->Bind(wxEVT_LEAVE_WINDOW,&Control::OnLeaveWin, this); + this->Bind(wxEVT_KEY_DOWN, &Control::OnKeyDown, this); + this->Bind(wxEVT_KEY_UP, &Control::OnKeyUp, this); + this->Bind(wxEVT_RIGHT_DOWN, &Control::OnRightDown,this); + this->Bind(wxEVT_RIGHT_UP, &Control::OnRightUp, this); + + // control's view variables + SLIDER_MARGIN = 4 + GUI::wxGetApp().em_unit(); + + DARK_ORANGE_PEN = wxPen(wxColour(237, 107, 33)); + ORANGE_PEN = wxPen(wxColour(253, 126, 66)); + LIGHT_ORANGE_PEN = wxPen(wxColour(254, 177, 139)); + + DARK_GREY_PEN = wxPen(wxColour(128, 128, 128)); + GREY_PEN = wxPen(wxColour(164, 164, 164)); + LIGHT_GREY_PEN = wxPen(wxColour(204, 204, 204)); + + m_line_pens = { &DARK_GREY_PEN, &GREY_PEN, &LIGHT_GREY_PEN }; + m_segm_pens = { &DARK_ORANGE_PEN, &ORANGE_PEN, &LIGHT_ORANGE_PEN }; + + const wxFont& font = GetFont(); + m_font = is_osx ? font.Smaller().Smaller() : font.Smaller(); +} + +void Control::msw_rescale() +{ + const wxFont& font = GUI::wxGetApp().normal_font(); + m_font = is_osx ? font.Smaller().Smaller() : font.Smaller(); + + m_bmp_thumb_higher.msw_rescale(); + m_bmp_thumb_lower .msw_rescale(); + m_thumb_size = m_bmp_thumb_lower.bmp().GetSize(); + + m_bmp_add_tick_on .msw_rescale(); + m_bmp_add_tick_off.msw_rescale(); + m_bmp_del_tick_on .msw_rescale(); + m_bmp_del_tick_off.msw_rescale(); + m_tick_icon_dim = m_bmp_add_tick_on.bmp().GetSize().x; + + m_bmp_one_layer_lock_on .msw_rescale(); + m_bmp_one_layer_lock_off .msw_rescale(); + m_bmp_one_layer_unlock_on .msw_rescale(); + m_bmp_one_layer_unlock_off.msw_rescale(); + m_lock_icon_dim = m_bmp_one_layer_lock_on.bmp().GetSize().x; + + m_bmp_revert.msw_rescale(); + m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x; + m_bmp_cog.msw_rescale(); + m_cog_icon_dim = m_bmp_cog.bmp().GetSize().x; + + SLIDER_MARGIN = 4 + GUI::wxGetApp().em_unit(); + + SetMinSize(get_min_size()); + GetParent()->Layout(); +} + +int Control::GetActiveValue() const +{ + return m_selection == ssLower ? + m_lower_value : m_selection == ssHigher ? + m_higher_value : -1; +} + +wxSize Control::get_min_size() const +{ + const int min_side = GUI::wxGetApp().em_unit() * ( is_horizontal() ? (is_osx ? 8 : 6) : 10 ); + + return wxSize(min_side, min_side); +} + +wxSize Control::DoGetBestSize() const +{ + const wxSize size = wxControl::DoGetBestSize(); + if (size.x > 1 && size.y > 1) + return size; + return get_min_size(); +} + +void Control::SetLowerValue(const int lower_val) +{ + m_selection = ssLower; + m_lower_value = lower_val; + correct_lower_value(); + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::SetHigherValue(const int higher_val) +{ + m_selection = ssHigher; + m_higher_value = higher_val; + correct_higher_value(); + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::SetSelectionSpan(const int lower_val, const int higher_val) +{ + m_lower_value = std::max(lower_val, m_min_value); + m_higher_value = std::max(std::min(higher_val, m_max_value), m_lower_value); + if (m_lower_value < m_higher_value) + m_is_one_layer = false; + + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::SetMaxValue(const int max_value) +{ + m_max_value = max_value; + Refresh(); + Update(); +} + +void Control::draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos) +{ + int width; + int height; + get_size(&width, &height); + + wxCoord line_beg_x = is_horizontal() ? SLIDER_MARGIN : width*0.5 - 1; + wxCoord line_beg_y = is_horizontal() ? height*0.5 - 1 : SLIDER_MARGIN; + wxCoord line_end_x = is_horizontal() ? width - SLIDER_MARGIN + 1 : width*0.5 - 1; + wxCoord line_end_y = is_horizontal() ? height*0.5 - 1 : height - SLIDER_MARGIN + 1; + + wxCoord segm_beg_x = is_horizontal() ? lower_pos : width*0.5 - 1; + wxCoord segm_beg_y = is_horizontal() ? height*0.5 - 1 : lower_pos/*-1*/; + wxCoord segm_end_x = is_horizontal() ? higher_pos : width*0.5 - 1; + wxCoord segm_end_y = is_horizontal() ? height*0.5 - 1 : higher_pos-1; + + for (size_t id = 0; id < m_line_pens.size(); id++) + { + dc.SetPen(*m_line_pens[id]); + dc.DrawLine(line_beg_x, line_beg_y, line_end_x, line_end_y); + dc.SetPen(*m_segm_pens[id]); + dc.DrawLine(segm_beg_x, segm_beg_y, segm_end_x, segm_end_y); + if (is_horizontal()) + line_beg_y = line_end_y = segm_beg_y = segm_end_y += 1; + else + line_beg_x = line_end_x = segm_beg_x = segm_end_x += 1; + } +} + +double Control::get_scroll_step() +{ + const wxSize sz = get_size(); + const int& slider_len = m_style == wxSL_HORIZONTAL ? sz.x : sz.y; + return double(slider_len - SLIDER_MARGIN * 2) / (m_max_value - m_min_value); +} + +// get position on the slider line from entered value +wxCoord Control::get_position_from_value(const int value) +{ + const double step = get_scroll_step(); + const int val = is_horizontal() ? value : m_max_value - value; + return wxCoord(SLIDER_MARGIN + int(val*step + 0.5)); +} + +wxSize Control::get_size() +{ + int w, h; + get_size(&w, &h); + return wxSize(w, h); +} + +void Control::get_size(int *w, int *h) +{ + GetSize(w, h); + is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim; +} + +double Control::get_double_value(const SelectedSlider& selection) +{ + if (m_values.empty() || m_lower_value<0) + return 0.0; + if (m_values.size() <= m_higher_value) { + correct_higher_value(); + return m_values.back(); + } + return m_values[selection == ssLower ? m_lower_value : m_higher_value]; +} + +using t_custom_code = CustomGCode::Item; +CustomGCode::Info Control::GetTicksValues() const +{ + CustomGCode::Info custom_gcode_per_print_z; + std::vector& values = custom_gcode_per_print_z.gcodes; + + const int val_size = m_values.size(); + if (!m_values.empty()) + for (const TickCode& tick : m_ticks.ticks) { + if (tick.tick > val_size) + break; + values.emplace_back(t_custom_code{m_values[tick.tick], tick.gcode, tick.extruder, tick.color}); + } + + if (m_force_mode_apply) + custom_gcode_per_print_z.mode = m_mode; + + return custom_gcode_per_print_z; +} + +void Control::SetTicksValues(const CustomGCode::Info& custom_gcode_per_print_z) +{ + if (m_values.empty()) + { + m_ticks.mode = m_mode; + return; + } + + const bool was_empty = m_ticks.empty(); + + m_ticks.ticks.clear(); + const std::vector& heights = custom_gcode_per_print_z.gcodes; + for (auto h : heights) { + auto it = std::lower_bound(m_values.begin(), m_values.end(), h.print_z - epsilon()); + + if (it == m_values.end()) + continue; + + m_ticks.ticks.emplace(TickCode{int(it-m_values.begin()), h.gcode, h.extruder, h.color}); + } + + if (!was_empty && m_ticks.empty()) + // Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one + post_ticks_changed_event(); + + if (custom_gcode_per_print_z.mode) + m_ticks.mode = custom_gcode_per_print_z.mode; + + Refresh(); + Update(); +} + +void Control::SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder) +{ + m_mode = !is_one_extruder_printed_model ? t_mode::MultiExtruder : + only_extruder < 0 ? t_mode::SingleExtruder : + t_mode::MultiAsSingle; + if (!m_ticks.mode) + m_ticks.mode = m_mode; + m_only_extruder = only_extruder; + + UseDefaultColors(m_mode == t_mode::SingleExtruder); +} + +void Control::get_lower_and_higher_position(int& lower_pos, int& higher_pos) +{ + const double step = get_scroll_step(); + if (is_horizontal()) { + lower_pos = SLIDER_MARGIN + int(m_lower_value*step + 0.5); + higher_pos = SLIDER_MARGIN + int(m_higher_value*step + 0.5); + } + else { + lower_pos = SLIDER_MARGIN + int((m_max_value - m_lower_value)*step + 0.5); + higher_pos = SLIDER_MARGIN + int((m_max_value - m_higher_value)*step + 0.5); + } +} + +void Control::draw_focus_rect() +{ + if (!m_is_focused) + return; + const wxSize sz = GetSize(); + wxPaintDC dc(this); + const wxPen pen = wxPen(wxColour(128, 128, 10), 1, wxPENSTYLE_DOT); + dc.SetPen(pen); + dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT)); + dc.DrawRectangle(1, 1, sz.x - 2, sz.y - 2); +} + +void Control::render() +{ + SetBackgroundColour(GetParent()->GetBackgroundColour()); + draw_focus_rect(); + + wxPaintDC dc(this); + dc.SetFont(m_font); + + const wxCoord lower_pos = get_position_from_value(m_lower_value); + const wxCoord higher_pos = get_position_from_value(m_higher_value); + + // draw colored band on the background of a scroll line + // and only in a case of no-empty m_values + draw_colored_band(dc); + + // draw line + draw_scroll_line(dc, lower_pos, higher_pos); + + //draw color print ticks + draw_ticks(dc); + + // draw both sliders + draw_thumbs(dc, lower_pos, higher_pos); + + //draw lock/unlock + draw_one_layer_icon(dc); + + //draw revert bitmap (if it's shown) + draw_revert_icon(dc); + + //draw cog bitmap (if it's shown) + draw_cog_icon(dc); + + //draw mouse position + draw_tick_on_mouse_position(dc); +} + +void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end) +{ + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + + // suppress add tick on first layer + if (tick == 0) + return; + + wxBitmap* icon = m_focus == fiActionIcon ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); + if (m_ticks.ticks.find(TickCode{tick}) != m_ticks.ticks.end()) + icon = m_focus == fiActionIcon ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp(); + + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = pt_beg.x - 0.5*m_tick_icon_dim : y_draw = pt_beg.y - 0.5*m_tick_icon_dim; + if (m_selection == ssLower) + is_horizontal() ? y_draw = pt_end.y + 3 : x_draw = pt_beg.x - m_tick_icon_dim-2; + else + is_horizontal() ? y_draw = pt_beg.y - m_tick_icon_dim-2 : x_draw = pt_end.x + 3; + + dc.DrawBitmap(*icon, x_draw, y_draw); + + //update rect of the tick action icon + m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim); +} + +void Control::draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, const SelectedSlider selection) +{ + if (m_selection == selection) { + //draw info line + dc.SetPen(DARK_ORANGE_PEN); + const wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x, pos.y - m_thumb_size.y) : wxPoint(pos.x - m_thumb_size.x, pos.y/* - 1*/); + const wxPoint pt_end = is_horizontal() ? wxPoint(pos.x, pos.y + m_thumb_size.y) : wxPoint(pos.x + m_thumb_size.x, pos.y/* - 1*/); + dc.DrawLine(pt_beg, pt_end); + + //draw action icon + if (m_is_enabled_tick_manipulation) + draw_action_icon(dc, pt_beg, pt_end); + } +} + +void Control::draw_tick_on_mouse_position(wxDC& dc) +{ + if (!m_is_focused || m_moving_pos == wxDefaultPosition) + return; + + //calculate thumb position on slider line + int width, height; + get_size(&width, &height); + + int tick = get_tick_near_point(m_moving_pos); + if (tick == m_higher_value || tick == m_lower_value) + return ; + + auto draw_ticks = [this](wxDC& dc, wxPoint pos, int margin=0 ) + { + wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x+margin, pos.y - m_thumb_size.y) : wxPoint(pos.x - m_thumb_size.x , pos.y+margin); + wxPoint pt_end = is_horizontal() ? wxPoint(pos.x+margin, pos.y + m_thumb_size.y) : wxPoint(pos.x - 0.5 * m_thumb_size.x + 1, pos.y+margin); + dc.DrawLine(pt_beg, pt_end); + + pt_beg = is_horizontal() ? wxPoint(pos.x + margin, pos.y - m_thumb_size.y) : wxPoint(pos.x + 0.5 * m_thumb_size.x, pos.y+margin); + pt_end = is_horizontal() ? wxPoint(pos.x + margin, pos.y + m_thumb_size.y) : wxPoint(pos.x + m_thumb_size.x + 1, pos.y+margin); + dc.DrawLine(pt_beg, pt_end); + }; + + auto draw_touch = [this](wxDC& dc, wxPoint pos, int margin, bool right_side ) + { + int mult = right_side ? 1 : -1; + wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x - margin, pos.y + mult * m_thumb_size.y) : wxPoint(pos.x + mult * m_thumb_size.x, pos.y - margin); + wxPoint pt_end = is_horizontal() ? wxPoint(pos.x + margin, pos.y + mult * m_thumb_size.y) : wxPoint(pos.x + mult * m_thumb_size.x, pos.y + margin); + dc.DrawLine(pt_beg, pt_end); + }; + + if (tick > 0) // this tick exists and should be marked as a focused + { + wxCoord new_pos = get_position_from_value(tick); + const wxPoint pos = is_horizontal() ? wxPoint(new_pos, height * 0.5) : wxPoint(0.5 * width, new_pos); + + dc.SetPen(DARK_ORANGE_PEN); + + draw_ticks(dc, pos, -2); + draw_ticks(dc, pos, 2 ); + draw_touch(dc, pos, 2, true); + draw_touch(dc, pos, 2, false); + + return; + } + + tick = get_value_from_position(m_moving_pos); + if (tick >= m_max_value || tick <= m_min_value || tick == m_higher_value || tick == m_lower_value) + return; + + wxCoord new_pos = get_position_from_value(tick); + const wxPoint pos = is_horizontal() ? wxPoint(new_pos, height * 0.5) : wxPoint(0.5 * width, new_pos); + + //draw info line + dc.SetPen(LIGHT_GREY_PEN); + draw_ticks(dc, pos); +} + +wxString Control::get_label(int tick) const +{ + const int value = tick; + + if (m_label_koef == 1.0 && m_values.empty()) + return wxString::Format("%d", value); + if (value >= m_values.size()) + return "ErrVal"; + + const wxString str = m_values.empty() ? + wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) : + wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None); + return wxString::Format("%s\n(%d)", str, m_values.empty() ? value : value+1); +} + +void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_side/*=true*/) const +{ + wxCoord text_width, text_height; + const wxString label = get_label(tick); + dc.GetMultiLineTextExtent(label, &text_width, &text_height); + wxPoint text_pos; + if (right_side) + text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x) : + wxPoint(pos.x + m_thumb_size.x+1, pos.y - 0.5*text_height - 1); + else + text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x - text_height) : + wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5*text_height + 1); + dc.DrawText(label, text_pos); +} + +void Control::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const +{ + draw_tick_text(dc, pos, selection == ssLower ? m_lower_value : m_higher_value, selection == ssLower); +} + +void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) +{ + wxCoord x_draw, y_draw; + if (selection == ssLower) { + if (is_horizontal()) { + x_draw = pos.x - m_thumb_size.x; + y_draw = pos.y - int(0.5*m_thumb_size.y); + } + else { + x_draw = pos.x - int(0.5*m_thumb_size.x); + y_draw = pos.y - int(0.5*m_thumb_size.y); + } + } + else{ + if (is_horizontal()) { + x_draw = pos.x; + y_draw = pos.y - int(0.5*m_thumb_size.y); + } + else { + x_draw = pos.x - int(0.5*m_thumb_size.x); + y_draw = pos.y - int(0.5*m_thumb_size.y); + } + } + dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower.bmp() : m_bmp_thumb_higher.bmp(), x_draw, y_draw); + + // Update thumb rect + update_thumb_rect(x_draw, y_draw, selection); +} + +void Control::draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection) +{ + //calculate thumb position on slider line + int width, height; + get_size(&width, &height); + const wxPoint pos = is_horizontal() ? wxPoint(pos_coord, height*0.5) : wxPoint(0.5*width, pos_coord); + + // Draw thumb + draw_thumb_item(dc, pos, selection); + + // Draw info_line + draw_info_line_with_icon(dc, pos, selection); + + // Draw thumb text + draw_thumb_text(dc, pos, selection); +} + +void Control::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos) +{ + //calculate thumb position on slider line + int width, height; + get_size(&width, &height); + const wxPoint pos_l = is_horizontal() ? wxPoint(lower_pos, height*0.5) : wxPoint(0.5*width, lower_pos); + const wxPoint pos_h = is_horizontal() ? wxPoint(higher_pos, height*0.5) : wxPoint(0.5*width, higher_pos); + + // Draw lower thumb + draw_thumb_item(dc, pos_l, ssLower); + // Draw lower info_line + draw_info_line_with_icon(dc, pos_l, ssLower); + + // Draw higher thumb + draw_thumb_item(dc, pos_h, ssHigher); + // Draw higher info_line + draw_info_line_with_icon(dc, pos_h, ssHigher); + // Draw higher thumb text + draw_thumb_text(dc, pos_h, ssHigher); + + // Draw lower thumb text + draw_thumb_text(dc, pos_l, ssLower); +} + +void Control::draw_ticks(wxDC& dc) +{ + if (!m_is_enabled_tick_manipulation) + return; + + dc.SetPen(m_is_enabled_tick_manipulation ? DARK_GREY_PEN : LIGHT_GREY_PEN ); + int height, width; + get_size(&width, &height); + const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width; + for (auto tick : m_ticks.ticks) + { + const wxCoord pos = get_position_from_value(tick.tick); + + is_horizontal() ? dc.DrawLine(pos, mid-14, pos, mid-9) : + dc.DrawLine(mid - 14, pos/* - 1*/, mid - 9, pos/* - 1*/); + is_horizontal() ? dc.DrawLine(pos, mid+14, pos, mid+9) : + dc.DrawLine(mid + 14, pos/* - 1*/, mid + 9, pos/* - 1*/); + + // if current tick if focused, we should to use a specific "focused" icon + bool focused_tick = m_moving_pos != wxDefaultPosition && tick.tick == get_tick_near_point(m_moving_pos); + + // get icon name if it is + std::string icon_name; + if (tick.gcode == ColorChangeCode || tick.gcode == ToolChangeCode) { + if (m_ticks.is_conflict_tick(tick, m_mode, m_only_extruder, m_values[tick.tick])) + icon_name = focused_tick ? "error_tick_f" : "error_tick"; + } + else if (tick.gcode == PausePrintCode) + icon_name = focused_tick ? "pause_print_f" : "pause_print"; + else + icon_name = focused_tick ? "edit_gcode_f" : "edit_gcode"; + + // Draw icon for "Pause print", "Custom Gcode" or conflict tick + if (!icon_name.empty()) + { + wxBitmap icon = create_scaled_bitmap(icon_name); + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = pos - 0.5 * m_tick_icon_dim : y_draw = pos - 0.5 * m_tick_icon_dim; + is_horizontal() ? y_draw = mid + 22 : x_draw = mid + m_thumb_size.x + 3; + + dc.DrawBitmap(icon, x_draw, y_draw); + } + } +} + +std::string Control::get_color_for_tool_change_tick(std::set::const_iterator it) const +{ + const int current_extruder = it->extruder == 0 ? std::max(m_only_extruder, 1) : it->extruder; + + auto it_n = it; + while (it_n != m_ticks.ticks.begin()) { + --it_n; + if (it_n->gcode == ColorChangeCode && it_n->extruder == current_extruder) + return it_n->color; + } + + return it->color; +} + +std::string Control::get_color_for_color_change_tick(std::set::const_iterator it) const +{ + const int def_extruder = std::max(1, m_only_extruder); + auto it_n = it; + bool is_tool_change = false; + while (it_n != m_ticks.ticks.begin()) { + --it_n; + if (it_n->gcode == ToolChangeCode) { + is_tool_change = true; + if (it_n->extruder == it->extruder) + return it->color; + break; + } + } + if (!is_tool_change && it->extruder == def_extruder) + return it->color; + + return ""; +} + +wxRect Control::get_colored_band_rect() +{ + int height, width; + get_size(&width, &height); + + const wxCoord mid = is_horizontal() ? 0.5 * height : 0.5 * width; + + return is_horizontal() ? + wxRect(SLIDER_MARGIN, lround(mid - 0.375 * m_thumb_size.y), + width - 2 * SLIDER_MARGIN + 1, lround(0.75 * m_thumb_size.y)) : + wxRect(lround(mid - 0.375 * m_thumb_size.x), SLIDER_MARGIN, + lround(0.75 * m_thumb_size.x), height - 2 * SLIDER_MARGIN + 1); +} + +void Control::draw_colored_band(wxDC& dc) +{ + if (!m_is_enabled_tick_manipulation) + return; + + auto draw_band = [](wxDC& dc, const wxColour& clr, const wxRect& band_rc) + { + dc.SetPen(clr); + dc.SetBrush(clr); + dc.DrawRectangle(band_rc); + }; + + wxRect main_band = get_colored_band_rect(); + + // don't color a band for MultiExtruder mode + if (m_ticks.empty() || m_mode == t_mode::MultiExtruder) + { + draw_band(dc, GetParent()->GetBackgroundColour(), main_band); + return; + } + + const int default_color_idx = m_mode==t_mode::MultiAsSingle ? std::max(m_only_extruder - 1, 0) : 0; + draw_band(dc, wxColour(GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config()[default_color_idx]), main_band); + + std::set::const_iterator tick_it = m_ticks.ticks.begin(); + + while (tick_it != m_ticks.ticks.end()) + { + if ( (m_mode == t_mode::SingleExtruder && tick_it->gcode == ColorChangeCode ) || + (m_mode == t_mode::MultiAsSingle && (tick_it->gcode == ToolChangeCode || tick_it->gcode == ColorChangeCode)) ) + { + const wxCoord pos = get_position_from_value(tick_it->tick); + is_horizontal() ? main_band.SetLeft(SLIDER_MARGIN + pos) : + main_band.SetBottom(pos - 1); + + const std::string clr_str = m_mode == t_mode::SingleExtruder ? tick_it->color : + tick_it->gcode == ToolChangeCode ? + get_color_for_tool_change_tick(tick_it) : + get_color_for_color_change_tick(tick_it); + + if (!clr_str.empty()) + draw_band(dc, wxColour(clr_str), main_band); + } + ++tick_it; + } +} + +void Control::draw_one_layer_icon(wxDC& dc) +{ + const wxBitmap& icon = m_is_one_layer ? + m_focus == fiOneLayerIcon ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() : + m_focus == fiOneLayerIcon ? m_bmp_one_layer_unlock_off.bmp() : m_bmp_one_layer_unlock_on.bmp(); + + int width, height; + get_size(&width, &height); + + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = width-2 : x_draw = 0.5*width - 0.5*m_lock_icon_dim; + is_horizontal() ? y_draw = 0.5*height - 0.5*m_lock_icon_dim : y_draw = height-2; + + dc.DrawBitmap(icon, x_draw, y_draw); + + //update rect of the lock/unlock icon + m_rect_one_layer_icon = wxRect(x_draw, y_draw, m_lock_icon_dim, m_lock_icon_dim); +} + +void Control::draw_revert_icon(wxDC& dc) +{ + if (m_ticks.empty() || !m_is_enabled_tick_manipulation) + return; + + int width, height; + get_size(&width, &height); + + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = width-2 : x_draw = 0.25*SLIDER_MARGIN; + is_horizontal() ? y_draw = 0.25*SLIDER_MARGIN: y_draw = height-2; + + dc.DrawBitmap(m_bmp_revert.bmp(), x_draw, y_draw); + + //update rect of the lock/unlock icon + m_rect_revert_icon = wxRect(x_draw, y_draw, m_revert_icon_dim, m_revert_icon_dim); +} + +void Control::draw_cog_icon(wxDC& dc) +{ + int width, height; + get_size(&width, &height); + + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = width-2 : x_draw = width - m_cog_icon_dim - 2; + is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height-2; + + dc.DrawBitmap(m_bmp_cog.bmp(), x_draw, y_draw); + + //update rect of the lock/unlock icon + m_rect_cog_icon = wxRect(x_draw, y_draw, m_cog_icon_dim, m_cog_icon_dim); +} + +void Control::update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection) +{ + const wxRect& rect = wxRect(begin_x, begin_y, m_thumb_size.x, int(m_thumb_size.y*0.5)); + if (selection == ssLower) + m_rect_lower_thumb = rect; + else + m_rect_higher_thumb = rect; +} + +int Control::get_value_from_position(const wxCoord x, const wxCoord y) +{ + const int height = get_size().y; + const double step = get_scroll_step(); + + if (is_horizontal()) + return int(double(x - SLIDER_MARGIN) / step + 0.5); + + return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5); +} + +void Control::detect_selected_slider(const wxPoint& pt) +{ + m_selection = is_point_in_rect(pt, m_rect_lower_thumb) ? ssLower : + is_point_in_rect(pt, m_rect_higher_thumb) ? ssHigher : ssUndef; +} + +bool Control::is_point_in_rect(const wxPoint& pt, const wxRect& rect) +{ + return rect.GetLeft() <= pt.x && pt.x <= rect.GetRight() && + rect.GetTop() <= pt.y && pt.y <= rect.GetBottom(); +} + +int Control::get_tick_near_point(const wxPoint& pt) +{ + for (auto tick : m_ticks.ticks) { + const wxCoord pos = get_position_from_value(tick.tick); + + if (is_horizontal()) { + if (pos - 4 <= pt.x && pt.x <= pos + 4) + return tick.tick; + } + else { + if (pos - 4 <= pt.y && pt.y <= pos + 4) + return tick.tick; + } + } + return -1; +} + +void Control::ChangeOneLayerLock() +{ + m_is_one_layer = !m_is_one_layer; + m_selection == ssLower ? correct_lower_value() : correct_higher_value(); + if (!m_selection) m_selection = ssHigher; + + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::OnLeftDown(wxMouseEvent& event) +{ + if (HasCapture()) + return; + this->CaptureMouse(); + + m_is_left_down = true; + m_mouse = maNone; + + wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); + + if (is_point_in_rect(pos, m_rect_one_layer_icon)) + m_mouse = maOneLayerIconClick; + else if (is_point_in_rect(pos, m_rect_cog_icon)) + m_mouse = maCogIconClick; + else if (m_is_enabled_tick_manipulation) + { + if (is_point_in_rect(pos, m_rect_tick_action)) { + auto it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_value : m_higher_value }); + m_mouse = it == m_ticks.ticks.end() ? maAddTick : maDeleteTick; + } + else if (is_point_in_rect(pos, m_rect_revert_icon)) + m_mouse = maRevertIconClick; + } + + event.Skip(); +} + +void Control::correct_lower_value() +{ + if (m_lower_value < m_min_value) + m_lower_value = m_min_value; + else if (m_lower_value > m_max_value) + m_lower_value = m_max_value; + + if ((m_lower_value >= m_higher_value && m_lower_value <= m_max_value) || m_is_one_layer) + m_higher_value = m_lower_value; +} + +void Control::correct_higher_value() +{ + if (m_higher_value > m_max_value) + m_higher_value = m_max_value; + else if (m_higher_value < m_min_value) + m_higher_value = m_min_value; + + if ((m_higher_value <= m_lower_value && m_higher_value >= m_min_value) || m_is_one_layer) + m_lower_value = m_higher_value; +} + +wxString Control::get_tooltip(int tick/*=-1*/) +{ + if (m_focus == fiNone) + return ""; + if (m_focus == fiOneLayerIcon) + return _(L("One layer mode")); + if (m_focus == fiRevertIcon) + return _(L("Discard all custom changes")); + if (m_focus == fiCogIcon) + return m_mode == t_mode::MultiAsSingle ? + wxString::Format(_(L("Jump to height %s or " + "Set extruder sequence for the entire print")), " (Shift + G)\n") : + _(L("Jump to height")) + " (Shift + G)"; + if (m_focus == fiColorBand) + return m_mode != t_mode::SingleExtruder ? "" : + _(L("Edit current color - Right click the colored slider segment")); + + wxString tooltip; + const auto tick_code_it = m_ticks.ticks.find(TickCode{tick}); + + if (tick_code_it == m_ticks.ticks.end() && m_focus == fiActionIcon) // tick doesn't exist + { + // Show mode as a first string of tooltop + tooltip = " " + _(L("Print mode")) + ": "; + tooltip += (m_mode == t_mode::SingleExtruder ? CustomGCode::SingleExtruderMode : + m_mode == t_mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode : + CustomGCode::MultiExtruderMode ); + tooltip += "\n\n"; + + /* Note: just on OSX!!! + * Right click event causes a little scrolling. + * So, as a workaround we use Ctrl+LeftMouseClick instead of RightMouseClick + * Show this information in tooltip + * */ + + // Show list of actions with new tick + tooltip += ( m_mode == t_mode::MultiAsSingle ? + _(L("Add extruder change - Left click")) : + m_mode == t_mode::SingleExtruder ? + _(L("Add color change - Left click for predefined color or" + "Shift + Left click for custom color selection")) : + _(L("Add color change - Left click")) ) + " " + + _(L("or press \"+\" key")) + "\n" + ( + is_osx ? + _(L("Add another code - Ctrl + Left click")) : + _(L("Add another code - Right click")) ); + } + + if (tick_code_it != m_ticks.ticks.end()) // tick exists + { + // Show custom Gcode as a first string of tooltop + tooltip = " "; + tooltip += tick_code_it->gcode == ColorChangeCode ? ( + m_mode == t_mode::SingleExtruder ? + from_u8((boost::format(_utf8(L("Color change (\"%1%\")"))) % tick_code_it->gcode ).str()) : + from_u8((boost::format(_utf8(L("Color change (\"%1%\") for Extruder %2%"))) % + tick_code_it->gcode % tick_code_it->extruder).str()) ) : + tick_code_it->gcode == PausePrintCode ? + from_u8((boost::format(_utf8(L("Pause print (\"%1%\")"))) % tick_code_it->gcode ).str()) : + tick_code_it->gcode == ToolChangeCode ? + from_u8((boost::format(_utf8(L("Extruder (tool) is changed to Extruder \"%1%\""))) % tick_code_it->extruder ).str()) : + from_u8((boost::format(_utf8(L("\"%1%\""))) % tick_code_it->gcode ).str()) ; + + // If tick is marked as a conflict (exclamation icon), + // we should to explain why + ConflictType conflict = m_ticks.is_conflict_tick(*tick_code_it, m_mode, m_only_extruder, m_values[tick]); + if (conflict != ctNone) + tooltip += "\n\n" + _(L("Note")) + "! "; + if (conflict == ctModeConflict) + tooltip += _(L("G-code associated to this tick mark is in a conflict with print mode.\n" + "Editing it will cause changes of Slider data.")); + else if (conflict == ctMeaninglessColorChange) + tooltip += _(L("There is a color change for extruder that won't be used till the end of print job.\n" + "This code won't be processed during G-code generation.")); + else if (conflict == ctMeaninglessToolChange) + tooltip += _(L("There is an extruder change set to the same extruder.\n" + "This code won't be processed during G-code generation.")); + else if (conflict == ctRedundant) + tooltip += _(L("There is a color change for extruder that has not been used before.\n" + "Check your settings to avoid redundant color changes.")); + + // Show list of actions with existing tick + if (m_focus == fiActionIcon) + tooltip += "\n\n" + _(L("Delete tick mark - Left click or press \"-\" key")) + "\n" + ( + is_osx ? + _(L("Edit tick mark - Ctrl + Left click")) : + _(L("Edit tick mark - Right click")) ); + } + return tooltip; + +} + +int Control::get_edited_tick_for_position(const wxPoint pos, const std::string& gcode /*= ColorChangeCode*/) +{ + if (m_ticks.empty()) + return -1; + + int tick = get_value_from_position(pos); + auto it = std::lower_bound(m_ticks.ticks.begin(), m_ticks.ticks.end(), TickCode{ tick }); + + while (it != m_ticks.ticks.begin()) { + --it; + if (it->gcode == gcode) + return it->tick; + } + + return -1; +} + +void Control::OnMotion(wxMouseEvent& event) +{ + bool action = false; + + const wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); + int tick = -1; + + /* Note: Checking "!m_is_one_layer" is commented now because of + * it looks like unnecessary and cause a tooltip "One layer" showing when OneLayerLock is on + * #ysFIXME : Delete it after testing + * */ + if (!m_is_left_down/* && !m_is_one_layer*/) + { + if (is_point_in_rect(pos, m_rect_one_layer_icon)) + m_focus = fiOneLayerIcon; + else if (is_point_in_rect(pos, m_rect_tick_action)) { + m_focus = fiActionIcon; + tick = m_selection == ssLower ? m_lower_value : m_higher_value; + } + else if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon)) + m_focus = fiRevertIcon; + else if (is_point_in_rect(pos, m_rect_cog_icon)) + m_focus = fiCogIcon; + else if (m_mode == t_mode::SingleExtruder && is_point_in_rect(pos, get_colored_band_rect()) && + get_edited_tick_for_position(pos) >= 0 ) + m_focus = fiColorBand; + else { + m_focus = fiTick; + tick = get_tick_near_point(pos); + } + m_moving_pos = pos; + } + else if (m_is_left_down || m_is_right_down) { + if (m_selection == ssLower) { + int current_value = m_lower_value; + m_lower_value = get_value_from_position(pos.x, pos.y); + correct_lower_value(); + action = (current_value != m_lower_value); + } + else if (m_selection == ssHigher) { + int current_value = m_higher_value; + m_higher_value = get_value_from_position(pos.x, pos.y); + correct_higher_value(); + action = (current_value != m_higher_value); + } + m_moving_pos = wxDefaultPosition; + } + Refresh(); + Update(); + event.Skip(); + + // Set tooltips with information for each icon + this->SetToolTip(get_tooltip(tick)); + + if (action) + { + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + e.SetString("moving"); + ProcessWindowEvent(e); + } +} + +void Control::append_change_extruder_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) +{ + const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); + if (extruders_cnt > 1) + { + std::array active_extruders = get_active_extruders_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); + + wxMenu* change_extruder_menu = new wxMenu(); + + for (int i = 1; i <= extruders_cnt; i++) + { + const bool is_active_extruder = i == active_extruders[0] || i == active_extruders[1]; + const wxString item_name = wxString::Format(_(L("Extruder %d")), i) + + (is_active_extruder ? " (" + _(L("active")) + ")" : ""); + + if (m_mode == t_mode::MultiAsSingle) + append_menu_item(change_extruder_menu, wxID_ANY, item_name, "", + [this, i](wxCommandEvent&) { add_code_as_tick(ToolChangeCode, i); }, "", menu, + [is_active_extruder]() { return !is_active_extruder; }, GUI::wxGetApp().plater()); + } + + const wxString change_extruder_menu_name = m_mode == t_mode::MultiAsSingle ? + (switch_current_code ? _(L("Switch code to Change extruder")) : _(L("Change extruder")) ) : + _(L("Change extruder (N/A)")); + + wxMenuItem* change_extruder_menu_item = menu->AppendSubMenu(change_extruder_menu, change_extruder_menu_name, _(L("Use another extruder"))); + change_extruder_menu_item->SetBitmap(create_scaled_bitmap(active_extruders[1] > 0 ? "edit_uni" : "change_extruder")); + + GUI::wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this, change_extruder_menu_item](wxUpdateUIEvent& evt) { + enable_menu_item(evt, [this]() {return m_mode == t_mode::MultiAsSingle; }, change_extruder_menu_item, this); }, + change_extruder_menu_item->GetId()); + } +} + +void Control::append_add_color_change_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) +{ + const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); + if (extruders_cnt > 1) + { + int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + std::set used_extruders_for_tick = m_ticks.get_used_extruders_for_tick(tick, m_only_extruder, m_values[tick]); + + wxMenu* add_color_change_menu = new wxMenu(); + + for (int i = 1; i <= extruders_cnt; i++) + { + const bool is_used_extruder = used_extruders_for_tick.empty() ? true : // #ys_FIXME till used_extruders_for_tick doesn't filled correct for mmMultiExtruder + used_extruders_for_tick.find(i) != used_extruders_for_tick.end(); + const wxString item_name = wxString::Format(_(L("Extruder %d")), i) + + (is_used_extruder ? " (" + _(L("used")) + ")" : ""); + + append_menu_item(add_color_change_menu, wxID_ANY, item_name, "", + [this, i](wxCommandEvent&) { add_code_as_tick(ColorChangeCode, i); }, "", menu, + []() { return true; }, GUI::wxGetApp().plater()); + } + + const wxString menu_name = switch_current_code ? + from_u8((boost::format(_utf8(L("Switch code to Color change (%1%) for:"))) % ColorChangeCode).str()) : + from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % ColorChangeCode).str()); + wxMenuItem* add_color_change_menu_item = menu->AppendSubMenu(add_color_change_menu, menu_name, ""); + add_color_change_menu_item->SetBitmap(create_scaled_bitmap("colorchange_add_m")); + } +} + +void Control::OnLeftUp(wxMouseEvent& event) +{ + if (!HasCapture()) + return; + this->ReleaseMouse(); + m_is_left_down = false; + + switch (m_mouse) { + case maNone : + move_current_thumb_to_pos(event.GetLogicalPosition(wxClientDC(this))); + break; + case maDeleteTick : + delete_current_tick(); + break; + case maAddTick : + add_current_tick(); + break; + case maCogIconClick : + if (m_mode == t_mode::MultiAsSingle) + show_cog_icon_context_menu(); + else + jump_to_print_z(); + break; + case maOneLayerIconClick: + switch_one_layer_mode(); + break; + case maRevertIconClick: + discard_all_thicks(); + break; + default : + break; + } + + Refresh(); + Update(); + event.Skip(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::enter_window(wxMouseEvent& event, const bool enter) +{ + m_is_focused = enter; + Refresh(); + Update(); + event.Skip(); +} + +// "condition" have to be true for: +// - value increase (if wxSL_VERTICAL) +// - value decrease (if wxSL_HORIZONTAL) +void Control::move_current_thumb(const bool condition) +{ +// m_is_one_layer = wxGetKeyState(WXK_CONTROL); + int delta = condition ? -1 : 1; + if (is_horizontal()) + delta *= -1; + + if (m_selection == ssLower) { + m_lower_value -= delta; + correct_lower_value(); + } + else if (m_selection == ssHigher) { + m_higher_value -= delta; + correct_higher_value(); + } + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::OnWheel(wxMouseEvent& event) +{ + // Set nearest to the mouse thumb as a selected, if there is not selected thumb + if (m_selection == ssUndef) + { + const wxPoint& pt = event.GetLogicalPosition(wxClientDC(this)); + + if (is_horizontal()) + m_selection = abs(pt.x - m_rect_lower_thumb.GetRight()) <= + abs(pt.x - m_rect_higher_thumb.GetLeft()) ? + ssLower : ssHigher; + else + m_selection = abs(pt.y - m_rect_lower_thumb.GetTop()) <= + abs(pt.y - m_rect_higher_thumb.GetBottom()) ? + ssLower : ssHigher; + } + + move_current_thumb(event.GetWheelRotation() > 0); +} + +void Control::OnKeyDown(wxKeyEvent &event) +{ + const int key = event.GetKeyCode(); + if (key == WXK_NUMPAD_ADD) { + // OnChar() is called immediately after OnKeyDown(), which can cause call of add_tick() twice. + // To avoid this case we should suppress second add_tick() call. + m_ticks.suppress_plus(true); + add_current_tick(true); + } + else if (key == 390 || key == WXK_DELETE || key == WXK_BACK) { + // OnChar() is called immediately after OnKeyDown(), which can cause call of delete_tick() twice. + // To avoid this case we should suppress second delete_tick() call. + m_ticks.suppress_minus(true); + delete_current_tick(); + } + else if (event.GetKeyCode() == WXK_SHIFT) + UseDefaultColors(false); + else if (is_horizontal()) + { + if (key == WXK_LEFT || key == WXK_RIGHT) + move_current_thumb(key == WXK_LEFT); + else if (key == WXK_UP || key == WXK_DOWN) { + m_selection = key == WXK_UP ? ssHigher : ssLower; + Refresh(); + } + } + else { + if (key == WXK_LEFT || key == WXK_RIGHT) { + m_selection = key == WXK_LEFT ? ssHigher : ssLower; + Refresh(); + } + else if (key == WXK_UP || key == WXK_DOWN) + move_current_thumb(key == WXK_UP); + } + + event.Skip(); // !Needed to have EVT_CHAR generated as well +} + +void Control::OnKeyUp(wxKeyEvent &event) +{ + if (event.GetKeyCode() == WXK_CONTROL) + m_is_one_layer = false; + else if (event.GetKeyCode() == WXK_SHIFT) + UseDefaultColors(true); + + Refresh(); + Update(); + event.Skip(); +} + +void Control::OnChar(wxKeyEvent& event) +{ + const int key = event.GetKeyCode(); + if (key == '+' && !m_ticks.suppressed_plus()) { + add_current_tick(true); + m_ticks.suppress_plus(false); + } + else if (key == '-' && !m_ticks.suppressed_minus()) { + delete_current_tick(); + m_ticks.suppress_minus(false); + } + if (key == 'G') + jump_to_print_z(); +} + +void Control::OnRightDown(wxMouseEvent& event) +{ + if (HasCapture()) return; + this->CaptureMouse(); + + const wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); + + m_mouse = maNone; + if (m_is_enabled_tick_manipulation) { + if (is_point_in_rect(pos, m_rect_tick_action)) + { + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + m_mouse = m_ticks.ticks.find(TickCode{ tick }) == m_ticks.ticks.end() ? + maAddMenu : maEditMenu; + } + else if (m_mode == t_mode::SingleExtruder && is_point_in_rect(pos, get_colored_band_rect())) + m_mouse = maForceColorEdit; + else if (m_mode == t_mode::MultiAsSingle && is_point_in_rect(pos, m_rect_cog_icon)) + m_mouse = maCogIconMenu; + } + if (m_mouse != maNone) + return; + + detect_selected_slider(pos); + if (!m_selection) + return; + + if (m_selection == ssLower) + m_higher_value = m_lower_value; + else + m_lower_value = m_higher_value; + + // set slider to "one layer" mode + m_is_right_down = m_is_one_layer = true; + + Refresh(); + Update(); + event.Skip(); +} + +// Get active extruders for tick. +// Means one current extruder for not existing tick OR +// 2 extruders - for existing tick (extruder before ToolChangeCode and extruder of current existing tick) +// Use those values to disable selection of active extruders +std::array Control::get_active_extruders_for_tick(int tick) const +{ + int default_initial_extruder = m_mode == t_mode::MultiAsSingle ? std::max(1, m_only_extruder) : 1; + std::array extruders = { default_initial_extruder, -1 }; + if (m_ticks.empty()) + return extruders; + + auto it = m_ticks.ticks.lower_bound(TickCode{tick}); + + if (it != m_ticks.ticks.end() && it->tick == tick) // current tick exists + extruders[1] = it->extruder; + + while (it != m_ticks.ticks.begin()) { + --it; + if(it->gcode == ToolChangeCode) { + extruders[0] = it->extruder; + break; + } + } + + return extruders; +} + +// Get used extruders for tick. +// Means all extruders(tools) which will be used during printing from current tick to the end +std::set TickCodeInfo::get_used_extruders_for_tick(int tick, int only_extruder, double print_z, t_mode force_mode/* = t_mode::Undef*/) const +{ + t_mode e_mode = !force_mode ? mode : force_mode; + + if (e_mode == t_mode::MultiExtruder) + { + // #ys_FIXME: get tool ordering from _correct_ place + const ToolOrdering& tool_ordering = GUI::wxGetApp().plater()->fff_print().get_tool_ordering(); + + if (tool_ordering.empty()) + return {}; + + std::set used_extruders; + + auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), LayerTools(print_z)); + for (; it_layer_tools != tool_ordering.end(); ++it_layer_tools) + { + const std::vector& extruders = it_layer_tools->extruders; + for (const auto& extruder : extruders) + used_extruders.emplace(extruder+1); + } + + return used_extruders; + } + + const int default_initial_extruder = e_mode == t_mode::MultiAsSingle ? std::max(only_extruder, 1) : 1; + if (ticks.empty() || e_mode == t_mode::SingleExtruder) + return {default_initial_extruder}; + + std::set used_extruders; + + auto it_start = ticks.lower_bound(TickCode{tick}); + auto it = it_start; + if (it == ticks.begin() && it->gcode == ToolChangeCode && + tick != it->tick ) // In case of switch of ToolChange to ColorChange, when tick exists, + // we shouldn't change color for extruder, which will be deleted + { + used_extruders.emplace(it->extruder); + if (tick < it->tick) + used_extruders.emplace(default_initial_extruder); + } + + while (it != ticks.begin()) { + --it; + if (it->gcode == ToolChangeCode && tick != it->tick) { + used_extruders.emplace(it->extruder); + break; + } + } + + if (it == ticks.begin() && used_extruders.empty()) + used_extruders.emplace(default_initial_extruder); + + for (it = it_start; it != ticks.end(); ++it) + if (it->gcode == ToolChangeCode && tick != it->tick) + used_extruders.emplace(it->extruder); + + return used_extruders; +} + +void Control::show_add_context_menu() +{ + wxMenu menu; + + if (m_mode == t_mode::SingleExtruder) { + append_menu_item(&menu, wxID_ANY, _(L("Add color change")) + " (M600)", "", + [this](wxCommandEvent&) { add_code_as_tick(ColorChangeCode); }, "colorchange_add_m", &menu); + + UseDefaultColors(false); + } + else { + append_change_extruder_menu_item(&menu); + append_add_color_change_menu_item(&menu); + } + + append_menu_item(&menu, wxID_ANY, _(L("Add pause print")) + " (M601)", "", + [this](wxCommandEvent&) { add_code_as_tick(PausePrintCode); }, "pause_print", &menu); + + append_menu_item(&menu, wxID_ANY, _(L("Add custom G-code")), "", + [this](wxCommandEvent&) { add_code_as_tick(""); }, "edit_gcode", &menu); + + GUI::wxGetApp().plater()->PopupMenu(&menu); +} + +void Control::show_edit_context_menu() +{ + wxMenu menu; + + std::set::iterator it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_value : m_higher_value }); + + if (it->gcode == ToolChangeCode) { + if (m_mode == t_mode::MultiAsSingle) + append_change_extruder_menu_item(&menu); + append_add_color_change_menu_item(&menu, true); + } + else + append_menu_item(&menu, wxID_ANY, it->gcode == ColorChangeCode ? _(L("Edit color")) : + it->gcode == PausePrintCode ? _(L("Edit pause print message")) : + _(L("Edit custom G-code")), "", + [this](wxCommandEvent&) { edit_tick(); }, "edit_uni", &menu); + + if (it->gcode == ColorChangeCode && m_mode == t_mode::MultiAsSingle) + append_change_extruder_menu_item(&menu, true); + + append_menu_item(&menu, wxID_ANY, it->gcode == ColorChangeCode ? _(L("Delete color change")) : + it->gcode == ToolChangeCode ? _(L("Delete tool change")) : + it->gcode == PausePrintCode ? _(L("Delete pause print")) : + _(L("Delete custom G-code")), "", + [this](wxCommandEvent&) { delete_current_tick();}, "colorchange_del_f", &menu); + + GUI::wxGetApp().plater()->PopupMenu(&menu); +} + +void Control::show_cog_icon_context_menu() +{ + wxMenu menu; + + append_menu_item(&menu, wxID_ANY, _(L("Jump to height")) + " (Shift+G)", "", + [this](wxCommandEvent&) { jump_to_print_z(); }, "", &menu); + + append_menu_item(&menu, wxID_ANY, _(L("Set extruder sequence for the entire print")), "", + [this](wxCommandEvent&) { edit_extruder_sequence(); }, "", &menu); + + GUI::wxGetApp().plater()->PopupMenu(&menu); +} + +void Control::OnRightUp(wxMouseEvent& event) +{ + if (!HasCapture()) + return; + this->ReleaseMouse(); + m_is_right_down = m_is_one_layer = false; + + if (m_mouse == maForceColorEdit) + { + wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); + int edited_tick = get_edited_tick_for_position(pos); + if (edited_tick >= 0) + edit_tick(edited_tick); + } + else if (m_mouse == maAddMenu) + show_add_context_menu(); + else if (m_mouse == maEditMenu) + show_edit_context_menu(); + else if (m_mouse == maCogIconMenu) + show_cog_icon_context_menu(); + + Refresh(); + Update(); + event.Skip(); +} + +static std::string get_new_color(const std::string& color) +{ + wxColour clr(color); + if (!clr.IsOk()) + clr = wxColour(0, 0, 0); // Don't set alfa to transparence + + auto data = new wxColourData(); + data->SetChooseFull(1); + data->SetColour(clr); + + wxColourDialog dialog(nullptr, data); + dialog.CenterOnParent(); + if (dialog.ShowModal() == wxID_OK) + return dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); + return ""; +} + +/* To avoid get an empty string from wxTextEntryDialog + * Let disable OK button, if TextCtrl is empty + * OR input value is our of range (min..max), when min a nd max are positive + * */ +static void upgrade_text_entry_dialog(wxTextEntryDialog* dlg, double min = -1.0, double max = -1.0) +{ + // detect TextCtrl and OK button + wxTextCtrl* textctrl {nullptr}; + wxWindowList& dlg_items = dlg->GetChildren(); + for (auto item : dlg_items) { + textctrl = dynamic_cast(item); + if (textctrl) + break; + } + + if (!textctrl) + return; + + wxButton* btn_OK = static_cast(dlg->FindWindowById(wxID_OK)); + btn_OK->Bind(wxEVT_UPDATE_UI, [textctrl, min, max](wxUpdateUIEvent& evt) + { + bool disable = textctrl->IsEmpty(); + if (!disable && min >= 0.0 && max >= 0.0) + { + double value = -1.0; + if (!textctrl->GetValue().ToCDouble(&value)) // input value couldn't be converted to double + disable = true; + else + disable = value < min || value > max; // is input value is out of valid range ? + } + + evt.Enable(!disable); + }, btn_OK->GetId()); +} + +static std::string get_custom_code(const std::string& code_in, double height) +{ + wxString msg_text = from_u8(_utf8(L("Enter custom G-code used on current layer"))) + ":"; + wxString msg_header = from_u8((boost::format(_utf8(L("Custom G-code on current layer (%1% mm)."))) % height).str()); + + // get custom gcode + wxTextEntryDialog dlg(nullptr, msg_text, msg_header, code_in, + wxTextEntryDialogStyle | wxTE_MULTILINE); + upgrade_text_entry_dialog(&dlg); + + if (dlg.ShowModal() != wxID_OK) + return ""; + + return dlg.GetValue().ToStdString(); +} + +static std::string get_pause_print_msg(const std::string& msg_in, double height) +{ + wxString msg_text = from_u8(_utf8(L("Enter short message shown on Printer display when a print is paused"))) + ":"; + wxString msg_header = from_u8((boost::format(_utf8(L("Message for pause print on current layer (%1% mm)."))) % height).str()); + + // get custom gcode + wxTextEntryDialog dlg(nullptr, msg_text, msg_header, from_u8(msg_in), + wxTextEntryDialogStyle); + upgrade_text_entry_dialog(&dlg); + + if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) + return ""; + + return into_u8(dlg.GetValue()); +} + +static double get_print_z_to_jump(double active_print_z, double min_z, double max_z) +{ + wxString msg_text = _(L("Enter the height you want to jump to")) + " :"; + wxString msg_header = _(L("Jump to height")); + wxString msg_in = GUI::double_to_string(active_print_z); + + // get custom gcode + wxTextEntryDialog dlg(nullptr, msg_text, msg_header, msg_in, wxTextEntryDialogStyle); + upgrade_text_entry_dialog(&dlg, min_z, max_z); + + if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) + return -1.0; + + double value = -1.0; + return dlg.GetValue().ToCDouble(&value) ? value : -1.0; +} + +void Control::add_code_as_tick(std::string code, int selected_extruder/* = -1*/) +{ + if (m_selection == ssUndef) + return; + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + + if ( !check_ticks_changed_event(code) ) + return; + + const int extruder = selected_extruder > 0 ? selected_extruder : std::max(1, m_only_extruder); + const auto it = m_ticks.ticks.find(TickCode{ tick }); + + if ( it == m_ticks.ticks.end() ) { + // try to add tick + if (!m_ticks.add_tick(tick, code, extruder, m_values[tick])) + return; + } + else if (code == ToolChangeCode || code == ColorChangeCode) { + // try to switch tick code to ToolChangeCode or ColorChangeCode accordingly + if (!m_ticks.switch_code_for_tick(it, code, extruder)) + return; + } + else + return; + + post_ticks_changed_event(code); +} + +void Control::add_current_tick(bool call_from_keyboard /*= false*/) +{ + if (m_selection == ssUndef) + return; + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + auto it = m_ticks.ticks.find(TickCode{ tick }); + + if (it != m_ticks.ticks.end() || // this tick is already exist + !check_ticks_changed_event(m_mode == t_mode::MultiAsSingle ? ToolChangeCode : ColorChangeCode)) + return; + + if (m_mode == t_mode::SingleExtruder) + add_code_as_tick(ColorChangeCode); + else + { + wxMenu menu; + + if (m_mode == t_mode::MultiAsSingle) + append_change_extruder_menu_item(&menu); + else + append_add_color_change_menu_item(&menu); + + wxPoint pos = wxDefaultPosition; + /* Menu position will be calculated from mouse click position, but... + * if function is called from keyboard (pressing "+"), we should to calculate it + * */ + if (call_from_keyboard) + { + int width, height; + get_size(&width, &height); + + const wxCoord coord = 0.75 * (is_horizontal() ? height : width); + this->GetPosition(&width, &height); + + pos = is_horizontal() ? + wxPoint(get_position_from_value(tick), height + coord) : + wxPoint(width + coord, get_position_from_value(tick)); + } + + GUI::wxGetApp().plater()->PopupMenu(&menu, pos); + } +} + +void Control::delete_current_tick() +{ + if (m_selection == ssUndef) + return; + + auto it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_value : m_higher_value }); + if (it == m_ticks.ticks.end() || + !check_ticks_changed_event(it->gcode)) + return; + + const std::string code = it->gcode; + m_ticks.ticks.erase(it); + post_ticks_changed_event(code); +} + +void Control::edit_tick(int tick/* = -1*/) +{ + if (tick < 0) + tick = m_selection == ssLower ? m_lower_value : m_higher_value; + const std::set::iterator it = m_ticks.ticks.find(TickCode{ tick }); + + if (it == m_ticks.ticks.end() || + !check_ticks_changed_event(it->gcode)) + return; + + const std::string code = it->gcode; + if (m_ticks.edit_tick(it, m_values[it->tick])) + post_ticks_changed_event(code); +} + +// switch on/off one layer mode +void Control::switch_one_layer_mode() +{ + m_is_one_layer = !m_is_one_layer; + if (!m_is_one_layer) { + SetLowerValue(m_min_value); + SetHigherValue(m_max_value); + } + m_selection == ssLower ? correct_lower_value() : correct_higher_value(); + if (!m_selection) m_selection = ssHigher; +} + +// discard all custom changes on DoubleSlider +void Control::discard_all_thicks() +{ + SetLowerValue(m_min_value); + SetHigherValue(m_max_value); + + m_selection == ssLower ? correct_lower_value() : correct_higher_value(); + if (!m_selection) m_selection = ssHigher; + + m_ticks.ticks.clear(); + post_ticks_changed_event(); + +} + +// Set current thumb position to the nearest tick (if it is) +// OR to a value corresponding to the mouse click (pos) +void Control::move_current_thumb_to_pos(wxPoint pos) +{ + const int tick_val = get_tick_near_point(pos); + const int mouse_val = tick_val >= 0 && m_is_enabled_tick_manipulation ? tick_val : + get_value_from_position(pos); + if (mouse_val >= 0) + { + // if (abs(mouse_val - m_lower_value) < abs(mouse_val - m_higher_value)) { + if (mouse_val <= m_lower_value) { + SetLowerValue(mouse_val); + correct_lower_value(); + m_selection = ssLower; + } + else { + SetHigherValue(mouse_val); + correct_higher_value(); + m_selection = ssHigher; + } + } +} + +void Control::edit_extruder_sequence() +{ + if (!check_ticks_changed_event(ToolChangeCode)) + return; + + GUI::ExtruderSequenceDialog dlg(m_extruders_sequence); + if (dlg.ShowModal() != wxID_OK) + return; + m_extruders_sequence = dlg.GetValue(); + + m_ticks.erase_all_ticks_with_code(ToolChangeCode); + + int tick = 0; + double value = 0.0; + int extruder = 0; + const int extr_cnt = m_extruders_sequence.extruders.size(); + + std::vector colors = GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + + while (tick <= m_max_value) + { + const int cur_extruder = m_extruders_sequence.extruders[extruder]; + + bool meaningless_tick = tick == 0.0 && cur_extruder == extruder; + if (!meaningless_tick) + m_ticks.ticks.emplace(TickCode{tick, ToolChangeCode, cur_extruder + 1, colors[cur_extruder]}); + + extruder++; + if (extruder == extr_cnt) + extruder = 0; + if (m_extruders_sequence.is_mm_intervals) + { + value += m_extruders_sequence.interval_by_mm; + auto val_it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon()); + + if (val_it == m_values.end()) + break; + + tick = val_it - m_values.begin(); + } + else + tick += m_extruders_sequence.interval_by_layers; + } + + post_ticks_changed_event(ToolChangeCode); +} + +void Control::jump_to_print_z() +{ + double print_z = get_print_z_to_jump(m_values[m_selection == ssLower ? m_lower_value : m_higher_value], + m_values[m_min_value], m_values[m_max_value]); + if (print_z < 0) + return; + + auto it = std::lower_bound(m_values.begin(), m_values.end(), print_z - epsilon()); + int tick_value = it - m_values.begin(); + + if (m_selection == ssLower) + SetLowerValue(tick_value); + else + SetHigherValue(tick_value); +} + +void Control::post_ticks_changed_event(const std::string& gcode /*= ""*/) +{ + m_force_mode_apply = (gcode.empty() || gcode == ColorChangeCode || gcode == ToolChangeCode); + + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); +} + +bool Control::check_ticks_changed_event(const std::string& gcode) +{ + if ( m_ticks.mode == m_mode || + (gcode != ColorChangeCode && gcode != ToolChangeCode) || + (m_ticks.mode == t_mode::SingleExtruder && m_mode == t_mode::MultiAsSingle) || // All ColorChanges will be applied for 1st extruder + (m_ticks.mode == t_mode::MultiExtruder && m_mode == t_mode::MultiAsSingle) ) // Just mark ColorChanges for all unused extruders + return true; + + if ((m_ticks.mode == t_mode::SingleExtruder && m_mode == t_mode::MultiExtruder ) || + (m_ticks.mode == t_mode::MultiExtruder && m_mode == t_mode::SingleExtruder) ) + { + if (!m_ticks.has_tick_with_code(ColorChangeCode)) + return true; + + wxString message = (m_ticks.mode == t_mode::SingleExtruder ? + _(L("The last color change data was saved for a single extruder printer profile.")) : + _(L("The last color change data was saved for a multiple extruder printer profile.")) + ) + "\n" + + _(L("Your current changes will delete all saved color changes.")) + "\n\n\t" + + _(L("Are you sure you want to continue?")); + + wxMessageDialog msg(this, message, _(L("Notice")), wxYES_NO); + if (msg.ShowModal() == wxID_YES) { + m_ticks.erase_all_ticks_with_code(ColorChangeCode); + post_ticks_changed_event(ColorChangeCode); + } + return false; + } + // m_ticks_mode == t_mode::MultiAsSingle + if( m_ticks.has_tick_with_code(ToolChangeCode) ) + { + wxString message = m_mode == t_mode::SingleExtruder ? ( + _(L("The last color change data was saved for a multi extruder printing.")) + "\n\n" + + _(L("Select YES if you want to delete all saved tool changes, \n\t" + "NO if you want all tool changes switch to color changes, \n\t" + "or CANCEL to leave it unchanged")) + "\n\n\t" + + _(L("Do you want to delete all saved tool changes?")) + ) : ( // t_mode::MultiExtruder + _(L("The last color change data was saved for a multi extruder printing with tool changes for whole print.")) + "\n\n" + + _(L("Your current changes will delete all saved extruder (tool) changes.")) + "\n\n\t" + + _(L("Are you sure you want to continue?")) ) ; + + wxMessageDialog msg(this, message, _(L("Notice")), wxYES_NO | (m_mode == t_mode::SingleExtruder ? wxCANCEL : 0)); + const int answer = msg.ShowModal(); + if (answer == wxID_YES) { + m_ticks.erase_all_ticks_with_code(ToolChangeCode); + post_ticks_changed_event(ToolChangeCode); + } + else if (m_mode == t_mode::SingleExtruder && answer == wxID_NO) { + m_ticks.switch_code(ToolChangeCode, ColorChangeCode); + post_ticks_changed_event(ColorChangeCode); + } + return false; + } + + return true; +} + +std::string TickCodeInfo::get_color_for_tick(TickCode tick, const std::string& code, const int extruder) +{ + if (mode == t_mode::SingleExtruder && code == ColorChangeCode && m_use_default_colors) + { + const std::vector& colors = GCodePreviewData::ColorPrintColors(); + if (ticks.empty()) + return colors[0]; + m_default_color_idx++; + + return colors[m_default_color_idx % colors.size()]; + } + + std::vector colors = GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + std::string color = colors[extruder - 1]; + + if (code == ColorChangeCode) + { + if (!ticks.empty()) + { + auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick ); + while (before_tick_it != ticks.begin()) { + --before_tick_it; + if (before_tick_it->gcode == ColorChangeCode && before_tick_it->extruder == extruder) { + color = before_tick_it->color; + break; + } + } + } + + color = get_new_color(color); + } + return color; +} + +bool TickCodeInfo::add_tick(const int tick, std::string& code, const int extruder, double print_z) +{ + std::string color; + if (code.empty()) // custom Gcode + { + code = get_custom_code(custom_gcode, print_z); + if (code.empty()) + return false; + custom_gcode = code; + } + else if (code == PausePrintCode) + { + /* PausePrintCode doesn't need a color, so + * this field is used for save a short message shown on Printer display + * */ + color = get_pause_print_msg(pause_print_msg, print_z); + if (color.empty()) + return false; + pause_print_msg = color; + } + else + { + color = get_color_for_tick(TickCode{ tick }, code, extruder); + if (color.empty()) + return false; + } + + if (mode == t_mode::SingleExtruder) + m_use_default_colors = true; + + ticks.emplace(TickCode{ tick, code, extruder, color }); + return true; +} + +bool TickCodeInfo::edit_tick(std::set::iterator it, double print_z) +{ + std::string edited_value; + if (it->gcode == ColorChangeCode) + edited_value = get_new_color(it->color); + else if (it->gcode == PausePrintCode) + edited_value = get_pause_print_msg(it->color, print_z); + else + edited_value = get_custom_code(it->gcode, print_z); + + if (edited_value.empty()) + return false; + + TickCode changed_tick = *it; + if (it->gcode == ColorChangeCode || it->gcode == PausePrintCode) { + if (it->color == edited_value) + return false; + changed_tick.color = edited_value; + } + else { + if (it->gcode == edited_value) + return false; + changed_tick.gcode = edited_value; + } + + ticks.erase(it); + ticks.emplace(changed_tick); + + return true; +} + +void TickCodeInfo::switch_code(const std::string& code_from, const std::string& code_to) +{ + for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) + if (it->gcode == code_from) + { + TickCode tick = *it; + tick.gcode = code_to; + tick.extruder = 1; + ticks.erase(it); + it = ticks.emplace(tick).first; + } + else + ++it; +} + +bool TickCodeInfo::switch_code_for_tick(std::set::iterator it, const std::string& code_to, const int extruder) +{ + const std::string color = get_color_for_tick(*it, code_to, extruder); + if (color.empty()) + return false; + + TickCode changed_tick = *it; + changed_tick.gcode = code_to; + changed_tick.extruder = extruder; + changed_tick.color = color; + + ticks.erase(it); + ticks.emplace(changed_tick); + + return true; +} + +void TickCodeInfo::erase_all_ticks_with_code(const std::string& gcode) +{ + for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) { + if (it->gcode == gcode) + it = ticks.erase(it); + else + ++it; + } +} + +bool TickCodeInfo::has_tick_with_code(const std::string& gcode) +{ + for (const TickCode& tick : ticks) + if (tick.gcode == gcode) + return true; + + return false; +} + +ConflictType TickCodeInfo::is_conflict_tick(const TickCode& tick, t_mode out_mode, int only_extruder, double print_z) +{ + if ((tick.gcode == ColorChangeCode && ( + (mode == t_mode::SingleExtruder && out_mode == t_mode::MultiExtruder ) || + (mode == t_mode::MultiExtruder && out_mode == t_mode::SingleExtruder) )) || + (tick.gcode == ToolChangeCode && + (mode == t_mode::MultiAsSingle && out_mode != t_mode::MultiAsSingle)) ) + return ctModeConflict; + + // check ColorChange tick + if (tick.gcode == ColorChangeCode) + { + // We should mark a tick as a "MeaninglessColorChange", + // if it has a ColorChange for unused extruder from current print to end of the print + std::set used_extruders_for_tick = get_used_extruders_for_tick(tick.tick, only_extruder, print_z, out_mode); + + if (used_extruders_for_tick.find(tick.extruder) == used_extruders_for_tick.end()) + return ctMeaninglessColorChange; + + // We should mark a tick as a "Redundant", + // if it has a ColorChange for extruder that has not been used before + if (mode == t_mode::MultiAsSingle && tick.extruder != std::max(only_extruder, 1) ) + { + auto it = ticks.lower_bound( tick ); + if (it == ticks.begin() && it->gcode == ToolChangeCode && tick.extruder == it->extruder) + return ctNone; + + while (it != ticks.begin()) { + --it; + if (it->gcode == ToolChangeCode && tick.extruder == it->extruder) + return ctNone; + } + + return ctRedundant; + } + } + + // check ToolChange tick + if (mode == t_mode::MultiAsSingle && tick.gcode == ToolChangeCode) + { + // We should mark a tick as a "MeaninglessToolChange", + // if it has a ToolChange to the same extruder + auto it = ticks.find(tick); + if (it == ticks.begin()) + return tick.extruder == std::max(only_extruder, 1) ? ctMeaninglessToolChange : ctNone; + + while (it != ticks.begin()) { + --it; + if (it->gcode == ToolChangeCode) + return tick.extruder == it->extruder ? ctMeaninglessToolChange : ctNone; + } + } + + return ctNone; +} + +} // DoubleSlider + +} // Slic3r + + diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp new file mode 100644 index 0000000000..c6c85fe0e1 --- /dev/null +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -0,0 +1,374 @@ +#ifndef slic3r_GUI_DoubleSlider_hpp_ +#define slic3r_GUI_DoubleSlider_hpp_ + +#include "libslic3r/CustomGCode.hpp" +#include "wxExtensions.hpp" + +#include +#include +#include +#include +#include + +#include +#include + +class wxMenu; + +namespace Slic3r { + +namespace DoubleSlider { + +/* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values. + * So, let use same value as a permissible error for layer height. + */ +static double epsilon() { return 0.0011;} + +// custom message the slider sends to its parent to notify a tick-change: +wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); + +enum SelectedSlider { + ssUndef, + ssLower, + ssHigher +}; + +enum FocusedItem { + fiNone, + fiRevertIcon, + fiOneLayerIcon, + fiCogIcon, + fiColorBand, + fiActionIcon, + fiTick +}; + +enum ConflictType +{ + ctNone, + ctModeConflict, + ctMeaninglessColorChange, + ctMeaninglessToolChange, + ctRedundant +}; + +enum MouseAction +{ + maNone, + maAddMenu, // show "Add" context menu for NOTexist active tick + maEditMenu, // show "Edit" context menu for exist active tick + maCogIconMenu, // show context for "cog" icon + maForceColorEdit, // force color editing from colored band + maAddTick, // force tick adding + maDeleteTick, // force tick deleting + maCogIconClick, // LeftMouseClick on "cog" icon + maOneLayerIconClick, // LeftMouseClick on "one_layer" icon + maRevertIconClick, // LeftMouseClick on "revert" icon +}; + +using t_mode = CustomGCode::Mode; + +struct TickCode +{ + bool operator<(const TickCode& other) const { return other.tick > this->tick; } + bool operator>(const TickCode& other) const { return other.tick < this->tick; } + + int tick = 0; + std::string gcode = ColorChangeCode; + int extruder = 0; + std::string color; +}; + +class TickCodeInfo +{ + std::string custom_gcode; + std::string pause_print_msg; + bool m_suppress_plus = false; + bool m_suppress_minus = false; + bool m_use_default_colors= false; + int m_default_color_idx = 0; + + std::string get_color_for_tick(TickCode tick, const std::string& code, const int extruder); + +public: + std::set ticks {}; + t_mode mode = t_mode::Undef; + + bool empty() const { return ticks.empty(); } + void set_pause_print_msg(const std::string& message) { pause_print_msg = message; } + + bool add_tick(const int tick, std::string& code, int extruder, double print_z); + bool edit_tick(std::set::iterator it, double print_z); + void switch_code(const std::string& code_from, const std::string& code_to); + bool switch_code_for_tick(std::set::iterator it, const std::string& code_to, const int extruder); + void erase_all_ticks_with_code(const std::string& gcode); + + bool has_tick_with_code(const std::string& gcode); + ConflictType is_conflict_tick(const TickCode& tick, t_mode out_mode, int only_extruder, double print_z); + + // Get used extruders for tick. + // Means all extruders(tools) which will be used during printing from current tick to the end + std::set get_used_extruders_for_tick(int tick, int only_extruder, double print_z, t_mode force_mode = t_mode::Undef) const; + + void suppress_plus (bool suppress) { m_suppress_plus = suppress; } + void suppress_minus(bool suppress) { m_suppress_minus = suppress; } + bool suppressed_plus () { return m_suppress_plus; } + bool suppressed_minus() { return m_suppress_minus; } + void set_default_colors(bool default_colors_on) { m_use_default_colors = default_colors_on; } +}; + + +struct ExtrudersSequence +{ + bool is_mm_intervals = true; + double interval_by_mm = 3.0; + int interval_by_layers = 10; + std::vector extruders = { 0 }; + + bool operator==(const ExtrudersSequence& other) const + { + return (other.is_mm_intervals == this->is_mm_intervals ) && + (other.interval_by_mm == this->interval_by_mm ) && + (other.interval_by_layers == this->interval_by_layers ) && + (other.extruders == this->extruders ) ; + } + bool operator!=(const ExtrudersSequence& other) const + { + return (other.is_mm_intervals != this->is_mm_intervals ) && + (other.interval_by_mm != this->interval_by_mm ) && + (other.interval_by_layers != this->interval_by_layers ) && + (other.extruders != this->extruders ) ; + } + + void add_extruder(size_t pos) + { + extruders.insert(extruders.begin() + pos+1, size_t(0)); + } + + void delete_extruder(size_t pos) + { + if (extruders.size() == 1) + return;// last item can't be deleted + extruders.erase(extruders.begin() + pos); + } +}; + +class Control : public wxControl +{ +public: + Control( + wxWindow *parent, + wxWindowID id, + int lowerValue, + int higherValue, + int minValue, + int maxValue, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxSL_VERTICAL, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxEmptyString); + ~Control() {} + + void msw_rescale(); + + int GetMinValue() const { return m_min_value; } + int GetMaxValue() const { return m_max_value; } + double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; } + double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; } + int GetLowerValue() const { return m_lower_value; } + int GetHigherValue() const { return m_higher_value; } + int GetActiveValue() const; + double GetLowerValueD() { return get_double_value(ssLower); } + double GetHigherValueD() { return get_double_value(ssHigher); } + wxSize DoGetBestSize() const override; + wxSize get_min_size() const ; + + // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. + void SetLowerValue (const int lower_val); + void SetHigherValue(const int higher_val); + void SetSelectionSpan(const int lower_val, const int higher_val); + + void SetMaxValue(const int max_value); + void SetKoefForLabels(const double koef) { m_label_koef = koef; } + void SetSliderValues(const std::vector& values) { m_values = values; } + void ChangeOneLayerLock(); + + CustomGCode::Info GetTicksValues() const; + void SetTicksValues(const Slic3r::CustomGCode::Info &custom_gcode_per_print_z); + + void EnableTickManipulation(bool enable = true) { m_is_enabled_tick_manipulation = enable; } + void DisableTickManipulation() { EnableTickManipulation(false); } + + void SetManipulationMode(t_mode mode) { m_mode = mode; } + t_mode GetManipulationMode() const { return m_mode; } + void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder); + + bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } + bool is_one_layer() const { return m_is_one_layer; } + bool is_lower_at_min() const { return m_lower_value == m_min_value; } + bool is_higher_at_max() const { return m_higher_value == m_max_value; } + bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } + + void OnPaint(wxPaintEvent& ) { render();} + void OnLeftDown(wxMouseEvent& event); + void OnMotion(wxMouseEvent& event); + void OnLeftUp(wxMouseEvent& event); + void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); } + void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); } + void UseDefaultColors(bool def_colors_on) { m_ticks.set_default_colors(def_colors_on); } + void OnWheel(wxMouseEvent& event); + void OnKeyDown(wxKeyEvent &event); + void OnKeyUp(wxKeyEvent &event); + void OnChar(wxKeyEvent &event); + void OnRightDown(wxMouseEvent& event); + void OnRightUp(wxMouseEvent& event); + + void add_code_as_tick(std::string code, int selected_extruder = -1); + // add default action for tick, when press "+" + void add_current_tick(bool call_from_keyboard = false); + // delete current tick, when press "-" + void delete_current_tick(); + void edit_tick(int tick = -1); + void switch_one_layer_mode(); + void discard_all_thicks(); + void move_current_thumb_to_pos(wxPoint pos); + void edit_extruder_sequence(); + void jump_to_print_z(); + void show_add_context_menu(); + void show_edit_context_menu(); + void show_cog_icon_context_menu(); + + ExtrudersSequence m_extruders_sequence; + +protected: + + void render(); + void draw_focus_rect(); + void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end); + void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos); + void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection); + void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos); + void draw_ticks(wxDC& dc); + void draw_colored_band(wxDC& dc); + void draw_one_layer_icon(wxDC& dc); + void draw_revert_icon(wxDC& dc); + void draw_cog_icon(wxDC &dc); + void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection); + void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection); + void draw_tick_on_mouse_position(wxDC &dc); + void draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_side = true) const; + void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const; + + void update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection); + void detect_selected_slider(const wxPoint& pt); + void correct_lower_value(); + void correct_higher_value(); + void move_current_thumb(const bool condition); + void enter_window(wxMouseEvent& event, const bool enter); + +private: + + bool is_point_in_rect(const wxPoint& pt, const wxRect& rect); + int get_tick_near_point(const wxPoint& pt); + + double get_scroll_step(); + wxString get_label(int tick) const; + void get_lower_and_higher_position(int& lower_pos, int& higher_pos); + int get_value_from_position(const wxCoord x, const wxCoord y); + int get_value_from_position(const wxPoint pos) { return get_value_from_position(pos.x, pos.y); } + wxCoord get_position_from_value(const int value); + wxSize get_size(); + void get_size(int *w, int *h); + double get_double_value(const SelectedSlider& selection); + wxString get_tooltip(int tick = -1); + int get_edited_tick_for_position(wxPoint pos, const std::string& gcode = ColorChangeCode); + + std::string get_color_for_tool_change_tick(std::set::const_iterator it) const; + std::string get_color_for_color_change_tick(std::set::const_iterator it) const; + wxRect get_colored_band_rect(); + + // Get active extruders for tick. + // Means one current extruder for not existing tick OR + // 2 extruders - for existing tick (extruder before ToolChangeCode and extruder of current existing tick) + // Use those values to disable selection of active extruders + std::array get_active_extruders_for_tick(int tick) const; + + void post_ticks_changed_event(const std::string& gcode = ""); + bool check_ticks_changed_event(const std::string& gcode); + + void append_change_extruder_menu_item (wxMenu*, bool switch_current_code = false); + void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false); + + bool is_osx { false }; + wxFont m_font; + int m_min_value; + int m_max_value; + int m_lower_value; + int m_higher_value; + ScalableBitmap m_bmp_thumb_higher; + ScalableBitmap m_bmp_thumb_lower; + ScalableBitmap m_bmp_add_tick_on; + ScalableBitmap m_bmp_add_tick_off; + ScalableBitmap m_bmp_del_tick_on; + ScalableBitmap m_bmp_del_tick_off; + ScalableBitmap m_bmp_one_layer_lock_on; + ScalableBitmap m_bmp_one_layer_lock_off; + ScalableBitmap m_bmp_one_layer_unlock_on; + ScalableBitmap m_bmp_one_layer_unlock_off; + ScalableBitmap m_bmp_revert; + ScalableBitmap m_bmp_cog; + SelectedSlider m_selection; + bool m_is_left_down = false; + bool m_is_right_down = false; + bool m_is_one_layer = false; + bool m_is_focused = false; + bool m_is_enabled_tick_manipulation = true; + bool m_force_mode_apply = true; + + t_mode m_mode = t_mode::SingleExtruder; + int m_only_extruder = -1; + + MouseAction m_mouse = maNone; + FocusedItem m_focus = fiNone; + wxPoint m_moving_pos = wxDefaultPosition; + + wxRect m_rect_lower_thumb; + wxRect m_rect_higher_thumb; + wxRect m_rect_tick_action; + wxRect m_rect_one_layer_icon; + wxRect m_rect_revert_icon; + wxRect m_rect_cog_icon; + wxSize m_thumb_size; + int m_tick_icon_dim; + int m_lock_icon_dim; + int m_revert_icon_dim; + int m_cog_icon_dim; + long m_style; + float m_label_koef = 1.0; + + std::vector m_values; + TickCodeInfo m_ticks; + +// control's view variables + wxCoord SLIDER_MARGIN; // margin around slider + + wxPen DARK_ORANGE_PEN; + wxPen ORANGE_PEN; + wxPen LIGHT_ORANGE_PEN; + + wxPen DARK_GREY_PEN; + wxPen GREY_PEN; + wxPen LIGHT_GREY_PEN; + + std::vector m_line_pens; + std::vector m_segm_pens; +}; + +} // DoubleSlider; + +} // Slic3r + + + +#endif // slic3r_GUI_DoubleSlider_hpp_ diff --git a/src/slic3r/GUI/ExtruderSequenceDialog.hpp b/src/slic3r/GUI/ExtruderSequenceDialog.hpp index 3efd9e3a22..97d57ef120 100644 --- a/src/slic3r/GUI/ExtruderSequenceDialog.hpp +++ b/src/slic3r/GUI/ExtruderSequenceDialog.hpp @@ -2,7 +2,7 @@ #define slic3r_GUI_ExtruderSequenceDialog_hpp_ #include "GUI_Utils.hpp" -#include "wxExtensions.hpp" +#include "DoubleSlider.hpp" class wxTextCtrl; class wxFlexGridSizer; diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index a8953f166e..2fd76e8ba6 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -250,6 +250,23 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true } } +void Field::msw_rescale(bool rescale_sidetext) +{ + m_Undo_to_sys_btn->msw_rescale(); + m_Undo_btn->msw_rescale(); + + // update em_unit value + m_em_unit = em_unit(m_parent); + + // update sidetext if it is needed + if (m_side_text && rescale_sidetext) + { + auto size = wxSize(def_width_thinner() * m_em_unit, -1); + m_side_text->SetSize(size); + m_side_text->SetMinSize(size); + } +} + template bool is_defined_input_value(wxWindow* win, const ConfigOptionType& type) { @@ -259,7 +276,7 @@ bool is_defined_input_value(wxWindow* win, const ConfigOptionType& type) } void TextCtrl::BUILD() { - auto size = wxSize(wxDefaultSize); + auto size = wxSize(def_width()*m_em_unit, wxDefaultCoord); if (m_opt.height >= 0) size.SetHeight(m_opt.height*m_em_unit); if (m_opt.width >= 0) size.SetWidth(m_opt.width*m_em_unit); @@ -455,10 +472,10 @@ boost::any& TextCtrl::get_value() return m_value; } -void TextCtrl::msw_rescale() +void TextCtrl::msw_rescale(bool rescale_sidetext/* = false*/) { - Field::msw_rescale(); - auto size = wxSize(wxDefaultSize); + Field::msw_rescale(rescale_sidetext); + auto size = wxSize(def_width() * m_em_unit, wxDefaultCoord); if (m_opt.height >= 0) size.SetHeight(m_opt.height*m_em_unit); if (m_opt.width >= 0) size.SetWidth(m_opt.width*m_em_unit); @@ -555,7 +572,7 @@ boost::any& CheckBox::get_value() return m_value; } -void CheckBox::msw_rescale() +void CheckBox::msw_rescale(bool rescale_sidetext/* = false*/) { Field::msw_rescale(); @@ -565,7 +582,7 @@ void CheckBox::msw_rescale() void SpinCtrl::BUILD() { - auto size = wxSize(wxDefaultSize); + auto size = wxSize(def_width() * m_em_unit, wxDefaultCoord); if (m_opt.height >= 0) size.SetHeight(m_opt.height*m_em_unit); if (m_opt.width >= 0) size.SetWidth(m_opt.width*m_em_unit); @@ -690,16 +707,16 @@ void SpinCtrl::propagate_value() suppress_propagation = false; } -void SpinCtrl::msw_rescale() +void SpinCtrl::msw_rescale(bool rescale_sidetext/* = false*/) { - Field::msw_rescale(); + Field::msw_rescale(rescale_sidetext); wxSpinCtrl* field = dynamic_cast(window); - field->SetMinSize(wxSize(-1, int(1.9f*field->GetFont().GetPixelSize().y))); + field->SetMinSize(wxSize(def_width() * m_em_unit, int(1.9f*field->GetFont().GetPixelSize().y))); } void Choice::BUILD() { - wxSize size(m_width * m_em_unit, -1); + wxSize size(def_width_wider() * m_em_unit, wxDefaultCoord); if (m_opt.height >= 0) size.SetHeight(m_opt.height*m_em_unit); if (m_opt.width >= 0) size.SetWidth(m_opt.width*m_em_unit); @@ -1018,7 +1035,7 @@ boost::any& Choice::get_value() return m_value; } -void Choice::msw_rescale() +void Choice::msw_rescale(bool rescale_sidetext/* = false*/) { Field::msw_rescale(); @@ -1034,7 +1051,7 @@ void Choice::msw_rescale() */ field->Clear(); wxSize size(wxDefaultSize); - size.SetWidth((m_opt.width > 0 ? m_opt.width : m_width) * m_em_unit); + size.SetWidth((m_opt.width > 0 ? m_opt.width : def_width_wider()) * m_em_unit); // Set rescaled min height to correct layout field->SetMinSize(wxSize(-1, int(1.5f*field->GetFont().GetPixelSize().y + 0.5f))); @@ -1065,7 +1082,7 @@ void Choice::msw_rescale() void ColourPicker::BUILD() { - auto size = wxSize(wxDefaultSize); + auto size = wxSize(def_width() * m_em_unit, wxDefaultCoord); if (m_opt.height >= 0) size.SetHeight(m_opt.height*m_em_unit); if (m_opt.width >= 0) size.SetWidth(m_opt.width*m_em_unit); @@ -1133,11 +1150,16 @@ boost::any& ColourPicker::get_value() return m_value; } -void ColourPicker::msw_rescale() +void ColourPicker::msw_rescale(bool rescale_sidetext/* = false*/) { Field::msw_rescale(); - wxColourPickerCtrl* field = dynamic_cast(window); + wxColourPickerCtrl* field = dynamic_cast(window); + auto size = wxSize(def_width() * m_em_unit, wxDefaultCoord); + if (m_opt.height >= 0) size.SetHeight(m_opt.height * m_em_unit); + if (m_opt.width >= 0) size.SetWidth(m_opt.width * m_em_unit); + field->SetMinSize(size); + if (field->GetColour() == wxTransparentColour) set_undef_value(field); } @@ -1189,7 +1211,7 @@ void PointCtrl::BUILD() y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y)); } -void PointCtrl::msw_rescale() +void PointCtrl::msw_rescale(bool rescale_sidetext/* = false*/) { Field::msw_rescale(); @@ -1259,7 +1281,7 @@ void StaticText::BUILD() temp->SetToolTip(get_tooltip_text(legend)); } -void StaticText::msw_rescale() +void StaticText::msw_rescale(bool rescale_sidetext/* = false*/) { Field::msw_rescale(); diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index b3cbf573f6..bd325005e2 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -220,17 +220,16 @@ public: m_side_text = side_text; } - virtual void msw_rescale() { - m_Undo_to_sys_btn->msw_rescale(); - m_Undo_btn->msw_rescale(); - - // update em_unit value - m_em_unit = em_unit(m_parent); - } + virtual void msw_rescale(bool rescale_sidetext = false); bool get_enter_pressed() const { return bEnterPressed; } void set_enter_pressed(bool pressed) { bEnterPressed = pressed; } + // Values of width to "systematic" alignments of fields + static int def_width() { return 7; } + static int def_width_wider() { return 14; } + static int def_width_thinner() { return 4; } + protected: RevertButton* m_Undo_btn = nullptr; // Bitmap and Tooltip text for m_Undo_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one. @@ -297,7 +296,7 @@ public: boost::any& get_value() override; - void msw_rescale() override; + void msw_rescale(bool rescale_sidetext = false) override; virtual void enable(); virtual void disable(); @@ -325,7 +324,7 @@ public: void set_na_value() override; boost::any& get_value() override; - void msw_rescale() override; + void msw_rescale(bool rescale_sidetext = false) override; void enable() override { dynamic_cast(window)->Enable(); } void disable() override { dynamic_cast(window)->Disable(); } @@ -368,7 +367,7 @@ public: return m_value = value; } - void msw_rescale() override; + void msw_rescale(bool rescale_sidetext = false) override; void enable() override { dynamic_cast(window)->Enable(); } void disable() override { dynamic_cast(window)->Disable(); } @@ -377,7 +376,6 @@ public: class Choice : public Field { using Field::Field; - int m_width{ 15 }; public: Choice(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} Choice(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} @@ -397,7 +395,7 @@ public: void set_values(const std::vector &values); boost::any& get_value() override; - void msw_rescale() override; + void msw_rescale(bool rescale_sidetext = false) override; void enable() override { dynamic_cast(window)->Enable(); }; void disable() override{ dynamic_cast(window)->Disable(); }; @@ -423,7 +421,7 @@ public: } void set_value(const boost::any& value, bool change_event = false) override; boost::any& get_value() override; - void msw_rescale() override; + void msw_rescale(bool rescale_sidetext = false) override; void enable() override { dynamic_cast(window)->Enable(); }; void disable() override{ dynamic_cast(window)->Disable(); }; @@ -448,7 +446,7 @@ public: void set_value(const boost::any& value, bool change_event = false); boost::any& get_value() override; - void msw_rescale() override; + void msw_rescale(bool rescale_sidetext = false) override; void enable() override { x_textctrl->Enable(); @@ -482,7 +480,7 @@ public: boost::any& get_value()override { return m_value; } - void msw_rescale() override; + void msw_rescale(bool rescale_sidetext = false) override; void enable() override { dynamic_cast(window)->Enable(); }; void disable() override{ dynamic_cast(window)->Disable(); }; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7cceb1a5f1..b5319a2f18 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -60,10 +60,13 @@ #include #include #include +#include "DoubleSlider.hpp" #if ENABLE_RENDER_STATISTICS #include #endif // ENABLE_RENDER_STATISTICS +#include + static const float TRACKBALLSIZE = 0.8f; static const float DEFAULT_BG_DARK_COLOR[3] = { 0.478f, 0.478f, 0.478f }; @@ -682,7 +685,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool case ObjectOutside : text = L("An object outside the print area was detected"); break; case ToolpathOutside : text = L("A toolpath outside the print area was detected"); break; case SlaSupportsOutside : text = L("SLA supports outside the print area were detected"); break; - case SomethingNotShown : text = L("Some objects are not visible when editing supports"); break; + case SomethingNotShown : text = L("Some objects are not visible"); break; case ObjectClashed: { text = L("An object outside the print area was detected\n" "Resolve the current problem to continue slicing"); @@ -892,7 +895,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D std::vector& colors, std::vector& cp_legend_items) { - std::vector custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; + std::vector custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes; const int extruders_cnt = wxGetApp().extruders_edited_cnt(); if (extruders_cnt == 1) @@ -910,7 +913,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D { if (custom_code.gcode != ColorChangeCode) continue; - auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.print_z - DoubleSlider::epsilon()); + auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.print_z - Slic3r::DoubleSlider::epsilon()); if (lower_b == print_zs.end()) continue; @@ -1229,6 +1232,139 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const } } +void GLCanvas3D::Labels::render(const std::vector& sorted_instances) const +{ + if (!m_enabled || !is_shown()) + return; + + const Camera& camera = m_canvas.get_camera(); + const Model* model = m_canvas.get_model(); + if (model == nullptr) + return; + + Transform3d world_to_eye = camera.get_view_matrix(); + Transform3d world_to_screen = camera.get_projection_matrix() * world_to_eye; + const std::array& viewport = camera.get_viewport(); + + struct Owner + { + int obj_idx; + int inst_idx; + size_t model_instance_id; + BoundingBoxf3 world_box; + double eye_center_z; + std::string title; + std::string label; + std::string print_order; + bool selected; + }; + + // collect owners world bounding boxes and data from volumes + std::vector owners; + const GLVolumeCollection& volumes = m_canvas.get_volumes(); + for (const GLVolume* volume : volumes.volumes) { + int obj_idx = volume->object_idx(); + if (0 <= obj_idx && obj_idx < (int)model->objects.size()) { + int inst_idx = volume->instance_idx(); + std::vector::iterator it = std::find_if(owners.begin(), owners.end(), [obj_idx, inst_idx](const Owner& owner) { + return (owner.obj_idx == obj_idx) && (owner.inst_idx == inst_idx); + }); + if (it != owners.end()) { + it->world_box.merge(volume->transformed_bounding_box()); + it->selected &= volume->selected; + } else { + const ModelObject* model_object = model->objects[obj_idx]; + Owner owner; + owner.obj_idx = obj_idx; + owner.inst_idx = inst_idx; + owner.model_instance_id = model_object->instances[inst_idx]->id().id; + owner.world_box = volume->transformed_bounding_box(); + owner.title = "object" + std::to_string(obj_idx) + "_inst##" + std::to_string(inst_idx); + owner.label = model_object->name; + if (model_object->instances.size() > 1) + owner.label += " (" + std::to_string(inst_idx + 1) + ")"; + owner.selected = volume->selected; + owners.push_back(owner); + } + } + } + + // updates print order strings + if (sorted_instances.size() > 1) { + for (int i = 0; i < sorted_instances.size(); ++i) { + size_t id = sorted_instances[i]->id().id; + std::vector::iterator it = std::find_if(owners.begin(), owners.end(), [id](const Owner& owner) { + return owner.model_instance_id == id; + }); + if (it != owners.end()) + it->print_order = _(L("Seq.")) + "#: " + std::to_string(i + 1); + } + } + + // calculate eye bounding boxes center zs + for (Owner& owner : owners) { + owner.eye_center_z = (world_to_eye * owner.world_box.center())(2); + } + + // sort owners by center eye zs and selection + std::sort(owners.begin(), owners.end(), [](const Owner& owner1, const Owner& owner2) { + if (!owner1.selected && owner2.selected) + return true; + else if (owner1.selected && !owner2.selected) + return false; + else + return (owner1.eye_center_z < owner2.eye_center_z); + }); + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + + // render info windows + for (const Owner& owner : owners) { + Vec3d screen_box_center = world_to_screen * owner.world_box.center(); + float x = 0.0f; + float y = 0.0f; + if (camera.get_type() == Camera::Perspective) { + x = (0.5f + 0.001f * 0.5f * (float)screen_box_center(0)) * viewport[2]; + y = (0.5f - 0.001f * 0.5f * (float)screen_box_center(1)) * viewport[3]; + } else { + x = (0.5f + 0.5f * (float)screen_box_center(0)) * viewport[2]; + y = (0.5f - 0.5f * (float)screen_box_center(1)) * viewport[3]; + } + + if (x < 0.0f || viewport[2] < x || y < 0.0f || viewport[3] < y) + continue; + + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, owner.selected ? 3.0f : 1.5f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleColor(ImGuiCol_Border, owner.selected ? ImVec4(0.757f, 0.404f, 0.216f, 1.0f) : ImVec4(0.75f, 0.75f, 0.75f, 1.0f)); + imgui.set_next_window_pos(x, y, ImGuiCond_Always, 0.5f, 0.5f); + imgui.begin(owner.title, ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + float win_w = ImGui::GetWindowWidth(); + float label_len = imgui.calc_text_size(owner.label).x; + ImGui::SetCursorPosX(0.5f * (win_w - label_len)); + ImGui::AlignTextToFramePadding(); + imgui.text(owner.label); + + if (!owner.print_order.empty()) + { + ImGui::Separator(); + float po_len = imgui.calc_text_size(owner.print_order).x; + ImGui::SetCursorPosX(0.5f * (win_w - po_len)); + ImGui::AlignTextToFramePadding(); + imgui.text(owner.print_order); + } + + // force re-render while the windows gets to its final size (it takes several frames) + if (ImGui::GetWindowContentRegionWidth() + 2.0f * ImGui::GetStyle().WindowPadding.x != ImGui::CalcWindowExpectedSize(ImGui::GetCurrentWindow()).x) + m_canvas.request_extra_frame(); + + imgui.end(); + ImGui::PopStyleColor(); + ImGui::PopStyleVar(2); + } +} + wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent); @@ -1240,6 +1376,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_ROTATED, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_FORCE_UPDATE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_ROTATED, Vec3dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); @@ -1255,6 +1392,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_RELOAD_FROM_DISK, SimpleEvent); #if ENABLE_THUMBNAIL_GENERATOR const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25; @@ -1296,6 +1434,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar , m_show_picking_texture(false) #endif // ENABLE_RENDER_PICKING_PASS , m_render_sla_auxiliaries(true) + , m_labels(*this) { if (m_canvas != nullptr) { m_timer.SetOwner(m_canvas); @@ -1437,14 +1576,14 @@ int GLCanvas3D::check_volumes_outside_state() const void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx) { + m_render_sla_auxiliaries = visible; + for (GLVolume* vol : m_volumes.volumes) { if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo) && (instance_idx == -1 || vol->composite_id.instance_id == instance_idx) && vol->composite_id.volume_id < 0) vol->is_active = visible; } - - m_render_sla_auxiliaries = visible; } void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject* mo, int instance_idx) @@ -1661,14 +1800,9 @@ void GLCanvas3D::zoom_to_selection() void GLCanvas3D::select_view(const std::string& direction) { -#if ENABLE_6DOF_CAMERA m_camera.select_view(direction); if (m_canvas != nullptr) m_canvas->Refresh(); -#else - if (m_camera.select_view(direction) && (m_canvas != nullptr)) - m_canvas->Refresh(); -#endif // ENABLE_6DOF_CAMERA } void GLCanvas3D::update_volumes_colors_by_extruder() @@ -1709,6 +1843,11 @@ void GLCanvas3D::render() } const Size& cnv_size = get_canvas_size(); + // Probably due to different order of events on Linux/GTK2, when one switched from 3D scene + // to preview, this was called before canvas had its final size. It reported zero width + // and the viewport was set incorrectly, leading to tripping glAsserts further down + // the road (in apply_projection). That's why the minimum size is forced to 10. + m_camera.apply_viewport(0, 0, std::max(10u, (unsigned int)cnv_size.get_width()), std::max(10u, (unsigned int)cnv_size.get_height())); if (m_camera.requires_zoom_to_bed) { @@ -1725,13 +1864,6 @@ void GLCanvas3D::render() GLfloat position_top[4] = { -0.5f, -0.5f, 1.0f, 0.0f }; glsafe(::glLightfv(GL_LIGHT0, GL_POSITION, position_top)); -#if !ENABLE_6DOF_CAMERA - float theta = m_camera.get_theta(); - if (theta > 180.f) - // absolute value of the rotation - theta = 360.f - theta; -#endif // !ENABLE_6DOF_CAMERA - wxGetApp().imgui()->new_frame(); if (m_picking_enabled) @@ -1755,11 +1887,7 @@ void GLCanvas3D::render() _render_objects(); _render_sla_slices(); _render_selection(); -#if ENABLE_6DOF_CAMERA _render_bed(!m_camera.is_looking_downward(), true); -#else - _render_bed(theta, true); -#endif // ENABLE_6DOF_CAMERA #if ENABLE_RENDER_SELECTION_CENTER _render_selection_center(); @@ -1968,8 +2096,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re }; // SLA steps to pull the preview meshes for. - typedef std::array SLASteps; - SLASteps sla_steps = { slaposSupportTree, slaposPad }; + typedef std::array SLASteps; + SLASteps sla_steps = { slaposDrillHoles, slaposSupportTree, slaposPad }; struct SLASupportState { std::array::value> step; }; @@ -2016,7 +2144,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // Consider the DONE step without a valid mesh as invalid for the purpose // of mesh visualization. state.step[istep].state = PrintStateBase::INVALID; - else + else if (sla_steps[istep] != slaposDrillHoles) for (const ModelInstance* model_instance : print_object->model_object()->instances) // Only the instances, which are currently printable, will have the SLA support structures kept. // The instances outside the print bed will have the GLVolumes of their support structures released. @@ -2029,7 +2157,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower); std::sort(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower); - // Release all ModelVolume based GLVolumes not found in the current Model. + // Release all ModelVolume based GLVolumes not found in the current Model. Find the GLVolume of a hollowed mesh. for (size_t volume_id = 0; volume_id < m_volumes.volumes.size(); ++volume_id) { GLVolume* volume = m_volumes.volumes[volume_id]; ModelVolumeState key(volume); @@ -2111,6 +2239,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (it_old_volume != deleted_volumes.end() && it_old_volume->composite_id == it->composite_id) // If a volume changed its ObjectID, but it reuses a GLVolume's CompositeID, maintain its selection. map_glvolume_old_to_new[it_old_volume->volume_idx] = m_volumes.volumes.size(); + // Note the index of the loaded volume, so that we can reload the main model GLVolume with the hollowed mesh + // later in this function. + it->volume_idx = m_volumes.volumes.size(); m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_initialized); m_volumes.volumes.back()->geometry_id = key.geometry_id; update_object_list = true; @@ -2139,8 +2270,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re const ModelObject *model_object = print_object->model_object(); // Find an index of the ModelObject int object_idx; - if (std::all_of(state.step.begin(), state.step.end(), [](const PrintStateBase::StateWithTimeStamp &state){ return state.state != PrintStateBase::DONE; })) - continue; // There may be new SLA volumes added to the scene for this print_object. // Find the object index of this print_object in the Model::objects list. auto it = std::find(sla_print->model().objects.begin(), sla_print->model().objects.end(), model_object); @@ -2159,29 +2288,53 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re assert(it != model_object->instances.end()); int instance_idx = it - model_object->instances.begin(); for (size_t istep = 0; istep < sla_steps.size(); ++ istep) - if (state.step[istep].state == PrintStateBase::DONE) { - ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); - auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); - assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id); - if (it->new_geometry()) { + if (sla_steps[istep] == slaposDrillHoles) { + // Hollowing is a special case, where the mesh from the backend is being loaded into the 1st volume of an instance, + // not into its own GLVolume. + // There shall always be such a GLVolume allocated. + ModelVolumeState key(model_object->volumes.front()->id(), instance.instance_id); + auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); + assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); + assert(!it->new_geometry()); + GLVolume &volume = *m_volumes.volumes[it->volume_idx]; + if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) { + // The backend either produced a new hollowed mesh, or it invalidated the one that the front end has seen. + volume.indexed_vertex_array.release_geometry(); + if (state.step[istep].state == PrintStateBase::DONE) { + TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles); + assert(! mesh.empty()); + mesh.transform(sla_print->sla_trafo(*m_model->objects[volume.object_idx()]).inverse()); + volume.indexed_vertex_array.load_mesh(mesh); + } else { + // Reload the original volume. + volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); + } + volume.finalize_geometry(true); + } + //FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable + // to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables + // of various concenrs (model vs. 3D print path). + volume.offsets = { state.step[istep].timestamp }; + } else if (state.step[istep].state == PrintStateBase::DONE) { + // Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created. + ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); + auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); + assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id); + if (it->new_geometry()) { // This can be an SLA support structure that should not be rendered (in case someone used undo // to revert to before it was generated). If that's the case, we should not generate anything. if (model_object->sla_points_status != sla::PointsStatus::NoPoints) instances[istep].emplace_back(std::pair(instance_idx, print_instance_idx)); else shift_zs[object_idx] = 0.; + } else { + // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. + m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); + m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); } - else { - // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. - m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); - m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); - } } } -// // stores the current volumes count -// size_t volumes_count = m_volumes.volumes.size(); - for (size_t istep = 0; istep < sla_steps.size(); ++istep) if (!instances[istep].empty()) m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized); @@ -2410,7 +2563,7 @@ void GLCanvas3D::load_sla_preview() } } -void GLCanvas3D::load_preview(const std::vector& str_tool_colors, const std::vector& color_print_values) +void GLCanvas3D::load_preview(const std::vector& str_tool_colors, const std::vector& color_print_values) { const Print *print = this->fff_print(); if (print == nullptr) @@ -2621,6 +2774,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE)); break; case WXK_ESCAPE: { deselect_all(); break; } + case WXK_F5: { post_event(SimpleEvent(EVT_GLCANVAS_RELOAD_FROM_DISK)); break; } case '0': { select_view("iso"); break; } case '1': { select_view("top"); break; } case '2': { select_view("bottom"); break; } @@ -2645,6 +2799,8 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 'a': { post_event(SimpleEvent(EVT_GLCANVAS_ARRANGE)); break; } case 'B': case 'b': { zoom_to_bed(); break; } + case 'E': + case 'e': { m_labels.show(!m_labels.is_shown()); m_dirty = true; break; } case 'I': case 'i': { _update_camera_zoom(1.0); break; } case 'K': @@ -2666,8 +2822,127 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) } } +class TranslationProcessor +{ + using UpAction = std::function; + using DownAction = std::function; + + UpAction m_up_action{ nullptr }; + DownAction m_down_action{ nullptr }; + + bool m_running{ false }; + Vec3d m_direction{ Vec3d::UnitX() }; + +public: + TranslationProcessor(UpAction up_action, DownAction down_action) + : m_up_action(up_action), m_down_action(down_action) + { + } + + void process(wxKeyEvent& evt) + { + const int keyCode = evt.GetKeyCode(); + wxEventType type = evt.GetEventType(); + if (type == wxEVT_KEY_UP) { + switch (keyCode) + { + case WXK_NUMPAD_LEFT: case WXK_LEFT: + case WXK_NUMPAD_RIGHT: case WXK_RIGHT: + case WXK_NUMPAD_UP: case WXK_UP: + case WXK_NUMPAD_DOWN: case WXK_DOWN: + { + m_running = false; + m_up_action(); + break; + } + default: { break; } + } + } + else if (type == wxEVT_KEY_DOWN) { + bool apply = false; + + switch (keyCode) + { + case WXK_SHIFT: + { + if (m_running) + apply = true; + + break; + } + case WXK_NUMPAD_LEFT: + case WXK_LEFT: + { + m_direction = -Vec3d::UnitX(); + apply = true; + break; + } + case WXK_NUMPAD_RIGHT: + case WXK_RIGHT: + { + m_direction = Vec3d::UnitX(); + apply = true; + break; + } + case WXK_NUMPAD_UP: + case WXK_UP: + { + m_direction = Vec3d::UnitY(); + apply = true; + break; + } + case WXK_NUMPAD_DOWN: + case WXK_DOWN: + { + m_direction = -Vec3d::UnitY(); + apply = true; + break; + } + default: { break; } + } + + if (apply) { + m_running = true; + m_down_action(m_direction, evt.ShiftDown(), evt.CmdDown()); + } + } + } +}; + void GLCanvas3D::on_key(wxKeyEvent& evt) { + static TranslationProcessor translationProcessor( + [this]() { + do_move(L("Gizmo-Move")); + m_gizmos.update_data(); + + wxGetApp().obj_manipul()->set_dirty(); + // Let the plater know that the dragging finished, so a delayed refresh + // of the scene with the background processing data should be performed. + post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); + // updates camera target constraints + refresh_camera_scene_box(); + m_dirty = true; + }, + [this](const Vec3d& direction, bool slow, bool camera_space) { + m_selection.start_dragging(); + double multiplier = slow ? 1.0 : 10.0; + + Vec3d displacement; + if (camera_space) + { + Eigen::Matrix inv_view_3x3 = m_camera.get_view_matrix().inverse().matrix().block(0, 0, 3, 3); + displacement = multiplier * (inv_view_3x3 * direction); + displacement(2) = 0.0; + } + else + displacement = multiplier * direction; + + m_selection.translate(displacement); + m_dirty = true; + } + ); + const int keyCode = evt.GetKeyCode(); auto imgui = wxGetApp().imgui(); @@ -2686,6 +2961,8 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) } else if (keyCode == WXK_SHIFT) { + translationProcessor.process(evt); + if (m_picking_enabled && m_rectangle_selection.is_dragging()) { _update_selection_from_hover(); @@ -2708,11 +2985,37 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) } else if (keyCode == WXK_CONTROL) m_dirty = true; + else if (m_gizmos.is_enabled() && !m_selection.is_empty()) { + translationProcessor.process(evt); + + switch (keyCode) + { + case WXK_NUMPAD_PAGEUP: case WXK_PAGEUP: + case WXK_NUMPAD_PAGEDOWN: case WXK_PAGEDOWN: + { + do_rotate(L("Gizmo-Rotate")); + m_gizmos.update_data(); + + wxGetApp().obj_manipul()->set_dirty(); + // Let the plater know that the dragging finished, so a delayed refresh + // of the scene with the background processing data should be performed. + post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); + // updates camera target constraints + refresh_camera_scene_box(); + m_dirty = true; + + break; + } + default: { break; } + } + } } else if (evt.GetEventType() == wxEVT_KEY_DOWN) { m_tab_down = keyCode == WXK_TAB && !evt.HasAnyModifiers(); if (keyCode == WXK_SHIFT) { + translationProcessor.process(evt); + if (m_picking_enabled && (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)) { m_mouse.ignore_left_up = false; @@ -2729,14 +3032,35 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) } else if (keyCode == WXK_CONTROL) m_dirty = true; - // DoubleSlider navigation in Preview - else if (keyCode == WXK_LEFT || - keyCode == WXK_RIGHT || - keyCode == WXK_UP || - keyCode == WXK_DOWN ) + else if (m_gizmos.is_enabled() && !m_selection.is_empty()) { - if (dynamic_cast(m_canvas->GetParent()) != nullptr) - post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, evt)); + auto do_rotate = [this](double angle_z_rad) { + m_selection.start_dragging(); + m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint)); + m_dirty = true; +// wxGetApp().obj_manipul()->set_dirty(); + }; + + translationProcessor.process(evt); + + switch (keyCode) + { + case WXK_NUMPAD_PAGEUP: case WXK_PAGEUP: { do_rotate(0.25 * M_PI); break; } + case WXK_NUMPAD_PAGEDOWN: case WXK_PAGEDOWN: { do_rotate(-0.25 * M_PI); break; } + default: { break; } + } + } + else if (!m_gizmos.is_enabled()) + { + // DoubleSlider navigation in Preview + if (keyCode == WXK_LEFT || + keyCode == WXK_RIGHT || + keyCode == WXK_UP || + keyCode == WXK_DOWN) + { + if (dynamic_cast(m_canvas->GetParent()) != nullptr) + post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, evt)); + } } } } @@ -3076,11 +3400,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag if (m_selection.contains_volume(get_first_hover_volume_idx())) { -#if ENABLE_6DOF_CAMERA if (std::abs(m_camera.get_dir_forward()(2)) < EPSILON) -#else - if (m_camera.get_theta() == 90.0f) -#endif // ENABLE_6DOF_CAMERA { // side view -> move selected volumes orthogonally to camera view direction Linef3 ray = mouse_ray(pos); @@ -3140,18 +3460,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_hover_volume_idxs.empty() && m_mouse.is_start_position_3D_defined()) { const Vec3d& orig = m_mouse.drag.start_position_3D; -#if ENABLE_6DOF_CAMERA double x = Geometry::deg2rad(pos(0) - orig(0)) * (double)TRACKBALLSIZE; double y = Geometry::deg2rad(pos(1) - orig(1)) * (double)TRACKBALLSIZE; if (wxGetApp().plater()->get_mouse3d_controller().is_running() || (wxGetApp().app_config->get("use_free_camera") == "1")) m_camera.rotate_local_around_target(Vec3d(y, x, 0.0)); else - m_camera.rotate_on_sphere(x, y); -#else - float sign = m_camera.inverted_phi ? -1.0f : 1.0f; - m_camera.phi += sign * ((float)pos(0) - (float)orig(0)) * TRACKBALLSIZE; - m_camera.set_theta(m_camera.get_theta() - ((float)pos(1) - (float)orig(1)) * TRACKBALLSIZE, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); -#endif // ENABLE_6DOF_CAMERA + m_camera.rotate_on_sphere(x, y, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); + m_dirty = true; } m_mouse.drag.start_position_3D = Vec3d((double)pos(0), (double)pos(1), 0.0); @@ -3201,11 +3516,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (!evt.ShiftDown() && m_picking_enabled) deselect_all(); } -#if !ENABLE_6DOF_CAMERA - else if (evt.LeftUp() && m_mouse.dragging) - // Flips X mouse deltas if bed is upside down - m_camera.inverted_phi = (m_camera.get_dir_up()(2) < 0.0); -#endif // !ENABLE_6DOF_CAMERA else if (evt.RightUp()) { m_mouse.position = pos.cast(); @@ -3810,11 +4120,9 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool Camera camera; camera.set_type(Camera::Ortho); -#if ENABLE_6DOF_CAMERA camera.set_scene_box(scene_bounding_box()); -#endif // ENABLE_6DOF_CAMERA - camera.zoom_to_volumes(visible_volumes, thumbnail_data.width, thumbnail_data.height); camera.apply_viewport(0, 0, thumbnail_data.width, thumbnail_data.height); + camera.zoom_to_volumes(visible_volumes); camera.apply_view_matrix(); double near_z = -1.0; @@ -3863,11 +4171,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool glsafe(::glDisable(GL_DEPTH_TEST)); if (show_bed) -#if ENABLE_6DOF_CAMERA _render_bed(!camera.is_looking_downward(), false); -#else - _render_bed(camera.get_theta(), false); -#endif // ENABLE_6DOF_CAMERA if (transparent_background) glsafe(::glClearColor(1.0f, 1.0f, 1.0f, 1.0f)); @@ -4404,9 +4708,6 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) // ensures that this canvas is current _set_current(); - - // updates camera - m_camera.apply_viewport(0, 0, w, h); } BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_bed_model) const @@ -4430,8 +4731,7 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be #if ENABLE_THUMBNAIL_GENERATOR void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box, double margin_factor) { - const Size& cnv_size = get_canvas_size(); - m_camera.zoom_to_box(box, cnv_size.get_width(), cnv_size.get_height(), margin_factor); + m_camera.zoom_to_box(box, margin_factor); m_dirty = true; } #else @@ -4734,6 +5034,17 @@ void GLCanvas3D::_render_overlays() const if ((m_layers_editing.last_object_id >= 0) && (m_layers_editing.object_max_z() > 0.0f)) m_layers_editing.render_overlay(*this); + const ConfigOptionBool* opt = dynamic_cast(m_config->option("complete_objects")); + bool sequential_print = opt != nullptr && opt->value; + std::vector sorted_instances; + if (sequential_print) { + for (ModelObject* model_object : m_model->objects) + for (ModelInstance* model_instance : model_object->instances) { + sorted_instances.push_back(model_instance); + } + } + m_labels.render(sorted_instances); + glsafe(::glPopMatrix()); } @@ -5245,13 +5556,13 @@ void GLCanvas3D::_load_print_toolpaths() volume->indexed_vertex_array.finalize_geometry(m_initialized); } -void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector& str_tool_colors, const std::vector& color_print_values) +void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector& str_tool_colors, const std::vector& color_print_values) { std::vector tool_colors = _parse_colors(str_tool_colors); struct Ctxt { - const Points *shifted_copies; + const PrintInstances *shifted_copies; std::vector layers; bool has_perimeters; bool has_infill; @@ -5259,7 +5570,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c const std::vector* tool_colors; bool is_single_material_print; int extruders_cnt; - const std::vector* color_print_values; + const std::vector* color_print_values; static const float* color_perimeters() { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow static const float* color_infill() { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish @@ -5274,7 +5585,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c // For coloring by a color_print(M600), return a parsed color. bool color_by_color_print() const { return color_print_values!=nullptr; } const size_t color_print_color_idx_by_layer_idx(const size_t layer_idx) const { - const Model::CustomGCode value{layers[layer_idx]->print_z + EPSILON, "", 0, ""}; + const CustomGCode::Item value{layers[layer_idx]->print_z + EPSILON, "", 0, ""}; auto it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value); return (it - color_print_values->begin()) % number_tools(); } @@ -5284,7 +5595,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c const coordf_t print_z = layers[layer_idx]->print_z; auto it = std::find_if(color_print_values->begin(), color_print_values->end(), - [print_z](const Model::CustomGCode& code) + [print_z](const CustomGCode::Item& code) { return fabs(code.print_z - print_z) < EPSILON; }); if (it != color_print_values->end()) { @@ -5305,7 +5616,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } } - const Model::CustomGCode value{print_z + EPSILON, "", 0, ""}; + const CustomGCode::Item value{print_z + EPSILON, "", 0, ""}; it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value); while (it != color_print_values->begin()) { @@ -5325,7 +5636,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } private: - int get_m600_color_idx(std::vector::const_iterator it) const + int get_m600_color_idx(std::vector::const_iterator it) const { int shift = 0; while (it != color_print_values->begin()) { @@ -5336,7 +5647,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c return extruders_cnt + shift; } - int get_color_idx_for_tool_change(std::vector::const_iterator it, const int extruder) const + int get_color_idx_for_tool_change(std::vector::const_iterator it, const int extruder) const { const int current_extruder = it->extruder == 0 ? extruder : it->extruder; if (number_tools() == extruders_cnt + 1) // there is no one "M600" @@ -5352,7 +5663,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c return std::min(extruders_cnt - 1, std::max(current_extruder - 1, 0)); } - int get_color_idx_for_color_change(std::vector::const_iterator it, const int extruder) const + int get_color_idx_for_color_change(std::vector::const_iterator it, const int extruder) const { if (extruders_cnt == 1) return get_m600_color_idx(it); @@ -5384,7 +5695,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c ctxt.is_single_material_print = this->fff_print()->extruders().size()==1; ctxt.extruders_cnt = wxGetApp().extruders_edited_cnt(); - ctxt.shifted_copies = &print_object.copies(); + ctxt.shifted_copies = &print_object.instances(); // order layers by print_z { @@ -5473,7 +5784,8 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c vol->offsets.push_back(vol->indexed_vertex_array.quad_indices.size()); vol->offsets.push_back(vol->indexed_vertex_array.triangle_indices.size()); } - for (const Point © : *ctxt.shifted_copies) { + for (const PrintInstance &instance : *ctxt.shifted_copies) { + const Point © = instance.shift; for (const LayerRegion *layerm : layer->regions()) { if (is_selected_separate_extruder) { @@ -6047,7 +6359,7 @@ void GLCanvas3D::_load_sla_shells() v.indexed_vertex_array.finalize_geometry(this->m_initialized); v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled; v.composite_id.volume_id = volume_id; - v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0)); + v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0)); v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation)); v.set_instance_mirror(X, object.is_left_handed() ? -1. : 1.); v.set_convex_hull(mesh.convex_hull_3d()); @@ -6058,7 +6370,7 @@ void GLCanvas3D::_load_sla_shells() if (obj->is_step_done(slaposSliceSupports)) { unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); for (const SLAPrintObject::Instance& instance : obj->instances()) { - add_volume(*obj, 0, instance, obj->transformed_mesh(), GLVolume::MODEL_COLOR[0], true); + add_volume(*obj, 0, instance, obj->get_mesh_to_print(), GLVolume::MODEL_COLOR[0], true); // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when // through the update_volumes_colors_by_extruder() call. m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d4386f7f39..9ae1278800 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -91,6 +91,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); // data: +1 => increase, -1 => decrease wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_FORCE_UPDATE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_ROTATED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent); @@ -108,6 +109,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_RELOAD_FROM_DISK, SimpleEvent); class GLCanvas3D { @@ -373,6 +375,20 @@ private: }; #endif // ENABLE_RENDER_STATISTICS + class Labels + { + bool m_enabled{ false }; + bool m_shown{ false }; + GLCanvas3D& m_canvas; + + public: + explicit Labels(GLCanvas3D& canvas) : m_canvas(canvas) {} + void enable(bool enable) { m_enabled = enable; } + void show(bool show) { m_shown = m_enabled ? show : false; } + bool is_shown() const { return m_shown; } + void render(const std::vector& sorted_instances) const; + }; + public: enum ECursorType : unsigned char { @@ -450,6 +466,8 @@ private: mutable int m_imgui_undo_redo_hovered_pos{ -1 }; int m_selected_extruder; + Labels m_labels; + public: GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar); ~GLCanvas3D(); @@ -465,6 +483,7 @@ public: void set_as_dirty(); unsigned int get_volumes_count() const; + const GLVolumeCollection& get_volumes() const { return m_volumes; } void reset_volumes(); int check_volumes_outside_state() const; @@ -476,6 +495,7 @@ public: void set_config(const DynamicPrintConfig* config); void set_process(BackgroundSlicingProcess* process); void set_model(Model* model); + const Model* get_model() const { return m_model; } const Selection& get_selection() const { return m_selection; } Selection& get_selection() { return m_selection; } @@ -499,6 +519,7 @@ public: void set_color_by(const std::string& value); const Camera& get_camera() const { return m_camera; } + const Shader& get_shader() const { return m_shader; } Camera& get_camera() { return m_camera; } BoundingBoxf3 volumes_bounding_box() const; @@ -522,6 +543,7 @@ public: void enable_main_toolbar(bool enable); void enable_undoredo_toolbar(bool enable); void enable_dynamic_background(bool enable); + void enable_labels(bool enable) { m_labels.enable(enable); } void allow_multisample(bool allow); void zoom_to_bed(); @@ -557,7 +579,7 @@ public: void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors); void load_sla_preview(); - void load_preview(const std::vector& str_tool_colors, const std::vector& color_print_values); + void load_preview(const std::vector& str_tool_colors, const std::vector& color_print_values); void bind_event_handlers(); void unbind_event_handlers(); @@ -643,6 +665,9 @@ public: void mouse_up_cleanup(); + bool are_labels_shown() const { return m_labels.is_shown(); } + void show_labels(bool show) { m_labels.show(show); } + private: bool _is_shown_on_screen() const; @@ -720,7 +745,7 @@ private: // Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes, // one for perimeters, one for infill and one for supports. void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector& str_tool_colors, - const std::vector& color_print_values); + const std::vector& color_print_values); // Create 3D thick extrusion lines for wipe tower extrusions void _load_wipe_tower_toolpaths(const std::vector& str_tool_colors); diff --git a/src/slic3r/GUI/GLCanvas3DManager.cpp b/src/slic3r/GUI/GLCanvas3DManager.cpp index 3594e85a42..a5d75d6012 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index 4ac69d71ff..f178ddc73c 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -168,25 +168,15 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vectorwidth, image->height); -#if ENABLE_MODIFIED_TOOLBAR_TEXTURES // offset by 1 to leave the first pixel empty (both in x and y) nsvgRasterize(rast, image, 1, 1, scale, sprite_data.data(), sprite_size_px, sprite_size_px, sprite_stride); -#else - nsvgRasterize(rast, image, 0, 0, scale, sprite_data.data(), sprite_size_px, sprite_size_px, sprite_stride); -#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES // makes white only copy of the sprite ::memcpy((void*)sprite_white_only_data.data(), (const void*)sprite_data.data(), sprite_bytes); @@ -250,11 +236,7 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector& state : states) { @@ -273,7 +255,6 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vectorGLTexture::Quad_UVs { assert((tex_width != 0) && (tex_height != 0)); @@ -112,9 +111,6 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b }; GLTexture::render_sub_texture(tex_id, left, right, bottom, top, uvs(tex_width, tex_height, icon_size)); -#else - GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(tex_width, tex_height, icon_size)); -#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES if (is_pressed()) { @@ -125,29 +121,6 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b } } -#if !ENABLE_MODIFIED_TOOLBAR_TEXTURES -GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const -{ - GLTexture::Quad_UVs uvs; - - float inv_tex_width = (tex_width != 0) ? 1.0f / (float)tex_width : 0.0f; - float inv_tex_height = (tex_height != 0) ? 1.0f / (float)tex_height : 0.0f; - - float scaled_icon_width = (float)icon_size * inv_tex_width; - float scaled_icon_height = (float)icon_size * inv_tex_height; - float left = (float)m_state * scaled_icon_width; - float right = left + scaled_icon_width; - float top = (float)m_data.sprite_id * scaled_icon_height; - float bottom = top + scaled_icon_height; - uvs.left_top = { left, top }; - uvs.left_bottom = { left, bottom }; - uvs.right_bottom = { right, bottom }; - uvs.right_top = { right, top }; - - return uvs; -} -#endif // !ENABLE_MODIFIED_TOOLBAR_TEXTURES - BackgroundTexture::Metadata::Metadata() : filename("") , left(0) diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index f53b8efb99..6f24e22cf8 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -143,9 +143,6 @@ public: void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const; private: -#if !ENABLE_MODIFIED_TOOLBAR_TEXTURES - GLTexture::Quad_UVs get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const; -#endif // !ENABLE_MODIFIED_TOOLBAR_TEXTURES void set_visible(bool visible) { m_data.visible = visible; } friend class GLToolbar; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 9574cdb8e1..3a097d9abe 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -278,34 +278,23 @@ bool GUI_App::on_init_inner() RemovableDriveManager::get_instance().update(wxGetLocalTime(), true); #endif - // Preset updating & Configwizard are done after the above initializations, - // and after MainFrame is created & shown. - // The extra CallAfter() is needed because of Mac, where this is the only way - // to popup a modal dialog on start without screwing combo boxes. - // This is ugly but I honestly found no better way to do it. - // Neither wxShowEvent nor wxWindowCreateEvent work reliably. + // Preset updating & Configwizard are done after the above initializations, + // and after MainFrame is created & shown. + // The extra CallAfter() is needed because of Mac, where this is the only way + // to popup a modal dialog on start without screwing combo boxes. + // This is ugly but I honestly found no better way to do it. + // Neither wxShowEvent nor wxWindowCreateEvent work reliably. + static bool once = true; if (once) { once = false; + check_updates(false); - PresetUpdater::UpdateResult updater_result; - try { - updater_result = preset_updater->config_update(app_config->orig_version()); - if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) { - mainframe->Close(); - } else if (updater_result == PresetUpdater::R_INCOMPAT_CONFIGURED) { - app_conf_exists = true; - } - } catch (const std::exception &ex) { - show_error(nullptr, from_u8(ex.what())); - } - - CallAfter([this] { - config_wizard_startup(); - preset_updater->slic3r_update_notify(); - preset_updater->sync(preset_bundle); - }); - + CallAfter([this] { + config_wizard_startup(); + preset_updater->slic3r_update_notify(); + preset_updater->sync(preset_bundle); + }); } }); @@ -810,7 +799,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu) local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip); local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("&Configuration Snapshots")) + dots, _(L("Inspect / activate configuration snapshots"))); local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration &Snapshot")), _(L("Capture a configuration snapshot"))); - // local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates"))); + local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates"))); local_menu->AppendSeparator(); local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("&Preferences")) + dots + #ifdef __APPLE__ @@ -841,6 +830,9 @@ void GUI_App::add_config_menu(wxMenuBar *menu) case ConfigMenuWizard: run_wizard(ConfigWizard::RR_USER); break; + case ConfigMenuUpdate: + check_updates(true); + break; case ConfigMenuTakeSnapshot: // Take a configuration snapshot. if (check_unsaved_changes()) { @@ -960,8 +952,11 @@ void GUI_App::load_current_presets() this->plater()->set_printer_technology(printer_technology); for (Tab *tab : tabs_list) if (tab->supports_printer_technology(printer_technology)) { - if (tab->type() == Preset::TYPE_PRINTER) + if (tab->type() == Preset::TYPE_PRINTER) { static_cast(tab)->update_pages(); + // Mark the plater to update print bed by tab->load_current_preset() from Plater::on_config_change(). + this->plater()->force_print_bed_update(); + } tab->load_current_preset(); } } @@ -1227,6 +1222,30 @@ bool GUI_App::config_wizard_startup() return false; } +void GUI_App::check_updates(const bool verbose) +{ + + PresetUpdater::UpdateResult updater_result; + try { + updater_result = preset_updater->config_update(app_config->orig_version()); + if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) { + mainframe->Close(); + } + else if (updater_result == PresetUpdater::R_INCOMPAT_CONFIGURED) { + app_conf_exists = true; + } + else if(verbose && updater_result == PresetUpdater::R_NOOP) + { + MsgNoUpdates dlg; + dlg.ShowModal(); + } + } + catch (const std::exception & ex) { + show_error(nullptr, from_u8(ex.what())); + } + + +} // static method accepting a wxWindow object as first parameter // void warning_catcher{ // my($self, $message_dialog) = @_; diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 250d8122a8..10b09b1da7 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -200,6 +200,7 @@ private: bool select_language(); bool config_wizard_startup(); + void check_updates(const bool verbose); #ifdef __WXMSW__ void associate_3mf_files(); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 31e83cc450..11eebb0de3 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -90,19 +90,20 @@ ObjectList::ObjectList(wxWindow* parent) : // see note in PresetBundle::load_compatible_bitmaps() // ptFFF - CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap(nullptr, "layers"); - CATEGORY_ICON[L("Infill")] = create_scaled_bitmap(nullptr, "infill"); - CATEGORY_ICON[L("Support material")] = create_scaled_bitmap(nullptr, "support"); - CATEGORY_ICON[L("Speed")] = create_scaled_bitmap(nullptr, "time"); - CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap(nullptr, "funnel"); - CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap(nullptr, "funnel"); - CATEGORY_ICON[L("Wipe options")] = create_scaled_bitmap(nullptr, "funnel"); -// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap(nullptr, "skirt+brim"); -// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap(nullptr, "time"); - CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap(nullptr, "wrench"); + CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers"); + CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill"); + CATEGORY_ICON[L("Support material")] = create_scaled_bitmap("support"); + CATEGORY_ICON[L("Speed")] = create_scaled_bitmap("time"); + CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel"); + CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap("funnel"); + CATEGORY_ICON[L("Wipe options")] = create_scaled_bitmap("funnel"); +// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap("skirt+brim"); +// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap("time"); + CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wrench"); // ptSLA - CATEGORY_ICON[L("Supports")] = create_scaled_bitmap(nullptr, "support"/*"sla_supports"*/); - CATEGORY_ICON[L("Pad")] = create_scaled_bitmap(nullptr, "pad"); + CATEGORY_ICON[L("Supports")] = create_scaled_bitmap("support"/*"sla_supports"*/); + CATEGORY_ICON[L("Pad")] = create_scaled_bitmap("pad"); + CATEGORY_ICON[L("Hollowing")] = create_scaled_bitmap("hollowing"); } // create control @@ -229,9 +230,9 @@ ObjectList::ObjectList(wxWindow* parent) : // So the postponed EnsureVisible() call is planned for an item, which may not exist at the Idle processing time, if this wxEVT_SIZE // event is succeeded by a delete of the currently active item. We are trying our luck by postponing the wxEVT_SIZE triggered EnsureVisible(), // which seems to be working as of now. - this->CallAfter([this](){ this->EnsureVisible(this->GetCurrentItem()); }); + this->CallAfter([this](){ ensure_current_item_visible(); }); #else - this->EnsureVisible(this->GetCurrentItem()); + ensure_current_item_visible(); #endif e.Skip(); })); @@ -264,7 +265,7 @@ void ObjectList::create_objects_ctrl() // column ItemName(Icon+Text) of the view control: // And Icon can be consisting of several bitmaps - AppendColumn(new wxDataViewColumn(_(L("Name")), new BitmapTextRenderer(), + AppendColumn(new wxDataViewColumn(_(L("Name")), new BitmapTextRenderer(this), colName, 20*em, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE)); // column PrintableProperty (Icon) of the view control: @@ -558,10 +559,10 @@ void ObjectList::update_name_in_model(const wxDataViewItem& item) const void ObjectList::init_icons() { - m_bmp_solidmesh = ScalableBitmap(nullptr, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART) ].second); - m_bmp_modifiermesh = ScalableBitmap(nullptr, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::PARAMETER_MODIFIER)].second); - m_bmp_support_enforcer = ScalableBitmap(nullptr, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER) ].second); - m_bmp_support_blocker = ScalableBitmap(nullptr, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER) ].second); + m_bmp_solidmesh = ScalableBitmap(this, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART) ].second); + m_bmp_modifiermesh = ScalableBitmap(this, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::PARAMETER_MODIFIER)].second); + m_bmp_support_enforcer = ScalableBitmap(this, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER) ].second); + m_bmp_support_blocker = ScalableBitmap(this, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER) ].second); m_bmp_vector.reserve(4); // bitmaps for different types of parts m_bmp_vector.push_back(&m_bmp_solidmesh.bmp()); @@ -574,12 +575,12 @@ void ObjectList::init_icons() m_objects_model->SetVolumeBitmaps(m_bmp_vector); // init icon for manifold warning - m_bmp_manifold_warning = ScalableBitmap(nullptr, "exclamation"); + m_bmp_manifold_warning = ScalableBitmap(this, "exclamation"); // Set warning bitmap for the model m_objects_model->SetWarningBitmap(&m_bmp_manifold_warning.bmp()); // init bitmap for "Add Settings" context menu - m_bmp_cog = ScalableBitmap(nullptr, "cog"); + m_bmp_cog = ScalableBitmap(this, "cog"); } void ObjectList::msw_rescale_icons() @@ -606,23 +607,20 @@ void ObjectList::msw_rescale_icons() // Update CATEGORY_ICON according to new scale { - // Note: `this` isn't passed to create_scaled_bitmap() here because of bugs in the widget, - // see note in PresetBundle::load_compatible_bitmaps() - // ptFFF - CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap(nullptr, "layers"); - CATEGORY_ICON[L("Infill")] = create_scaled_bitmap(nullptr, "infill"); - CATEGORY_ICON[L("Support material")] = create_scaled_bitmap(nullptr, "support"); - CATEGORY_ICON[L("Speed")] = create_scaled_bitmap(nullptr, "time"); - CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap(nullptr, "funnel"); - CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap(nullptr, "funnel"); - CATEGORY_ICON[L("Wipe options")] = create_scaled_bitmap(nullptr, "funnel"); -// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap(nullptr, "skirt+brim"); -// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap(nullptr, "time"); - CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap(nullptr, "wrench"); + CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers"); + CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill"); + CATEGORY_ICON[L("Support material")] = create_scaled_bitmap("support"); + CATEGORY_ICON[L("Speed")] = create_scaled_bitmap("time"); + CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel"); + CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap("funnel"); + CATEGORY_ICON[L("Wipe options")] = create_scaled_bitmap("funnel"); +// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap("skirt+brim"); +// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap("time"); + CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wrench"); // ptSLA - CATEGORY_ICON[L("Supports")] = create_scaled_bitmap(nullptr, "support"/*"sla_supports"*/); - CATEGORY_ICON[L("Pad")] = create_scaled_bitmap(nullptr, "pad"); + CATEGORY_ICON[L("Supports")] = create_scaled_bitmap("support"/*"sla_supports"*/); + CATEGORY_ICON[L("Pad")] = create_scaled_bitmap("pad"); } } @@ -980,6 +978,8 @@ void ObjectList::key_event(wxKeyEvent& event) ) { remove(); } + else if (event.GetKeyCode() == WXK_F5) + wxGetApp().plater()->reload_all_from_disk(); else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/)) select_item_all_children(); else if (wxGetKeyState(wxKeyCode('C')) && wxGetKeyState(WXK_CONTROL)) @@ -1002,14 +1002,13 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event) const bool mult_sel = multiple_selection(); if ((mult_sel && !selected_instances_of_same_object()) || - (!mult_sel && (GetSelection() != item)) || - m_objects_model->GetParent(item) == wxDataViewItem(nullptr) ) { + (!mult_sel && (GetSelection() != item)) ) { event.Veto(); return; } const ItemType& type = m_objects_model->GetItemType(item); - if (!(type & (itVolume | itInstance))) { + if (!(type & (itVolume | itObject | itInstance))) { event.Veto(); return; } @@ -1023,11 +1022,13 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event) for (auto sel : sels ) sub_obj_idxs.insert(m_objects_model->GetInstanceIdByItem(sel)); } - else + else if (type & itObject) + m_dragged_data.init(m_objects_model->GetIdByItem(item), type); + else m_dragged_data.init(m_objects_model->GetObjectIdByItem(item), - type&itVolume ? m_objects_model->GetVolumeIdByItem(item) : + type&itVolume ? m_objects_model->GetVolumeIdByItem(item) : m_objects_model->GetInstanceIdByItem(item), - type); + type); /* Under MSW or OSX, DnD moves an item to the place of another selected item * But under GTK, DnD moves an item between another two items. @@ -1048,10 +1049,20 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event) bool ObjectList::can_drop(const wxDataViewItem& item) const { - return (m_dragged_data.type() == itInstance && !item.IsOk()) || - (m_dragged_data.type() == itVolume && item.IsOk() && - m_objects_model->GetItemType(item) == itVolume && - m_dragged_data.obj_idx() == m_objects_model->GetObjectIdByItem(item)); + // move instance(s) or object on "empty place" of ObjectList + if ( (m_dragged_data.type() & (itInstance | itObject)) && !item.IsOk() ) + return true; + + // type of moved item should be the same as a "destination" item + if (!item.IsOk() || !(m_dragged_data.type() & (itVolume|itObject)) || + m_objects_model->GetItemType(item) != m_dragged_data.type() ) + return false; + + // move volumes inside one object only + if (m_dragged_data.type() & itVolume) + return m_dragged_data.obj_idx() == m_objects_model->GetObjectIdByItem(item); + + return true; } void ObjectList::OnDropPossible(wxDataViewEvent &event) @@ -1081,9 +1092,6 @@ void ObjectList::OnDrop(wxDataViewEvent &event) return; } - const int from_volume_id = m_dragged_data.sub_obj_idx(); - int to_volume_id = m_objects_model->GetVolumeIdByItem(item); - // It looks like a fixed in current version of the wxWidgets // #ifdef __WXGTK__ // /* Under GTK, DnD moves an item between another two items. @@ -1095,18 +1103,39 @@ void ObjectList::OnDrop(wxDataViewEvent &event) take_snapshot(_((m_dragged_data.type() == itVolume) ? L("Volumes in Object reordered") : L("Object reordered"))); - auto& volumes = (*m_objects)[m_dragged_data.obj_idx()]->volumes; - auto delta = to_volume_id < from_volume_id ? -1 : 1; - int cnt = 0; - for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id += delta, cnt++) - std::swap(volumes[id], volumes[id + delta]); + if (m_dragged_data.type() & itVolume) + { + int from_volume_id = m_dragged_data.sub_obj_idx(); + int to_volume_id = m_objects_model->GetVolumeIdByItem(item); + int delta = to_volume_id < from_volume_id ? -1 : 1; - select_item(m_objects_model->ReorganizeChildren(from_volume_id, to_volume_id, - m_objects_model->GetParent(item))); + auto& volumes = (*m_objects)[m_dragged_data.obj_idx()]->volumes; + + int cnt = 0; + for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id += delta, cnt++) + std::swap(volumes[id], volumes[id + delta]); + + select_item(m_objects_model->ReorganizeChildren(from_volume_id, to_volume_id, m_objects_model->GetParent(item))); + + } + else if (m_dragged_data.type() & itObject) + { + int from_obj_id = m_dragged_data.obj_idx(); + int to_obj_id = item.IsOk() ? m_objects_model->GetIdByItem(item) : ((int)m_objects->size()-1); + int delta = to_obj_id < from_obj_id ? -1 : 1; + + int cnt = 0; + for (int id = from_obj_id; cnt < abs(from_obj_id - to_obj_id); id += delta, cnt++) + std::swap((*m_objects)[id], (*m_objects)[id + delta]); + + select_item(m_objects_model->ReorganizeObjects(from_obj_id, to_obj_id)); + } changed_object(m_dragged_data.obj_idx()); m_dragged_data.clear(); + + wxGetApp().plater()->set_current_canvas_as_dirty(); } @@ -1622,14 +1651,9 @@ void ObjectList::append_menu_item_export_stl(wxMenu* menu) const void ObjectList::append_menu_item_reload_from_disk(wxMenu* menu) const { -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK - append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), - [this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu); -#else append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), [this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu, []() { return wxGetApp().plater()->can_reload_from_disk(); }, wxGetApp().plater()); -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK } void ObjectList::append_menu_item_change_extruder(wxMenu* menu) const @@ -1740,7 +1764,8 @@ void ObjectList::create_instance_popupmenu(wxMenu*menu) void ObjectList::create_default_popupmenu(wxMenu*menu) { wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType::INVALID); - append_submenu(menu, sub_menu, wxID_ANY, _(L("Add Shape")), "", "add_part"); + append_submenu(menu, sub_menu, wxID_ANY, _(L("Add Shape")), "", "add_part", + [](){return true;}, this); } wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu) @@ -2245,6 +2270,8 @@ void ObjectList::split() add_settings_item(vol_item, &volume->config); } + model_object->input_file.clear(); + if (parent == item) Expand(parent); @@ -3161,7 +3188,7 @@ void ObjectList::update_selections() select_items(sels); // Scroll selected Item in the middle of an object list - this->EnsureVisible(this->GetCurrentItem()); + ensure_current_item_visible(); } void ObjectList::update_selections_on_canvas() @@ -3877,6 +3904,8 @@ void ObjectList::OnEditingDone(wxDataViewEvent &event) // Here the last active column is forgotten, so when leaving the editing mode, the next mouse click will not enter the editing mode of the newly selected column. m_last_selected_column = -1; #endif //__WXMSW__ + + wxGetApp().plater()->set_current_canvas_as_dirty(); } void ObjectList::show_multi_selection_menu() @@ -3896,15 +3925,10 @@ void ObjectList::show_multi_selection_menu() _(L("Select extruder number for selected objects and/or parts")), [this](wxCommandEvent&) { extruder_selection(); }, "", menu); -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK - append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), - [this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu); -#else append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), [this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu, []() { return wxGetApp().plater()->can_reload_from_disk(); }, wxGetApp().plater()); -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK wxGetApp().plater()->PopupMenu(menu); } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index a5a72ad8c5..3b51c17613 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -10,6 +11,7 @@ #include "Event.hpp" #include "wxExtensions.hpp" +#include "ObjectDataViewModel.hpp" class wxBoxSizer; class wxBitmapComboBox; @@ -171,6 +173,12 @@ private: SettingsBundle m_freq_settings_sla; #endif + inline void ensure_current_item_visible() + { + if (const auto &item = this->GetCurrentItem()) + this->EnsureVisible(item); + } + public: ObjectList(wxWindow* parent); ~ObjectList(); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index f7010a5037..5afdb3bb43 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -10,7 +10,7 @@ #include "GLCanvas3DManager.hpp" #include "GLCanvas3D.hpp" #include "PresetBundle.hpp" -#include "wxExtensions.hpp" +#include "DoubleSlider.hpp" #include #include @@ -65,6 +65,7 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_ m_canvas->enable_selection(true); m_canvas->enable_main_toolbar(true); m_canvas->enable_undoredo_toolbar(true); + m_canvas->enable_labels(true); wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); main_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0); @@ -584,18 +585,23 @@ void Preview::update_view_type(bool slice_completed) void Preview::create_double_slider() { - m_slider = new DoubleSlider(this, wxID_ANY, 0, 0, 0, 100); + m_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100); m_slider->EnableTickManipulation(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF); m_double_slider_sizer->Add(m_slider, 0, wxEXPAND, 0); // sizer, m_canvas_widget m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_double_slider_from_canvas, this); + m_canvas_widget->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event) { + if (event.GetKeyCode() == WXK_SHIFT) + m_slider->UseDefaultColors(true); + event.Skip(); + }); m_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_sliders_scroll_changed, this); - Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { + Bind(DoubleSlider::wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { Model& model = wxGetApp().plater()->model(); model.custom_gcode_per_print_z = m_slider->GetTicksValues(); m_schedule_background_process(); @@ -633,7 +639,7 @@ static int find_close_layer_idx(const std::vector& zs, double &z, double return -1; } -void Preview::check_slider_values(std::vector& ticks_from_model, +void Preview::check_slider_values(std::vector& ticks_from_model, const std::vector& layers_z) { // All ticks that would end up outside the slider range should be erased. @@ -641,7 +647,7 @@ void Preview::check_slider_values(std::vector& ticks_from_mo // this function is e.g. not called when the last object is deleted unsigned int old_size = ticks_from_model.size(); ticks_from_model.erase(std::remove_if(ticks_from_model.begin(), ticks_from_model.end(), - [layers_z](Model::CustomGCode val) + [layers_z](CustomGCode::Item val) { auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.print_z - DoubleSlider::epsilon()); return it == layers_z.end(); @@ -669,7 +675,7 @@ void Preview::update_double_slider(const std::vector& layers_z, bool kee // Detect and set manipulation mode for double slider update_double_slider_mode(); - Model::CustomGCodeInfo &ticks_info_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z; + CustomGCode::Info &ticks_info_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z; check_slider_values(ticks_info_from_model.gcodes, layers_z); m_slider->SetSliderValues(layers_z); @@ -776,6 +782,8 @@ void Preview::update_double_slider_from_canvas(wxKeyEvent& event) } else if (key == 'S') m_slider->ChangeOneLayerLock(); + else if (key == WXK_SHIFT) + m_slider->UseDefaultColors(false); else event.Skip(); } @@ -830,7 +838,7 @@ void Preview::load_print_as_fff(bool keep_z_range) bool gcode_preview_data_valid = print->is_step_done(psGCodeExport) && ! m_gcode_preview_data->empty(); // Collect colors per extruder. std::vector colors; - std::vector color_print_values = {}; + std::vector color_print_values = {}; // set color print values, if it si selected "ColorPrint" view type if (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint) { diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index ae93e2ff05..cc8f153251 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -15,7 +15,6 @@ class wxChoice; class wxComboCtrl; class wxBitmapComboBox; class wxCheckBox; -class DoubleSlider; namespace Slic3r { @@ -25,6 +24,10 @@ class BackgroundSlicingProcess; class GCodePreviewData; class Model; +namespace DoubleSlider { + class Control; +}; + namespace GUI { class GLCanvas3D; @@ -103,7 +106,7 @@ class Preview : public wxPanel bool m_loaded; bool m_enabled; - DoubleSlider* m_slider {nullptr}; + DoubleSlider::Control* m_slider {nullptr}; public: Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, @@ -155,7 +158,7 @@ private: // Create/Update/Reset double slider on 3dPreview void create_double_slider(); - void check_slider_values(std::vector &ticks_from_model, + void check_slider_values(std::vector &ticks_from_model, const std::vector &layers_z); void reset_double_slider(); void update_double_slider(const std::vector& layers_z, bool keep_z_range = false); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index cb18bdb166..daf7e1fd1d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -4,6 +4,9 @@ #include #include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "libslic3r/SLAPrint.hpp" +#include "slic3r/GUI/MeshUtils.hpp" @@ -135,7 +138,7 @@ void GLGizmoBase::Grabber::render_face(float half_size) const } -GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) +GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, CommonGizmosData* common_data_ptr) : m_parent(parent) , m_group_id(-1) , m_state(Off) @@ -146,6 +149,7 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, u , m_dragging(false) , m_imgui(wxGetApp().imgui()) , m_first_input_window_render(true) + , m_c(common_data_ptr) { ::memcpy((void*)m_base_color, (const void*)DEFAULT_BASE_COLOR, 4 * sizeof(float)); ::memcpy((void*)m_drag_color, (const void*)DEFAULT_DRAG_COLOR, 4 * sizeof(float)); @@ -301,5 +305,89 @@ unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char gr return b; } + + +bool CommonGizmosData::update_from_backend(GLCanvas3D& canvas, ModelObject* model_object) +{ + recent_update = false; + + if (m_model_object != model_object + || (model_object && m_model_object_id != model_object->id())) { + m_model_object = model_object; + m_print_object_idx = -1; + m_mesh_raycaster.reset(); + m_object_clipper.reset(); + m_supports_clipper.reset(); + m_old_mesh = nullptr; + m_mesh = nullptr; + m_backend_mesh_transformed.clear(); + if (m_model_object) { + m_active_instance = canvas.get_selection().get_instance_idx(); + m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius(); + } + + recent_update = true; + } + + + if (! m_model_object || ! canvas.get_selection().is_from_single_instance()) + return false; + + int old_po_idx = m_print_object_idx; + + // First we need a pointer to the respective SLAPrintObject. The index into objects vector is + // cached so we don't have todo it on each render. We only search for the po if needed: + if (m_print_object_idx < 0 || (int)canvas.sla_print()->objects().size() != m_print_objects_count) { + m_print_objects_count = canvas.sla_print()->objects().size(); + m_print_object_idx = -1; + for (const SLAPrintObject* po : canvas.sla_print()->objects()) { + ++m_print_object_idx; + if (po->model_object()->id() == m_model_object->id()) + break; + } + } + + m_mesh = nullptr; + // Load either the model_object mesh, or one provided by the backend + // This mesh does not account for the possible Z up SLA offset. + // The backend mesh needs to be transformed and because a pointer to it is + // saved, a copy is stored as a member (FIXME) + if (m_print_object_idx >=0) { + const SLAPrintObject* po = canvas.sla_print()->objects()[m_print_object_idx]; + if (po->is_step_done(slaposDrillHoles)) { + m_backend_mesh_transformed = po->get_mesh_to_print(); + m_backend_mesh_transformed.transform(canvas.sla_print()->sla_trafo(*m_model_object).inverse()); + m_mesh = &m_backend_mesh_transformed; + m_has_drilled_mesh = true; + } + } + + if (! m_mesh) { + m_mesh = &m_model_object->volumes.front()->mesh(); + m_backend_mesh_transformed.clear(); + m_has_drilled_mesh = false; + } + + m_model_object_id = m_model_object->id(); + + if (m_mesh != m_old_mesh) { + wxBusyCursor wait; + m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh)); + m_object_clipper.reset(); + m_supports_clipper.reset(); + m_old_mesh = m_mesh; + m_clipping_plane_distance = 0.f; + m_clipping_plane_distance_stash = 0.f; + recent_update = true; + return true; + } + if (! recent_update) + recent_update = m_print_object_idx < 0 && old_po_idx >= 0; + + return recent_update; +} + + + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index da30427793..f3941b0a10 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -30,7 +30,9 @@ static const float CONSTRAINED_COLOR[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; class ImGuiWrapper; - +class CommonGizmosData; +class GLCanvas3D; +class ClippingPlane; class GLGizmoBase { @@ -99,9 +101,13 @@ protected: mutable std::vector m_grabbers; ImGuiWrapper* m_imgui; bool m_first_input_window_render; + CommonGizmosData* m_c = nullptr; public: - GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoBase(GLCanvas3D& parent, + const std::string& icon_filename, + unsigned int sprite_id, + CommonGizmosData* common_data = nullptr); virtual ~GLGizmoBase() {} bool init() { return on_init(); } @@ -179,6 +185,57 @@ protected: // were not interpolated by alpha blending or multi sampling. extern unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue); +class MeshRaycaster; +class MeshClipper; + +class CommonGizmosData { +public: + const TriangleMesh* mesh() const { + return (! m_mesh ? nullptr : m_mesh); //(m_cavity_mesh ? m_cavity_mesh.get() : m_mesh)); + } + + bool update_from_backend(GLCanvas3D& canvas, ModelObject* model_object); + + bool recent_update = false; + + + + ModelObject* m_model_object = nullptr; + const TriangleMesh* m_mesh; + std::unique_ptr m_mesh_raycaster; + std::unique_ptr m_object_clipper; + std::unique_ptr m_supports_clipper; + + //std::unique_ptr m_cavity_mesh; + //std::unique_ptr m_volume_with_cavity; + + int m_active_instance = -1; + float m_active_instance_bb_radius = 0; + ObjectID m_model_object_id = 0; + int m_print_object_idx = -1; + int m_print_objects_count = -1; + int m_old_timestamp = -1; + + float m_clipping_plane_distance = 0.f; + std::unique_ptr m_clipping_plane; + + void stash_clipping_plane() { + m_clipping_plane_distance_stash = m_clipping_plane_distance; + } + + void unstash_clipping_plane() { + m_clipping_plane_distance = m_clipping_plane_distance_stash; + } + + bool has_drilled_mesh() const { return m_has_drilled_mesh; } + +private: + const TriangleMesh* m_old_mesh; + TriangleMesh m_backend_mesh_transformed; + float m_clipping_plane_distance_stash = 0.f; + bool m_has_drilled_mesh = false; +}; + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index e98446749f..52d710249b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include "slic3r/GUI/GUI_App.hpp" @@ -189,7 +191,7 @@ void GLGizmoCut::update_max_z(const Selection& selection) const void GLGizmoCut::set_cut_z(double cut_z) const { // Clamp the plane to the object's bounding box - m_cut_z = std::max(0.0, std::min(m_max_z, cut_z)); + m_cut_z = std::clamp(cut_z, 0.0, m_max_z); } void GLGizmoCut::perform_cut(const Selection& selection) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 6e5738a422..b6e10861fc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -25,6 +25,9 @@ class GLGizmoCut : public GLGizmoBase public: GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + double get_cut_z() const { return m_cut_z; } + void set_cut_z(double cut_z) const; + protected: virtual bool on_init(); virtual void on_load(cereal::BinaryInputArchive& ar) { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } @@ -40,7 +43,6 @@ protected: private: void update_max_z(const Selection& selection) const; - void set_cut_z(double cut_z) const; void perform_cut(const Selection& selection); double calc_projection(const Linef3& mouse_ray) const; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp new file mode 100644 index 0000000000..31dd919a71 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -0,0 +1,1134 @@ +#include "GLGizmoHollow.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/Gizmos/GLGizmos.hpp" + +#include + +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/GUI_ObjectSettings.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/MeshUtils.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/PresetBundle.hpp" +#include "libslic3r/SLAPrint.hpp" +#include "libslic3r/TriangleMesh.hpp" + + +namespace Slic3r { +namespace GUI { + +GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, CommonGizmosData* cd) + : GLGizmoBase(parent, icon_filename, sprite_id, cd) + , m_quadric(nullptr) +{ + m_c->m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); + m_quadric = ::gluNewQuadric(); + if (m_quadric != nullptr) + // using GLU_FILL does not work when the instance's transformation + // contains mirroring (normals are reverted) + ::gluQuadricDrawStyle(m_quadric, GLU_FILL); +} + +GLGizmoHollow::~GLGizmoHollow() +{ + if (m_quadric != nullptr) + ::gluDeleteQuadric(m_quadric); +} + +bool GLGizmoHollow::on_init() +{ + m_shortcut_key = WXK_CONTROL_H; + m_desc["enable"] = _(L("Hollow this object")); + m_desc["preview"] = _(L("Preview hollowed and drilled model")); + m_desc["offset"] = _(L("Offset")) + ": "; + m_desc["quality"] = _(L("Quality")) + ": "; + m_desc["closing_distance"] = _(L("Closing distance")) + ": "; + m_desc["hole_diameter"] = _(L("Hole diameter")) + ": "; + m_desc["hole_depth"] = _(L("Hole depth")) + ": "; + m_desc["remove_selected"] = _(L("Remove selected holes")); + m_desc["remove_all"] = _(L("Remove all holes")); + m_desc["clipping_of_view"] = _(L("Clipping of view"))+ ": "; + m_desc["reset_direction"] = _(L("Reset direction")); + m_desc["show_supports"] = _(L("Show supports")); + + return true; +} + +void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&) +{ + if (m_c->recent_update) { + + if (m_c->m_model_object) { + reload_cache(); + if (m_c->has_drilled_mesh()) + m_holes_in_drilled_mesh = m_c->m_model_object->sla_drain_holes; + } + + if (m_state == On) { + m_parent.toggle_model_objects_visibility(false); + m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); + } + else + m_parent.toggle_model_objects_visibility(true, nullptr, -1); + } +} + + + +void GLGizmoHollow::on_render() const +{ + const Selection& selection = m_parent.get_selection(); + + // If current m_c->m_model_object does not match selection, ask GLCanvas3D to turn us off + if (m_state == On + && (m_c->m_model_object != selection.get_model()->objects[selection.get_object_idx()] + || m_c->m_active_instance != selection.get_instance_idx() + || m_c->m_model_object_id != m_c->m_model_object->id())) { + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS)); + return; + } + + // !!! is it necessary? + //const_cast(this)->m_c->update_from_backend(m_parent, m_c->m_model_object); + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); + + render_hollowed_mesh(); + + if (m_quadric != nullptr && selection.is_from_single_instance()) + render_points(selection, false); + + m_selection_rectangle.render(m_parent); + render_clipping_plane(selection); + + glsafe(::glDisable(GL_BLEND)); +} + + + +void GLGizmoHollow::render_hollowed_mesh() const +{ + /*if (m_c->m_volume_with_cavity) { + m_c->m_volume_with_cavity->set_sla_shift_z(m_z_shift); + m_parent.get_shader().start_using(); + + GLint current_program_id; + glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); + GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; + GLint print_box_detection_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1; + GLint print_box_worldmatrix_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1; + glcheck(); + m_c->m_volume_with_cavity->set_render_color(); + const Geometry::Transformation& volume_trafo = m_c->m_model_object->volumes.front()->get_transformation(); + m_c->m_volume_with_cavity->set_volume_transformation(volume_trafo); + m_c->m_volume_with_cavity->set_instance_transformation(m_c->m_model_object->instances[size_t(m_c->m_active_instance)]->get_transformation()); + m_c->m_volume_with_cavity->render(color_id, print_box_detection_id, print_box_worldmatrix_id); + m_parent.get_shader().stop_using(); + }*/ +} + + +void GLGizmoHollow::render_clipping_plane(const Selection& selection) const +{ + if (m_c->m_clipping_plane_distance == 0.f) + return; + + // Get transformation of the instance + const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); + Geometry::Transformation trafo = vol->get_instance_transformation(); + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + + // Get transformation of supports + Geometry::Transformation supports_trafo; + supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), vol->get_sla_shift_z())); + supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2))); + // I don't know why, but following seems to be correct. + supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2), + 1, + 1.)); + + // Now initialize the TMS for the object, perform the cut and save the result. + if (! m_c->m_object_clipper) { + m_c->m_object_clipper.reset(new MeshClipper); + m_c->m_object_clipper->set_mesh(*m_c->mesh()); + } + m_c->m_object_clipper->set_plane(*m_c->m_clipping_plane); + m_c->m_object_clipper->set_transformation(trafo); + + + // Next, ask the backend if supports are already calculated. If so, we are gonna cut them too. + //if (m_c->m_print_object_idx < 0) + // m_c->update_from_backend(m_parent, m_c->m_model_object); + + if (m_c->m_print_object_idx >= 0) { + const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_c->m_print_object_idx]; + + if (print_object->is_step_done(slaposSupportTree) && !print_object->get_mesh(slaposSupportTree).empty()) { + // If the supports are already calculated, save the timestamp of the respective step + // so we can later tell they were recalculated. + size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp; + + if (! m_c->m_supports_clipper || (int)timestamp != m_c->m_old_timestamp) { + // The timestamp has changed. + m_c->m_supports_clipper.reset(new MeshClipper); + // The mesh should already have the shared vertices calculated. + m_c->m_supports_clipper->set_mesh(print_object->support_mesh()); + m_c->m_old_timestamp = timestamp; + } + m_c->m_supports_clipper->set_plane(*m_c->m_clipping_plane); + m_c->m_supports_clipper->set_transformation(supports_trafo); + } + else + // The supports are not valid. We better dump the cached data. + m_c->m_supports_clipper.reset(); + } + + // At this point we have the triangulated cuts for both the object and supports - let's render. + if (! m_c->m_object_clipper->get_triangles().empty()) { + ::glPushMatrix(); + ::glColor3f(1.0f, 0.37f, 0.0f); + ::glBegin(GL_TRIANGLES); + for (const Vec3f& point : m_c->m_object_clipper->get_triangles()) + ::glVertex3f(point(0), point(1), point(2)); + ::glEnd(); + ::glPopMatrix(); + } + + if (m_show_supports && m_c->m_supports_clipper && ! m_c->m_supports_clipper->get_triangles().empty()) { + ::glPushMatrix(); + ::glColor3f(1.0f, 0.f, 0.37f); + ::glBegin(GL_TRIANGLES); + for (const Vec3f& point : m_c->m_supports_clipper->get_triangles()) + ::glVertex3f(point(0), point(1), point(2)); + ::glEnd(); + ::glPopMatrix(); + } +} + + +void GLGizmoHollow::on_render_for_picking() const +{ + const Selection& selection = m_parent.get_selection(); +#if ENABLE_RENDER_PICKING_PASS + m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); +#endif + + glsafe(::glEnable(GL_DEPTH_TEST)); + render_points(selection, true); + render_hollowed_mesh(); +} + +void GLGizmoHollow::render_points(const Selection& selection, bool picking) const +{ + if (!picking) + glsafe(::glEnable(GL_LIGHTING)); + + const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); + const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); + const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); + + glsafe(::glPushMatrix()); + glsafe(::glTranslated(0.0, 0.0, m_z_shift)); + glsafe(::glMultMatrixd(instance_matrix.data())); + + float render_color[4]; + size_t cache_size = m_c->m_model_object->sla_drain_holes.size(); + for (size_t i = 0; i < cache_size; ++i) + { + const sla::DrainHole& drain_hole = m_c->m_model_object->sla_drain_holes[i]; + const bool& point_selected = m_selected[i]; + + if (is_mesh_point_clipped((drain_hole.pos+HoleStickOutLength*drain_hole.normal).cast())) + continue; + + // First decide about the color of the point. + if (picking) { + std::array color = picking_color_component(i); + render_color[0] = color[0]; + render_color[1] = color[1]; + render_color[2] = color[2]; + render_color[3] = color[3]; + } + else { + render_color[3] = 1.f; + if (size_t(m_hover_id) == i) { + render_color[0] = 0.f; + render_color[1] = 1.0f; + render_color[2] = 1.0f; + } + else { // neigher hover nor picking + render_color[0] = point_selected ? 1.0f : 0.7f; + render_color[1] = point_selected ? 0.3f : 0.7f; + render_color[2] = point_selected ? 0.3f : 0.7f; + render_color[3] = 0.5f; + } + } + glsafe(::glColor4fv(render_color)); + float render_color_emissive[4] = { 0.5f * render_color[0], 0.5f * render_color[1], 0.5f * render_color[2], 1.f}; + glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive)); + + // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. + glsafe(::glPushMatrix()); + glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2))); + glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); + + if (vol->is_left_handed()) + glFrontFace(GL_CW); + + // Matrices set, we can render the point mark now. + + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); + Eigen::AngleAxisd aa(q); + glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); + glsafe(::glPushMatrix()); + glsafe(::glTranslated(0., 0., -drain_hole.height)); + ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height, 24, 1); + glsafe(::glTranslated(0., 0., drain_hole.height)); + ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); + glsafe(::glTranslated(0., 0., -drain_hole.height)); + glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); + ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); + glsafe(::glPopMatrix()); + + if (vol->is_left_handed()) + glFrontFace(GL_CCW); + glsafe(::glPopMatrix()); + } + + { + // Reset emissive component to zero (the default value) + float render_color_emissive[4] = { 0.f, 0.f, 0.f, 1.f }; + glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive)); + } + + if (!picking) + glsafe(::glDisable(GL_LIGHTING)); + + glsafe(::glPopMatrix()); +} + + + +bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const +{ + if (m_c->m_clipping_plane_distance == 0.f) + return false; + + Vec3d transformed_point = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation().get_matrix() * point; + transformed_point(2) += m_z_shift; + return m_c->m_clipping_plane->is_point_clipped(transformed_point); +} + + + +// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal +// Return false if no intersection was found, true otherwise. +bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) +{ + if (! m_c->m_mesh_raycaster) + return false; + + // if the gizmo doesn't have the V, F structures for igl, calculate them first: + // !!! is it really necessary? + //m_c->update_from_backend(m_parent, m_c->m_model_object); + + const Camera& camera = m_parent.get_camera(); + const Selection& selection = m_parent.get_selection(); + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + Geometry::Transformation trafo = volume->get_instance_transformation(); + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + + // The raycaster query + Vec3f hit; + Vec3f normal; + if (m_c->m_mesh_raycaster->unproject_on_mesh( + mouse_pos, + trafo.get_matrix(), + camera, + hit, + normal, + m_c->m_clipping_plane_distance != 0.f ? m_c->m_clipping_plane.get() : nullptr)) + { + if (m_c->has_drilled_mesh()) { + // in this case the raycaster sees the hollowed and drilled mesh. + // if the point lies on the surface created by the hole, we want + // to ignore it. + for (const sla::DrainHole& hole : m_holes_in_drilled_mesh) { + sla::DrainHole outer(hole); + outer.radius *= 1.001f; + outer.height *= 1.001f; + if (outer.is_inside(hit)) + return false; + } + } + + // Return both the point and the facet normal. + pos_and_normal = std::make_pair(hit, normal); + return true; + } + else + return false; +} + +// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. +// The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is +// aware that the event was reacted to and stops trying to make different sense of it. If the gizmo +// concludes that the event was not intended for it, it should return false. +bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) +{ + // left down with shift - show the selection rectangle: + if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) { + if (m_hover_id == -1) { + if (shift_down || alt_down) { + m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect); + } + } + else { + if (m_selected[m_hover_id]) + unselect_point(m_hover_id); + else { + if (!alt_down) + select_point(m_hover_id); + } + } + + return true; + } + + // left down without selection rectangle - place point on the mesh: + if (action == SLAGizmoEventType::LeftDown && !m_selection_rectangle.is_dragging() && !shift_down) { + // If any point is in hover state, this should initiate its move - return control back to GLCanvas: + if (m_hover_id != -1) + return false; + + // If there is some selection, don't add new point and deselect everything instead. + if (m_selection_empty) { + std::pair pos_and_normal; + if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole"))); + + Vec3d scaling = m_c->m_model_object->instances[m_c->m_active_instance]->get_scaling_factor(); + Vec3f normal_transformed(pos_and_normal.second(0)/scaling(0), + pos_and_normal.second(1)/scaling(1), + pos_and_normal.second(2)/scaling(2)); + + m_c->m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first + HoleStickOutLength * pos_and_normal.second/* normal_transformed.normalized()*/, + -pos_and_normal.second, m_new_hole_radius, m_new_hole_height+HoleStickOutLength); + m_selected.push_back(false); + assert(m_selected.size() == m_c->m_model_object->sla_drain_holes.size()); + m_parent.set_as_dirty(); + m_wait_for_up_event = true; + } + else + return false; + } + else + select_point(NoPoints); + + return true; + } + + // left up with selection rectangle - select points inside the rectangle: + if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp || action == SLAGizmoEventType::AltUp) && m_selection_rectangle.is_dragging()) { + // Is this a selection or deselection rectangle? + GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); + + // First collect positions of all the points in world coordinates. + Geometry::Transformation trafo = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation(); + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + std::vector points; + for (unsigned int i=0; im_model_object->sla_drain_holes.size(); ++i) + points.push_back(trafo.get_matrix() * m_c->m_model_object->sla_drain_holes[i].pos.cast()); + + // Now ask the rectangle which of the points are inside. + std::vector points_inside; + std::vector points_idxs = m_selection_rectangle.stop_dragging(m_parent, points); + for (size_t idx : points_idxs) + points_inside.push_back(points[idx].cast()); + + // Only select/deselect points that are actually visible + for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_c->m_clipping_plane.get())) + { + if (rectangle_status == GLSelectionRectangle::Deselect) + unselect_point(points_idxs[idx]); + else + select_point(points_idxs[idx]); + } + return true; + } + + // left up with no selection rectangle + if (action == SLAGizmoEventType::LeftUp) { + if (m_wait_for_up_event) { + m_wait_for_up_event = false; + return true; + } + } + + // dragging the selection rectangle: + if (action == SLAGizmoEventType::Dragging) { + if (m_wait_for_up_event) + return true; // point has been placed and the button not released yet + // this prevents GLCanvas from starting scene rotation + + if (m_selection_rectangle.is_dragging()) { + m_selection_rectangle.dragging(mouse_position); + return true; + } + + return false; + } + + if (action == SLAGizmoEventType::Delete) { + // delete key pressed + delete_selected_points(); + return true; + } + + if (action == SLAGizmoEventType::RightDown) { + if (m_hover_id != -1) { + select_point(NoPoints); + select_point(m_hover_id); + delete_selected_points(); + return true; + } + return false; + } + + if (action == SLAGizmoEventType::SelectAll) { + select_point(AllPoints); + return true; + } + + if (action == SLAGizmoEventType::MouseWheelUp && control_down) { + m_c->m_clipping_plane_distance = std::min(1.f, m_c->m_clipping_plane_distance + 0.01f); + update_clipping_plane(true); + return true; + } + + if (action == SLAGizmoEventType::MouseWheelDown && control_down) { + m_c->m_clipping_plane_distance = std::max(0.f, m_c->m_clipping_plane_distance - 0.01f); + update_clipping_plane(true); + return true; + } + + if (action == SLAGizmoEventType::ResetClippingPlane) { + update_clipping_plane(); + return true; + } + + return false; +} + +void GLGizmoHollow::delete_selected_points() +{ + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete drainage hole"))); + + for (unsigned int idx=0; idxm_model_object->sla_drain_holes.size(); ++idx) { + if (m_selected[idx]) { + m_selected.erase(m_selected.begin()+idx); + m_c->m_model_object->sla_drain_holes.erase(m_c->m_model_object->sla_drain_holes.begin() + (idx--)); + } + } + + select_point(NoPoints); +} + +void GLGizmoHollow::on_update(const UpdateData& data) +{ + if (m_hover_id != -1) { + std::pair pos_and_normal; + if (! unproject_on_mesh(data.mouse_pos.cast(), pos_and_normal)) + return; + m_c->m_model_object->sla_drain_holes[m_hover_id].pos = pos_and_normal.first + HoleStickOutLength * pos_and_normal.second; + m_c->m_model_object->sla_drain_holes[m_hover_id].normal = -pos_and_normal.second; + } +} + +std::pair GLGizmoHollow::get_hollowing_parameters() const +{ + // FIXME this function is probably obsolete, caller could + // get the data from model config himself + auto opts = get_config_options({"hollowing_min_thickness", "hollowing_quality", "hollowing_closing_distance"}); + double offset = static_cast(opts[0].first)->value; + double quality = static_cast(opts[1].first)->value; + double closing_d = static_cast(opts[2].first)->value; + return std::make_pair(m_c->m_mesh, sla::HollowingConfig{offset, quality, closing_d}); +} + +void GLGizmoHollow::update_mesh_raycaster(std::unique_ptr &&rc) +{ + m_c->m_mesh_raycaster = std::move(rc); + m_c->m_object_clipper.reset(); + //m_c->m_volume_with_cavity.reset(); +} + +void GLGizmoHollow::hollow_mesh(bool postpone_error_messages) +{ + // Trigger a UI job to hollow the mesh. + // wxGetApp().plater()->hollow(); + + wxGetApp().CallAfter([this, postpone_error_messages]() { + wxGetApp().plater()->reslice_SLA_hollowing(*m_c->m_model_object, postpone_error_messages); + }); +} + + +void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) +{ + // Called from Plater when the UI job finishes + /*m_c->m_cavity_mesh = std::move(mesh); + + if(m_c->m_cavity_mesh) { + // First subtract the holes: + if (! m_c->m_model_object->sla_drain_holes.empty()) { + TriangleMesh holes_mesh; + for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) { + TriangleMesh hole_mesh = make_cylinder(hole.radius, hole.height, 2*M_PI/32); + + Vec3d scaling = m_c->m_model_object->instances[m_c->m_active_instance]->get_scaling_factor(); + Vec3d normal_transformed = Vec3d(hole.normal(0)/scaling(0), hole.normal(1)/scaling(1), hole.normal(2)/scaling(2)); + normal_transformed.normalize(); + + // Rotate the cylinder appropriately + Eigen::Quaterniond q; + Transform3d m = Transform3d::Identity(); + m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), normal_transformed).toRotationMatrix(); + hole_mesh.transform(m); + + // If the instance is scaled, undo the scaling of the hole + hole_mesh.scale(Vec3d(1/scaling(0), 1/scaling(1), 1/scaling(2))); + + // Translate the hole into position and merge with the others + hole_mesh.translate(hole.pos); + holes_mesh.merge(hole_mesh); + holes_mesh.repair(); + } + MeshBoolean::minus(*m_c->m_cavity_mesh.get(), holes_mesh); + } + + // create a new GLVolume that only has the cavity inside + m_c->m_volume_with_cavity.reset(new GLVolume(GLVolume::MODEL_COLOR[2])); + m_c->m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_c->m_cavity_mesh.get()); + m_c->m_volume_with_cavity->finalize_geometry(true); + m_c->m_volume_with_cavity->force_transparent = false; + + m_parent.toggle_model_objects_visibility(false, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(true, m_c->m_model_object, m_c->m_active_instance); + + // Reset raycaster so it works with the new mesh: + m_c->m_mesh_raycaster.reset(new MeshRaycaster(*m_c->mesh())); + } + + if (m_c->m_clipping_plane_distance == 0.f) { + m_c->m_clipping_plane_distance = 0.5f; + update_clipping_plane(); + }*/ +} + +std::vector> GLGizmoHollow::get_config_options(const std::vector& keys) const +{ + std::vector> out; + + if (!m_c->m_model_object) + return out; + + const DynamicPrintConfig& object_cfg = m_c->m_model_object->config; + const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; + std::unique_ptr default_cfg = nullptr; + + for (const std::string& key : keys) { + if (object_cfg.has(key)) + out.emplace_back(object_cfg.option(key), &object_cfg.def()->options.at(key)); // at() needed for const map + else + if (print_cfg.has(key)) + out.emplace_back(print_cfg.option(key), &print_cfg.def()->options.at(key)); + else { // we must get it from defaults + if (default_cfg == nullptr) + default_cfg.reset(DynamicPrintConfig::new_from_defaults_keys(keys)); + out.emplace_back(default_cfg->option(key), &default_cfg->def()->options.at(key)); + } + } + + return out; +} + + +ClippingPlane GLGizmoHollow::get_sla_clipping_plane() const +{ + if (!m_c->m_model_object || m_state == Off || m_c->m_clipping_plane_distance == 0.f) + return ClippingPlane::ClipsNothing(); + else + return ClippingPlane(-m_c->m_clipping_plane->get_normal(), m_c->m_clipping_plane->get_data()[3]); +} + + +void GLGizmoHollow::on_render_input_window(float x, float y, float bottom_limit) +{ + if (! m_c->m_model_object) + return; + + bool first_run = true; // This is a hack to redraw the button when all points are removed, + // so it is not delayed until the background process finishes. + + ConfigOptionMode current_mode = wxGetApp().get_mode(); + + std::vector opts_keys = {"hollowing_min_thickness", "hollowing_quality", "hollowing_closing_distance"}; + auto opts = get_config_options(opts_keys); + auto* offset_cfg = static_cast(opts[0].first); + float offset = offset_cfg->value; + double offset_min = opts[0].second->min; + double offset_max = opts[0].second->max; + + auto* quality_cfg = static_cast(opts[1].first); + float quality = quality_cfg->value; + double quality_min = opts[1].second->min; + double quality_max = opts[1].second->max; + ConfigOptionMode quality_mode = opts[1].second->mode; + + auto* closing_d_cfg = static_cast(opts[2].first); + float closing_d = closing_d_cfg->value; + double closing_d_min = opts[2].second->min; + double closing_d_max = opts[2].second->max; + ConfigOptionMode closing_d_mode = opts[2].second->mode; + + m_desc["offset"] = _(opts[0].second->label).ToUTF8() + wxString(":"); + m_desc["quality"] = _(opts[1].second->label).ToUTF8() + wxString(":"); + m_desc["closing_distance"] = _(opts[2].second->label).ToUTF8() + wxString(":"); + + +RENDER_AGAIN: + const float approx_height = m_imgui->scaled(20.0f); + y = std::min(y, bottom_limit - approx_height); + m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + + m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + + // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: + const float settings_sliders_left = + std::max({m_imgui->calc_text_size(m_desc.at("offset")).x, + m_imgui->calc_text_size(m_desc.at("quality")).x, + m_imgui->calc_text_size(m_desc.at("closing_distance")).x, + m_imgui->calc_text_size(m_desc.at("hole_diameter")).x, + m_imgui->calc_text_size(m_desc.at("hole_depth")).x}) + + m_imgui->scaled(1.f); + + const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); + const float diameter_slider_left = settings_sliders_left; //m_imgui->calc_text_size(m_desc.at("hole_diameter")).x + m_imgui->scaled(1.f); + const float minimal_slider_width = m_imgui->scaled(4.f); + + float window_width = minimal_slider_width + std::max({settings_sliders_left, clipping_slider_left, diameter_slider_left}); + window_width = std::max(window_width, m_imgui->calc_text_size(m_desc.at("preview")).x); + + if (m_imgui->button(m_desc["preview"])) + hollow_mesh(); + + bool config_changed = false; + + ImGui::Separator(); + + { + auto opts = get_config_options({"hollowing_enable"}); + m_enable_hollowing = static_cast(opts[0].first)->value; + if (m_imgui->checkbox(m_desc["enable"], m_enable_hollowing)) { + m_c->m_model_object->config.opt("hollowing_enable", true)->value = m_enable_hollowing; + wxGetApp().obj_list()->update_and_show_object_settings_item(); + config_changed = true; + } + } + + m_imgui->disabled_begin(! m_enable_hollowing); + + m_imgui->text(m_desc.at("offset")); + ImGui::SameLine(settings_sliders_left); + ImGui::PushItemWidth(window_width - settings_sliders_left); + ImGui::SliderFloat(" ", &offset, offset_min, offset_max, "%.1f"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(_(opts[0].second->tooltip).ToUTF8()); + ImGui::EndTooltip(); + } + bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider + bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider + bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider + + if (current_mode >= quality_mode) { + m_imgui->text(m_desc.at("quality")); + ImGui::SameLine(settings_sliders_left); + ImGui::SliderFloat(" ", &quality, quality_min, quality_max, "%.1f"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(_(opts[1].second->tooltip).ToUTF8()); + ImGui::EndTooltip(); + } + slider_clicked |= ImGui::IsItemClicked(); + slider_edited |= ImGui::IsItemEdited(); + slider_released |= ImGui::IsItemDeactivatedAfterEdit(); + } + + if (current_mode >= closing_d_mode) { + m_imgui->text(m_desc.at("closing_distance")); + ImGui::SameLine(settings_sliders_left); + ImGui::SliderFloat(" ", &closing_d, closing_d_min, closing_d_max, "%.1f"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(_(opts[2].second->tooltip).ToUTF8()); + ImGui::EndTooltip(); + } + slider_clicked |= ImGui::IsItemClicked(); + slider_edited |= ImGui::IsItemEdited(); + slider_released |= ImGui::IsItemDeactivatedAfterEdit(); + } + + if (slider_clicked) { + m_offset_stash = offset; + m_quality_stash = quality; + m_closing_d_stash = closing_d; + } + if (slider_edited || slider_released) { + if (slider_released) { + m_c->m_model_object->config.opt("hollowing_min_thickness", true)->value = m_offset_stash; + m_c->m_model_object->config.opt("hollowing_quality", true)->value = m_quality_stash; + m_c->m_model_object->config.opt("hollowing_closing_distance", true)->value = m_closing_d_stash; + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Hollowing parameter change"))); + } + m_c->m_model_object->config.opt("hollowing_min_thickness", true)->value = offset; + m_c->m_model_object->config.opt("hollowing_quality", true)->value = quality; + m_c->m_model_object->config.opt("hollowing_closing_distance", true)->value = closing_d; + if (slider_released) { + wxGetApp().obj_list()->update_and_show_object_settings_item(); + config_changed = true; + } + } + + m_imgui->disabled_end(); + + bool force_refresh = false; + bool remove_selected = false; + bool remove_all = false; + + // m_imgui->text(" "); // vertical gap + ImGui::Separator(); + + float diameter_upper_cap = 15.; + if (m_new_hole_radius > diameter_upper_cap) + m_new_hole_radius = diameter_upper_cap; + m_imgui->text(m_desc.at("hole_diameter")); + ImGui::SameLine(diameter_slider_left); + ImGui::PushItemWidth(window_width - diameter_slider_left); + + float diam = 2.f * m_new_hole_radius; + ImGui::SliderFloat("", &diam, 1.f, diameter_upper_cap, "%.1f"); + m_new_hole_radius = diam / 2.f; + bool clicked = ImGui::IsItemClicked(); + bool edited = ImGui::IsItemEdited(); + bool deactivated = ImGui::IsItemDeactivatedAfterEdit(); + + m_imgui->text(m_desc["hole_depth"]); + ImGui::SameLine(diameter_slider_left); + m_new_hole_height -= HoleStickOutLength; + ImGui::SliderFloat(" ", &m_new_hole_height, 0.f, 10.f, "%.1f"); + m_new_hole_height += HoleStickOutLength; + + clicked |= ImGui::IsItemClicked(); + edited |= ImGui::IsItemEdited(); + deactivated |= ImGui::IsItemDeactivatedAfterEdit(); + + // Following is a nasty way to: + // - save the initial value of the slider before one starts messing with it + // - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene + // - take correct undo/redo snapshot after the user is done with moving the slider + if (! m_selection_empty) { + if (clicked) { + m_holes_stash = m_c->m_model_object->sla_drain_holes; + } + if (edited) { + for (size_t idx=0; idxm_model_object->sla_drain_holes[idx].radius = m_new_hole_radius; + m_c->m_model_object->sla_drain_holes[idx].height = m_new_hole_height; + } + } + if (deactivated) { + // momentarily restore the old value to take snapshot + sla::DrainHoles new_holes = m_c->m_model_object->sla_drain_holes; + m_c->m_model_object->sla_drain_holes = m_holes_stash; + float backup_rad = m_new_hole_radius; + float backup_hei = m_new_hole_height; + for (size_t i=0; im_model_object->sla_drain_holes = new_holes; + } + } + + m_imgui->disabled_begin(m_selection_empty); + remove_selected = m_imgui->button(m_desc.at("remove_selected")); + m_imgui->disabled_end(); + + m_imgui->disabled_begin(m_c->m_model_object->sla_drain_holes.empty()); + remove_all = m_imgui->button(m_desc.at("remove_all")); + m_imgui->disabled_end(); + + // Following is rendered in both editing and non-editing mode: + // m_imgui->text(""); + ImGui::Separator(); + if (m_c->m_clipping_plane_distance == 0.f) + m_imgui->text(m_desc.at("clipping_of_view")); + else { + if (m_imgui->button(m_desc.at("reset_direction"))) { + wxGetApp().CallAfter([this](){ + update_clipping_plane(); + }); + } + } + + ImGui::SameLine(clipping_slider_left); + ImGui::PushItemWidth(window_width - clipping_slider_left); + if (ImGui::SliderFloat(" ", &m_c->m_clipping_plane_distance, 0.f, 1.f, "%.2f")) + update_clipping_plane(true); + + // make sure supports are shown/hidden as appropriate + if (m_imgui->checkbox(m_desc["show_supports"], m_show_supports)) { + m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); + force_refresh = true; + } + + m_imgui->end(); + + + if (remove_selected || remove_all) { + force_refresh = false; + m_parent.set_as_dirty(); + + if (remove_all) { + select_point(AllPoints); + delete_selected_points(); + } + if (remove_selected) + delete_selected_points(); + + if (first_run) { + first_run = false; + goto RENDER_AGAIN; + } + } + + if (force_refresh) + m_parent.set_as_dirty(); + + if (config_changed) + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE)); +} + +bool GLGizmoHollow::on_is_activable() const +{ + const Selection& selection = m_parent.get_selection(); + + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA + || !selection.is_from_single_instance()) + return false; + + // Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside. + const Selection::IndicesList& list = selection.get_volume_idxs(); + for (const auto& idx : list) + if (selection.get_volume(idx)->is_outside && selection.get_volume(idx)->composite_id.volume_id >= 0) + return false; + + return true; +} + +bool GLGizmoHollow::on_is_selectable() const +{ + return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA); +} + +std::string GLGizmoHollow::on_get_name() const +{ + return (_(L("Hollow and drill")) + " [H]").ToUTF8().data(); +} + + + +void GLGizmoHollow::on_set_state() +{ + // m_c->m_model_object pointer can be invalid (for instance because of undo/redo action), + // we should recover it from the object id + m_c->m_model_object = nullptr; + for (const auto mo : wxGetApp().model().objects) { + if (mo->id() == m_c->m_model_object_id) { + m_c->m_model_object = mo; + break; + } + } + + if (m_state == m_old_state) + return; + + if (m_state == On && m_old_state != On) { // the gizmo was just turned on + //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); + //m_c->update_from_backend(m_parent, m_c->m_model_object); + m_c->unstash_clipping_plane(); + update_clipping_plane(m_c->m_clipping_plane_distance != 0.f); + + // we'll now reload support points: + if (m_c->m_model_object) + reload_cache(); + + m_parent.toggle_model_objects_visibility(false); + if (m_c->m_model_object) { + m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); + } + + // Set default head diameter from config. + //const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; + //m_new_hole_radius = static_cast(cfg.option("support_head_front_diameter"))->value; + } + if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off + //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE)); + m_parent.toggle_model_objects_visibility(true); + m_c->stash_clipping_plane(); + m_c->m_clipping_plane_distance = 0.f; + update_clipping_plane(true); + // Release clippers and the AABB raycaster. + m_c->m_object_clipper.reset(); + m_c->m_supports_clipper.reset(); + //m_c->m_mesh_raycaster.reset(); + //m_c->m_cavity_mesh.reset(); + //m_c->m_volume_with_cavity.reset(); + } + m_old_state = m_state; +} + + + +void GLGizmoHollow::on_start_dragging() +{ + if (m_hover_id != -1) { + select_point(NoPoints); + select_point(m_hover_id); + m_hole_before_drag = m_c->m_model_object->sla_drain_holes[m_hover_id].pos; + } + else + m_hole_before_drag = Vec3f::Zero(); +} + + +void GLGizmoHollow::on_stop_dragging() +{ + if (m_hover_id != -1) { + Vec3f backup = m_c->m_model_object->sla_drain_holes[m_hover_id].pos; + + if (m_hole_before_drag != Vec3f::Zero() // some point was touched + && backup != m_hole_before_drag) // and it was moved, not just selected + { + m_c->m_model_object->sla_drain_holes[m_hover_id].pos = m_hole_before_drag; + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move drainage hole"))); + m_c->m_model_object->sla_drain_holes[m_hover_id].pos = backup; + } + } + m_hole_before_drag = Vec3f::Zero(); +} + + + +void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar) +{ + ar(m_c->m_clipping_plane_distance, + *m_c->m_clipping_plane, + m_c->m_model_object_id, + m_new_hole_radius, + m_new_hole_height, + m_selected, + m_selection_empty + ); +} + + + +void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const +{ + ar(m_c->m_clipping_plane_distance, + *m_c->m_clipping_plane, + m_c->m_model_object_id, + m_new_hole_radius, + m_new_hole_height, + m_selected, + m_selection_empty + ); +} + + + +void GLGizmoHollow::select_point(int i) +{ + if (i == AllPoints || i == NoPoints) { + m_selected.assign(m_selected.size(), i == AllPoints); + m_selection_empty = (i == NoPoints); + + if (i == AllPoints) { + m_new_hole_radius = m_c->m_model_object->sla_drain_holes[0].radius; + m_new_hole_height = m_c->m_model_object->sla_drain_holes[0].height - HoleStickOutLength; + } + } + else { + while (size_t(i) >= m_selected.size()) + m_selected.push_back(false); + m_selected[i] = true; + m_selection_empty = false; + m_new_hole_radius = m_c->m_model_object->sla_drain_holes[i].radius; + m_new_hole_height = m_c->m_model_object->sla_drain_holes[i].height - HoleStickOutLength; + } +} + + +void GLGizmoHollow::unselect_point(int i) +{ + m_selected[i] = false; + m_selection_empty = true; + for (const bool sel : m_selected) { + if (sel) { + m_selection_empty = false; + break; + } + } +} + +void GLGizmoHollow::reload_cache() +{ + m_selected.clear(); + m_selected.assign(m_c->m_model_object->sla_drain_holes.size(), false); +} + +void GLGizmoHollow::update_clipping_plane(bool keep_normal) const +{ + if (! m_c->m_model_object) + return; + Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ? + m_c->m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); + + const Vec3d& center = m_c->m_model_object->instances[m_c->m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift); + float dist = normal.dot(center); + *m_c->m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_c->m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius)); + m_parent.set_as_dirty(); +} + + + + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp new file mode 100644 index 0000000000..2daf28b2af --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -0,0 +1,126 @@ +#ifndef slic3r_GLGizmoHollow_hpp_ +#define slic3r_GLGizmoHollow_hpp_ + +#include "GLGizmoBase.hpp" +#include "slic3r/GUI/GLSelectionRectangle.hpp" + +#include +#include + +#include + + +namespace Slic3r { +namespace GUI { + +class ClippingPlane; +class MeshClipper; +class MeshRaycaster; +enum class SLAGizmoEventType : unsigned char; + +class GLGizmoHollow : public GLGizmoBase +{ +private: + mutable double m_z_shift = 0.; + bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); + + const float HoleStickOutLength = 1.f; + + GLUquadricObj* m_quadric; + + +public: + GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, CommonGizmosData* cd); + ~GLGizmoHollow() override; + void set_sla_support_data(ModelObject* model_object, const Selection& selection); + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + void delete_selected_points(); + ClippingPlane get_sla_clipping_plane() const; + + + std::pair get_hollowing_parameters() const; + void update_mesh_raycaster(std::unique_ptr &&rc); + void update_hollowed_mesh(std::unique_ptr &&mesh); + + bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); } + void update_clipping_plane(bool keep_normal = false) const; + +private: + bool on_init() override; + void on_update(const UpdateData& data) override; + void on_render() const override; + void on_render_for_picking() const override; + + void render_points(const Selection& selection, bool picking = false) const; + void render_clipping_plane(const Selection& selection) const; + void render_hollowed_mesh() const; + void hollow_mesh(bool postpone_error_messages = false); + bool unsaved_changes() const; + + bool m_show_supports = true; + float m_new_hole_radius = 2.f; // Size of a new hole. + float m_new_hole_height = 6.f; + mutable std::vector m_selected; // which holes are currently selected + + bool m_enable_hollowing = true; + + // Stashes to keep data for undo redo. Is taken after the editing + // is done, the data are updated continuously. + float m_offset_stash = 3.0f; + float m_quality_stash = 0.5f; + float m_closing_d_stash = 2.f; + Vec3f m_hole_before_drag = Vec3f::Zero(); + sla::DrainHoles m_holes_in_drilled_mesh; + + sla::DrainHoles m_holes_stash; + + //std::unique_ptr m_clipping_plane; + + // This map holds all translated description texts, so they can be easily referenced during layout calculations + // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. + std::map m_desc; + + GLSelectionRectangle m_selection_rectangle; + + bool m_wait_for_up_event = false; + bool m_selection_empty = true; + EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) + + std::vector> get_config_options(const std::vector& keys) const; + bool is_mesh_point_clipped(const Vec3d& point) const; + + // Methods that do the model_object and editing cache synchronization, + // editing mode selection, etc: + enum { + AllPoints = -2, + NoPoints, + }; + void select_point(int i); + void unselect_point(int i); + void reload_cache(); + +protected: + void on_set_state() override; + void on_set_hover_id() override + + { + if (int(m_c->m_model_object->sla_drain_holes.size()) <= m_hover_id) + m_hover_id = -1; + } + void on_start_dragging() override; + void on_stop_dragging() override; + void on_render_input_window(float x, float y, float bottom_limit) override; + + std::string on_get_name() const override; + bool on_is_activable() const override; + bool on_is_selectable() const override; + void on_load(cereal::BinaryInputArchive& ar) override; + void on_save(cereal::BinaryOutputArchive& ar) const override; +}; + + + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoHollow_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index a0e84d4903..05f33ae52c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -22,12 +22,12 @@ namespace Slic3r { namespace GUI { -GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) +GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, CommonGizmosData* cd) + : GLGizmoBase(parent, icon_filename, sprite_id, cd) , m_quadric(nullptr) , m_its(nullptr) { - m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); + m_c->m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); m_quadric = ::gluNewQuadric(); if (m_quadric != nullptr) // using GLU_FILL does not work when the instance's transformation @@ -63,40 +63,24 @@ bool GLGizmoSlaSupports::on_init() void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const Selection& selection) { - if (! model_object || selection.is_empty()) { - m_model_object = nullptr; - return; - } - - if (m_model_object != model_object || m_model_object_id != model_object->id()) { - m_model_object = model_object; - m_print_object_idx = -1; - } - - m_active_instance = selection.get_instance_idx(); - - if (model_object && selection.is_from_single_instance()) - { - // Cache the bb - it's needed for dealing with the clipping plane quite often - // It could be done inside update_mesh but one has to account for scaling of the instance. - //FIXME calling ModelObject::instance_bounding_box() is expensive! - m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius(); - - if (is_mesh_update_necessary()) { - update_mesh(); - reload_cache(); - } - - // If we triggered autogeneration before, check backend and fetch results if they are there - if (m_model_object->sla_points_status == sla::PointsStatus::Generating) - get_data_from_backend(); - + if (m_c->recent_update) { if (m_state == On) { m_parent.toggle_model_objects_visibility(false); - m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance); + m_parent.toggle_model_objects_visibility(/*! m_c->m_cavity_mesh*/ true, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); } else m_parent.toggle_model_objects_visibility(true, nullptr, -1); + + disable_editing_mode(); + if (m_c->m_model_object) + reload_cache(); + } + + // If we triggered autogeneration before, check backend and fetch results if they are there + if (m_c->m_model_object) { + if (m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating) + get_data_from_backend(); } } @@ -106,23 +90,22 @@ void GLGizmoSlaSupports::on_render() const { const Selection& selection = m_parent.get_selection(); - // If current m_model_object does not match selection, ask GLCanvas3D to turn us off + // If current m_c->m_model_object does not match selection, ask GLCanvas3D to turn us off if (m_state == On - && (m_model_object != selection.get_model()->objects[selection.get_object_idx()] - || m_active_instance != selection.get_instance_idx() - || m_model_object_id != m_model_object->id())) { + && (m_c->m_model_object != selection.get_model()->objects[selection.get_object_idx()] + || m_c->m_active_instance != selection.get_instance_idx() + || m_c->m_model_object_id != m_c->m_model_object->id())) { m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS)); return; } - if (! m_its || ! m_mesh) - const_cast(this)->update_mesh(); - glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); + render_hollowed_mesh(); + if (m_quadric != nullptr && selection.is_from_single_instance()) render_points(selection, false); @@ -134,9 +117,32 @@ void GLGizmoSlaSupports::on_render() const +void GLGizmoSlaSupports::render_hollowed_mesh() const +{ + /*if (m_c->m_volume_with_cavity) { + m_c->m_volume_with_cavity->set_sla_shift_z(m_z_shift); + m_parent.get_shader().start_using(); + + GLint current_program_id; + glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); + GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; + GLint print_box_detection_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1; + GLint print_box_worldmatrix_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1; + glcheck(); + m_c->m_volume_with_cavity->set_render_color(); + const Geometry::Transformation& volume_trafo = m_c->m_model_object->volumes.front()->get_transformation(); + m_c->m_volume_with_cavity->set_volume_transformation(volume_trafo); + m_c->m_volume_with_cavity->set_instance_transformation(m_c->m_model_object->instances[size_t(m_c->m_active_instance)]->get_transformation()); + m_c->m_volume_with_cavity->render(color_id, print_box_detection_id, print_box_worldmatrix_id); + m_parent.get_shader().stop_using(); + }*/ +} + + + void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const { - if (m_clipping_plane_distance == 0.f) + if (m_c->m_clipping_plane_distance == 0.f || m_c->m_mesh->empty()) return; // Get transformation of the instance @@ -154,66 +160,66 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const 1.)); // Now initialize the TMS for the object, perform the cut and save the result. - if (! m_object_clipper) { - m_object_clipper.reset(new MeshClipper); - m_object_clipper->set_mesh(*m_mesh); + if (! m_c->m_object_clipper) { + m_c->m_object_clipper.reset(new MeshClipper); + m_c->m_object_clipper->set_mesh(*m_c->mesh()); } - m_object_clipper->set_plane(*m_clipping_plane); - m_object_clipper->set_transformation(trafo); + m_c->m_object_clipper->set_plane(*m_c->m_clipping_plane); + m_c->m_object_clipper->set_transformation(trafo); // Next, ask the backend if supports are already calculated. If so, we are gonna cut them too. // First we need a pointer to the respective SLAPrintObject. The index into objects vector is // cached so we don't have todo it on each render. We only search for the po if needed: - if (m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_print_objects_count) { - m_print_objects_count = m_parent.sla_print()->objects().size(); - m_print_object_idx = -1; + if (m_c->m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_c->m_print_objects_count) { + m_c->m_print_objects_count = m_parent.sla_print()->objects().size(); + m_c->m_print_object_idx = -1; for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { - ++m_print_object_idx; - if (po->model_object()->id() == m_model_object->id()) + ++m_c->m_print_object_idx; + if (po->model_object()->id() == m_c->m_model_object->id()) break; } } - if (m_print_object_idx >= 0) { - const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_print_object_idx]; + if (m_c->m_print_object_idx >= 0) { + const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_c->m_print_object_idx]; - if (print_object->is_step_done(slaposSupportTree)) { + if (print_object->is_step_done(slaposSupportTree) && !print_object->get_mesh(slaposSupportTree).empty()) { // If the supports are already calculated, save the timestamp of the respective step // so we can later tell they were recalculated. size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp; - if (! m_supports_clipper || (int)timestamp != m_old_timestamp) { + if (! m_c->m_supports_clipper || (int)timestamp != m_c->m_old_timestamp) { // The timestamp has changed. - m_supports_clipper.reset(new MeshClipper); + m_c->m_supports_clipper.reset(new MeshClipper); // The mesh should already have the shared vertices calculated. - m_supports_clipper->set_mesh(print_object->support_mesh()); - m_old_timestamp = timestamp; + m_c->m_supports_clipper->set_mesh(print_object->support_mesh()); + m_c->m_old_timestamp = timestamp; } - m_supports_clipper->set_plane(*m_clipping_plane); - m_supports_clipper->set_transformation(supports_trafo); + m_c->m_supports_clipper->set_plane(*m_c->m_clipping_plane); + m_c->m_supports_clipper->set_transformation(supports_trafo); } else // The supports are not valid. We better dump the cached data. - m_supports_clipper.reset(); + m_c->m_supports_clipper.reset(); } // At this point we have the triangulated cuts for both the object and supports - let's render. - if (! m_object_clipper->get_triangles().empty()) { + if (! m_c->m_object_clipper->get_triangles().empty()) { ::glPushMatrix(); ::glColor3f(1.0f, 0.37f, 0.0f); ::glBegin(GL_TRIANGLES); - for (const Vec3f& point : m_object_clipper->get_triangles()) + for (const Vec3f& point : m_c->m_object_clipper->get_triangles()) ::glVertex3f(point(0), point(1), point(2)); ::glEnd(); ::glPopMatrix(); } - if (m_supports_clipper && ! m_supports_clipper->get_triangles().empty() && !m_editing_mode) { + if (m_c->m_supports_clipper && ! m_c->m_supports_clipper->get_triangles().empty() && !m_editing_mode) { // The supports are hidden in the editing mode, so it makes no sense to render the cuts. ::glPushMatrix(); ::glColor3f(1.0f, 0.f, 0.37f); ::glBegin(GL_TRIANGLES); - for (const Vec3f& point : m_supports_clipper->get_triangles()) + for (const Vec3f& point : m_c->m_supports_clipper->get_triangles()) ::glVertex3f(point(0), point(1), point(2)); ::glEnd(); ::glPopMatrix(); @@ -230,6 +236,7 @@ void GLGizmoSlaSupports::on_render_for_picking() const glsafe(::glEnable(GL_DEPTH_TEST)); render_points(selection, true); + render_hollowed_mesh(); } void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const @@ -298,7 +305,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) if (m_editing_mode) { // in case the normal is not yet cached, find and cache it if (m_editing_cache[i].normal == Vec3f::Zero()) - m_mesh_raycaster->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); + m_c->m_mesh_raycaster->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); @@ -327,6 +334,44 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive)); } + // Now render the drain holes: + if (! m_c->has_drilled_mesh()) { + render_color[0] = 0.7f; + render_color[1] = 0.7f; + render_color[2] = 0.7f; + render_color[3] = 0.7f; + glsafe(::glColor4fv(render_color)); + for (const sla::DrainHole& drain_hole : m_c->m_model_object->sla_drain_holes) { + // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. + glsafe(::glPushMatrix()); + glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2))); + glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); + + if (vol->is_left_handed()) + glFrontFace(GL_CW); + + // Matrices set, we can render the point mark now. + + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); + Eigen::AngleAxisd aa(q); + glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); + glsafe(::glPushMatrix()); + glsafe(::glTranslated(0., 0., -drain_hole.height)); + ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height, 24, 1); + glsafe(::glTranslated(0., 0., drain_hole.height)); + ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); + glsafe(::glTranslated(0., 0., -drain_hole.height)); + glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); + ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); + glsafe(::glPopMatrix()); + + if (vol->is_left_handed()) + glFrontFace(GL_CCW); + glsafe(::glPopMatrix()); + } + } + if (!picking) glsafe(::glDisable(GL_LIGHTING)); @@ -337,41 +382,12 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const { - if (m_clipping_plane_distance == 0.f) + if (m_c->m_clipping_plane_distance == 0.f) return false; - Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point; + Vec3d transformed_point = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation().get_matrix() * point; transformed_point(2) += m_z_shift; - return m_clipping_plane->is_point_clipped(transformed_point); -} - - - -bool GLGizmoSlaSupports::is_mesh_update_necessary() const -{ - return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty()) - && ((m_model_object->id() != m_model_object_id) || m_its == nullptr); -} - - - -void GLGizmoSlaSupports::update_mesh() -{ - if (! m_model_object) - return; - - wxBusyCursor wait; - // this way we can use that mesh directly. - // This mesh does not account for the possible Z up SLA offset. - m_mesh = &m_model_object->volumes.front()->mesh(); - m_its = &m_mesh->its; - - // If this is different mesh than last time or if the AABB tree is uninitialized, recalculate it. - if (m_model_object_id != m_model_object->id() || ! m_mesh_raycaster) - m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh)); - - m_model_object_id = m_model_object->id(); - disable_editing_mode(); + return m_c->m_clipping_plane->is_point_clipped(transformed_point); } @@ -380,9 +396,8 @@ void GLGizmoSlaSupports::update_mesh() // Return false if no intersection was found, true otherwise. bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) { - // if the gizmo doesn't have the V, F structures for igl, calculate them first: - if (! m_mesh_raycaster) - update_mesh(); + if (! m_c->m_mesh_raycaster) + return false; const Camera& camera = m_parent.get_camera(); const Selection& selection = m_parent.get_selection(); @@ -393,13 +408,35 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pairunproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) { - // Return both the point and the facet normal. - pos_and_normal = std::make_pair(hit, normal); - return true; + if (m_c->m_mesh_raycaster->unproject_on_mesh( + mouse_pos, + trafo.get_matrix(), + camera, + hit, + normal, + m_c->m_clipping_plane_distance != 0.f ? m_c->m_clipping_plane.get() : nullptr)) + { + // Check whether the hit is in a hole + bool in_hole = false; + // In case the hollowed and drilled mesh is available, we can allow + // placing points in holes, because they should never end up + // on surface that's been drilled away. + if (! m_c->has_drilled_mesh()) { + for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) { + if (hole.is_inside(hit)) { + in_hole = true; + break; + } + } + } + if (! in_hole) { + // Return both the point and the facet normal. + pos_and_normal = std::make_pair(hit, normal); + return true; + } } - else - return false; + + return false; } // Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. @@ -459,7 +496,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); // First collect positions of all the points in world coordinates. - Geometry::Transformation trafo = m_model_object->instances[m_active_instance]->get_transformation(); + Geometry::Transformation trafo = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation(); trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); std::vector points; for (unsigned int i=0; i()); // Only select/deselect points that are actually visible - for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) + for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_c->m_clipping_plane.get())) { if (rectangle_status == GLSelectionRectangle::Deselect) unselect_point(points_idxs[idx]); @@ -549,13 +586,13 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } if (action == SLAGizmoEventType::MouseWheelUp && control_down) { - m_clipping_plane_distance = std::min(1.f, m_clipping_plane_distance + 0.01f); + m_c->m_clipping_plane_distance = std::min(1.f, m_c->m_clipping_plane_distance + 0.01f); update_clipping_plane(true); return true; } if (action == SLAGizmoEventType::MouseWheelDown && control_down) { - m_clipping_plane_distance = std::max(0.f, m_clipping_plane_distance - 0.01f); + m_c->m_clipping_plane_distance = std::max(0.f, m_c->m_clipping_plane_distance - 0.01f); update_clipping_plane(true); return true; } @@ -610,10 +647,10 @@ std::vector GLGizmoSlaSupports::get_config_options(const st { std::vector out; - if (!m_model_object) + if (!m_c->m_model_object) return out; - const DynamicPrintConfig& object_cfg = m_model_object->config; + const DynamicPrintConfig& object_cfg = m_c->m_model_object->config; const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; std::unique_ptr default_cfg = nullptr; @@ -636,10 +673,10 @@ std::vector GLGizmoSlaSupports::get_config_options(const st ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const { - if (!m_model_object || m_state == Off || m_clipping_plane_distance == 0.f) + if (!m_c->m_model_object || m_state == Off || m_c->m_clipping_plane_distance == 0.f) return ClippingPlane::ClipsNothing(); else - return ClippingPlane(-m_clipping_plane->get_normal(), m_clipping_plane->get_data()[3]); + return ClippingPlane(-m_c->m_clipping_plane->get_normal(), m_c->m_clipping_plane->get_data()[3]); } @@ -665,7 +702,7 @@ void GLGizmoSlaSupports::find_intersecting_facets(const igl::AABBvolumes.front()->mesh); + TriangleMeshSlicer tms(&m_c->m_model_object->volumes.front()->mesh); Vec3f normal(0.f, 1.f, 1.f); double d = 0.; @@ -689,7 +726,7 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_l static float last_y = 0.0f; static float last_h = 0.0f; - if (!m_model_object) + if (! m_c->m_model_object) return; bool first_run = true; // This is a hack to redraw the button when all points are removed, @@ -699,7 +736,7 @@ RENDER_AGAIN: //const ImVec2 window_size(m_imgui->scaled(18.f, 16.f)); //ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) )); //ImGui::SetNextWindowSize(ImVec2(window_size)); - + m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); // adjust window position to avoid overlap the view toolbar @@ -728,7 +765,6 @@ RENDER_AGAIN: float window_width = minimal_slider_width + std::max(std::max(settings_sliders_left, clipping_slider_left), diameter_slider_left); window_width = std::max(std::max(window_width, buttons_width_approx), lock_supports_width_approx); - bool force_refresh = false; bool remove_selected = false; bool remove_all = false; @@ -827,15 +863,15 @@ RENDER_AGAIN: m_density_stash = density; } if (slider_edited) { - m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; - m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; + m_c->m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; + m_c->m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; } if (slider_released) { - m_model_object->config.opt("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash; - m_model_object->config.opt("support_points_density_relative", true)->value = (int)m_density_stash; + m_c->m_model_object->config.opt("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash; + m_c->m_model_object->config.opt("support_points_density_relative", true)->value = (int)m_density_stash; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change"))); - m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; - m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; + m_c->m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; + m_c->m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; wxGetApp().obj_list()->update_and_show_object_settings_item(); } @@ -853,16 +889,16 @@ RENDER_AGAIN: m_imgui->disabled_end(); // m_imgui->text(""); - // m_imgui->text(m_model_object->sla_points_status == sla::PointsStatus::NoPoints ? _(L("No points (will be autogenerated)")) : - // (m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated ? _(L("Autogenerated points (no modifications)")) : - // (m_model_object->sla_points_status == sla::PointsStatus::UserModified ? _(L("User-modified points")) : - // (m_model_object->sla_points_status == sla::PointsStatus::Generating ? _(L("Generation in progress...")) : "UNKNOWN STATUS")))); + // m_imgui->text(m_c->m_model_object->sla_points_status == sla::PointsStatus::NoPoints ? _(L("No points (will be autogenerated)")) : + // (m_c->m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated ? _(L("Autogenerated points (no modifications)")) : + // (m_c->m_model_object->sla_points_status == sla::PointsStatus::UserModified ? _(L("User-modified points")) : + // (m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating ? _(L("Generation in progress...")) : "UNKNOWN STATUS")))); } // Following is rendered in both editing and non-editing mode: ImGui::Separator(); - if (m_clipping_plane_distance == 0.f) + if (m_c->m_clipping_plane_distance == 0.f) { ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("clipping_of_view")); @@ -877,7 +913,7 @@ RENDER_AGAIN: ImGui::SameLine(clipping_slider_left); ImGui::PushItemWidth(window_width - clipping_slider_left); - if (ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) + if (ImGui::SliderFloat(" ", &m_c->m_clipping_plane_distance, 0.f, 1.f, "%.2f")) update_clipping_plane(true); @@ -890,12 +926,6 @@ RENDER_AGAIN: m_imgui->end(); - if (m_editing_mode != m_old_editing_state) { // user toggled between editing/non-editing mode - m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode, m_model_object, m_active_instance); - force_refresh = true; - } - m_old_editing_state = m_editing_mode; - if (remove_selected || remove_all) { force_refresh = false; m_parent.set_as_dirty(); @@ -952,12 +982,12 @@ std::string GLGizmoSlaSupports::on_get_name() const void GLGizmoSlaSupports::on_set_state() { - // m_model_object pointer can be invalid (for instance because of undo/redo action), + // m_c->m_model_object pointer can be invalid (for instance because of undo/redo action), // we should recover it from the object id - m_model_object = nullptr; + m_c->m_model_object = nullptr; for (const auto mo : wxGetApp().model().objects) { - if (mo->id() == m_model_object_id) { - m_model_object = mo; + if (mo->id() == m_c->m_model_object_id) { + m_c->m_model_object = mo; break; } } @@ -967,23 +997,26 @@ void GLGizmoSlaSupports::on_set_state() if (m_state == On && m_old_state != On) { // the gizmo was just turned on Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); - if (is_mesh_update_necessary()) - update_mesh(); + + m_c->unstash_clipping_plane(); + update_clipping_plane(m_c->m_clipping_plane_distance != 0.f); + // we'll now reload support points: - if (m_model_object) + if (m_c->m_model_object) reload_cache(); m_parent.toggle_model_objects_visibility(false); - if (m_model_object) - m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance); + if (m_c->m_model_object /*&& ! m_c->m_cavity_mesh*/) + m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); // Set default head diameter from config. const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value; } if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off - bool will_ask = m_model_object && m_editing_mode && unsaved_changes(); + bool will_ask = m_c->m_model_object && m_editing_mode && unsaved_changes(); if (will_ask) { wxGetApp().CallAfter([this]() { // Following is called through CallAfter, because otherwise there was a problem @@ -1004,12 +1037,14 @@ void GLGizmoSlaSupports::on_set_state() Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); m_parent.toggle_model_objects_visibility(true); m_normal_cache.clear(); - m_clipping_plane_distance = 0.f; + m_c->stash_clipping_plane(); + m_c->m_clipping_plane_distance = 0.f; + update_clipping_plane(true); // Release clippers and the AABB raycaster. m_its = nullptr; - m_object_clipper.reset(); - m_supports_clipper.reset(); - m_mesh_raycaster.reset(); + m_c->m_object_clipper.reset(); + m_c->m_supports_clipper.reset(); + //m_c->m_mesh_raycaster.reset(); } } m_old_state = m_state; @@ -1049,9 +1084,9 @@ void GLGizmoSlaSupports::on_stop_dragging() void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar) { - ar(m_clipping_plane_distance, - *m_clipping_plane, - m_model_object_id, + ar(m_c->m_clipping_plane_distance, + *m_c->m_clipping_plane, + m_c->m_model_object_id, m_new_point_head_diameter, m_normal_cache, m_editing_cache, @@ -1063,9 +1098,9 @@ void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar) void GLGizmoSlaSupports::on_save(cereal::BinaryOutputArchive& ar) const { - ar(m_clipping_plane_distance, - *m_clipping_plane, - m_model_object_id, + ar(m_c->m_clipping_plane_distance, + *m_c->m_clipping_plane, + m_c->m_model_object_id, m_new_point_head_diameter, m_normal_cache, m_editing_cache, @@ -1143,9 +1178,9 @@ void GLGizmoSlaSupports::editing_mode_apply_changes() for (const CacheEntry& ce : m_editing_cache) m_normal_cache.push_back(ce.support_point); - m_model_object->sla_points_status = sla::PointsStatus::UserModified; - m_model_object->sla_support_points.clear(); - m_model_object->sla_support_points = m_normal_cache; + m_c->m_model_object->sla_points_status = sla::PointsStatus::UserModified; + m_c->m_model_object->sla_support_points.clear(); + m_c->m_model_object->sla_support_points = m_normal_cache; reslice_SLA_supports(); } @@ -1156,19 +1191,22 @@ void GLGizmoSlaSupports::editing_mode_apply_changes() void GLGizmoSlaSupports::reload_cache() { m_normal_cache.clear(); - if (m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated || m_model_object->sla_points_status == sla::PointsStatus::Generating) + if (m_c->m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated || m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating) get_data_from_backend(); else - for (const sla::SupportPoint& point : m_model_object->sla_support_points) + for (const sla::SupportPoint& point : m_c->m_model_object->sla_support_points) m_normal_cache.emplace_back(point); } bool GLGizmoSlaSupports::has_backend_supports() const { + if (! m_c->m_model_object) + return false; + // find SlaPrintObject with this ID for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { - if (po->model_object()->id() == m_model_object->id()) + if (po->model_object()->id() == m_c->m_model_object->id()) return po->is_step_done(slaposSupportPoints); } return false; @@ -1176,7 +1214,7 @@ bool GLGizmoSlaSupports::has_backend_supports() const void GLGizmoSlaSupports::reslice_SLA_supports(bool postpone_error_messages) const { - wxGetApp().CallAfter([this, postpone_error_messages]() { wxGetApp().plater()->reslice_SLA_supports(*m_model_object, postpone_error_messages); }); + wxGetApp().CallAfter([this, postpone_error_messages]() { wxGetApp().plater()->reslice_SLA_supports(*m_c->m_model_object, postpone_error_messages); }); } void GLGizmoSlaSupports::get_data_from_backend() @@ -1186,14 +1224,14 @@ void GLGizmoSlaSupports::get_data_from_backend() // find the respective SLAPrintObject, we need a pointer to it for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { - if (po->model_object()->id() == m_model_object->id()) { + if (po->model_object()->id() == m_c->m_model_object->id()) { m_normal_cache.clear(); const std::vector& points = po->get_support_points(); auto mat = po->trafo().inverse().cast(); for (unsigned int i=0; isla_points_status = sla::PointsStatus::AutoGenerated; + m_c->m_model_object->sla_points_status = sla::PointsStatus::AutoGenerated; break; } } @@ -1210,10 +1248,10 @@ void GLGizmoSlaSupports::auto_generate() _(L("Are you sure you want to do it?")) + "\n", _(L("Warning")), wxICON_WARNING | wxYES | wxNO); - if (m_model_object->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) { + if (m_c->m_model_object->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Autogenerate support points"))); wxGetApp().CallAfter([this]() { reslice_SLA_supports(); }); - m_model_object->sla_points_status = sla::PointsStatus::Generating; + m_c->m_model_object->sla_points_status = sla::PointsStatus::Generating; } } @@ -1227,6 +1265,9 @@ void GLGizmoSlaSupports::switch_to_editing_mode() for (const sla::SupportPoint& sp : m_normal_cache) m_editing_cache.emplace_back(sp); select_point(NoPoints); + + m_parent.toggle_sla_auxiliaries_visibility(false, m_c->m_model_object, m_c->m_active_instance); + m_parent.set_as_dirty(); } @@ -1235,6 +1276,8 @@ void GLGizmoSlaSupports::disable_editing_mode() if (m_editing_mode) { m_editing_mode = false; wxGetApp().plater()->leave_gizmos_stack(); + m_parent.toggle_sla_auxiliaries_visibility(true, m_c->m_model_object, m_c->m_active_instance); + m_parent.set_as_dirty(); } } @@ -1255,12 +1298,14 @@ bool GLGizmoSlaSupports::unsaved_changes() const void GLGizmoSlaSupports::update_clipping_plane(bool keep_normal) const { - Vec3d normal = (keep_normal && m_clipping_plane->get_normal() != Vec3d::Zero() ? - m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); + if (! m_c->m_model_object) + return; + Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ? + m_c->m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); - const Vec3d& center = m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift); + const Vec3d& center = m_c->m_model_object->instances[m_c->m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift); float dist = normal.dot(center); - *m_clipping_plane = ClippingPlane(normal, (dist - (-m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_active_instance_bb_radius)); + *m_c->m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_c->m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius)); m_parent.set_as_dirty(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 15b2df80ea..a2da5e506a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -4,7 +4,7 @@ #include "GLGizmoBase.hpp" #include "slic3r/GUI/GLSelectionRectangle.hpp" -#include "libslic3r/SLA/SLACommon.hpp" +#include "libslic3r/SLA/Common.hpp" #include #include @@ -21,10 +21,10 @@ enum class SLAGizmoEventType : unsigned char; class GLGizmoSlaSupports : public GLGizmoBase { private: - ModelObject* m_model_object = nullptr; - ObjectID m_model_object_id = 0; - int m_active_instance = -1; - float m_active_instance_bb_radius; // to cache the bb + //ModelObject* m_model_object = nullptr; + //ObjectID m_model_object_id = 0; + //int m_active_instance = -1; + //float m_active_instance_bb_radius; // to cache the bb mutable double m_z_shift = 0.f; bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); @@ -34,15 +34,12 @@ private: typedef Eigen::Map> MapMatrixXfUnaligned; typedef Eigen::Map> MapMatrixXiUnaligned; - std::unique_ptr m_mesh_raycaster; - const TriangleMesh* m_mesh; + //std::unique_ptr m_mesh_raycaster; + //const TriangleMesh* m_mesh; const indexed_triangle_set* m_its; - mutable const TriangleMesh* m_supports_mesh; - mutable std::vector m_triangles; - mutable std::vector m_supports_triangles; - mutable int m_old_timestamp = -1; - mutable int m_print_object_idx = -1; - mutable int m_print_objects_count = -1; + //mutable int m_old_timestamp = -1; + //mutable int m_print_object_idx = -1; + //mutable int m_print_objects_count = -1; class CacheEntry { public: @@ -72,7 +69,7 @@ private: }; public: - GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, CommonGizmosData* cd); ~GLGizmoSlaSupports() override; void set_sla_support_data(ModelObject* model_object, const Selection& selection); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); @@ -83,6 +80,7 @@ public: bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); } bool has_backend_supports() const; void reslice_SLA_supports(bool postpone_error_messages = false) const; + void update_clipping_plane(bool keep_normal = false) const; private: bool on_init() override; @@ -93,13 +91,11 @@ private: //void render_selection_rectangle() const; void render_points(const Selection& selection, bool picking = false) const; void render_clipping_plane(const Selection& selection) const; - bool is_mesh_update_necessary() const; - void update_mesh(); + void render_hollowed_mesh() const; bool unsaved_changes() const; bool m_lock_unique_islands = false; bool m_editing_mode = false; // Is editing mode active? - bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes). float m_new_point_head_diameter; // Size of a new point. CacheEntry m_point_before_drag; // undo/redo - so we know what state was edited float m_old_point_head_diameter = 0.; // the same @@ -108,8 +104,7 @@ private: mutable std::vector m_editing_cache; // a support point and whether it is currently selected std::vector m_normal_cache; // to restore after discarding changes or undo/redo - float m_clipping_plane_distance = 0.f; - std::unique_ptr m_clipping_plane; + //std::unique_ptr m_clipping_plane; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. @@ -121,11 +116,12 @@ private: bool m_selection_empty = true; EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) - mutable std::unique_ptr m_object_clipper; - mutable std::unique_ptr m_supports_clipper; + //mutable std::unique_ptr m_object_clipper; + //mutable std::unique_ptr m_supports_clipper; std::vector get_config_options(const std::vector& keys) const; bool is_mesh_point_clipped(const Vec3d& point) const; + bool is_point_in_hole(const Vec3f& pt) const; //void find_intersecting_facets(const igl::AABB* aabb, const Vec3f& normal, double offset, std::vector& out) const; // Methods that do the model_object and editing cache synchronization, @@ -144,7 +140,6 @@ private: void switch_to_editing_mode(); void disable_editing_mode(); void reset_clipping_plane_normal() const; - void update_clipping_plane(bool keep_normal = false) const; protected: void on_set_state() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmos.hpp b/src/slic3r/GUI/Gizmos/GLGizmos.hpp index 272fa098a3..9f97c42b46 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmos.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmos.hpp @@ -32,5 +32,6 @@ enum class SLAGizmoEventType : unsigned char { #include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp" #include "slic3r/GUI/Gizmos/GLGizmoCut.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" #endif //slic3r_GLGizmos_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index e639e3d892..7e334c29f2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -83,12 +83,16 @@ bool GLGizmosManager::init() return false; } + m_common_gizmos_data.reset(new CommonGizmosData()); + + // Order of gizmos in the vector must match order in EType! m_gizmos.emplace_back(new GLGizmoMove3D(m_parent, "move.svg", 0)); m_gizmos.emplace_back(new GLGizmoScale3D(m_parent, "scale.svg", 1)); m_gizmos.emplace_back(new GLGizmoRotate3D(m_parent, "rotate.svg", 2)); m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, "place.svg", 3)); m_gizmos.emplace_back(new GLGizmoCut(m_parent, "cut.svg", 4)); - m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 5)); + m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", 5, m_common_gizmos_data.get())); + m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 6, m_common_gizmos_data.get())); for (auto& gizmo : m_gizmos) { if (! gizmo->init()) { @@ -344,7 +348,24 @@ void GLGizmosManager::set_sla_support_data(ModelObject* model_object) if (!m_enabled || m_gizmos.empty()) return; - dynamic_cast(m_gizmos[SlaSupports].get())->set_sla_support_data(model_object, m_parent.get_selection()); + auto* gizmo_supports = dynamic_cast(m_gizmos[SlaSupports].get()); + auto* gizmo_hollow = dynamic_cast(m_gizmos[Hollow].get()); + + + // Update common data for hollowing and sla support gizmos. + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) { + if (m_common_gizmos_data->update_from_backend(m_parent, model_object)) { + // FIXME: this is a hack to make that the clipping plane is + // updated when the update set its position to zero. The clipping + // plane itself should be common, including the update_function. + // Then update_from_backend could do it itself. + gizmo_supports->update_clipping_plane(); + gizmo_hollow->update_clipping_plane(); + } + } + + gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection()); + gizmo_hollow->set_sla_support_data(model_object, m_parent.get_selection()); } // Returns true if the gizmo used the event to do something, false otherwise. @@ -353,15 +374,22 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p if (!m_enabled || m_gizmos.empty()) return false; - return dynamic_cast(m_gizmos[SlaSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + if (m_current == SlaSupports) + return dynamic_cast(m_gizmos[SlaSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + if (m_current == Hollow) + return dynamic_cast(m_gizmos[Hollow].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + return false; } ClippingPlane GLGizmosManager::get_sla_clipping_plane() const { - if (!m_enabled || m_current != SlaSupports || m_gizmos.empty()) + if (!m_enabled || (m_current != SlaSupports && m_current != Hollow) || m_gizmos.empty()) return ClippingPlane::ClipsNothing(); - return dynamic_cast(m_gizmos[SlaSupports].get())->get_sla_clipping_plane(); + if (m_current == SlaSupports) + return dynamic_cast(m_gizmos[SlaSupports].get())->get_sla_clipping_plane(); + else + return dynamic_cast(m_gizmos[Hollow].get())->get_sla_clipping_plane(); } bool GLGizmosManager::wants_reslice_supports_on_undo() const @@ -401,7 +429,7 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) { bool processed = false; - if (m_current == SlaSupports) { + if (m_current == SlaSupports || m_current == Hollow) { float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) processed = true; @@ -459,7 +487,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) if (evt.LeftDown()) { - if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) + if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) // the gizmo got the event and took some action, there is no need to do anything more processed = true; else if (!selection.is_empty() && grabber_contains_mouse()) { @@ -477,17 +505,17 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) processed = true; } } - else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::RightDown)) + else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::RightDown)) { // we need to set the following right up as processed to avoid showing the context menu if the user release the mouse over the object pending_right_up = true; // event was taken care of by the SlaSupports gizmo processed = true; } - else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) && (m_current == SlaSupports)) - // don't allow dragging objects with the Sla gizmo on + else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) && (m_current == SlaSupports || m_current == Hollow)) + // don't allow dragging objects with the Sla gizmo on processed = true; - else if (evt.Dragging() && (m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) + else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) { // the gizmo got the event and took some action, no need to do anything more here m_parent.set_as_dirty(); @@ -513,10 +541,10 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) case Scale: { // Apply new temporary scale factors - TransformationType transformation_type(TransformationType::Local_Absolute_Joint); - if (evt.AltDown()) - transformation_type.set_independent(); - selection.scale(get_scale(), transformation_type); + TransformationType transformation_type(TransformationType::Local_Absolute_Joint); + if (evt.AltDown()) + transformation_type.set_independent(); + selection.scale(get_scale(), transformation_type); if (evt.ControlDown()) selection.translate(get_scale_offset(), true); wxGetApp().obj_manipul()->set_dirty(); @@ -542,12 +570,9 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) else if (evt.LeftUp() && is_dragging()) { switch (m_current) { - case Move : m_parent.do_move(L("Gizmo-Move")); - break; - case Scale : m_parent.do_scale(L("Gizmo-Scale")); - break; - case Rotate : m_parent.do_rotate(L("Gizmo-Rotate")); - break; + case Move : m_parent.do_move(L("Gizmo-Move")); break; + case Scale : m_parent.do_scale(L("Gizmo-Scale")); break; + case Rotate : m_parent.do_rotate(L("Gizmo-Rotate")); break; default : break; } @@ -563,7 +588,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) processed = true; } - else if (evt.LeftUp() && (m_current == SlaSupports) && !m_parent.is_mouse_dragging()) + else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow) && !m_parent.is_mouse_dragging()) { // in case SLA gizmo is selected, we just pass the LeftUp event and stop processing - neither // object moving or selecting is suppressed in that case @@ -628,7 +653,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) #endif /* __APPLE__ */ { // Sla gizmo selects all support points - if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::SelectAll)) + if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::SelectAll)) processed = true; break; @@ -662,7 +687,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case 'r' : case 'R' : { - if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) + if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) processed = true; break; @@ -674,7 +699,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case WXK_DELETE: #endif /* __APPLE__ */ { - if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::Delete)) + if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Delete)) processed = true; break; @@ -695,7 +720,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) { if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::ManualEditing)) processed = true; - + break; } case 'F': @@ -736,20 +761,31 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) if (evt.GetEventType() == wxEVT_KEY_UP) { - if (m_current == SlaSupports) + if (m_current == SlaSupports || m_current == Hollow) { - GLGizmoSlaSupports* gizmo = dynamic_cast(get_current()); + bool is_editing = true; + bool is_rectangle_dragging = false; + + if (m_current == SlaSupports) { + GLGizmoSlaSupports* gizmo = dynamic_cast(get_current()); + is_editing = gizmo->is_in_editing_mode(); + is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); + } + else { + GLGizmoHollow* gizmo = dynamic_cast(get_current()); + is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); + } if (keyCode == WXK_SHIFT) { // shift has been just released - SLA gizmo might want to close rectangular selection. - if (gizmo_event(SLAGizmoEventType::ShiftUp) || (gizmo->is_in_editing_mode() && gizmo->is_selection_rectangle_dragging())) + if (gizmo_event(SLAGizmoEventType::ShiftUp) || (is_editing && is_rectangle_dragging)) processed = true; } else if (keyCode == WXK_ALT) { // alt has been just released - SLA gizmo might want to close rectangular selection. - if (gizmo_event(SLAGizmoEventType::AltUp) || (gizmo->is_in_editing_mode() && gizmo->is_selection_rectangle_dragging())) + if (gizmo_event(SLAGizmoEventType::AltUp) || (is_editing && is_rectangle_dragging)) processed = true; } } @@ -765,6 +801,21 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) // m_parent.set_cursor(GLCanvas3D::Cross); processed = true; } + else if (m_current == Cut) + { + auto do_move = [this, &processed](double delta_z) { + GLGizmoCut* cut = dynamic_cast(get_current()); + cut->set_cut_z(delta_z + cut->get_cut_z()); + processed = true; + }; + + switch (keyCode) + { + case WXK_NUMPAD_UP: case WXK_UP: { do_move(1.0); break; } + case WXK_NUMPAD_DOWN: case WXK_DOWN: { do_move(-1.0); break; } + default: { break; } + } + } } if (processed) @@ -838,14 +889,9 @@ void GLGizmosManager::render_background(float left, float top, float right, floa void GLGizmosManager::do_render_overlay() const { -#if ENABLE_MODIFIED_TOOLBAR_TEXTURES std::vector selectable_idxs = get_selectable_idxs(); if (selectable_idxs.empty()) return; -#else - if (m_gizmos.empty()) - return; -#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES float cnv_w = (float)m_parent.get_canvas_size().get_width(); float cnv_h = (float)m_parent.get_canvas_size().get_height(); @@ -877,7 +923,6 @@ void GLGizmosManager::do_render_overlay() const int tex_width = m_icons_texture.get_width(); int tex_height = m_icons_texture.get_height(); -#if ENABLE_MODIFIED_TOOLBAR_TEXTURES if ((icons_texture_id == 0) || (tex_width <= 1) || (tex_height <= 1)) return; @@ -887,39 +932,18 @@ void GLGizmosManager::do_render_overlay() const // tiles in the texture are spaced by 1 pixel float u_offset = 1.0f / (float)tex_width; float v_offset = 1.0f / (float)tex_height; -#else - if ((icons_texture_id == 0) || (tex_width <= 0) || (tex_height <= 0)) - return; - float inv_tex_width = (tex_width != 0) ? 1.0f / tex_width : 0.0f; - float inv_tex_height = (tex_height != 0) ? 1.0f / tex_height : 0.0f; -#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES - -#if ENABLE_MODIFIED_TOOLBAR_TEXTURES for (size_t idx : selectable_idxs) -#else - for (size_t idx : get_selectable_idxs()) -#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES { GLGizmoBase* gizmo = m_gizmos[idx].get(); unsigned int sprite_id = gizmo->get_sprite_id(); int icon_idx = (m_current == idx) ? 2 : ((m_hover == idx) ? 1 : (gizmo->is_activable()? 0 : 3)); -#if ENABLE_MODIFIED_TOOLBAR_TEXTURES float v_top = v_offset + sprite_id * dv; float u_left = u_offset + icon_idx * du; float v_bottom = v_top + dv - v_offset; float u_right = u_left + du - u_offset; -#else - float u_icon_size = icons_size * inv_tex_width; - float v_icon_size = icons_size * inv_tex_height; - - float v_top = sprite_id * v_icon_size; - float u_left = icon_idx * u_icon_size; - float v_bottom = v_top + v_icon_size; - float u_right = u_left + u_icon_size; -#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES GLTexture::render_sub_texture(icons_texture_id, zoomed_top_x, zoomed_top_x + zoomed_icons_size, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); if (idx == m_current) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 6120e961a7..2110e7b69b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -54,11 +54,13 @@ public: enum EType : unsigned char { + // Order must match index in m_gizmos! Move, Scale, Rotate, Flatten, Cut, + Hollow, SlaSupports, Undefined }; @@ -111,6 +113,7 @@ private: MouseCapture m_mouse_capture; std::string m_tooltip; bool m_serializing; + std::unique_ptr m_common_gizmos_data; public: explicit GLGizmosManager(GLCanvas3D& parent); @@ -168,6 +171,7 @@ public: void update_data(); EType get_current_type() const { return m_current; } + GLGizmoBase* get_current() const; bool is_running() const; bool handle_shortcut(int key); @@ -216,8 +220,6 @@ private: float get_scaled_total_height() const; float get_scaled_total_width() const; - GLGizmoBase* get_current() const; - bool generate_icons_texture() const; void update_on_off_state(const Vec2d& mouse_pos); diff --git a/src/slic3r/GUI/Job.hpp b/src/slic3r/GUI/Job.hpp new file mode 100644 index 0000000000..ac31b9bdb0 --- /dev/null +++ b/src/slic3r/GUI/Job.hpp @@ -0,0 +1,155 @@ +#ifndef JOB_HPP +#define JOB_HPP + +#include + +#include +#include +#include + +#include + +#include + +namespace Slic3r { namespace GUI { + +// A class to handle UI jobs like arranging and optimizing rotation. +// These are not instant jobs, the user has to be informed about their +// state in the status progress indicator. On the other hand they are +// separated from the background slicing process. Ideally, these jobs should +// run when the background process is not running. +// +// TODO: A mechanism would be useful for blocking the plater interactions: +// objects would be frozen for the user. In case of arrange, an animation +// could be shown, or with the optimize orientations, partial results +// could be displayed. +class Job : public wxEvtHandler +{ + int m_range = 100; + boost::thread m_thread; + std::atomic m_running{false}, m_canceled{false}; + bool m_finalized = false; + std::shared_ptr m_progress; + + void run() + { + m_running.store(true); + process(); + m_running.store(false); + + // ensure to call the last status to finalize the job + update_status(status_range(), ""); + } + +protected: + // status range for a particular job + virtual int status_range() const { return 100; } + + // status update, to be used from the work thread (process() method) + void update_status(int st, const wxString &msg = "") + { + auto evt = new wxThreadEvent(); + evt->SetInt(st); + evt->SetString(msg); + wxQueueEvent(this, evt); + } + + bool was_canceled() const { return m_canceled.load(); } + + // Launched just before start(), a job can use it to prepare internals + virtual void prepare() {} + + // Launched when the job is finished. It refreshes the 3Dscene by def. + virtual void finalize() { m_finalized = true; } + + +public: + Job(std::shared_ptr pri) : m_progress(pri) + { + Bind(wxEVT_THREAD, [this](const wxThreadEvent &evt) { + auto msg = evt.GetString(); + if (!msg.empty()) + m_progress->set_status_text(msg.ToUTF8().data()); + + if (m_finalized) return; + + m_progress->set_progress(evt.GetInt()); + if (evt.GetInt() == status_range()) { + // set back the original range and cancel callback + m_progress->set_range(m_range); + m_progress->set_cancel_callback(); + wxEndBusyCursor(); + + finalize(); + + // dont do finalization again for the same process + m_finalized = true; + } + }); + } + + bool is_finalized() const { return m_finalized; } + + Job(const Job &) = delete; + Job(Job &&) = delete; + Job &operator=(const Job &) = delete; + Job &operator=(Job &&) = delete; + + virtual void process() = 0; + + void start() + { // Start the job. No effect if the job is already running + if (!m_running.load()) { + prepare(); + + // Save the current status indicatior range and push the new one + m_range = m_progress->get_range(); + m_progress->set_range(status_range()); + + // init cancellation flag and set the cancel callback + m_canceled.store(false); + m_progress->set_cancel_callback( + [this]() { m_canceled.store(true); }); + + m_finalized = false; + + // Changing cursor to busy + wxBeginBusyCursor(); + + try { // Execute the job + m_thread = create_thread([this] { this->run(); }); + } catch (std::exception &) { + update_status(status_range(), + _(L("ERROR: not enough resources to " + "execute a new job."))); + } + + // The state changes will be undone when the process hits the + // last status value, in the status update handler (see ctor) + } + } + + // To wait for the running job and join the threads. False is + // returned if the timeout has been reached and the job is still + // running. Call cancel() before this fn if you want to explicitly + // end the job. + bool join(int timeout_ms = 0) + { + if (!m_thread.joinable()) return true; + + if (timeout_ms <= 0) + m_thread.join(); + else if (!m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms))) + return false; + + return true; + } + + bool is_running() const { return m_running.load(); } + void cancel() { m_canceled.store(true); } +}; + +} +} + +#endif // JOB_HPP diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 268682b817..7d4211b246 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -7,249 +7,297 @@ #include "GUI_App.hpp" #include "wxExtensions.hpp" +#define NOTEBOOK_TOP 1 +#define NOTEBOOK_LEFT 2 +#define LISTBOOK_TOP 3 +#define LISTBOOK_LEFT 4 +#define TOOLBOOK 5 +#define CHOICEBOOK 6 +#define BOOK_TYPE NOTEBOOK_TOP + +#if (BOOK_TYPE == NOTEBOOK_TOP) || (BOOK_TYPE == NOTEBOOK_LEFT) +#include +#elif (BOOK_TYPE == LISTBOOK_TOP) || (BOOK_TYPE == LISTBOOK_LEFT) +#include +#elif BOOK_TYPE == TOOLBOOK +#include +#elif BOOK_TYPE == CHOICEBOOK +#include +#endif // BOOK_TYPE + +#if ENABLE_SCROLLABLE +static wxSize get_screen_size(wxWindow* window) +{ + const auto idx = wxDisplay::GetFromWindow(window); + wxDisplay display(idx != wxNOT_FOUND ? idx : 0u); + return display.GetClientArea().GetSize(); +} +#endif // ENABLE_SCROLLABLE + namespace Slic3r { namespace GUI { KBShortcutsDialog::KBShortcutsDialog() : DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(L("Keyboard Shortcuts")), - wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +#if ENABLE_SCROLLABLE + wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +#else + wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) +#endif // ENABLE_SCROLLABLE { SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - auto main_sizer = new wxBoxSizer(wxVERTICAL); - - // logo - m_logo_bmp = ScalableBitmap(this, "PrusaSlicer_32px.png", 32); - // fonts const wxFont& font = wxGetApp().normal_font(); const wxFont& bold_font = wxGetApp().bold_font(); SetFont(font); - wxFont head_font = bold_font; -#ifdef __WXOSX__ - head_font.SetPointSize(14); -#else - head_font.SetPointSize(bold_font.GetPointSize() + 2); -#endif // __WXOSX__ + auto main_sizer = new wxBoxSizer(wxVERTICAL); + + main_sizer->Add(create_header(this, bold_font), 0, wxEXPAND | wxALL, 10); + +#if BOOK_TYPE == NOTEBOOK_TOP + wxNotebook* book = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP); +#elif BOOK_TYPE == NOTEBOOK_LEFT + wxNotebook* book = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_LEFT); +#elif BOOK_TYPE == LISTBOOK_TOP + wxListbook* book = new wxListbook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLB_TOP); +#elif BOOK_TYPE == LISTBOOK_LEFT + wxListbook* book = new wxListbook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLB_LEFT); +#elif BOOK_TYPE == TOOLBOOK + wxToolbook* book = new wxToolbook(this, wxID_ANY); +#elif BOOK_TYPE == CHOICEBOOK + wxChoicebook* book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxCHB_TOP); +#endif // BOOK_TYPE +main_sizer->Add(book, 1, wxEXPAND | wxALL, 10); fill_shortcuts(); - - panel = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, get_size()); - panel->SetScrollbars(1, 20, 1, 2); - - auto main_grid_sizer = new wxFlexGridSizer(2, 10, 10); - panel->SetSizer(main_grid_sizer); - main_sizer->Add(panel, 1, wxEXPAND | wxALL, 0); - - wxBoxSizer* l_sizer = new wxBoxSizer(wxVERTICAL); - main_grid_sizer->Add(l_sizer, 0); - - wxBoxSizer* r_sizer = new wxBoxSizer(wxVERTICAL); - main_grid_sizer->Add(r_sizer, 0); - - m_head_bitmaps.reserve(m_full_shortcuts.size()); - - for (auto& shortcut : m_full_shortcuts) + for (size_t i = 0; i < m_full_shortcuts.size(); ++i) { - auto sizer = shortcut.second.second == szLeft ? l_sizer : r_sizer; - wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(hsizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10); - - // logo - m_head_bitmaps.push_back(new wxStaticBitmap(panel, wxID_ANY, m_logo_bmp.bmp())); - hsizer->Add(m_head_bitmaps.back(), 0, wxEXPAND | wxLEFT | wxRIGHT, 15); - - // head - wxStaticText* head = new wxStaticText(panel, wxID_ANY, shortcut.first); - head->SetFont(head_font); - hsizer->Add(head, 0, wxALIGN_CENTER_VERTICAL); - - - // Shortcuts list - auto grid_sizer = new wxFlexGridSizer(2, 5, 15); - sizer->Add(grid_sizer, 0, wxEXPAND | wxLEFT| wxRIGHT, 15); - - for (auto pair : shortcut.second.first) - { - auto shortcut = new wxStaticText(panel, wxID_ANY, _(pair.first)); - shortcut->SetFont(bold_font); - grid_sizer->Add(shortcut, -1, wxALIGN_CENTRE_VERTICAL); - - auto description = new wxStaticText(panel, wxID_ANY, _(pair.second)); - description->SetFont(font); - grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL); - } +#if ENABLE_SCROLLABLE + wxPanel* page = create_page(book, m_full_shortcuts[i], font, bold_font); + m_pages.push_back(page); + book->AddPage(page, m_full_shortcuts[i].first, i == 0); +#else + book->AddPage(create_page(book, m_full_shortcuts[i], font, bold_font), m_full_shortcuts[i].first, i == 0); +#endif // ENABLE_SCROLLABLE } wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK); - this->SetEscapeId(wxID_OK); - this->Bind(wxEVT_BUTTON, &KBShortcutsDialog::onCloseDialog, this, wxID_OK); - main_sizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 15); - - this->Bind(wxEVT_LEFT_DOWN, &KBShortcutsDialog::onCloseDialog, this); + main_sizer->Add(buttons, 0, wxEXPAND | wxALL, 5); SetSizer(main_sizer); main_sizer->SetSizeHints(this); } -void KBShortcutsDialog::fill_shortcuts() -{ - const std::string &ctrl = GUI::shortkey_ctrl_prefix(); - const std::string &alt = GUI::shortkey_alt_prefix(); - - m_full_shortcuts.reserve(4); - - Shortcuts main_shortcuts; - main_shortcuts.reserve(25); - - main_shortcuts.push_back(Shortcut(ctrl+"O" ,L("Open project STL/OBJ/AMF/3MF with config, delete bed"))); - main_shortcuts.push_back(Shortcut(ctrl+"I" ,L("Import STL/OBJ/AMF/3MF without config, keep bed"))); - main_shortcuts.push_back(Shortcut(ctrl+"L" ,L("Load Config from .ini/amf/3mf/gcode"))); - main_shortcuts.push_back(Shortcut(ctrl+"G" ,L("Export G-code"))); - main_shortcuts.push_back(Shortcut(ctrl+"S" ,L("Save project (3MF)"))); - main_shortcuts.push_back(Shortcut(ctrl+alt+"L" ,L("Load Config from .ini/amf/3mf/gcode and merge"))); - main_shortcuts.push_back(Shortcut(ctrl+"R" ,L("(Re)slice"))); -// main_shortcuts.push_back(Shortcut(ctrl+"U" ,L("Quick slice"))); -// main_shortcuts.push_back(Shortcut(ctrl+"Shift+U" ,L("Repeat last quick slice"))); - main_shortcuts.push_back(Shortcut(ctrl+"1" ,L("Select Plater Tab"))); -// main_shortcuts.push_back(Shortcut(ctrl+alt+"U" ,L("Quick slice and Save as"))); - main_shortcuts.push_back(Shortcut(ctrl+"2" ,L("Select Print Settings Tab"))); - main_shortcuts.push_back(Shortcut(ctrl+"3" ,L("Select Filament Settings Tab"))); - main_shortcuts.push_back(Shortcut(ctrl+"4" ,L("Select Printer Settings Tab"))); - main_shortcuts.push_back(Shortcut(ctrl+"5" ,L("Switch to 3D"))); - main_shortcuts.push_back(Shortcut(ctrl+"6" ,L("Switch to Preview"))); - main_shortcuts.push_back(Shortcut(ctrl+"P" ,L("Preferences"))); - main_shortcuts.push_back(Shortcut(ctrl+"J" ,L("Print host upload queue"))); - main_shortcuts.push_back(Shortcut("0-6" ,L("Camera view"))); - main_shortcuts.push_back(Shortcut("+" ,L("Add Instance of the selected object"))); - main_shortcuts.push_back(Shortcut("-" ,L("Remove Instance of the selected object"))); - main_shortcuts.push_back(Shortcut("?" ,L("Show keyboard shortcuts list"))); - main_shortcuts.push_back(Shortcut(ctrl/*+"LeftMouse"*/,L("Press to select multiple object or move multiple object with mouse"))); - - m_full_shortcuts.push_back(std::make_pair(_(L("Main Shortcuts")), std::make_pair(main_shortcuts, szLeft))); - - - Shortcuts plater_shortcuts; - plater_shortcuts.reserve(20); - - plater_shortcuts.push_back(Shortcut("A", L("Arrange"))); - plater_shortcuts.push_back(Shortcut("Shift+A", L("Arrange selection"))); - plater_shortcuts.push_back(Shortcut(ctrl+"A", L("Select All objects"))); - plater_shortcuts.push_back(Shortcut("Del", L("Delete selected"))); - plater_shortcuts.push_back(Shortcut(ctrl+"Del", L("Delete All"))); - plater_shortcuts.push_back(Shortcut(ctrl+"C", L("Copy to clipboard"))); - plater_shortcuts.push_back(Shortcut(ctrl+"V", L("Paste from clipboard"))); - plater_shortcuts.push_back(Shortcut("M", L("Gizmo move"))); - plater_shortcuts.push_back(Shortcut("S", L("Gizmo scale"))); - plater_shortcuts.push_back(Shortcut("R", L("Gizmo rotate"))); - plater_shortcuts.push_back(Shortcut("C", L("Gizmo cut"))); - plater_shortcuts.push_back(Shortcut("F", L("Gizmo Place face on bed"))); - plater_shortcuts.push_back(Shortcut("L", L("Gizmo SLA support points"))); - plater_shortcuts.push_back(Shortcut("Shift+", L("Press to activate selection rectangle\nor to snap by 5% in Gizmo scale\nor to snap by 1mm in Gizmo move"))); - plater_shortcuts.push_back(Shortcut("F", L("Press to scale selection to fit print volume\nin Gizmo scale"))); - plater_shortcuts.push_back(Shortcut(alt, L("Press to activate deselection rectangle\nor to scale or rotate selected objects\naround their own center"))); - plater_shortcuts.push_back(Shortcut(ctrl, L("Press to activate one direction scaling in Gizmo scale"))); - plater_shortcuts.push_back(Shortcut("K", L("Change camera type (perspective, orthographic)"))); - plater_shortcuts.push_back(Shortcut("B", L("Zoom to Bed"))); - plater_shortcuts.push_back(Shortcut("Z", L("Zoom to all objects in scene, if none selected"))); - plater_shortcuts.push_back(Shortcut("Z", L("Zoom to selected object"))); - plater_shortcuts.push_back(Shortcut("I", L("Zoom in"))); - plater_shortcuts.push_back(Shortcut("O", L("Zoom out"))); - plater_shortcuts.push_back(Shortcut(ctrl+"M", L("Show/Hide 3Dconnexion devices settings dialog"))); - plater_shortcuts.push_back(Shortcut("ESC", L("Unselect gizmo / Clear selection"))); -#if ENABLE_RENDER_PICKING_PASS - // Don't localize debugging texts. - plater_shortcuts.push_back(Shortcut("T", "Toggle picking pass texture rendering on/off")); -#endif // ENABLE_RENDER_PICKING_PASS - - m_full_shortcuts.push_back(std::make_pair(_(L("Plater Shortcuts")), std::make_pair(plater_shortcuts, szRight))); - - -// Shortcuts gizmo_shortcuts; -// gizmo_shortcuts.reserve(2); -// -// gizmo_shortcuts.push_back(Shortcut("Shift+", L("Press to snap by 5% in Gizmo Scale\n or by 1mm in Gizmo Move"))); -// gizmo_shortcuts.push_back(Shortcut(alt, L("Press to scale or rotate selected objects around their own center"))); -// -// m_full_shortcuts.push_back(std::make_pair(_(L("Gizmo Shortcuts")), std::make_pair(gizmo_shortcuts, 1))); - - - Shortcuts preview_shortcuts; - preview_shortcuts.reserve(4); - - preview_shortcuts.push_back(Shortcut(L("Arrow Up"), L("Upper Layer"))); - preview_shortcuts.push_back(Shortcut(L("Arrow Down"), L("Lower Layer"))); - preview_shortcuts.push_back(Shortcut("U", L("Upper Layer"))); - preview_shortcuts.push_back(Shortcut("D", L("Lower Layer"))); - preview_shortcuts.push_back(Shortcut("L", L("Show/Hide (L)egend"))); - - m_full_shortcuts.push_back(std::make_pair(_(L("Preview Shortcuts")), std::make_pair(preview_shortcuts, szLeft))); - - - Shortcuts layers_slider_shortcuts; - layers_slider_shortcuts.reserve(6); - - layers_slider_shortcuts.push_back(Shortcut(L("Arrow Up"), L("Move current slider thumb Up"))); - layers_slider_shortcuts.push_back(Shortcut(L("Arrow Down"), L("Move current slider thumb Down"))); - layers_slider_shortcuts.push_back(Shortcut(L("Arrow Left"), L("Set upper thumb to current slider thumb"))); - layers_slider_shortcuts.push_back(Shortcut(L("Arrow Right"),L("Set lower thumb to current slider thumb"))); - layers_slider_shortcuts.push_back(Shortcut("+", L("Add color change marker for current layer"))); - layers_slider_shortcuts.push_back(Shortcut("-", L("Delete color change marker for current layer"))); - - m_full_shortcuts.push_back(std::make_pair(_(L("Layers Slider Shortcuts")), std::make_pair(layers_slider_shortcuts, szRight))); -} - -void KBShortcutsDialog::on_dpi_changed(const wxRect &suggested_rect) +void KBShortcutsDialog::on_dpi_changed(const wxRect& suggested_rect) { m_logo_bmp.msw_rescale(); + m_header_bitmap->SetBitmap(m_logo_bmp.bmp()); + msw_buttons_rescale(this, em_unit(), { wxID_OK }); - for (wxStaticBitmap* bmp : m_head_bitmaps) - bmp->SetBitmap(m_logo_bmp.bmp()); - - const int em = em_unit(); - - msw_buttons_rescale(this, em, { wxID_OK }); - - wxSize size = get_size(); - - panel->SetMinSize(size); - - SetMinSize(size); + Layout(); Fit(); - Refresh(); } -void KBShortcutsDialog::onCloseDialog(wxEvent &) +void KBShortcutsDialog::fill_shortcuts() { - this->EndModal(wxID_CLOSE); + const std::string& ctrl = GUI::shortkey_ctrl_prefix(); + const std::string& alt = GUI::shortkey_alt_prefix(); + + Shortcuts commands_shortcuts = { + // File + { ctrl + "N", L("New project, clear plater") }, + { ctrl + "O", L("Open project STL/OBJ/AMF/3MF with config, clear plater") }, + { ctrl + "S", L("Save project (3mf)") }, + { ctrl + alt + "S", L("Save project as (3mf)") }, + { ctrl + "R", L("(Re)slice") }, + // File>Import + { ctrl + "I", L("Import STL/OBJ/AMF/3MF without config, keep plater") }, + { ctrl + "L", L("Import Config from ini/amf/3mf/gcode") }, + { ctrl + alt + "L", L("Load Config from ini/amf/3mf/gcode and merge") }, + // File>Export + { ctrl + "G", L("Export G-code") }, + { ctrl + "Shift+" + "G", L("Send G-code") }, + { ctrl + "E", L("Export config") }, + // Edit + { ctrl + "A", L("Select all objects") }, + { "Esc", L("Deselect all") }, + { "Del", L("Delete selected") }, + { ctrl + "Del", L("Delete all") }, + { ctrl + "Z", L("Undo") }, + { ctrl + "Y", L("Redo") }, + { ctrl + "C", L("Copy to clipboard") }, + { ctrl + "V", L("Paste from clipboard") }, + { "F5", L("Reload plater from disk") }, + // Window + { ctrl + "1", L("Select Plater Tab") }, + { ctrl + "2", L("Select Print Settings Tab") }, + { ctrl + "3", L("Select Filament Settings Tab") }, + { ctrl + "4", L("Select Printer Settings Tab") }, + { ctrl + "5", L("Switch to 3D") }, + { ctrl + "6", L("Switch to Preview") }, + { ctrl + "J", L("Print host upload queue") }, + // View + { "0-6", L("Camera view") }, +#if ENABLE_SHOW_SCENE_LABELS + { "E", L("Show/Hide object/instance labels") }, +#endif // ENABLE_SHOW_SCENE_LABELS + // Configuration + { ctrl + "P", L("Preferences") }, + // Help + { "?", L("Show keyboard shortcuts list") } + }; + + m_full_shortcuts.push_back(std::make_pair(_(L("Commands")), commands_shortcuts)); + + Shortcuts plater_shortcuts = { + { "A", L("Arrange") }, + { "Shift+A", L("Arrange selection") }, + { "+", L("Add Instance of the selected object") }, + { "-", L("Remove Instance of the selected object") }, + { ctrl, L("Press to select multiple object\nor move multiple object with mouse") }, + { "Shift+", L("Press to activate selection rectangle") }, + { alt, L("Press to activate deselection rectangle") }, + { L("Arrow Up"), L("Move selection 10 mm in positive Y direction") }, + { L("Arrow Down"), L("Move selection 10 mm in negative Y direction") }, + { L("Arrow Left"), L("Move selection 10 mm in negative X direction") }, + { L("Arrow Right"), L("Move selection 10 mm in positive X direction") }, + { std::string("Shift+") + L("Any arrow"), L("Movement step set to 1 mm") }, + { ctrl + L("Any arrow"), L("Movement in camera space") }, + { L("Page Up"), L("Rotate selection 45 degrees CCW") }, + { L("Page Down"), L("Rotate selection 45 degrees CW") }, + { "M", L("Gizmo move") }, + { "S", L("Gizmo scale") }, + { "R", L("Gizmo rotate") }, + { "C", L("Gizmo cut") }, + { "F", L("Gizmo Place face on bed") }, + { "H", L("Gizmo SLA hollow") }, + { "L", L("Gizmo SLA support points") }, + { "Esc", L("Unselect gizmo or clear selection") }, + { "K", L("Change camera type (perspective, orthographic)") }, + { "B", L("Zoom to Bed") }, + { "Z", L("Zoom to selected object\nor all objects in scene, if none selected") }, + { "I", L("Zoom in") }, + { "O", L("Zoom out") }, + { ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog") } +#if ENABLE_RENDER_PICKING_PASS + // Don't localize debugging texts. + , { "T", "Toggle picking pass texture rendering on/off" } +#endif // ENABLE_RENDER_PICKING_PASS + }; + + m_full_shortcuts.push_back(std::make_pair(_(L("Plater")), plater_shortcuts)); + + Shortcuts gizmos_shortcuts = { + { "Shift+", L("Press to to snap by 5% in Gizmo scale\nor to snap by 1mm in Gizmo move") }, + { "F", L("Scale selection to fit print volume\nin Gizmo scale") }, + { ctrl, L("Press to activate one direction scaling in Gizmo scale") }, + { alt, L("Press to scale (in Gizmo scale) or rotate (in Gizmo rotate)\nselected objects around their own center") }, + }; + + m_full_shortcuts.push_back(std::make_pair(_(L("Gizmos")), gizmos_shortcuts)); + + Shortcuts preview_shortcuts = { + { L("Arrow Up"), L("Upper Layer") }, + { L("Arrow Down"), L("Lower Layer") }, + { "U", L("Upper Layer") }, + { "D", L("Lower Layer") }, + { "L", L("Show/Hide Legend") } + }; + + m_full_shortcuts.push_back(std::make_pair(_(L("Preview")), preview_shortcuts)); + + Shortcuts layers_slider_shortcuts = { + { L("Arrow Up"), L("Move current slider thumb Up") }, + { L("Arrow Down"), L("Move current slider thumb Down") }, + { L("Arrow Left"), L("Set upper thumb to current slider thumb") }, + { L("Arrow Right"), L("Set lower thumb to current slider thumb") }, + { "+", L("Add color change marker for current layer") }, + { "-", L("Delete color change marker for current layer") } + }; + + m_full_shortcuts.push_back(std::make_pair(_(L("Layers Slider")), layers_slider_shortcuts)); } -wxSize KBShortcutsDialog::get_size() +wxPanel* KBShortcutsDialog::create_header(wxWindow* parent, const wxFont& bold_font) { - wxTopLevelWindow* window = Slic3r::GUI::find_toplevel_parent(this); - const int display_idx = wxDisplay::GetFromWindow(window); - wxRect display; - if (display_idx == wxNOT_FOUND) { - display = wxDisplay(0u).GetClientArea(); - window->Move(display.GetTopLeft()); - } - else { - display = wxDisplay(display_idx).GetClientArea(); + wxPanel* panel = new wxPanel(parent); + wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); + + wxFont header_font = bold_font; +#ifdef __WXOSX__ + header_font.SetPointSize(14); +#else + header_font.SetPointSize(bold_font.GetPointSize() + 2); +#endif // __WXOSX__ + + sizer->AddStretchSpacer(); + + // logo + m_logo_bmp = ScalableBitmap(this, "PrusaSlicer_32px.png", 32); + m_header_bitmap = new wxStaticBitmap(panel, wxID_ANY, m_logo_bmp.bmp()); + sizer->Add(m_header_bitmap, 0, wxEXPAND | wxLEFT | wxRIGHT, 10); + + // text + wxStaticText* text = new wxStaticText(panel, wxID_ANY, _(L("Keyboard shortcuts"))); + text->SetFont(header_font); + sizer->Add(text, 0, wxALIGN_CENTER_VERTICAL); + + sizer->AddStretchSpacer(); + + panel->SetSizer(sizer); + return panel; +} + +wxPanel* KBShortcutsDialog::create_page(wxWindow* parent, const std::pair& shortcuts, const wxFont& font, const wxFont& bold_font) +{ + static const int max_items_per_column = 20; + int columns_count = 1 + (int)shortcuts.second.size() / max_items_per_column; + +#if ENABLE_SCROLLABLE + wxScrolledWindow* page = new wxScrolledWindow(parent); + page->SetScrollbars(20, 20, 50, 50); + page->SetInitialSize(wxSize(850, 450)); +#else + wxPanel* page = new wxPanel(parent); +#endif // ENABLE_SCROLLABLE + +#if (BOOK_TYPE == LISTBOOK_TOP) || (BOOK_TYPE == LISTBOOK_LEFT) + wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, page, " " + shortcuts.first + " "); + sizer->GetStaticBox()->SetFont(bold_font); +#else + wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); +#endif // BOOK_TYPE + + wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(2 * columns_count, 5, 15); + + int items_count = (int)shortcuts.second.size(); + for (int i = 0; i < max_items_per_column; ++i) + { + for (int j = 0; j < columns_count; ++j) + { + int id = j * max_items_per_column + i; + if (id >= items_count) + break; + + const auto& [shortcut, description] = shortcuts.second[id]; + auto key = new wxStaticText(page, wxID_ANY, _(shortcut)); + key->SetFont(bold_font); + grid_sizer->Add(key, 0, wxALIGN_CENTRE_VERTICAL); + + auto desc = new wxStaticText(page, wxID_ANY, _(description)); + desc->SetFont(font); + grid_sizer->Add(desc, 0, wxALIGN_CENTRE_VERTICAL); + } } - const int em = em_unit(); - wxSize dialog_size = wxSize(90 * em, 85 * em); + sizer->Add(grid_sizer, 1, wxEXPAND | wxALL, 10); - const int margin = 10 * em; - if (dialog_size.x > display.GetWidth()) - dialog_size.x = display.GetWidth() - margin; - if (dialog_size.y > display.GetHeight()) - dialog_size.y = display.GetHeight() - margin; - - return dialog_size; + page->SetSizer(sizer); + return page; } } // namespace GUI diff --git a/src/slic3r/GUI/KBShortcutsDialog.hpp b/src/slic3r/GUI/KBShortcutsDialog.hpp index 7ac28778b5..70820ae774 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.hpp +++ b/src/slic3r/GUI/KBShortcutsDialog.hpp @@ -8,38 +8,36 @@ #include "GUI_Utils.hpp" #include "wxExtensions.hpp" +#define ENABLE_SCROLLABLE 1 + namespace Slic3r { namespace GUI { class KBShortcutsDialog : public DPIDialog { - enum PLACED_SIZER_ID - { - szLeft = 0, - szRight - }; - typedef std::pair Shortcut; - typedef std::vector< Shortcut > Shortcuts; - typedef std::vector< std::pair> > ShortcutsVec; + typedef std::vector Shortcuts; + typedef std::vector> ShortcutsVec; - wxScrolledWindow* panel; - - ShortcutsVec m_full_shortcuts; - ScalableBitmap m_logo_bmp; - std::vector m_head_bitmaps; + ShortcutsVec m_full_shortcuts; + ScalableBitmap m_logo_bmp; + wxStaticBitmap* m_header_bitmap; +#if ENABLE_SCROLLABLE + std::vector m_pages; +#endif // ENABLE_SCROLLABLE public: KBShortcutsDialog(); - void fill_shortcuts(); - protected: void on_dpi_changed(const wxRect &suggested_rect) override; private: - void onCloseDialog(wxEvent &); - wxSize get_size(); + void fill_shortcuts(); + + wxPanel* create_header(wxWindow* parent, const wxFont& bold_font); + wxPanel* create_page(wxWindow* parent, const std::pair& shortcuts, const wxFont& font, const wxFont& bold_font); + }; } // namespace GUI diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 9fc74bcb86..428dc283bd 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -58,7 +58,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S #endif // _WIN32 // initialize status bar - m_statusbar.reset(new ProgressStatusBar(this)); + m_statusbar = std::make_shared(this); + m_statusbar->set_font(GUI::wxGetApp().normal_font()); m_statusbar->embed(this); m_statusbar->set_status_text(_(L("Version")) + " " + SLIC3R_VERSION + @@ -67,7 +68,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S /* Load default preset bitmaps before a tabpanel initialization, * but after filling of an em_unit value */ - wxGetApp().preset_bundle->load_default_preset_bitmaps(this); + wxGetApp().preset_bundle->load_default_preset_bitmaps(); // initialize tabpanel and menubar init_tabpanel(); @@ -144,8 +145,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S update_ui_from_settings(); // FIXME (?) } -MainFrame::~MainFrame() = default; - void MainFrame::update_title() { wxString title = wxEmptyString; @@ -157,7 +156,22 @@ void MainFrame::update_title() if (!project.empty()) title += (project + " - "); } - title += (wxString(SLIC3R_BUILD_ID) + " " + _(L("based on Slic3r"))); + + std::string build_id = SLIC3R_BUILD_ID; + size_t idx_plus = build_id.find('+'); + if (idx_plus != build_id.npos) { + // Parse what is behind the '+'. If there is a number, then it is a build number after the label, and full build ID is shown. + int commit_after_label; + if (! boost::starts_with(build_id.data() + idx_plus + 1, "UNKNOWN") && sscanf(build_id.data() + idx_plus + 1, "%d-", &commit_after_label) == 0) { + // It is a release build. + build_id.erase(build_id.begin() + idx_plus, build_id.end()); +#if defined(_WIN32) && ! defined(_WIN64) + // People are using 32bit slicer on a 64bit machine by mistake. Make it explicit. + build_id += " 32 bit"; +#endif + } + } + title += (wxString(build_id) + " " + _(L("based on Slic3r"))); SetTitle(title); } @@ -344,7 +358,7 @@ void MainFrame::on_dpi_changed(const wxRect &suggested_rect) /* Load default preset bitmaps before a tabpanel initialization, * but after filling of an em_unit value */ - wxGetApp().preset_bundle->load_default_preset_bitmaps(this); + wxGetApp().preset_bundle->load_default_preset_bitmaps(); // update Plater wxGetApp().plater()->msw_rescale(); @@ -549,10 +563,10 @@ void MainFrame::init_menubar() wxString hotkey_delete = "Del"; #endif append_menu_item(editMenu, wxID_ANY, _(L("&Select all")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "A", - _(L("Selects all objects")), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->select_all(); }, + _(L("Selects all objects")), [this](wxCommandEvent&) { m_plater->select_all(); }, "", nullptr, [this](){return can_select(); }, this); append_menu_item(editMenu, wxID_ANY, _(L("D&eselect all")) + sep + "Esc", - _(L("Deselects all objects")), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->deselect_all(); }, + _(L("Deselects all objects")), [this](wxCommandEvent&) { m_plater->deselect_all(); }, "", nullptr, [this](){return can_deselect(); }, this); editMenu->AppendSeparator(); append_menu_item(editMenu, wxID_ANY, _(L("&Delete selected")) + sep + hotkey_delete, @@ -577,6 +591,11 @@ void MainFrame::init_menubar() append_menu_item(editMenu, wxID_ANY, _(L("&Paste")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "V", _(L("Paste clipboard")), [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); }, "paste_menu", nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this); + + editMenu->AppendSeparator(); + append_menu_item(editMenu, wxID_ANY, _(L("Re&load from disk")) + sep + "F5", + _(L("Reload the plater from disk")), [this](wxCommandEvent&) { m_plater->reload_all_from_disk(); }, + "", nullptr, [this]() {return !m_plater->model().objects.empty(); }, this); } // Window menu @@ -653,6 +672,10 @@ void MainFrame::init_menubar() "", nullptr, [this](){return can_change_view(); }, this); append_menu_item(viewMenu, wxID_ANY, _(L("Right")) + sep + "&6", _(L("Right View")), [this](wxCommandEvent&) { select_view("right"); }, "", nullptr, [this](){return can_change_view(); }, this); + viewMenu->AppendSeparator(); + append_menu_check_item(viewMenu, wxID_ANY, _(L("Show &labels")) + sep + "E", _(L("Show object/instance labels in 3D scene")), + [this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); }, this, + [this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this); } // Help menu @@ -727,7 +750,7 @@ void MainFrame::update_menubar() m_changeable_menu_items[miSend] ->SetItemLabel((is_fff ? _(L("S&end G-code")) : _(L("S&end to print"))) + dots + "\tCtrl+Shift+G"); m_changeable_menu_items[miMaterialTab] ->SetItemLabel((is_fff ? _(L("&Filament Settings Tab")) : _(L("Mate&rial Settings Tab"))) + "\tCtrl+3"); - m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_scaled_bitmap(this, is_fff ? "spool": "resin")); + m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_scaled_bitmap(is_fff ? "spool": "resin")); } // To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG". diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 7b93e70cb5..a6d0749ab6 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -96,7 +96,7 @@ protected: public: MainFrame(); - ~MainFrame(); + ~MainFrame() = default; Plater* plater() { return m_plater; } @@ -135,7 +135,7 @@ public: Plater* m_plater { nullptr }; wxNotebook* m_tabpanel { nullptr }; wxProgressDialog* m_progress_dialog { nullptr }; - std::unique_ptr m_statusbar; + std::shared_ptr m_statusbar; }; } // GUI diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index d344667420..37b6efd87e 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -5,10 +5,6 @@ #include "slic3r/GUI/Camera.hpp" -// There is an L function in igl that would be overridden by our localization macro. -#undef L -#include - #include @@ -78,7 +74,7 @@ void MeshClipper::recalculate_triangles() // Now do the cutting std::vector list_of_expolys; m_tms->set_up_direction(up); - m_tms->slice(std::vector{height_mesh}, 0.f, &list_of_expolys, [](){}); + m_tms->slice(std::vector{height_mesh}, SlicingMode::Regular, 0.f, &list_of_expolys, [](){}); m_triangles2d = triangulate_expolygons_2f(list_of_expolys[0], m_trafo.get_matrix().matrix().determinant() < 0.); // Rotate the cut into world coords: @@ -99,57 +95,6 @@ void MeshClipper::recalculate_triangles() } -class MeshRaycaster::AABBWrapper { -public: - AABBWrapper(const TriangleMesh* mesh); - ~AABBWrapper() { m_AABB.deinit(); } - - typedef Eigen::Map> MapMatrixXfUnaligned; - typedef Eigen::Map> MapMatrixXiUnaligned; - igl::AABB m_AABB; - - Vec3f get_hit_pos(const igl::Hit& hit) const; - Vec3f get_hit_normal(const igl::Hit& hit) const; - -private: - const TriangleMesh* m_mesh; -}; - -MeshRaycaster::AABBWrapper::AABBWrapper(const TriangleMesh* mesh) - : m_mesh(mesh) -{ - m_AABB.init( - MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3), - MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3)); -} - - -MeshRaycaster::MeshRaycaster(const TriangleMesh& mesh) - : m_AABB_wrapper(new AABBWrapper(&mesh)), m_mesh(&mesh) -{ -} - -// Define the default destructor here. This is needed for the PIMPL with -// unique_ptr to work, the AABBWrapper is complete here. -MeshRaycaster::~MeshRaycaster() = default; - -Vec3f MeshRaycaster::AABBWrapper::get_hit_pos(const igl::Hit& hit) const -{ - const stl_triangle_vertex_indices& indices = m_mesh->its.indices[hit.id]; - return Vec3f((1-hit.u-hit.v) * m_mesh->its.vertices[indices(0)] - + hit.u * m_mesh->its.vertices[indices(1)] - + hit.v * m_mesh->its.vertices[indices(2)]); -} - - -Vec3f MeshRaycaster::AABBWrapper::get_hit_normal(const igl::Hit& hit) const -{ - const stl_triangle_vertex_indices& indices = m_mesh->its.indices[hit.id]; - Vec3f a(m_mesh->its.vertices[indices(1)] - m_mesh->its.vertices[indices(0)]); - Vec3f b(m_mesh->its.vertices[indices(2)] - m_mesh->its.vertices[indices(0)]); - return Vec3f(a.cross(b)); -} - bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane) const @@ -163,27 +108,20 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& ::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 0., model_mat.data(), proj_mat.data(), viewport.data(), &pt1(0), &pt1(1), &pt1(2)); ::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 1., model_mat.data(), proj_mat.data(), viewport.data(), &pt2(0), &pt2(1), &pt2(2)); - std::vector hits; - Transform3d inv = trafo.inverse(); - pt1 = inv * pt1; pt2 = inv * pt2; - if (! m_AABB_wrapper->m_AABB.intersect_ray( - AABBWrapper::MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3), - AABBWrapper::MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3), - pt1.cast(), (pt2-pt1).cast(), hits)) + std::vector hits = m_emesh.query_ray_hits(pt1, pt2-pt1); + if (hits.empty()) return false; // no intersection found - std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; }); - unsigned i = 0; // Remove points that are obscured or cut by the clipping plane if (clipping_plane) { for (i=0; iis_point_clipped(trafo * m_AABB_wrapper->get_hit_pos(hits[i]).cast())) + if (! clipping_plane->is_point_clipped(trafo * hits[i].position())) break; if (i==hits.size() || (hits.size()-i) % 2 != 0) { @@ -194,8 +132,8 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& } // Now stuff the points in the provided vector and calculate normals if asked about them: - position = m_AABB_wrapper->get_hit_pos(hits[i]); - normal = m_AABB_wrapper->get_hit_normal(hits[i]); + position = hits[i].position().cast(); + normal = hits[i].normal().cast(); return true; } @@ -219,24 +157,21 @@ std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo bool is_obscured = false; // Cast a ray in the direction of the camera and look for intersection with the mesh: - std::vector hits; + std::vector hits; // Offset the start of the ray by EPSILON to account for numerical inaccuracies. - if (m_AABB_wrapper->m_AABB.intersect_ray( - AABBWrapper::MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3), - AABBWrapper::MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3), - inverse_trafo * pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) { + hits = m_emesh.query_ray_hits((inverse_trafo * pt + direction_to_camera_mesh * EPSILON).cast(), + direction_to_camera.cast()); - std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; }); + if (! hits.empty()) { // If the closest hit facet normal points in the same direction as the ray, // we are looking through the mesh and should therefore discard the point: - if (m_AABB_wrapper->get_hit_normal(hits.front()).dot(direction_to_camera_mesh) > 0.f) + if (hits.front().normal().dot(direction_to_camera_mesh.cast()) > 0) is_obscured = true; // Eradicate all hits that the caller wants to ignore for (unsigned j=0; jis_point_clipped(trafo.get_matrix() * m_AABB_wrapper->get_hit_pos(hit).cast())) { + if (clipping_plane && clipping_plane->is_point_clipped(trafo.get_matrix() * hits[j].position())) { hits.erase(hits.begin()+j); --j; } @@ -257,17 +192,15 @@ std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const { int idx = 0; - Eigen::Matrix closest_point; - m_AABB_wrapper->m_AABB.squared_distance( - AABBWrapper::MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3), - AABBWrapper::MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3), - point, idx, closest_point); + Vec3d closest_point; + m_emesh.squared_distance(point.cast(), idx, closest_point); if (normal) { - igl::Hit imag_hit; - imag_hit.id = idx; - *normal = m_AABB_wrapper->get_hit_normal(imag_hit); + auto indices = m_emesh.F().row(idx); + Vec3d a(m_emesh.V().row(indices(1)) - m_emesh.V().row(indices(0))); + Vec3d b(m_emesh.V().row(indices(2)) - m_emesh.V().row(indices(0))); + *normal = Vec3f(a.cross(b).cast()); } - return closest_point; + return closest_point.cast(); } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index d05cc42065..b4ad030114 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -3,6 +3,7 @@ #include "libslic3r/Point.hpp" #include "libslic3r/Geometry.hpp" +#include "libslic3r/SLA/EigenMesh3D.hpp" #include @@ -46,7 +47,7 @@ public: bool operator!=(const ClippingPlane& cp) const { return ! (*this==cp); } double distance(const Vec3d& pt) const { - assert(is_approx(get_normal().norm(), 1.)); + // FIXME: this fails: assert(is_approx(get_normal().norm(), 1.)); return (-get_normal().dot(pt) + m_data[3]); } @@ -104,11 +105,11 @@ private: // whether certain points are visible or obscured by the mesh etc. class MeshRaycaster { public: - // The class saves a const* to the mesh, called is responsible - // for making sure it does not get invalid. - MeshRaycaster(const TriangleMesh& mesh); - - ~MeshRaycaster(); + // The class makes a copy of the mesh as EigenMesh3D. + // The pointer can be invalidated after constructor returns. + MeshRaycaster(const TriangleMesh& mesh) + : m_emesh(mesh) + {} // Given a mouse position, this returns true in case it is on the mesh. bool unproject_on_mesh( @@ -136,10 +137,7 @@ public: Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const; private: - // PIMPL wrapper around igl::AABB so I don't have to include the header-only IGL here - class AABBWrapper; - std::unique_ptr m_AABB_wrapper; - const TriangleMesh* m_mesh = nullptr; + sla::EigenMesh3D m_emesh; }; diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index acadc5b2ed..2be017e540 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -60,17 +60,13 @@ const double Mouse3DController::State::DefaultTranslationDeadzone = 0.5 * Mouse3 const float Mouse3DController::State::DefaultRotationScale = 1.0f; const float Mouse3DController::State::MaxRotationDeadzone = 0.2f; const float Mouse3DController::State::DefaultRotationDeadzone = 0.5f * Mouse3DController::State::MaxRotationDeadzone; -#if ENABLE_3DCONNEXION_Y_AS_ZOOM const double Mouse3DController::State::DefaultZoomScale = 0.1; -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM Mouse3DController::State::State() : m_buttons_enabled(false) , m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone) , m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone) -#if ENABLE_3DCONNEXION_Y_AS_ZOOM , m_zoom_params(DefaultZoomScale, 0.0) -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM , m_mouse_wheel_counter(0) #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT , m_translation_queue_max_size(0) @@ -156,31 +152,18 @@ bool Mouse3DController::State::apply(Camera& camera) if (has_translation()) { const Vec3d& translation = m_translation.queue.front(); -#if ENABLE_3DCONNEXION_Y_AS_ZOOM double zoom_factor = camera.min_zoom() / camera.get_zoom(); camera.set_target(camera.get_target() + zoom_factor * m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(2) * camera.get_dir_up())); if (translation(1) != 0.0) camera.update_zoom(m_zoom_params.scale * translation(1) / std::abs(translation(1))); -#else - camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM m_translation.queue.pop(); ret = true; } if (has_rotation()) { -#if ENABLE_6DOF_CAMERA Vec3d rotation = (m_rotation_params.scale * m_rotation.queue.front()).cast(); camera.rotate_local_around_target(Vec3d(Geometry::deg2rad(rotation(0)), Geometry::deg2rad(-rotation(2)), Geometry::deg2rad(-rotation(1)))); -#else - const Vec3f& rotation = m_rotation.queue.front(); - float theta = m_rotation_params.scale * rotation(0); - float phi = m_rotation_params.scale * rotation(2); - float sign = camera.inverted_phi ? -1.0f : 1.0f; - camera.phi += sign * phi; - camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); -#endif // ENABLE_6DOF_CAMERA m_rotation.queue.pop(); ret = true; } @@ -321,11 +304,9 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const if (imgui.slider_float(_(L("Rotation")) + "##1", &rotation_scale, 0.1f, 10.0f, "%.1f")) m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale); -#if ENABLE_3DCONNEXION_Y_AS_ZOOM float zoom_scale = m_state.get_zoom_scale() / State::DefaultZoomScale; if (imgui.slider_float(_(L("Zoom")), &zoom_scale, 0.1f, 10.0f, "%.1f")) m_state.set_zoom_scale(State::DefaultZoomScale * zoom_scale); -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM ImGui::Separator(); ImGui::PushStyleColor(ImGuiCol_Text, color); @@ -333,11 +314,7 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const ImGui::PopStyleColor(); float translation_deadzone = (float)m_state.get_translation_deadzone(); -#if ENABLE_3DCONNEXION_Y_AS_ZOOM if (imgui.slider_float(_(L("Translation")) + "/" + _(L("Zoom")), &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f")) -#else - if (imgui.slider_float(_(L("Translation")) + "##2", &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f")) -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM m_state.set_translation_deadzone((double)translation_deadzone); float rotation_deadzone = m_state.get_rotation_deadzone(); @@ -651,24 +628,18 @@ bool Mouse3DController::connect_device() float rotation_speed = 4.0; double translation_deadzone = State::DefaultTranslationDeadzone; float rotation_deadzone = State::DefaultRotationDeadzone; -#if ENABLE_3DCONNEXION_Y_AS_ZOOM double zoom_speed = 2.0; -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation_speed); wxGetApp().app_config->get_mouse_device_translation_deadzone(m_device_str, translation_deadzone); wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation_speed); wxGetApp().app_config->get_mouse_device_rotation_deadzone(m_device_str, rotation_deadzone); -#if ENABLE_3DCONNEXION_Y_AS_ZOOM wxGetApp().app_config->get_mouse_device_zoom_speed(m_device_str, zoom_speed); -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM // clamp to valid values m_state.set_translation_scale(State::DefaultTranslationScale * std::clamp(translation_speed, 0.1, 10.0)); m_state.set_translation_deadzone(std::clamp(translation_deadzone, 0.0, State::MaxTranslationDeadzone)); m_state.set_rotation_scale(State::DefaultRotationScale * std::clamp(rotation_speed, 0.1f, 10.0f)); m_state.set_rotation_deadzone(std::clamp(rotation_deadzone, 0.0f, State::MaxRotationDeadzone)); -#if ENABLE_3DCONNEXION_Y_AS_ZOOM m_state.set_zoom_scale(State::DefaultZoomScale * std::clamp(zoom_speed, 0.1, 10.0)); -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM } #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT else @@ -694,13 +665,9 @@ void Mouse3DController::disconnect_device() m_thread.join(); // Store current device parameters into the config -#if ENABLE_3DCONNEXION_Y_AS_ZOOM wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_translation_deadzone(), m_state.get_rotation_scale() / State::DefaultRotationScale, m_state.get_rotation_deadzone(), m_state.get_zoom_scale() / State::DefaultZoomScale); -#else - wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_translation_deadzone(), - m_state.get_rotation_scale() / State::DefaultRotationScale, m_state.get_rotation_deadzone()); -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM + wxGetApp().app_config->save(); // Close the 3Dconnexion device @@ -911,15 +878,9 @@ bool Mouse3DController::handle_packet_translation(const DataPacketRaw& packet) bool Mouse3DController::handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte) { double deadzone = (double)m_state.get_rotation_deadzone(); -#if ENABLE_6DOF_CAMERA Vec3f rotation((float)convert_input(packet[first_byte + 0], packet[first_byte + 1], deadzone), (float)convert_input(packet[first_byte + 2], packet[first_byte + 3], deadzone), (float)convert_input(packet[first_byte + 4], packet[first_byte + 5], deadzone)); -#else - Vec3f rotation(-(float)convert_input(packet[first_byte + 0], packet[first_byte + 1], deadzone), - (float)convert_input(packet[first_byte + 2], packet[first_byte + 3], deadzone), - -(float)convert_input(packet[first_byte + 4], packet[first_byte + 5], deadzone)); -#endif // ENABLE_6DOF_CAMERA if (!rotation.isApprox(Vec3f::Zero())) { diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 018cb98e6e..f987451245 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -37,9 +37,7 @@ class Mouse3DController static const float DefaultRotationScale; static const float MaxRotationDeadzone; static const float DefaultRotationDeadzone; -#if ENABLE_3DCONNEXION_Y_AS_ZOOM static const double DefaultZoomScale; -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM private: template @@ -71,9 +69,7 @@ class Mouse3DController CustomParameters m_translation_params; CustomParameters m_rotation_params; -#if ENABLE_3DCONNEXION_Y_AS_ZOOM CustomParameters m_zoom_params; -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM // When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected. // We want to filter these out because we are getting the data directly from the device, bypassing the driver, and those mouse wheel events interfere @@ -109,10 +105,8 @@ class Mouse3DController float get_rotation_scale() const { return m_rotation_params.scale; } void set_rotation_scale(float scale) { m_rotation_params.scale = scale; } -#if ENABLE_3DCONNEXION_Y_AS_ZOOM double get_zoom_scale() const { return m_zoom_params.scale; } void set_zoom_scale(double scale) { m_zoom_params.scale = scale; } -#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM double get_translation_deadzone() const { return m_translation_params.deadzone; } void set_translation_deadzone(double deadzone) { m_translation_params.deadzone = deadzone; } diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index d1879e4bab..d4a82a03d6 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -53,7 +53,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he rightsizer->Add(btn_sizer, 0, wxALIGN_RIGHT); if (! bitmap.IsOk()) { - bitmap = create_scaled_bitmap(this, "PrusaSlicer_192px.png", 192); + bitmap = create_scaled_bitmap("PrusaSlicer_192px.png", this, 192); } logo = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap); @@ -99,7 +99,7 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) btn_ok->SetFocus(); btn_sizer->Add(btn_ok, 0, wxRIGHT, HORIZ_SPACING); - logo->SetBitmap(create_scaled_bitmap(this, "PrusaSlicer_192px_grayscale.png", 192)); + logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192)); SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT*wxGetApp().em_unit())); Fit(); diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp new file mode 100644 index 0000000000..b49b27e332 --- /dev/null +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -0,0 +1,1764 @@ +#include "ObjectDataViewModel.hpp" +#include "wxExtensions.hpp" +#include "BitmapCache.hpp" +#include "GUI_App.hpp" +#include "GUI_ObjectList.hpp" +#include "I18N.hpp" + +#include +#include + + +namespace Slic3r { + +namespace GUI { + +wxDEFINE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent); + +static wxBitmap get_extruder_color_icon(size_t extruder_idx, bool thin_icon = false) +{ + // Create the bitmap with color bars. + std::vector bmps = get_extruder_color_icons(thin_icon); + if (bmps.empty()) + return wxNullBitmap; + + return *bmps[extruder_idx >= bmps.size() ? 0 : extruder_idx]; +} + +BitmapCache* m_bitmap_cache = nullptr; + +// ***************************************************************************** +// ---------------------------------------------------------------------------- +// ObjectDataViewModelNode +// ---------------------------------------------------------------------------- + +void ObjectDataViewModelNode::init_container() +{ +#ifdef __WXGTK__ + // it's necessary on GTK because of control have to know if this item will be container + // in another case you couldn't to add subitem for this item + // it will be produce "segmentation fault" + m_container = true; +#endif //__WXGTK__ +} + +#define LAYER_ROOT_ICON "edit_layers_all" +#define LAYER_ICON "edit_layers_some" + +ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type) : + m_parent(parent), + m_type(type), + m_extruder(wxEmptyString) +{ + if (type == itSettings) + m_name = "Settings to modified"; + else if (type == itInstanceRoot) + m_name = _(L("Instances")); + else if (type == itInstance) + { + m_idx = parent->GetChildCount(); + m_name = wxString::Format(_(L("Instance %d")), m_idx + 1); + + set_action_and_extruder_icons(); + } + else if (type == itLayerRoot) + { + m_bmp = create_scaled_bitmap(LAYER_ROOT_ICON); // FIXME: pass window ptr + m_name = _(L("Layers")); + } + + if (type & (itInstanceRoot | itLayerRoot)) + init_container(); +} + +ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, + const t_layer_height_range& layer_range, + const int idx /*= -1 */, + const wxString& extruder) : + m_parent(parent), + m_type(itLayer), + m_idx(idx), + m_layer_range(layer_range), + m_extruder(extruder) +{ + const int children_cnt = parent->GetChildCount(); + if (idx < 0) + m_idx = children_cnt; + else + { + // update indexes for another Laeyr Nodes + for (int i = m_idx; i < children_cnt; i++) + parent->GetNthChild(i)->SetIdx(i + 1); + } + const std::string label_range = (boost::format(" %.2f-%.2f ") % layer_range.first % layer_range.second).str(); + m_name = _(L("Range")) + label_range + "(" + _(L("mm")) + ")"; + m_bmp = create_scaled_bitmap(LAYER_ICON); // FIXME: pass window ptr + + set_action_and_extruder_icons(); + init_container(); +} + +#ifndef NDEBUG +bool ObjectDataViewModelNode::valid() +{ + // Verify that the object was not deleted yet. + assert(m_idx >= -1); + return m_idx >= -1; +} +#endif /* NDEBUG */ + +void ObjectDataViewModelNode::set_action_and_extruder_icons() +{ + m_action_icon_name = m_type & itObject ? "advanced_plus" : + m_type & (itVolume | itLayer) ? "cog" : /*m_type & itInstance*/ "set_separate_obj"; + m_action_icon = create_scaled_bitmap(m_action_icon_name); // FIXME: pass window ptr + + if (m_type & itInstance) + return; // don't set colored bitmap for Instance + + // set extruder bitmap + int extruder_idx = atoi(m_extruder.c_str()); + if (extruder_idx > 0) --extruder_idx; + m_extruder_bmp = get_extruder_color_icon(extruder_idx); +} + +void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable) +{ + m_printable = printable; + m_printable_icon = m_printable == piUndef ? m_empty_bmp : + create_scaled_bitmap(m_printable == piPrintable ? "eye_open.png" : "eye_closed.png"); +} + +void ObjectDataViewModelNode::update_settings_digest_bitmaps() +{ + m_bmp = m_empty_bmp; + + std::map& categories_icon = Slic3r::GUI::wxGetApp().obj_list()->CATEGORY_ICON; + + std::string scaled_bitmap_name = m_name.ToUTF8().data(); + scaled_bitmap_name += "-em" + std::to_string(Slic3r::GUI::wxGetApp().em_unit()); + + wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name); + if (bmp == nullptr) { + std::vector bmps; + for (auto& cat : m_opt_categories) + bmps.emplace_back( categories_icon.find(cat) == categories_icon.end() ? + wxNullBitmap : categories_icon.at(cat)); + bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); + } + + m_bmp = *bmp; +} + +bool ObjectDataViewModelNode::update_settings_digest(const std::vector& categories) +{ + if (m_type != itSettings || m_opt_categories == categories) + return false; + + m_opt_categories = categories; + m_name = wxEmptyString; + + for (auto& cat : m_opt_categories) + m_name += _(cat) + "; "; + if (!m_name.IsEmpty()) + m_name.erase(m_name.Length()-2, 2); // Delete last "; " + + update_settings_digest_bitmaps(); + + return true; +} + +void ObjectDataViewModelNode::msw_rescale() +{ + if (!m_action_icon_name.empty()) + m_action_icon = create_scaled_bitmap(m_action_icon_name); + + if (m_printable != piUndef) + m_printable_icon = create_scaled_bitmap(m_printable == piPrintable ? "eye_open.png" : "eye_closed.png"); + + if (!m_opt_categories.empty()) + update_settings_digest_bitmaps(); +} + +bool ObjectDataViewModelNode::SetValue(const wxVariant& variant, unsigned col) +{ + switch (col) + { + case colPrint: + m_printable_icon << variant; + return true; + case colName: { + DataViewBitmapText data; + data << variant; + m_bmp = data.GetBitmap(); + m_name = data.GetText(); + return true; } + case colExtruder: { + DataViewBitmapText data; + data << variant; + m_extruder_bmp = data.GetBitmap(); + m_extruder = data.GetText() == "0" ? _(L("default")) : data.GetText(); + return true; } + case colEditing: + m_action_icon << variant; + return true; + default: + printf("MyObjectTreeModel::SetValue: wrong column"); + } + return false; +} + +void ObjectDataViewModelNode::SetIdx(const int& idx) +{ + m_idx = idx; + // update name if this node is instance + if (m_type == itInstance) + m_name = wxString::Format(_(L("Instance %d")), m_idx + 1); +} + +// ***************************************************************************** +// ---------------------------------------------------------------------------- +// ObjectDataViewModel +// ---------------------------------------------------------------------------- + +static int get_root_idx(ObjectDataViewModelNode *parent_node, const ItemType root_type) +{ + // because of istance_root and layers_root are at the end of the list, so + // start locking from the end + for (int root_idx = parent_node->GetChildCount() - 1; root_idx >= 0; root_idx--) + { + // if there is SettingsItem or VolumeItem, then RootItems don't exist in current ObjectItem + if (parent_node->GetNthChild(root_idx)->GetType() & (itSettings | itVolume)) + break; + if (parent_node->GetNthChild(root_idx)->GetType() & root_type) + return root_idx; + } + + return -1; +} + +ObjectDataViewModel::ObjectDataViewModel() +{ + m_bitmap_cache = new Slic3r::GUI::BitmapCache; +} + +ObjectDataViewModel::~ObjectDataViewModel() +{ + for (auto object : m_objects) + delete object; + delete m_bitmap_cache; + m_bitmap_cache = nullptr; +} + +wxDataViewItem ObjectDataViewModel::Add(const wxString &name, + const int extruder, + const bool has_errors/* = false*/) +{ + const wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder); + auto root = new ObjectDataViewModelNode(name, extruder_str); + // Add error icon if detected auto-repaire + if (has_errors) + root->m_bmp = *m_warning_bmp; + + m_objects.push_back(root); + // notify control + wxDataViewItem child((void*)root); + wxDataViewItem parent((void*)NULL); + + ItemAdded(parent, child); + return child; +} + +wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent_item, + const wxString &name, + const Slic3r::ModelVolumeType volume_type, + const bool has_errors/* = false*/, + const int extruder/* = 0*/, + const bool create_frst_child/* = true*/) +{ + ObjectDataViewModelNode *root = (ObjectDataViewModelNode*)parent_item.GetID(); + if (!root) return wxDataViewItem(0); + + wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder); + + // get insertion position according to the existed Layers and/or Instances Items + int insert_position = get_root_idx(root, itLayerRoot); + if (insert_position < 0) + insert_position = get_root_idx(root, itInstanceRoot); + + const bool obj_errors = root->m_bmp.IsOk(); + + if (create_frst_child && root->m_volumes_cnt == 0) + { + const Slic3r::ModelVolumeType type = Slic3r::ModelVolumeType::MODEL_PART; + const auto node = new ObjectDataViewModelNode(root, root->m_name, GetVolumeIcon(type, obj_errors), extruder_str, 0); + node->m_volume_type = type; + + insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); + // notify control + const wxDataViewItem child((void*)node); + ItemAdded(parent_item, child); + + root->m_volumes_cnt++; + if (insert_position >= 0) insert_position++; + } + + const auto node = new ObjectDataViewModelNode(root, name, GetVolumeIcon(volume_type, has_errors), extruder_str, root->m_volumes_cnt); + insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); + + // if part with errors is added, but object wasn't marked, then mark it + if (!obj_errors && has_errors) + root->SetBitmap(*m_warning_bmp); + + // notify control + const wxDataViewItem child((void*)node); + ItemAdded(parent_item, child); + root->m_volumes_cnt++; + + node->m_volume_type = volume_type; + + return child; +} + +wxDataViewItem ObjectDataViewModel::AddSettingsChild(const wxDataViewItem &parent_item) +{ + ObjectDataViewModelNode *root = (ObjectDataViewModelNode*)parent_item.GetID(); + if (!root) return wxDataViewItem(0); + + const auto node = new ObjectDataViewModelNode(root, itSettings); + root->Insert(node, 0); + // notify control + const wxDataViewItem child((void*)node); + ItemAdded(parent_item, child); + return child; +} + +/* return values: + * true => root_node is created and added to the parent_root + * false => root node alredy exists +*/ +static bool append_root_node(ObjectDataViewModelNode *parent_node, + ObjectDataViewModelNode **root_node, + const ItemType root_type) +{ + const int inst_root_id = get_root_idx(parent_node, root_type); + + *root_node = inst_root_id < 0 ? + new ObjectDataViewModelNode(parent_node, root_type) : + parent_node->GetNthChild(inst_root_id); + + if (inst_root_id < 0) { + if ((root_type&itInstanceRoot) || + ( (root_type&itLayerRoot) && get_root_idx(parent_node, itInstanceRoot)<0) ) + parent_node->Append(*root_node); + else if (root_type&itLayerRoot) + parent_node->Insert(*root_node, static_cast(get_root_idx(parent_node, itInstanceRoot))); + return true; + } + + return false; +} + +wxDataViewItem ObjectDataViewModel::AddRoot(const wxDataViewItem &parent_item, ItemType root_type) +{ + ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID(); + if (!parent_node) return wxDataViewItem(0); + + // get InstanceRoot node + ObjectDataViewModelNode *root_node { nullptr }; + const bool appended = append_root_node(parent_node, &root_node, root_type); + if (!root_node) return wxDataViewItem(0); + + const wxDataViewItem root_item((void*)root_node); + + if (appended) + ItemAdded(parent_item, root_item);// notify control + return root_item; +} + +wxDataViewItem ObjectDataViewModel::AddInstanceRoot(const wxDataViewItem &parent_item) +{ + return AddRoot(parent_item, itInstanceRoot); +} + +wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num) +{ + std::vector print_indicator(num, true); + + // if InstanceRoot item isn't created for this moment + if (!GetInstanceRootItem(parent_item).IsOk()) + // use object's printable state to first instance + print_indicator[0] = IsPrintable(parent_item); + + return wxDataViewItem((void*)AddInstanceChild(parent_item, print_indicator)); +} + +wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem& parent_item, + const std::vector& print_indicator) +{ + const wxDataViewItem inst_root_item = AddInstanceRoot(parent_item); + if (!inst_root_item) return wxDataViewItem(0); + + ObjectDataViewModelNode* inst_root_node = (ObjectDataViewModelNode*)inst_root_item.GetID(); + + // Add instance nodes + ObjectDataViewModelNode *instance_node = nullptr; + size_t counter = 0; + while (counter < print_indicator.size()) { + instance_node = new ObjectDataViewModelNode(inst_root_node, itInstance); + + instance_node->set_printable_icon(print_indicator[counter] ? piPrintable : piUnprintable); + + inst_root_node->Append(instance_node); + // notify control + const wxDataViewItem instance_item((void*)instance_node); + ItemAdded(inst_root_item, instance_item); + ++counter; + } + + // update object_node printable property + UpdateObjectPrintable(parent_item); + + return wxDataViewItem((void*)instance_node); +} + +void ObjectDataViewModel::UpdateObjectPrintable(wxDataViewItem parent_item) +{ + const wxDataViewItem inst_root_item = GetInstanceRootItem(parent_item); + if (!inst_root_item) + return; + + ObjectDataViewModelNode* inst_root_node = (ObjectDataViewModelNode*)inst_root_item.GetID(); + + const size_t child_cnt = inst_root_node->GetChildren().Count(); + PrintIndicator obj_pi = piUnprintable; + for (size_t i=0; i < child_cnt; i++) + if (inst_root_node->GetNthChild(i)->IsPrintable() & piPrintable) { + obj_pi = piPrintable; + break; + } + // and set printable state for object_node to piUndef + ObjectDataViewModelNode* obj_node = (ObjectDataViewModelNode*)parent_item.GetID(); + obj_node->set_printable_icon(obj_pi); + ItemChanged(parent_item); +} + +// update printable property for all instances from object +void ObjectDataViewModel::UpdateInstancesPrintable(wxDataViewItem parent_item) +{ + const wxDataViewItem inst_root_item = GetInstanceRootItem(parent_item); + if (!inst_root_item) + return; + + ObjectDataViewModelNode* obj_node = (ObjectDataViewModelNode*)parent_item.GetID(); + const PrintIndicator obj_pi = obj_node->IsPrintable(); + + ObjectDataViewModelNode* inst_root_node = (ObjectDataViewModelNode*)inst_root_item.GetID(); + const size_t child_cnt = inst_root_node->GetChildren().Count(); + + for (size_t i=0; i < child_cnt; i++) + { + ObjectDataViewModelNode* inst_node = inst_root_node->GetNthChild(i); + // and set printable state for object_node to piUndef + inst_node->set_printable_icon(obj_pi); + ItemChanged(wxDataViewItem((void*)inst_node)); + } +} + +bool ObjectDataViewModel::IsPrintable(const wxDataViewItem& item) const +{ + ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID(); + if (!node) + return false; + + return node->IsPrintable() == piPrintable; +} + +wxDataViewItem ObjectDataViewModel::AddLayersRoot(const wxDataViewItem &parent_item) +{ + return AddRoot(parent_item, itLayerRoot); +} + +wxDataViewItem ObjectDataViewModel::AddLayersChild(const wxDataViewItem &parent_item, + const t_layer_height_range& layer_range, + const int extruder/* = 0*/, + const int index /* = -1*/) +{ + ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID(); + if (!parent_node) return wxDataViewItem(0); + + wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder); + + // get LayerRoot node + ObjectDataViewModelNode *layer_root_node; + wxDataViewItem layer_root_item; + + if (parent_node->GetType() & itLayerRoot) { + layer_root_node = parent_node; + layer_root_item = parent_item; + } + else { + const int root_idx = get_root_idx(parent_node, itLayerRoot); + if (root_idx < 0) return wxDataViewItem(0); + layer_root_node = parent_node->GetNthChild(root_idx); + layer_root_item = wxDataViewItem((void*)layer_root_node); + } + + // Add layer node + ObjectDataViewModelNode *layer_node = new ObjectDataViewModelNode(layer_root_node, layer_range, index, extruder_str); + if (index < 0) + layer_root_node->Append(layer_node); + else + layer_root_node->Insert(layer_node, index); + + // notify control + const wxDataViewItem layer_item((void*)layer_node); + ItemAdded(layer_root_item, layer_item); + + return layer_item; +} + +wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item) +{ + auto ret_item = wxDataViewItem(0); + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + if (!node) // happens if item.IsOk()==false + return ret_item; + + auto node_parent = node->GetParent(); + wxDataViewItem parent(node_parent); + + // first remove the node from the parent's array of children; + // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_ + // thus removing the node from it doesn't result in freeing it + if (node_parent) { + if (node->m_type & (itInstanceRoot|itLayerRoot)) + { + // node can be deleted by the Delete, let's check its type while we safely can + bool is_instance_root = (node->m_type & itInstanceRoot); + + for (int i = int(node->GetChildCount() - 1); i >= (is_instance_root ? 1 : 0); i--) + Delete(wxDataViewItem(node->GetNthChild(i))); + + return parent; + } + + auto id = node_parent->GetChildren().Index(node); + auto idx = node->GetIdx(); + + + if (node->m_type & (itVolume|itLayer)) { + node_parent->m_volumes_cnt--; + DeleteSettings(item); + } + node_parent->GetChildren().Remove(node); + + if (id > 0) { + if (size_t(id) == node_parent->GetChildCount()) id--; + ret_item = wxDataViewItem(node_parent->GetChildren().Item(id)); + } + + //update idx value for remaining child-nodes + auto children = node_parent->GetChildren(); + for (size_t i = 0; i < node_parent->GetChildCount() && idx>=0; i++) + { + auto cur_idx = children[i]->GetIdx(); + if (cur_idx > idx) + children[i]->SetIdx(cur_idx-1); + } + + // if there is last instance item, delete both of it and instance root item + if (node_parent->GetChildCount() == 1 && node_parent->GetNthChild(0)->m_type == itInstance) + { + delete node; + ItemDeleted(parent, item); + + ObjectDataViewModelNode *last_instance_node = node_parent->GetNthChild(0); + PrintIndicator last_instance_printable = last_instance_node->IsPrintable(); + node_parent->GetChildren().Remove(last_instance_node); + delete last_instance_node; + ItemDeleted(parent, wxDataViewItem(last_instance_node)); + + ObjectDataViewModelNode *obj_node = node_parent->GetParent(); + obj_node->set_printable_icon(last_instance_printable); + obj_node->GetChildren().Remove(node_parent); + delete node_parent; + ret_item = wxDataViewItem(obj_node); + +#ifndef __WXGTK__ + if (obj_node->GetChildCount() == 0) + obj_node->m_container = false; +#endif //__WXGTK__ + ItemDeleted(ret_item, wxDataViewItem(node_parent)); + return ret_item; + } + + if (node->m_type & itInstance) + UpdateObjectPrintable(wxDataViewItem(node_parent->GetParent())); + + // if there was last layer item, delete this one and layers root item + if (node_parent->GetChildCount() == 0 && node_parent->m_type == itLayerRoot) + { + ObjectDataViewModelNode *obj_node = node_parent->GetParent(); + obj_node->GetChildren().Remove(node_parent); + delete node_parent; + ret_item = wxDataViewItem(obj_node); + +#ifndef __WXGTK__ + if (obj_node->GetChildCount() == 0) + obj_node->m_container = false; +#endif //__WXGTK__ + ItemDeleted(ret_item, wxDataViewItem(node_parent)); + return ret_item; + } + + // if there is last volume item after deleting, delete this last volume too + if (node_parent->GetChildCount() <= 3) // 3??? #ys_FIXME + { + int vol_cnt = 0; + int vol_idx = 0; + for (size_t i = 0; i < node_parent->GetChildCount(); ++i) { + if (node_parent->GetNthChild(i)->GetType() == itVolume) { + vol_idx = i; + vol_cnt++; + } + if (vol_cnt > 1) + break; + } + + if (vol_cnt == 1) { + delete node; + ItemDeleted(parent, item); + + ObjectDataViewModelNode *last_child_node = node_parent->GetNthChild(vol_idx); + DeleteSettings(wxDataViewItem(last_child_node)); + node_parent->GetChildren().Remove(last_child_node); + node_parent->m_volumes_cnt = 0; + delete last_child_node; + +#ifndef __WXGTK__ + if (node_parent->GetChildCount() == 0) + node_parent->m_container = false; +#endif //__WXGTK__ + ItemDeleted(parent, wxDataViewItem(last_child_node)); + + wxCommandEvent event(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED); + auto it = find(m_objects.begin(), m_objects.end(), node_parent); + event.SetInt(it == m_objects.end() ? -1 : it - m_objects.begin()); + wxPostEvent(m_ctrl, event); + + ret_item = parent; + + return ret_item; + } + } + } + else + { + auto it = find(m_objects.begin(), m_objects.end(), node); + size_t id = it - m_objects.begin(); + if (it != m_objects.end()) + { + // Delete all sub-items + int i = m_objects[id]->GetChildCount() - 1; + while (i >= 0) { + Delete(wxDataViewItem(m_objects[id]->GetNthChild(i))); + i = m_objects[id]->GetChildCount() - 1; + } + m_objects.erase(it); + } + if (id > 0) { + if(id == m_objects.size()) id--; + ret_item = wxDataViewItem(m_objects[id]); + } + } + // free the node + delete node; + + // set m_containet to FALSE if parent has no child + if (node_parent) { +#ifndef __WXGTK__ + if (node_parent->GetChildCount() == 0) + node_parent->m_container = false; +#endif //__WXGTK__ + ret_item = parent; + } + + // notify control + ItemDeleted(parent, item); + return ret_item; +} + +wxDataViewItem ObjectDataViewModel::DeleteLastInstance(const wxDataViewItem &parent_item, size_t num) +{ + auto ret_item = wxDataViewItem(0); + ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID(); + if (!parent_node) return ret_item; + + const int inst_root_id = get_root_idx(parent_node, itInstanceRoot); + if (inst_root_id < 0) return ret_item; + + wxDataViewItemArray items; + ObjectDataViewModelNode *inst_root_node = parent_node->GetNthChild(inst_root_id); + const wxDataViewItem inst_root_item((void*)inst_root_node); + + const int inst_cnt = inst_root_node->GetChildCount(); + const bool delete_inst_root_item = inst_cnt - num < 2 ? true : false; + + PrintIndicator last_inst_printable = piUndef; + + int stop = delete_inst_root_item ? 0 : inst_cnt - num; + for (int i = inst_cnt - 1; i >= stop;--i) { + ObjectDataViewModelNode *last_instance_node = inst_root_node->GetNthChild(i); + if (i==0) last_inst_printable = last_instance_node->IsPrintable(); + inst_root_node->GetChildren().Remove(last_instance_node); + delete last_instance_node; + ItemDeleted(inst_root_item, wxDataViewItem(last_instance_node)); + } + + if (delete_inst_root_item) { + ret_item = parent_item; + parent_node->GetChildren().Remove(inst_root_node); + parent_node->set_printable_icon(last_inst_printable); + ItemDeleted(parent_item, inst_root_item); + ItemChanged(parent_item); +#ifndef __WXGTK__ + if (parent_node->GetChildCount() == 0) + parent_node->m_container = false; +#endif //__WXGTK__ + } + + // update object_node printable property + UpdateObjectPrintable(parent_item); + + return ret_item; +} + +void ObjectDataViewModel::DeleteAll() +{ + while (!m_objects.empty()) + { + auto object = m_objects.back(); +// object->RemoveAllChildren(); + Delete(wxDataViewItem(object)); + } +} + +void ObjectDataViewModel::DeleteChildren(wxDataViewItem& parent) +{ + ObjectDataViewModelNode *root = (ObjectDataViewModelNode*)parent.GetID(); + if (!root) // happens if item.IsOk()==false + return; + + // first remove the node from the parent's array of children; + // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_ + // thus removing the node from it doesn't result in freeing it + auto& children = root->GetChildren(); + for (int id = root->GetChildCount() - 1; id >= 0; --id) + { + auto node = children[id]; + auto item = wxDataViewItem(node); + children.RemoveAt(id); + + if (node->m_type == itVolume) + root->m_volumes_cnt--; + + // free the node + delete node; + + // notify control + ItemDeleted(parent, item); + } + + // set m_containet to FALSE if parent has no child +#ifndef __WXGTK__ + root->m_container = false; +#endif //__WXGTK__ +} + +void ObjectDataViewModel::DeleteVolumeChildren(wxDataViewItem& parent) +{ + ObjectDataViewModelNode *root = (ObjectDataViewModelNode*)parent.GetID(); + if (!root) // happens if item.IsOk()==false + return; + + // first remove the node from the parent's array of children; + // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_ + // thus removing the node from it doesn't result in freeing it + auto& children = root->GetChildren(); + for (int id = root->GetChildCount() - 1; id >= 0; --id) + { + auto node = children[id]; + if (node->m_type != itVolume) + continue; + + auto item = wxDataViewItem(node); + DeleteSettings(item); + children.RemoveAt(id); + + // free the node + delete node; + + // notify control + ItemDeleted(parent, item); + } + root->m_volumes_cnt = 0; + + // set m_containet to FALSE if parent has no child +#ifndef __WXGTK__ + root->m_container = false; +#endif //__WXGTK__ +} + +void ObjectDataViewModel::DeleteSettings(const wxDataViewItem& parent) +{ + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)parent.GetID(); + if (!node) return; + + // if volume has a "settings"item, than delete it before volume deleting + if (node->GetChildCount() > 0 && node->GetNthChild(0)->GetType() == itSettings) { + auto settings_node = node->GetNthChild(0); + auto settings_item = wxDataViewItem(settings_node); + node->GetChildren().RemoveAt(0); + delete settings_node; + ItemDeleted(parent, settings_item); + } +} + +wxDataViewItem ObjectDataViewModel::GetItemById(int obj_idx) +{ + if (size_t(obj_idx) >= m_objects.size()) + { + printf("Error! Out of objects range.\n"); + return wxDataViewItem(0); + } + return wxDataViewItem(m_objects[obj_idx]); +} + + +wxDataViewItem ObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_idx) +{ + if (size_t(obj_idx) >= m_objects.size()) { + printf("Error! Out of objects range.\n"); + return wxDataViewItem(0); + } + + auto parent = m_objects[obj_idx]; + if (parent->GetChildCount() == 0 || + (parent->GetChildCount() == 1 && parent->GetNthChild(0)->GetType() & itSettings )) { + if (volume_idx == 0) + return GetItemById(obj_idx); + + printf("Error! Object has no one volume.\n"); + return wxDataViewItem(0); + } + + for (size_t i = 0; i < parent->GetChildCount(); i++) + if (parent->GetNthChild(i)->m_idx == volume_idx && parent->GetNthChild(i)->GetType() & itVolume) + return wxDataViewItem(parent->GetNthChild(i)); + + return wxDataViewItem(0); +} + +wxDataViewItem ObjectDataViewModel::GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type) +{ + if (size_t(obj_idx) >= m_objects.size()) { + printf("Error! Out of objects range.\n"); + return wxDataViewItem(0); + } + + auto item = GetItemByType(wxDataViewItem(m_objects[obj_idx]), parent_type); + if (!item) + return wxDataViewItem(0); + + auto parent = (ObjectDataViewModelNode*)item.GetID(); + for (size_t i = 0; i < parent->GetChildCount(); i++) + if (parent->GetNthChild(i)->m_idx == sub_obj_idx) + return wxDataViewItem(parent->GetNthChild(i)); + + return wxDataViewItem(0); +} + +wxDataViewItem ObjectDataViewModel::GetItemByInstanceId(int obj_idx, int inst_idx) +{ + return GetItemById(obj_idx, inst_idx, itInstanceRoot); +} + +wxDataViewItem ObjectDataViewModel::GetItemByLayerId(int obj_idx, int layer_idx) +{ + return GetItemById(obj_idx, layer_idx, itLayerRoot); +} + +wxDataViewItem ObjectDataViewModel::GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range) +{ + if (size_t(obj_idx) >= m_objects.size()) { + printf("Error! Out of objects range.\n"); + return wxDataViewItem(0); + } + + auto item = GetItemByType(wxDataViewItem(m_objects[obj_idx]), itLayerRoot); + if (!item) + return wxDataViewItem(0); + + auto parent = (ObjectDataViewModelNode*)item.GetID(); + for (size_t i = 0; i < parent->GetChildCount(); i++) + if (parent->GetNthChild(i)->m_layer_range == layer_range) + return wxDataViewItem(parent->GetNthChild(i)); + + return wxDataViewItem(0); +} + +int ObjectDataViewModel::GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range) +{ + wxDataViewItem item = GetItemByLayerRange(obj_idx, layer_range); + if (!item) + return -1; + + return GetLayerIdByItem(item); +} + +int ObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) const +{ + if(!item.IsOk()) + return -1; + + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + auto it = find(m_objects.begin(), m_objects.end(), node); + if (it == m_objects.end()) + return -1; + + return it - m_objects.begin(); +} + +int ObjectDataViewModel::GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const +{ + wxASSERT(item.IsOk()); + + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + if (!node || node->m_type != type) + return -1; + return node->GetIdx(); +} + +int ObjectDataViewModel::GetObjectIdByItem(const wxDataViewItem& item) const +{ + return GetIdByItem(GetTopParent(item)); +} + +int ObjectDataViewModel::GetVolumeIdByItem(const wxDataViewItem& item) const +{ + return GetIdByItemAndType(item, itVolume); +} + +int ObjectDataViewModel::GetInstanceIdByItem(const wxDataViewItem& item) const +{ + return GetIdByItemAndType(item, itInstance); +} + +int ObjectDataViewModel::GetLayerIdByItem(const wxDataViewItem& item) const +{ + return GetIdByItemAndType(item, itLayer); +} + +t_layer_height_range ObjectDataViewModel::GetLayerRangeByItem(const wxDataViewItem& item) const +{ + wxASSERT(item.IsOk()); + + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + if (!node || node->m_type != itLayer) + return { 0.0f, 0.0f }; + return node->GetLayerRange(); +} + +bool ObjectDataViewModel::UpdateColumValues(unsigned col) +{ + switch (col) + { + case colPrint: + case colName: + case colEditing: + return true; + case colExtruder: + { + wxDataViewItemArray items; + GetAllChildren(wxDataViewItem(nullptr), items); + + if (items.IsEmpty()) return false; + + for (auto item : items) + UpdateExtruderBitmap(item); + + return true; + } + default: + printf("MyObjectTreeModel::SetValue: wrong column"); + } + return false; +} + + +void ObjectDataViewModel::UpdateExtruderBitmap(wxDataViewItem item) +{ + wxString extruder = GetExtruder(item); + if (extruder.IsEmpty()) + return; + + // set extruder bitmap + int extruder_idx = atoi(extruder.c_str()); + if (extruder_idx > 0) --extruder_idx; + + const DataViewBitmapText extruder_val(extruder, get_extruder_color_icon(extruder_idx)); + + wxVariant value; + value << extruder_val; + + SetValue(value, item, colExtruder); +} + +void ObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx) +{ + wxASSERT(item.IsOk()); + type = itUndef; + + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + if (!node || + node->GetIdx() <-1 || + ( node->GetIdx() == -1 && + !(node->GetType() & (itObject | itSettings | itInstanceRoot | itLayerRoot/* | itLayer*/)) + ) + ) + return; + + idx = node->GetIdx(); + type = node->GetType(); + + ObjectDataViewModelNode *parent_node = node->GetParent(); + if (!parent_node) return; + + // get top parent (Object) node + while (parent_node->m_type != itObject) + parent_node = parent_node->GetParent(); + + auto it = find(m_objects.begin(), m_objects.end(), parent_node); + if (it != m_objects.end()) + obj_idx = it - m_objects.begin(); + else + type = itUndef; +} + +int ObjectDataViewModel::GetRowByItem(const wxDataViewItem& item) const +{ + if (m_objects.empty()) + return -1; + + int row_num = 0; + + for (size_t i = 0; i < m_objects.size(); i++) + { + row_num++; + if (item == wxDataViewItem(m_objects[i])) + return row_num; + + for (size_t j = 0; j < m_objects[i]->GetChildCount(); j++) + { + row_num++; + ObjectDataViewModelNode* cur_node = m_objects[i]->GetNthChild(j); + if (item == wxDataViewItem(cur_node)) + return row_num; + + if (cur_node->m_type == itVolume && cur_node->GetChildCount() == 1) + row_num++; + if (cur_node->m_type == itInstanceRoot) + { + row_num++; + for (size_t t = 0; t < cur_node->GetChildCount(); t++) + { + row_num++; + if (item == wxDataViewItem(cur_node->GetNthChild(t))) + return row_num; + } + } + } + } + + return -1; +} + +bool ObjectDataViewModel::InvalidItem(const wxDataViewItem& item) +{ + if (!item) + return true; + + ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID(); + if (!node || node->invalid()) + return true; + + return false; +} + +wxString ObjectDataViewModel::GetName(const wxDataViewItem &item) const +{ + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + if (!node) // happens if item.IsOk()==false + return wxEmptyString; + + return node->m_name; +} + +wxBitmap& ObjectDataViewModel::GetBitmap(const wxDataViewItem &item) const +{ + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + return node->m_bmp; +} + +wxString ObjectDataViewModel::GetExtruder(const wxDataViewItem& item) const +{ + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + if (!node) // happens if item.IsOk()==false + return wxEmptyString; + + return node->m_extruder; +} + +int ObjectDataViewModel::GetExtruderNumber(const wxDataViewItem& item) const +{ + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + if (!node) // happens if item.IsOk()==false + return 0; + + return atoi(node->m_extruder.c_str()); +} + +void ObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const +{ + wxASSERT(item.IsOk()); + + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + switch (col) + { + case colPrint: + variant << node->m_printable_icon; + break; + case colName: + variant << DataViewBitmapText(node->m_name, node->m_bmp); + break; + case colExtruder: + variant << DataViewBitmapText(node->m_extruder, node->m_extruder_bmp); + break; + case colEditing: + variant << node->m_action_icon; + break; + default: + ; + } +} + +bool ObjectDataViewModel::SetValue(const wxVariant &variant, const wxDataViewItem &item, unsigned int col) +{ + wxASSERT(item.IsOk()); + + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + return node->SetValue(variant, col); +} + +bool ObjectDataViewModel::SetValue(const wxVariant &variant, const int item_idx, unsigned int col) +{ + if (size_t(item_idx) >= m_objects.size()) + return false; + + return m_objects[item_idx]->SetValue(variant, col); +} + +void ObjectDataViewModel::SetExtruder(const wxString& extruder, wxDataViewItem item) +{ + DataViewBitmapText extruder_val; + extruder_val.SetText(extruder); + + // set extruder bitmap + int extruder_idx = atoi(extruder.c_str()); + if (extruder_idx > 0) --extruder_idx; + extruder_val.SetBitmap(get_extruder_color_icon(extruder_idx)); + + wxVariant value; + value << extruder_val; + + SetValue(value, item, colExtruder); +} + +wxDataViewItem ObjectDataViewModel::ReorganizeChildren( const int current_volume_id, + const int new_volume_id, + const wxDataViewItem &parent) +{ + auto ret_item = wxDataViewItem(0); + if (current_volume_id == new_volume_id) + return ret_item; + wxASSERT(parent.IsOk()); + ObjectDataViewModelNode *node_parent = (ObjectDataViewModelNode*)parent.GetID(); + if (!node_parent) // happens if item.IsOk()==false + return ret_item; + + const size_t shift = node_parent->GetChildren().Item(0)->m_type == itSettings ? 1 : 0; + + ObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id+shift); + node_parent->GetChildren().Remove(deleted_node); + ItemDeleted(parent, wxDataViewItem(deleted_node)); + node_parent->Insert(deleted_node, new_volume_id+shift); + ItemAdded(parent, wxDataViewItem(deleted_node)); + + //update volume_id value for child-nodes + auto children = node_parent->GetChildren(); + int id_frst = current_volume_id < new_volume_id ? current_volume_id : new_volume_id; + int id_last = current_volume_id > new_volume_id ? current_volume_id : new_volume_id; + for (int id = id_frst; id <= id_last; ++id) + children[id+shift]->SetIdx(id); + + return wxDataViewItem(node_parent->GetNthChild(new_volume_id+shift)); +} + +wxDataViewItem ObjectDataViewModel::ReorganizeObjects( const int current_id, const int new_id) +{ + if (current_id == new_id) + return wxDataViewItem(nullptr); + + ObjectDataViewModelNode* deleted_node = m_objects[current_id]; + m_objects.erase(m_objects.begin() + current_id); + ItemDeleted(wxDataViewItem(nullptr), wxDataViewItem(deleted_node)); + + m_objects.emplace(m_objects.begin() + new_id, deleted_node); + ItemAdded(wxDataViewItem(nullptr), wxDataViewItem(deleted_node)); + + return wxDataViewItem(deleted_node); +} + +bool ObjectDataViewModel::IsEnabled(const wxDataViewItem &item, unsigned int col) const +{ + wxASSERT(item.IsOk()); + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + + // disable extruder selection for the non "itObject|itVolume" item + return !(col == colExtruder && node->m_extruder.IsEmpty()); +} + +wxDataViewItem ObjectDataViewModel::GetParent(const wxDataViewItem &item) const +{ + // the invisible root node has no parent + if (!item.IsOk()) + return wxDataViewItem(0); + + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + assert(node != nullptr && node->valid()); + + // objects nodes has no parent too + if (node->m_type == itObject) + return wxDataViewItem(0); + + return wxDataViewItem((void*)node->GetParent()); +} + +wxDataViewItem ObjectDataViewModel::GetTopParent(const wxDataViewItem &item) const +{ + // the invisible root node has no parent + if (!item.IsOk()) + return wxDataViewItem(0); + + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + if (node->m_type == itObject) + return item; + + ObjectDataViewModelNode *parent_node = node->GetParent(); + while (parent_node->m_type != itObject) + parent_node = parent_node->GetParent(); + + return wxDataViewItem((void*)parent_node); +} + +bool ObjectDataViewModel::IsContainer(const wxDataViewItem &item) const +{ + // the invisible root node can have children + if (!item.IsOk()) + return true; + + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + return node->IsContainer(); +} + +unsigned int ObjectDataViewModel::GetChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const +{ + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)parent.GetID(); + if (!node) + { + for (auto object : m_objects) + array.Add(wxDataViewItem((void*)object)); + return m_objects.size(); + } + + if (node->GetChildCount() == 0) + { + return 0; + } + + unsigned int count = node->GetChildren().GetCount(); + for (unsigned int pos = 0; pos < count; pos++) + { + ObjectDataViewModelNode *child = node->GetChildren().Item(pos); + array.Add(wxDataViewItem((void*)child)); + } + + return count; +} + +void ObjectDataViewModel::GetAllChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const +{ + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)parent.GetID(); + if (!node) { + for (auto object : m_objects) + array.Add(wxDataViewItem((void*)object)); + } + else if (node->GetChildCount() == 0) + return; + else { + const size_t count = node->GetChildren().GetCount(); + for (size_t pos = 0; pos < count; pos++) { + ObjectDataViewModelNode *child = node->GetChildren().Item(pos); + array.Add(wxDataViewItem((void*)child)); + } + } + + wxDataViewItemArray new_array = array; + for (const auto item : new_array) + { + wxDataViewItemArray children; + GetAllChildren(item, children); + WX_APPEND_ARRAY(array, children); + } +} + +ItemType ObjectDataViewModel::GetItemType(const wxDataViewItem &item) const +{ + if (!item.IsOk()) + return itUndef; + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + return node->m_type < 0 ? itUndef : node->m_type; +} + +wxDataViewItem ObjectDataViewModel::GetItemByType(const wxDataViewItem &parent_item, ItemType type) const +{ + if (!parent_item.IsOk()) + return wxDataViewItem(0); + + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)parent_item.GetID(); + if (node->GetChildCount() == 0) + return wxDataViewItem(0); + + for (size_t i = 0; i < node->GetChildCount(); i++) { + if (node->GetNthChild(i)->m_type == type) + return wxDataViewItem((void*)node->GetNthChild(i)); + } + + return wxDataViewItem(0); +} + +wxDataViewItem ObjectDataViewModel::GetSettingsItem(const wxDataViewItem &item) const +{ + return GetItemByType(item, itSettings); +} + +wxDataViewItem ObjectDataViewModel::GetInstanceRootItem(const wxDataViewItem &item) const +{ + return GetItemByType(item, itInstanceRoot); +} + +wxDataViewItem ObjectDataViewModel::GetLayerRootItem(const wxDataViewItem &item) const +{ + return GetItemByType(item, itLayerRoot); +} + +bool ObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const +{ + if (!item.IsOk()) + return false; + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + return node->m_type == itSettings; +} + +void ObjectDataViewModel::UpdateSettingsDigest(const wxDataViewItem &item, + const std::vector& categories) +{ + if (!item.IsOk()) return; + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + if (!node->update_settings_digest(categories)) + return; + ItemChanged(item); +} + +void ObjectDataViewModel::SetVolumeType(const wxDataViewItem &item, const Slic3r::ModelVolumeType type) +{ + if (!item.IsOk() || GetItemType(item) != itVolume) + return; + + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + node->SetBitmap(*m_volume_bmps[int(type)]); + ItemChanged(item); +} + +wxDataViewItem ObjectDataViewModel::SetPrintableState( + PrintIndicator printable, + int obj_idx, + int subobj_idx /* = -1*/, + ItemType subobj_type/* = itInstance*/) +{ + wxDataViewItem item = wxDataViewItem(0); + if (subobj_idx < 0) + item = GetItemById(obj_idx); + else + item = subobj_type&itInstance ? GetItemByInstanceId(obj_idx, subobj_idx) : + GetItemByVolumeId(obj_idx, subobj_idx); + + ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID(); + if (!node) + return wxDataViewItem(0); + node->set_printable_icon(printable); + ItemChanged(item); + + if (subobj_idx >= 0) + UpdateObjectPrintable(GetItemById(obj_idx)); + + return item; +} + +wxDataViewItem ObjectDataViewModel::SetObjectPrintableState( + PrintIndicator printable, + wxDataViewItem obj_item) +{ + ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)obj_item.GetID(); + if (!node) + return wxDataViewItem(0); + node->set_printable_icon(printable); + ItemChanged(obj_item); + + UpdateInstancesPrintable(obj_item); + + return obj_item; +} + +void ObjectDataViewModel::Rescale() +{ + wxDataViewItemArray all_items; + GetAllChildren(wxDataViewItem(0), all_items); + + for (wxDataViewItem item : all_items) + { + if (!item.IsOk()) + continue; + + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + node->msw_rescale(); + + switch (node->m_type) + { + case itObject: + if (node->m_bmp.IsOk()) node->m_bmp = *m_warning_bmp; + break; + case itVolume: + node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_bmp.GetWidth() != node->m_bmp.GetHeight()); + break; + case itLayerRoot: + node->m_bmp = create_scaled_bitmap(LAYER_ROOT_ICON); + case itLayer: + node->m_bmp = create_scaled_bitmap(LAYER_ICON); + break; + default: break; + } + + ItemChanged(item); + } +} + +wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const bool is_marked/* = false*/) +{ + if (!is_marked) + return *m_volume_bmps[static_cast(vol_type)]; + + std::string scaled_bitmap_name = "warning" + std::to_string(static_cast(vol_type)); + scaled_bitmap_name += "-em" + std::to_string(Slic3r::GUI::wxGetApp().em_unit()); + + wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name); + if (bmp == nullptr) { + std::vector bmps; + + bmps.emplace_back(*m_warning_bmp); + bmps.emplace_back(*m_volume_bmps[static_cast(vol_type)]); + + bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); + } + + return *bmp; +} + +void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object/* = false*/) +{ + if (!item.IsOk()) + return; + + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + + if (!node->GetBitmap().IsOk() || !(node->GetType() & (itVolume | itObject))) + return; + + if (node->GetType() & itVolume) { + node->SetBitmap(*m_volume_bmps[static_cast(node->volume_type())]); + return; + } + + node->SetBitmap(wxNullBitmap); + if (unmark_object) + { + wxDataViewItemArray children; + GetChildren(item, children); + for (const wxDataViewItem& child : children) + DeleteWarningIcon(child); + } +} +/* +} +} +*/ +//----------------------------------------------------------------------------- +// DataViewBitmapText +//----------------------------------------------------------------------------- + +wxIMPLEMENT_DYNAMIC_CLASS(DataViewBitmapText, wxObject) + +IMPLEMENT_VARIANT_OBJECT(DataViewBitmapText) + +// --------------------------------------------------------- +// BitmapTextRenderer +// --------------------------------------------------------- + +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING +BitmapTextRenderer::BitmapTextRenderer(wxDataViewCellMode mode /*= wxDATAVIEW_CELL_EDITABLE*/, + int align /*= wxDVR_DEFAULT_ALIGNMENT*/): +wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align) +{ + SetMode(mode); + SetAlignment(align); +} +#endif // ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + +bool BitmapTextRenderer::SetValue(const wxVariant &value) +{ + m_value << value; + return true; +} + +bool BitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const +{ + return false; +} + +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY +wxString BitmapTextRenderer::GetAccessibleDescription() const +{ + return m_value.GetText(); +} +#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + +bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) +{ + int xoffset = 0; + + const wxBitmap& icon = m_value.GetBitmap(); + if (icon.IsOk()) + { +#ifdef __APPLE__ + wxSize icon_sz = icon.GetScaledSize(); +#else + wxSize icon_sz = icon.GetSize(); +#endif + dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon_sz.y) / 2); + xoffset = icon_sz.x + 4; + } + + RenderText(m_value.GetText(), xoffset, rect, dc, state); + + return true; +} + +wxSize BitmapTextRenderer::GetSize() const +{ + if (!m_value.GetText().empty()) + { + wxSize size = GetTextExtent(m_value.GetText()); + + if (m_value.GetBitmap().IsOk()) + size.x += m_value.GetBitmap().GetWidth() + 4; + return size; + } + return wxSize(80, 20); +} + + +wxWindow* BitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) +{ + wxDataViewCtrl* const dv_ctrl = GetOwner()->GetOwner(); + ObjectDataViewModel* const model = dynamic_cast(dv_ctrl->GetModel()); + + if ( !(model->GetItemType(dv_ctrl->GetSelection()) & (itVolume | itObject)) ) + return nullptr; + + DataViewBitmapText data; + data << value; + + m_was_unusable_symbol = false; + + wxPoint position = labelRect.GetPosition(); + if (data.GetBitmap().IsOk()) { + const int bmp_width = data.GetBitmap().GetWidth(); + position.x += bmp_width; + labelRect.SetWidth(labelRect.GetWidth() - bmp_width); + } + + wxTextCtrl* text_editor = new wxTextCtrl(parent, wxID_ANY, data.GetText(), + position, labelRect.GetSize(), wxTE_PROCESS_ENTER); + text_editor->SetInsertionPointEnd(); + text_editor->SelectAll(); + + return text_editor; +} + +bool BitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) +{ + wxTextCtrl* text_editor = wxDynamicCast(ctrl, wxTextCtrl); + if (!text_editor || text_editor->GetValue().IsEmpty()) + return false; + + std::string chosen_name = Slic3r::normalize_utf8_nfc(text_editor->GetValue().ToUTF8()); + const char* unusable_symbols = "<>:/\\|?*\""; + for (size_t i = 0; i < std::strlen(unusable_symbols); i++) { + if (chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) { + m_was_unusable_symbol = true; + return false; + } + } + + // The icon can't be edited so get its old value and reuse it. + wxVariant valueOld; + GetView()->GetModel()->GetValue(valueOld, m_item, colName); + + DataViewBitmapText bmpText; + bmpText << valueOld; + + // But replace the text with the value entered by user. + bmpText.SetText(text_editor->GetValue()); + + value << bmpText; + return true; +} + +// ---------------------------------------------------------------------------- +// BitmapChoiceRenderer +// ---------------------------------------------------------------------------- + +bool BitmapChoiceRenderer::SetValue(const wxVariant& value) +{ + m_value << value; + return true; +} + +bool BitmapChoiceRenderer::GetValue(wxVariant& value) const +{ + value << m_value; + return true; +} + +bool BitmapChoiceRenderer::Render(wxRect rect, wxDC* dc, int state) +{ + int xoffset = 0; + + const wxBitmap& icon = m_value.GetBitmap(); + if (icon.IsOk()) + { + dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); + xoffset = icon.GetWidth() + 4; + } + + if (rect.height==0) + rect.height= icon.GetHeight(); + RenderText(m_value.GetText(), xoffset, rect, dc, state); + + return true; +} + +wxSize BitmapChoiceRenderer::GetSize() const +{ + wxSize sz = GetTextExtent(m_value.GetText()); + + if (m_value.GetBitmap().IsOk()) + sz.x += m_value.GetBitmap().GetWidth() + 4; + + return sz; +} + + +wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) +{ + wxDataViewCtrl* const dv_ctrl = GetOwner()->GetOwner(); + ObjectDataViewModel* const model = dynamic_cast(dv_ctrl->GetModel()); + + if (!(model->GetItemType(dv_ctrl->GetSelection()) & (itVolume | itLayer | itObject))) + return nullptr; + + std::vector icons = get_extruder_color_icons(); + if (icons.empty()) + return nullptr; + + DataViewBitmapText data; + data << value; + + auto c_editor = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, + labelRect.GetTopLeft(), wxSize(labelRect.GetWidth(), -1), + 0, nullptr , wxCB_READONLY); + + int i=0; + for (wxBitmap* bmp : icons) { + if (i==0) { + c_editor->Append(_(L("default")), *bmp); + ++i; + } + + c_editor->Append(wxString::Format("%d", i), *bmp); + ++i; + } + c_editor->SetSelection(atoi(data.GetText().c_str())); + + // to avoid event propagation to other sidebar items + c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) { + evt.StopPropagation(); + // FinishEditing grabs new selection and triggers config update. We better call + // it explicitly, automatic update on KILL_FOCUS didn't work on Linux. + this->FinishEditing(); + }); + + return c_editor; +} + +bool BitmapChoiceRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) +{ + wxBitmapComboBox* c = (wxBitmapComboBox*)ctrl; + int selection = c->GetSelection(); + if (selection < 0) + return false; + + DataViewBitmapText bmpText; + + bmpText.SetText(c->GetString(selection)); + bmpText.SetBitmap(c->GetItemBitmap(selection)); + + value << bmpText; + return true; +} + +} // namespace GUI +} // namespace Slic3r + + diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp new file mode 100644 index 0000000000..3d838cd435 --- /dev/null +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -0,0 +1,516 @@ +#ifndef slic3r_GUI_ObjectDataViewModel_hpp_ +#define slic3r_GUI_ObjectDataViewModel_hpp_ + +#include + +#include + +namespace Slic3r { + +enum class ModelVolumeType : int; + +namespace GUI { + +typedef double coordf_t; +typedef std::pair t_layer_height_range; + +// ---------------------------------------------------------------------------- +// DataViewBitmapText: helper class used by BitmapTextRenderer +// ---------------------------------------------------------------------------- + +class DataViewBitmapText : public wxObject +{ +public: + DataViewBitmapText( const wxString &text = wxEmptyString, + const wxBitmap& bmp = wxNullBitmap) : + m_text(text), + m_bmp(bmp) + { } + + DataViewBitmapText(const DataViewBitmapText &other) + : wxObject(), + m_text(other.m_text), + m_bmp(other.m_bmp) + { } + + void SetText(const wxString &text) { m_text = text; } + wxString GetText() const { return m_text; } + void SetBitmap(const wxBitmap &bmp) { m_bmp = bmp; } + const wxBitmap &GetBitmap() const { return m_bmp; } + + bool IsSameAs(const DataViewBitmapText& other) const { + return m_text == other.m_text && m_bmp.IsSameAs(other.m_bmp); + } + + bool operator==(const DataViewBitmapText& other) const { + return IsSameAs(other); + } + + bool operator!=(const DataViewBitmapText& other) const { + return !IsSameAs(other); + } + +private: + wxString m_text; + wxBitmap m_bmp; + + wxDECLARE_DYNAMIC_CLASS(DataViewBitmapText); +}; +DECLARE_VARIANT_OBJECT(DataViewBitmapText) + +// ---------------------------------------------------------------------------- +// BitmapTextRenderer +// ---------------------------------------------------------------------------- +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING +class BitmapTextRenderer : public wxDataViewRenderer +#else +class BitmapTextRenderer : public wxDataViewCustomRenderer +#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING +{ +public: + BitmapTextRenderer(wxWindow* parent, + wxDataViewCellMode mode = +#ifdef __WXOSX__ + wxDATAVIEW_CELL_INERT +#else + wxDATAVIEW_CELL_EDITABLE +#endif + + , int align = wxDVR_DEFAULT_ALIGNMENT +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + ); +#else + ) : + wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align), + m_parent(parent) + {} +#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + + bool SetValue(const wxVariant& value); + bool GetValue(wxVariant& value) const; +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY + virtual wxString GetAccessibleDescription() const override; +#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + + virtual bool Render(wxRect cell, wxDC* dc, int state) override; + virtual wxSize GetSize() const override; + + bool HasEditorCtrl() const override + { +#ifdef __WXOSX__ + return false; +#else + return true; +#endif + } + wxWindow* CreateEditorCtrl(wxWindow* parent, + wxRect labelRect, + const wxVariant& value) override; + bool GetValueFromEditorCtrl(wxWindow* ctrl, + wxVariant& value) override; + bool WasCanceled() const { return m_was_unusable_symbol; } + +private: + DataViewBitmapText m_value; + bool m_was_unusable_symbol{ false }; + wxWindow* m_parent{ nullptr }; +}; + + +// ---------------------------------------------------------------------------- +// BitmapChoiceRenderer +// ---------------------------------------------------------------------------- + +class BitmapChoiceRenderer : public wxDataViewCustomRenderer +{ +public: + BitmapChoiceRenderer(wxDataViewCellMode mode = +#ifdef __WXOSX__ + wxDATAVIEW_CELL_INERT +#else + wxDATAVIEW_CELL_EDITABLE +#endif + , int align = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL + ) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) {} + + bool SetValue(const wxVariant& value); + bool GetValue(wxVariant& value) const; + + virtual bool Render(wxRect cell, wxDC* dc, int state) override; + virtual wxSize GetSize() const override; + + bool HasEditorCtrl() const override { return true; } + wxWindow* CreateEditorCtrl(wxWindow* parent, + wxRect labelRect, + const wxVariant& value) override; + bool GetValueFromEditorCtrl(wxWindow* ctrl, + wxVariant& value) override; + +private: + DataViewBitmapText m_value; +}; + + +// ---------------------------------------------------------------------------- +// ObjectDataViewModelNode: a node inside ObjectDataViewModel +// ---------------------------------------------------------------------------- +enum ItemType { + itUndef = 0, + itObject = 1, + itVolume = 2, + itInstanceRoot = 4, + itInstance = 8, + itSettings = 16, + itLayerRoot = 32, + itLayer = 64, +}; + +enum ColumnNumber +{ + colName = 0, // item name + colPrint , // printable property + colExtruder , // extruder selection + colEditing , // item editing +}; + +enum PrintIndicator +{ + piUndef = 0, // no print indicator + piPrintable , // printable + piUnprintable , // unprintable +}; + +class ObjectDataViewModelNode; +WX_DEFINE_ARRAY_PTR(ObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray); + +class ObjectDataViewModelNode +{ + ObjectDataViewModelNode* m_parent; + MyObjectTreeModelNodePtrArray m_children; + wxBitmap m_empty_bmp; + size_t m_volumes_cnt = 0; + std::vector< std::string > m_opt_categories; + t_layer_height_range m_layer_range = { 0.0f, 0.0f }; + + wxString m_name; + wxBitmap& m_bmp = m_empty_bmp; + ItemType m_type; + int m_idx = -1; + bool m_container = false; + wxString m_extruder = "default"; + wxBitmap m_extruder_bmp; + wxBitmap m_action_icon; + PrintIndicator m_printable {piUndef}; + wxBitmap m_printable_icon; + + std::string m_action_icon_name = ""; + ModelVolumeType m_volume_type; + +public: + ObjectDataViewModelNode(const wxString& name, + const wxString& extruder): + m_parent(NULL), + m_name(name), + m_type(itObject), + m_extruder(extruder) + { + set_action_and_extruder_icons(); + init_container(); + } + + ObjectDataViewModelNode(ObjectDataViewModelNode* parent, + const wxString& sub_obj_name, + const wxBitmap& bmp, + const wxString& extruder, + const int idx = -1 ) : + m_parent (parent), + m_name (sub_obj_name), + m_type (itVolume), + m_idx (idx), + m_extruder (extruder) + { + m_bmp = bmp; + set_action_and_extruder_icons(); + init_container(); + } + + ObjectDataViewModelNode(ObjectDataViewModelNode* parent, + const t_layer_height_range& layer_range, + const int idx = -1, + const wxString& extruder = wxEmptyString ); + + ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type); + + ~ObjectDataViewModelNode() + { + // free all our children nodes + size_t count = m_children.GetCount(); + for (size_t i = 0; i < count; i++) + { + ObjectDataViewModelNode *child = m_children[i]; + delete child; + } +#ifndef NDEBUG + // Indicate that the object was deleted. + m_idx = -2; +#endif /* NDEBUG */ + } + + void init_container(); + bool IsContainer() const + { + return m_container; + } + + ObjectDataViewModelNode* GetParent() + { + assert(m_parent == nullptr || m_parent->valid()); + return m_parent; + } + MyObjectTreeModelNodePtrArray& GetChildren() + { + return m_children; + } + ObjectDataViewModelNode* GetNthChild(unsigned int n) + { + return m_children.Item(n); + } + void Insert(ObjectDataViewModelNode* child, unsigned int n) + { + if (!m_container) + m_container = true; + m_children.Insert(child, n); + } + void Append(ObjectDataViewModelNode* child) + { + if (!m_container) + m_container = true; + m_children.Add(child); + } + void RemoveAllChildren() + { + if (GetChildCount() == 0) + return; + for (int id = int(GetChildCount()) - 1; id >= 0; --id) + { + if (m_children.Item(id)->GetChildCount() > 0) + m_children[id]->RemoveAllChildren(); + auto node = m_children[id]; + m_children.RemoveAt(id); + delete node; + } + } + + size_t GetChildCount() const + { + return m_children.GetCount(); + } + + bool SetValue(const wxVariant &variant, unsigned int col); + + void SetBitmap(const wxBitmap &icon) { m_bmp = icon; } + const wxBitmap& GetBitmap() const { return m_bmp; } + const wxString& GetName() const { return m_name; } + ItemType GetType() const { return m_type; } + void SetIdx(const int& idx); + int GetIdx() const { return m_idx; } + t_layer_height_range GetLayerRange() const { return m_layer_range; } + PrintIndicator IsPrintable() const { return m_printable; } + + // use this function only for childrens + void AssignAllVal(ObjectDataViewModelNode& from_node) + { + // ! Don't overwrite other values because of equality of this values for all children -- + m_name = from_node.m_name; + m_bmp = from_node.m_bmp; + m_idx = from_node.m_idx; + m_extruder = from_node.m_extruder; + m_type = from_node.m_type; + } + + bool SwapChildrens(int frst_id, int scnd_id) { + if (GetChildCount() < 2 || + frst_id < 0 || (size_t)frst_id >= GetChildCount() || + scnd_id < 0 || (size_t)scnd_id >= GetChildCount()) + return false; + + ObjectDataViewModelNode new_scnd = *GetNthChild(frst_id); + ObjectDataViewModelNode new_frst = *GetNthChild(scnd_id); + + new_scnd.m_idx = m_children.Item(scnd_id)->m_idx; + new_frst.m_idx = m_children.Item(frst_id)->m_idx; + + m_children.Item(frst_id)->AssignAllVal(new_frst); + m_children.Item(scnd_id)->AssignAllVal(new_scnd); + return true; + } + + // Set action icons for node + void set_action_and_extruder_icons(); + // Set printable icon for node + void set_printable_icon(PrintIndicator printable); + + void update_settings_digest_bitmaps(); + bool update_settings_digest(const std::vector& categories); + int volume_type() const { return int(m_volume_type); } + void msw_rescale(); + +#ifndef NDEBUG + bool valid(); +#endif /* NDEBUG */ + bool invalid() const { return m_idx < -1; } + +private: + friend class ObjectDataViewModel; +}; + + +// ---------------------------------------------------------------------------- +// ObjectDataViewModel +// ---------------------------------------------------------------------------- + +// custom message the model sends to associated control to notify a last volume deleted from the object: +wxDECLARE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent); + +class ObjectDataViewModel :public wxDataViewModel +{ + std::vector m_objects; + std::vector m_volume_bmps; + wxBitmap* m_warning_bmp { nullptr }; + + wxDataViewCtrl* m_ctrl { nullptr }; + +public: + ObjectDataViewModel(); + ~ObjectDataViewModel(); + + wxDataViewItem Add( const wxString &name, + const int extruder, + const bool has_errors = false); + wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item, + const wxString &name, + const Slic3r::ModelVolumeType volume_type, + const bool has_errors = false, + const int extruder = 0, + const bool create_frst_child = true); + wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); + wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num); + wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, const std::vector& print_indicator); + wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item); + wxDataViewItem AddLayersChild( const wxDataViewItem &parent_item, + const t_layer_height_range& layer_range, + const int extruder = 0, + const int index = -1); + wxDataViewItem Delete(const wxDataViewItem &item); + wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num); + void DeleteAll(); + void DeleteChildren(wxDataViewItem& parent); + void DeleteVolumeChildren(wxDataViewItem& parent); + void DeleteSettings(const wxDataViewItem& parent); + wxDataViewItem GetItemById(int obj_idx); + wxDataViewItem GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type); + wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx); + wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx); + wxDataViewItem GetItemByLayerId(int obj_idx, int layer_idx); + wxDataViewItem GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range); + int GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range); + int GetIdByItem(const wxDataViewItem& item) const; + int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const; + int GetObjectIdByItem(const wxDataViewItem& item) const; + int GetVolumeIdByItem(const wxDataViewItem& item) const; + int GetInstanceIdByItem(const wxDataViewItem& item) const; + int GetLayerIdByItem(const wxDataViewItem& item) const; + void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx); + int GetRowByItem(const wxDataViewItem& item) const; + bool IsEmpty() { return m_objects.empty(); } + bool InvalidItem(const wxDataViewItem& item); + + // helper method for wxLog + + wxString GetName(const wxDataViewItem &item) const; + wxBitmap& GetBitmap(const wxDataViewItem &item) const; + wxString GetExtruder(const wxDataViewItem &item) const; + int GetExtruderNumber(const wxDataViewItem &item) const; + + // helper methods to change the model + + virtual unsigned int GetColumnCount() const override { return 3;} + virtual wxString GetColumnType(unsigned int col) const override{ return wxT("string"); } + + virtual void GetValue( wxVariant &variant, + const wxDataViewItem &item, + unsigned int col) const override; + virtual bool SetValue( const wxVariant &variant, + const wxDataViewItem &item, + unsigned int col) override; + bool SetValue( const wxVariant &variant, + const int item_idx, + unsigned int col); + + void SetExtruder(const wxString& extruder, wxDataViewItem item); + + // For parent move child from cur_volume_id place to new_volume_id + // Remaining items will moved up/down accordingly + wxDataViewItem ReorganizeChildren( const int cur_volume_id, + const int new_volume_id, + const wxDataViewItem &parent); + wxDataViewItem ReorganizeObjects( int current_id, int new_id); + + virtual bool IsEnabled(const wxDataViewItem &item, unsigned int col) const override; + + virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override; + // get object item + wxDataViewItem GetTopParent(const wxDataViewItem &item) const; + virtual bool IsContainer(const wxDataViewItem &item) const override; + virtual unsigned int GetChildren(const wxDataViewItem &parent, + wxDataViewItemArray &array) const override; + void GetAllChildren(const wxDataViewItem &parent,wxDataViewItemArray &array) const; + // Is the container just a header or an item with all columns + // In our case it is an item with all columns + virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } + + ItemType GetItemType(const wxDataViewItem &item) const ; + wxDataViewItem GetItemByType( const wxDataViewItem &parent_item, + ItemType type) const; + wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const; + wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const; + wxDataViewItem GetLayerRootItem(const wxDataViewItem &item) const; + bool IsSettingsItem(const wxDataViewItem &item) const; + void UpdateSettingsDigest( const wxDataViewItem &item, + const std::vector& categories); + + bool IsPrintable(const wxDataViewItem &item) const; + void UpdateObjectPrintable(wxDataViewItem parent_item); + void UpdateInstancesPrintable(wxDataViewItem parent_item); + + void SetVolumeBitmaps(const std::vector& volume_bmps) { m_volume_bmps = volume_bmps; } + void SetWarningBitmap(wxBitmap* bitmap) { m_warning_bmp = bitmap; } + void SetVolumeType(const wxDataViewItem &item, const Slic3r::ModelVolumeType type); + wxDataViewItem SetPrintableState( PrintIndicator printable, int obj_idx, + int subobj_idx = -1, + ItemType subobj_type = itInstance); + wxDataViewItem SetObjectPrintableState(PrintIndicator printable, wxDataViewItem obj_item); + + void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; } + // Rescale bitmaps for existing Items + void Rescale(); + + wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, + const bool is_marked = false); + void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false); + t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const; + + bool UpdateColumValues(unsigned col); + void UpdateExtruderBitmap(wxDataViewItem item); + +private: + wxDataViewItem AddRoot(const wxDataViewItem& parent_item, const ItemType root_type); + wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item); +}; + + +} +} + + +#endif // slic3r_GUI_ObjectDataViewModel_hpp_ diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 8b6f5bc304..4e964ff74e 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -128,6 +128,10 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n for (auto opt : option_set) m_options.emplace(opt.opt_id, opt); + // Set sidetext width for a better alignment of options in line + if (option_set.size() > 1) + sidetext_width = Field::def_width_thinner(); + // add mode value for current line to m_options_mode if (!option_set.empty()) m_options_mode.push_back(option_set[0].opt.mode); @@ -274,9 +278,9 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n sizer_tmp->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0); // add sidetext if any - if (option.sidetext != "") { + if (!option.sidetext.empty() || sidetext_width > 0) { auto sidetext = new wxStaticText( this->ctrl_parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition, - wxSize(sidetext_width != -1 ? sidetext_width*wxGetApp().em_unit() : -1, -1) /*wxDefaultSize*/, wxALIGN_LEFT); + wxSize(sidetext_width != -1 ? sidetext_width*wxGetApp().em_unit() : -1, -1), wxALIGN_LEFT); sidetext->SetBackgroundStyle(wxBG_STYLE_PAINT); sidetext->SetFont(wxGetApp().normal_font()); sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); @@ -498,7 +502,7 @@ void ConfigOptionsGroup::msw_rescale() // update undo buttons : rescale bitmaps for (const auto& field : m_fields) - field.second->msw_rescale(); + field.second->msw_rescale(sidetext_width>0); const int em = em_unit(parent()); @@ -703,7 +707,7 @@ Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int op void ogStaticText::SetText(const wxString& value, bool wrap/* = true*/) { SetLabel(value); - if (wrap) Wrap(40 * wxGetApp().em_unit()); + if (wrap) Wrap(60 * wxGetApp().em_unit()); GetParent()->Layout(); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a86dd5ceba..3171818875 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -26,9 +26,7 @@ #include #include #include -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK #include -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK #include "libslic3r/libslic3r.h" #include "libslic3r/Format/STL.hpp" @@ -39,11 +37,13 @@ #include "libslic3r/GCode/ThumbnailData.hpp" #endif // ENABLE_THUMBNAIL_GENERATOR #include "libslic3r/Model.hpp" +#include "libslic3r/SLA/Hollowing.hpp" +#include "libslic3r/SLA/Rotfinder.hpp" +#include "libslic3r/SLA/SupportPoint.hpp" #include "libslic3r/Polygon.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/SLAPrint.hpp" -#include "libslic3r/SLA/SLARotfinder.hpp" #include "libslic3r/Utils.hpp" //#include "libslic3r/ClipperUtils.hpp" @@ -70,6 +70,7 @@ #include "Camera.hpp" #include "Mouse3DController.hpp" #include "Tab.hpp" +#include "Job.hpp" #include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" #include "ProgressStatusBar.hpp" @@ -84,6 +85,7 @@ #include // Needs to be last because reasons :-/ #include "WipeTowerDialog.hpp" +#include "libslic3r/CustomGCode.hpp" using boost::optional; namespace fs = boost::filesystem; @@ -165,7 +167,7 @@ ObjectInfo::ObjectInfo(wxWindow *parent) : info_manifold_text->SetFont(wxGetApp().small_font()); info_manifold = new wxStaticText(parent, wxID_ANY, ""); info_manifold->SetFont(wxGetApp().small_font()); - manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap(parent, "exclamation")); + manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap("exclamation")); auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL); sizer_manifold->Add(info_manifold_text, 0); sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2); @@ -184,7 +186,7 @@ void ObjectInfo::show_sizer(bool show) void ObjectInfo::msw_rescale() { - manifold_warning_icon->SetBitmap(create_scaled_bitmap(nullptr, "exclamation")); + manifold_warning_icon->SetBitmap(create_scaled_bitmap("exclamation")); } enum SlicedInfoIdx @@ -254,7 +256,7 @@ void SlicedInfo::SetTextAndShow(SlicedInfoIdx idx, const wxString& text, const w } PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) : -wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 * wxGetApp().em_unit(), -1), 0, nullptr, wxCB_READONLY), +PresetBitmapComboBox(parent, wxSize(15 * wxGetApp().em_unit(), -1)), preset_type(preset_type), last_selected(wxNOT_FOUND), m_em_unit(wxGetApp().em_unit()) @@ -870,7 +872,7 @@ Sidebar::Sidebar(Plater *parent) init_scalable_btn(&p->btn_send_gcode , "export_gcode", _(L("Send to printer"))); init_scalable_btn(&p->btn_remove_device, "cross" , _(L("Remove device"))); - init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _(L("Export to SD card/ USB thumb drive"))); + init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _(L("Export to SD card / Flash drive"))); // regular buttons "Slice now" and "Export G-code" @@ -1502,144 +1504,39 @@ struct Plater::priv // objects would be frozen for the user. In case of arrange, an animation // could be shown, or with the optimize orientations, partial results // could be displayed. - class Job : public wxEvtHandler + class PlaterJob: public Job { - int m_range = 100; - boost::thread m_thread; - priv * m_plater = nullptr; - std::atomic m_running{false}, m_canceled{false}; - bool m_finalized = false; - - void run() - { - m_running.store(true); - process(); - m_running.store(false); - - // ensure to call the last status to finalize the job - update_status(status_range(), ""); - } - + priv *m_plater; protected: - // status range for a particular job - virtual int status_range() const { return 100; } - - // status update, to be used from the work thread (process() method) - void update_status(int st, const wxString &msg = "") - { - auto evt = new wxThreadEvent(); - evt->SetInt(st); - evt->SetString(msg); - wxQueueEvent(this, evt); - } priv & plater() { return *m_plater; } const priv &plater() const { return *m_plater; } - bool was_canceled() const { return m_canceled.load(); } - - // Launched just before start(), a job can use it to prepare internals - virtual void prepare() {} // Launched when the job is finished. It refreshes the 3Dscene by def. - virtual void finalize() + void finalize() override { // Do a full refresh of scene tree, including regenerating // all the GLVolumes. FIXME The update function shall just // reload the modified matrices. - if (!was_canceled()) + if (!Job::was_canceled()) plater().update(unsigned(UpdateParams::FORCE_FULL_SCREEN_REFRESH)); + + Job::finalize(); } public: - Job(priv *_plater) : m_plater(_plater) - { - Bind(wxEVT_THREAD, [this](const wxThreadEvent &evt) { - auto msg = evt.GetString(); - if (!msg.empty()) - plater().statusbar()->set_status_text(msg); - - if (m_finalized) return; - - plater().statusbar()->set_progress(evt.GetInt()); - if (evt.GetInt() == status_range()) { - // set back the original range and cancel callback - plater().statusbar()->set_range(m_range); - plater().statusbar()->set_cancel_callback(); - wxEndBusyCursor(); - - finalize(); - - // dont do finalization again for the same process - m_finalized = true; - } - }); - } - - Job(const Job &) = delete; - Job(Job &&) = delete; - Job &operator=(const Job &) = delete; - Job &operator=(Job &&) = delete; - - virtual void process() = 0; - - void start() - { // Start the job. No effect if the job is already running - if (!m_running.load()) { - prepare(); - - // Save the current status indicatior range and push the new one - m_range = plater().statusbar()->get_range(); - plater().statusbar()->set_range(status_range()); - - // init cancellation flag and set the cancel callback - m_canceled.store(false); - plater().statusbar()->set_cancel_callback( - [this]() { m_canceled.store(true); }); - - m_finalized = false; - - // Changing cursor to busy - wxBeginBusyCursor(); - - try { // Execute the job - m_thread = create_thread([this] { this->run(); }); - } catch (std::exception &) { - update_status(status_range(), - _(L("ERROR: not enough resources to " - "execute a new job."))); - } - - // The state changes will be undone when the process hits the - // last status value, in the status update handler (see ctor) - } - } - - // To wait for the running job and join the threads. False is - // returned if the timeout has been reached and the job is still - // running. Call cancel() before this fn if you want to explicitly - // end the job. - bool join(int timeout_ms = 0) - { - if (!m_thread.joinable()) return true; - - if (timeout_ms <= 0) - m_thread.join(); - else if (!m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms))) - return false; - - return true; - } - - bool is_running() const { return m_running.load(); } - void cancel() { m_canceled.store(true); } + PlaterJob(priv *_plater) + : Job(_plater->statusbar()), m_plater(_plater) + {} }; enum class Jobs : size_t { Arrange, - Rotoptimize + Rotoptimize, + Hollow }; - class ArrangeJob : public Job + class ArrangeJob : public PlaterJob { using ArrangePolygon = arrangement::ArrangePolygon; using ArrangePolygons = arrangement::ArrangePolygons; @@ -1756,7 +1653,7 @@ struct Plater::priv } public: - using Job::Job; + using PlaterJob::PlaterJob; int status_range() const override { return int(m_selected.size()); } @@ -1773,13 +1670,30 @@ struct Plater::priv } }; - class RotoptimizeJob : public Job + class RotoptimizeJob : public PlaterJob { public: - using Job::Job; + using PlaterJob::PlaterJob; void process() override; }; + class HollowJob : public PlaterJob + { + public: + using PlaterJob::PlaterJob; + void prepare() override; + void process() override; + void finalize() override; + private: + GLGizmoHollow * get_gizmo(); + const GLGizmoHollow * get_gizmo() const; + + std::unique_ptr m_output_mesh; + std::unique_ptr m_output_raycaster; + const TriangleMesh* m_object_mesh = nullptr; + sla::HollowingConfig m_cfg; + }; + // Jobs defined inside the group class will be managed so that only one can // run at a time. Also, the background process will be stopped if a job is // started. @@ -1791,6 +1705,7 @@ struct Plater::priv ArrangeJob arrange_job{m_plater}; RotoptimizeJob rotoptimize_job{m_plater}; + HollowJob hollow_job{m_plater}; // To create a new job, just define a new subclass of Job, implement // the process and the optional prepare() and finalize() methods @@ -1798,7 +1713,8 @@ struct Plater::priv // if it cannot run concurrently with other jobs in this group std::vector> m_jobs{arrange_job, - rotoptimize_job}; + rotoptimize_job, + hollow_job}; public: ExclusiveJobGroup(priv *_plater) : m_plater(_plater) {} @@ -1866,16 +1782,17 @@ struct Plater::priv bool is_preview_loaded() const { return preview->is_loaded(); } bool is_view3D_shown() const { return current_panel == view3D; } + bool are_view3D_labels_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->are_labels_shown(); } + void show_view3D_labels(bool show) { if (current_panel == view3D) view3D->get_canvas3d()->show_labels(show); } + void set_current_canvas_as_dirty(); -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK GLCanvas3D* get_current_canvas3D(); -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK bool init_view_toolbar(); void reset_all_gizmos(); void update_ui_from_settings(); - ProgressStatusBar* statusbar(); + std::shared_ptr statusbar(); std::string get_config(const std::string &key) const; BoundingBoxf bed_shape_bb() const; BoundingBox scaled_bed_shape_bb() const; @@ -1900,6 +1817,7 @@ struct Plater::priv void reset(); void mirror(Axis axis); void arrange(); + void hollow(); void sla_optimize_rotation(); void split_object(); void split_volume(); @@ -1956,6 +1874,7 @@ struct Plater::priv } void export_gcode(fs::path output_path, PrintHostJob upload_job); void reload_from_disk(); + void reload_all_from_disk(); void fix_through_netfabb(const int obj_idx, const int vol_idx = -1); void set_current_panel(wxPanel* panel); @@ -1998,9 +1917,7 @@ struct Plater::priv bool can_fix_through_netfabb() const; bool can_set_instance_to_object() const; bool can_mirror() const; -#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK bool can_reload_from_disk() const; -#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK #if ENABLE_THUMBNAIL_GENERATOR void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background); @@ -2060,7 +1977,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) "extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology", // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor. "layer_height", "first_layer_height", "min_layer_height", "max_layer_height", - "brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers", "bottom_solid_layers", "solid_infill_extruder", + "brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers", "support_material", "support_material_extruder", "support_material_interface_extruder", "support_material_contact_distance", "raft_layers" })) , sidebar(new Sidebar(q)) @@ -2142,6 +2059,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event &evt) { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); }); view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); + view3D_canvas->Bind(EVT_GLCANVAS_FORCE_UPDATE, [this](SimpleEvent&) { update(); }); view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_ROTATED, &priv::on_wipetower_rotated, this); view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_ROTATED, [this](SimpleEvent&) { update(); }); @@ -2156,6 +2074,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); }); view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); }); + view3D_canvas->Bind(EVT_GLCANVAS_RELOAD_FROM_DISK, [this](SimpleEvent&) { this->reload_all_from_disk(); }); // 3DScene/Toolbar: view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); @@ -2290,9 +2209,9 @@ void Plater::priv::update_ui_from_settings() preview->get_canvas3d()->update_ui_from_settings(); } -ProgressStatusBar* Plater::priv::statusbar() +std::shared_ptr Plater::priv::statusbar() { - return main_frame->m_statusbar.get(); + return main_frame->m_statusbar; } std::string Plater::priv::get_config(const std::string &key) const @@ -2814,6 +2733,12 @@ void Plater::priv::arrange() m_ui_jobs.start(Jobs::Arrange); } +void Plater::priv::hollow() +{ + this->take_snapshot(_(L("Hollow"))); + m_ui_jobs.start(Jobs::Hollow); +} + // This method will find an optimal orientation for the currently selected item // Very similar in nature to the arrange method above... void Plater::priv::sla_optimize_rotation() { @@ -2945,12 +2870,74 @@ void Plater::priv::RotoptimizeJob::process() : _(L("Orientation found."))); } +void Plater::priv::HollowJob::prepare() +{ + const GLGizmosManager& gizmo_manager = plater().q->canvas3D()->get_gizmos_manager(); + const GLGizmoHollow* gizmo_hollow = dynamic_cast(gizmo_manager.get_current()); + assert(gizmo_hollow); + auto hlw_data = gizmo_hollow->get_hollowing_parameters(); + m_object_mesh = hlw_data.first; + m_cfg = hlw_data.second; + m_output_mesh.reset(); +} + +void Plater::priv::HollowJob::process() +{ + sla::JobController ctl; + ctl.stopcondition = [this]{ return was_canceled(); }; + ctl.statuscb = [this](unsigned st, const std::string &s) { + if (st < 100) update_status(int(st), s); + }; + + std::unique_ptr omesh = + sla::generate_interior(*m_object_mesh, m_cfg, ctl); + + if (omesh && !omesh->empty()) { + m_output_mesh.reset(new TriangleMesh{*m_object_mesh}); + m_output_mesh->merge(*omesh); + m_output_mesh->require_shared_vertices(); + + update_status(90, _(L("Indexing hollowed object"))); + + m_output_raycaster.reset(new MeshRaycaster(*m_output_mesh)); + + update_status(100, was_canceled() ? _(L("Hollowing cancelled.")) : + _(L("Hollowing done."))); + } else { + update_status(100, _(L("Hollowing failed."))); + } +} + +void Plater::priv::HollowJob::finalize() +{ + if (auto gizmo = get_gizmo()) { + gizmo->update_mesh_raycaster(std::move(m_output_raycaster)); + gizmo->update_hollowed_mesh(std::move(m_output_mesh)); + } +} + +GLGizmoHollow *Plater::priv::HollowJob::get_gizmo() +{ + const GLGizmosManager& gizmo_manager = plater().q->canvas3D()->get_gizmos_manager(); + auto ret = dynamic_cast(gizmo_manager.get_current()); + assert(ret); + return ret; +} + +const GLGizmoHollow *Plater::priv::HollowJob::get_gizmo() const +{ + const GLGizmosManager& gizmo_manager = plater().q->canvas3D()->get_gizmos_manager(); + auto ret = dynamic_cast(gizmo_manager.get_current()); + assert(ret); + return ret; +} + void Plater::priv::split_object() { int obj_idx = get_selected_object_idx(); if (obj_idx == -1) return; - + // we clone model object because split_object() adds the split volumes // into the same model object, thus causing duplicates when we call load_model_objects() Model new_model = model; @@ -3263,10 +3250,8 @@ void Plater::priv::reload_from_disk() else missing_input_paths.push_back(volume->source.input_file); } -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK - else if (!volume->name.empty()) + else if (!object->input_file.empty() && !volume->name.empty()) missing_input_paths.push_back(volume->name); -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK } std::sort(missing_input_paths.begin(), missing_input_paths.end()); @@ -3321,19 +3306,15 @@ void Plater::priv::reload_from_disk() std::sort(input_paths.begin(), input_paths.end()); input_paths.erase(std::unique(input_paths.begin(), input_paths.end()), input_paths.end()); -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK std::vector fail_list; -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK // load one file at a time for (size_t i = 0; i < input_paths.size(); ++i) { const auto& path = input_paths[i].string(); -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK wxBusyCursor wait; wxBusyInfo info(_(L("Reload from: ")) + from_u8(path), q->get_current_canvas3D()->get_wxglcanvas()); -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK Model new_model; try @@ -3357,19 +3338,10 @@ void Plater::priv::reload_from_disk() ModelObject* old_model_object = model.objects[sel_v.object_idx]; ModelVolume* old_volume = old_model_object->volumes[sel_v.volume_idx]; -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK bool has_source = !old_volume->source.input_file.empty() && boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(), fs::path(path).filename().string()); bool has_name = !old_volume->name.empty() && boost::algorithm::iequals(old_volume->name, fs::path(path).filename().string()); if (has_source || has_name) -#else - int new_volume_idx = old_volume->source.volume_idx; - int new_object_idx = old_volume->source.object_idx; - - if (boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(), - fs::path(path).filename().string())) -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK { -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK int new_volume_idx = -1; int new_object_idx = -1; if (has_source) @@ -3405,17 +3377,12 @@ void Plater::priv::reload_from_disk() fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name)); continue; } -#else - assert(new_object_idx < (int)new_model.objects.size()); -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK ModelObject* new_model_object = new_model.objects[new_object_idx]; -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK if ((new_volume_idx < 0) && ((int)new_model.objects.size() <= new_volume_idx)) { fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name)); continue; } -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK if (new_volume_idx < (int)new_model_object->volumes.size()) { old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]); @@ -3426,9 +3393,6 @@ void Plater::priv::reload_from_disk() new_volume->set_material_id(old_volume->material_id()); new_volume->set_transformation(old_volume->get_transformation() * old_volume->source.transform); new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset)); -#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK - new_volume->source.input_file = path; -#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back()); old_model_object->delete_volume(old_model_object->volumes.size() - 1); old_model_object->ensure_on_bed(); @@ -3437,7 +3401,6 @@ void Plater::priv::reload_from_disk() } } -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK if (!fail_list.empty()) { wxString message = _(L("Unable to reload:")) + "\n"; @@ -3448,7 +3411,6 @@ void Plater::priv::reload_from_disk() wxMessageDialog dlg(q, message, _(L("Error during reload")), wxOK | wxOK_DEFAULT | wxICON_WARNING); dlg.ShowModal(); } -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK // update 3D scene update(); @@ -3460,6 +3422,27 @@ void Plater::priv::reload_from_disk() } } +void Plater::priv::reload_all_from_disk() +{ + if (model.objects.empty()) + return; + + Plater::TakeSnapshot snapshot(q, _(L("Reload all from disk"))); + Plater::SuppressSnapshots suppress(q); + + Selection& selection = get_selection(); + Selection::IndicesList curr_idxs = selection.get_volume_idxs(); + // reload from disk uses selection + select_all(); + reload_from_disk(); + // restore previous selection + selection.clear(); + for (unsigned int idx : curr_idxs) + { + selection.add(idx, false); + } +} + void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { if (obj_idx < 0) @@ -3926,13 +3909,8 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/ append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")), [this](wxCommandEvent&) { q->remove_selected(); }, "delete", nullptr, [this]() { return can_delete(); }, q); -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK - append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), - [this](wxCommandEvent&) { q->reload_from_disk(); }, "", menu); -#else append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), [this](wxCommandEvent&) { q->reload_from_disk(); }, "", menu, [this]() { return can_reload_from_disk(); }, q); -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK sidebar->obj_list()->append_menu_item_export_stl(menu); } @@ -3960,13 +3938,8 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/ wxMenuItem* menu_item_printable = sidebar->obj_list()->append_menu_item_printable(menu, q); menu->AppendSeparator(); -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK - append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected object from disk")), - [this](wxCommandEvent&) { reload_from_disk(); }, "", nullptr); -#else append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected object from disk")), [this](wxCommandEvent&) { reload_from_disk(); }, "", nullptr, [this]() { return can_reload_from_disk(); }, q); -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, _(L("Export the selected object as STL file")), [this](wxCommandEvent&) { q->export_stl(false, true); }, "", nullptr, @@ -4069,12 +4042,10 @@ void Plater::priv::set_current_canvas_as_dirty() preview->set_as_dirty(); } -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK GLCanvas3D* Plater::priv::get_current_canvas3D() { return (current_panel == view3D) ? view3D->get_canvas3d() : ((current_panel == preview) ? preview->get_canvas3d() : nullptr); } -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK bool Plater::priv::init_view_toolbar() { @@ -4146,7 +4117,6 @@ bool Plater::priv::can_mirror() const return get_selection().is_from_single_instance(); } -#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK bool Plater::priv::can_reload_from_disk() const { // struct to hold selected ModelVolumes by their indices @@ -4183,16 +4153,18 @@ bool Plater::priv::can_reload_from_disk() const std::vector paths; for (const SelectedVolume& v : selected_volumes) { - const ModelVolume* volume = model.objects[v.object_idx]->volumes[v.volume_idx]; + const ModelObject* object = model.objects[v.object_idx]; + const ModelVolume* volume = object->volumes[v.volume_idx]; if (!volume->source.input_file.empty()) paths.push_back(volume->source.input_file); + else if (!object->input_file.empty() && !volume->name.empty()) + paths.push_back(volume->name); } std::sort(paths.begin(), paths.end()); paths.erase(std::unique(paths.begin(), paths.end()), paths.end()); return !paths.empty(); } -#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model) { @@ -4662,6 +4634,9 @@ bool Plater::is_preview_shown() const { return p->is_preview_shown(); } bool Plater::is_preview_loaded() const { return p->is_preview_loaded(); } bool Plater::is_view3D_shown() const { return p->is_view3D_shown(); } +bool Plater::are_view3D_labels_shown() const { return p->are_view3D_labels_shown(); } +void Plater::show_view3D_labels(bool show) { p->show_view3D_labels(show); } + void Plater::select_all() { p->select_all(); } void Plater::deselect_all() { p->deselect_all(); } @@ -4881,85 +4856,104 @@ void Plater::export_stl(bool extended, bool selection_only) wxBusyCursor wait; + const auto &selection = p->get_selection(); + const auto obj_idx = selection.get_object_idx(); + if (selection_only && (obj_idx == -1 || selection.is_wipe_tower())) + return; + TriangleMesh mesh; - if (selection_only) { - const auto &selection = p->get_selection(); - if (selection.is_wipe_tower()) { return; } - - const auto obj_idx = selection.get_object_idx(); - if (obj_idx == -1) { return; } - - const ModelObject* model_object = p->model.objects[obj_idx]; - if (selection.get_mode() == Selection::Instance) - { - if (selection.is_single_full_object()) - mesh = model_object->mesh(); + if (p->printer_technology == ptFFF) { + if (selection_only) { + const ModelObject* model_object = p->model.objects[obj_idx]; + if (selection.get_mode() == Selection::Instance) + { + if (selection.is_single_full_object()) + mesh = model_object->mesh(); + else + mesh = model_object->full_raw_mesh(); + } else - mesh = model_object->full_raw_mesh(); + { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + mesh = model_object->volumes[volume->volume_idx()]->mesh(); + mesh.transform(volume->get_volume_transformation().get_matrix()); + mesh.translate(-model_object->origin_translation.cast()); + } } - else - { - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - mesh = model_object->volumes[volume->volume_idx()]->mesh(); - mesh.transform(volume->get_volume_transformation().get_matrix()); - mesh.translate(-model_object->origin_translation.cast()); + else { + mesh = p->model.mesh(); } } else { - mesh = p->model.mesh(); + // This is SLA mode, all objects have only one volume. + // However, we must have a look at the backend to load + // hollowed mesh and/or supports - if (extended && (p->printer_technology == ptSLA)) + const PrintObjects& objects = p->sla_print.objects(); + for (const SLAPrintObject* object : objects) { - const PrintObjects& objects = p->sla_print.objects(); - for (const SLAPrintObject* object : objects) + const ModelObject* model_object = object->model_object(); + if (selection_only) { + if (model_object->id() != p->model.objects[obj_idx]->id()) + continue; + } + Transform3d mesh_trafo_inv = object->trafo().inverse(); + bool is_left_handed = object->is_left_handed(); + + TriangleMesh pad_mesh; + bool has_pad_mesh = extended && object->has_mesh(slaposPad); + if (has_pad_mesh) { - const ModelObject* model_object = object->model_object(); - Transform3d mesh_trafo_inv = object->trafo().inverse(); - bool is_left_handed = object->is_left_handed(); + pad_mesh = object->get_mesh(slaposPad); + pad_mesh.transform(mesh_trafo_inv); + } - TriangleMesh pad_mesh; - bool has_pad_mesh = object->has_mesh(slaposPad); - if (has_pad_mesh) + TriangleMesh supports_mesh; + bool has_supports_mesh = extended && object->has_mesh(slaposSupportTree); + if (has_supports_mesh) + { + supports_mesh = object->get_mesh(slaposSupportTree); + supports_mesh.transform(mesh_trafo_inv); + } + const std::vector& obj_instances = object->instances(); + for (const SLAPrintObject::Instance& obj_instance : obj_instances) + { + auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(), + [&obj_instance](const ModelInstance *mi) { return mi->id() == obj_instance.instance_id; }); + assert(it != model_object->instances.end()); + + if (it != model_object->instances.end()) { - pad_mesh = object->get_mesh(slaposPad); - pad_mesh.transform(mesh_trafo_inv); - } + bool one_inst_only = selection_only && ! selection.is_single_full_object(); - TriangleMesh supports_mesh; - bool has_supports_mesh = object->has_mesh(slaposSupportTree); - if (has_supports_mesh) - { - supports_mesh = object->get_mesh(slaposSupportTree); - supports_mesh.transform(mesh_trafo_inv); - } + int instance_idx = it - model_object->instances.begin(); + const Transform3d& inst_transform = one_inst_only + ? Transform3d::Identity() + : object->model_object()->instances[instance_idx]->get_transformation().get_matrix(); - const std::vector& obj_instances = object->instances(); - for (const SLAPrintObject::Instance& obj_instance : obj_instances) - { - auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(), - [&obj_instance](const ModelInstance *mi) { return mi->id() == obj_instance.instance_id; }); - assert(it != model_object->instances.end()); - - if (it != model_object->instances.end()) + if (has_pad_mesh) { - int instance_idx = it - model_object->instances.begin(); - const Transform3d& inst_transform = object->model_object()->instances[instance_idx]->get_transformation().get_matrix(); - - if (has_pad_mesh) - { - TriangleMesh inst_pad_mesh = pad_mesh; - inst_pad_mesh.transform(inst_transform, is_left_handed); - mesh.merge(inst_pad_mesh); - } - - if (has_supports_mesh) - { - TriangleMesh inst_supports_mesh = supports_mesh; - inst_supports_mesh.transform(inst_transform, is_left_handed); - mesh.merge(inst_supports_mesh); - } + TriangleMesh inst_pad_mesh = pad_mesh; + inst_pad_mesh.transform(inst_transform, is_left_handed); + mesh.merge(inst_pad_mesh); } + + if (has_supports_mesh) + { + TriangleMesh inst_supports_mesh = supports_mesh; + inst_supports_mesh.transform(inst_transform, is_left_handed); + mesh.merge(inst_supports_mesh); + } + + TriangleMesh inst_object_mesh = object->get_mesh_to_print(); + inst_object_mesh.transform(mesh_trafo_inv); + inst_object_mesh.transform(inst_transform, is_left_handed); + + mesh.merge(inst_object_mesh); + + if (one_inst_only) + break; } } } @@ -4980,12 +4974,8 @@ void Plater::export_amf() wxBusyCursor wait; bool export_config = true; DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); -#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1"; if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) { -#else - if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) { -#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF // Success p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); } else { @@ -5014,7 +5004,6 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); const std::string path_u8 = into_u8(path); wxBusyCursor wait; -#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1"; #if ENABLE_THUMBNAIL_GENERATOR ThumbnailData thumbnail_data; @@ -5023,15 +5012,6 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) #else if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) { #endif // ENABLE_THUMBNAIL_GENERATOR -#else -#if ENABLE_THUMBNAIL_GENERATOR - ThumbnailData thumbnail_data; - p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, false, true, true, true); - if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, &thumbnail_data)) { -#else - if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) { -#endif // ENABLE_THUMBNAIL_GENERATOR -#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF // Success p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path)); p->set_project_filename(path); @@ -5047,6 +5027,11 @@ void Plater::reload_from_disk() p->reload_from_disk(); } +void Plater::reload_all_from_disk() +{ + p->reload_all_from_disk(); +} + bool Plater::has_toolpaths_to_export() const { return p->preview->get_canvas3d()->has_toolpaths_to_export(); @@ -5065,6 +5050,11 @@ void Plater::export_toolpaths_to_obj() const p->preview->get_canvas3d()->export_toolpaths_to_obj(into_u8(path).c_str()); } +void Plater::hollow() +{ + p->hollow(); +} + void Plater::reslice() { // Stop arrange and (or) optimize rotation tasks. @@ -5107,6 +5097,16 @@ void Plater::reslice() } void Plater::reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages) +{ + reslice_SLA_until_step(slaposPad, object, postpone_error_messages); +} + +void Plater::reslice_SLA_hollowing(const ModelObject &object, bool postpone_error_messages) +{ + reslice_SLA_until_step(slaposDrillHoles, object, postpone_error_messages); +} + +void Plater::reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &object, bool postpone_error_messages) { //FIXME Don't reslice if export of G-code or sending to OctoPrint is running. // bitmask of UpdateBackgroundProcessReturnState @@ -5125,7 +5125,7 @@ void Plater::reslice_SLA_supports(const ModelObject &object, bool postpone_error // Otherwise calculate everything, but start with the provided object. if (!this->p->background_processing_enabled()) { task.single_model_instance_only = true; - task.to_object_step = slaposPad; + task.to_object_step = step; } this->p->background_process.set_task(task); // and let the background processing start. @@ -5344,6 +5344,13 @@ void Plater::on_config_change(const DynamicPrintConfig &config) this->p->schedule_background_process(); } +void Plater::set_bed_shape() const +{ + p->set_bed_shape(p->config->option("bed_shape")->values, + p->config->option("bed_custom_texture")->value, + p->config->option("bed_custom_model")->value); +} + void Plater::force_filament_colors_update() { bool update_scheduled = false; @@ -5374,6 +5381,13 @@ void Plater::force_filament_colors_update() this->p->schedule_background_process(); } +void Plater::force_print_bed_update() +{ + // Fill in the printer model key with something which cannot possibly be valid, so that Plater::on_config_change() will update the print bed + // once a new Printer profile config is loaded. + p->config->opt_string("printer_model", true) = "\x01\x00\x01"; +} + void Plater::on_activate() { #ifdef __linux__ @@ -5390,11 +5404,6 @@ void Plater::on_activate() this->p->show_delayed_error_message(); } -const DynamicPrintConfig* Plater::get_plater_config() const -{ - return p->config; -} - // Get vector of extruder colors considering filament color, if extruder color is undefined. std::vector Plater::get_extruder_colors_from_plater_config() const { @@ -5423,7 +5432,7 @@ std::vector Plater::get_colors_for_color_print() const std::vector colors = get_extruder_colors_from_plater_config(); colors.reserve(colors.size() + p->model.custom_gcode_per_print_z.gcodes.size()); - for (const Model::CustomGCode& code : p->model.custom_gcode_per_print_z.gcodes) + for (const CustomGCode::Item& code : p->model.custom_gcode_per_print_z.gcodes) if (code.gcode == ColorChangeCode) colors.emplace_back(code.color); @@ -5460,12 +5469,10 @@ GLCanvas3D* Plater::canvas3D() return p->view3D->get_canvas3d(); } -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK GLCanvas3D* Plater::get_current_canvas3D() { return p->get_current_canvas3D(); } -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK BoundingBoxf Plater::bed_shape_bb() const { @@ -5659,9 +5666,7 @@ bool Plater::can_copy_to_clipboard() const bool Plater::can_undo() const { return p->undo_redo_stack().has_undo_snapshot(); } bool Plater::can_redo() const { return p->undo_redo_stack().has_redo_snapshot(); } -#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); } -#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); } void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); } void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 5747923057..2e4f2ca85e 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -12,6 +12,7 @@ #include "3DScene.hpp" #include "GLTexture.hpp" +#include "wxExtensions.hpp" class wxButton; class ScalableButton; @@ -26,6 +27,7 @@ class Model; class ModelObject; class Print; class SLAPrint; +enum SLAPrintObjectStep : unsigned int; namespace UndoRedo { class Stack; @@ -48,7 +50,7 @@ using t_optgroups = std::vector >; class Plater; enum class ActionButtonType : int; -class PresetComboBox : public wxBitmapComboBox +class PresetComboBox : public PresetBitmapComboBox { public: PresetComboBox(wxWindow *parent, Preset::Type preset_type); @@ -168,6 +170,9 @@ public: bool is_preview_loaded() const; bool is_view3D_shown() const; + bool are_view3D_labels_shown() const; + void show_view3D_labels(bool show); + // Called after the Preferences dialog is closed and the program settings are saved. // Update the UI based on the current preferences. void update_ui_from_settings(); @@ -192,10 +197,14 @@ public: void export_amf(); void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); void reload_from_disk(); + void reload_all_from_disk(); bool has_toolpaths_to_export() const; void export_toolpaths_to_obj() const; + void hollow(); void reslice(); void reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages = false); + void reslice_SLA_hollowing(const ModelObject &object, bool postpone_error_messages = false); + void reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &object, bool postpone_error_messages = false); void changed_object(int obj_idx); void changed_objects(const std::vector& object_idxs); void schedule_background_process(bool schedule = true); @@ -223,9 +232,9 @@ public: void on_extruders_change(size_t extruders_count); void on_config_change(const DynamicPrintConfig &config); void force_filament_colors_update(); + void force_print_bed_update(); // On activating the parent window. void on_activate(); - const DynamicPrintConfig* get_plater_config() const; std::vector get_extruder_colors_from_plater_config() const; std::vector get_colors_for_color_print() const; @@ -239,9 +248,7 @@ public: int get_selected_object_idx(); bool is_single_full_object_selection() const; GLCanvas3D* canvas3D(); -#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK GLCanvas3D* get_current_canvas3D(); -#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK BoundingBoxf bed_shape_bb() const; void set_current_canvas_as_dirty(); @@ -266,9 +273,7 @@ public: bool can_copy_to_clipboard() const; bool can_undo() const; bool can_redo() const; -#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK bool can_reload_from_disk() const; -#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK void msw_rescale(); @@ -278,6 +283,8 @@ public: const Mouse3DController& get_mouse3d_controller() const; Mouse3DController& get_mouse3d_controller(); + void set_bed_shape() const; + // ROII wrapper for suppressing the Undo / Redo snapshot to be taken. class SuppressSnapshots { diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 4fd63fe0ed..1299d9a48a 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -68,7 +68,6 @@ void PreferencesDialog::build() option = Option (def, "version_check"); m_optgroup_general->append_single_option_line(option); -#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF // Please keep in sync with ConfigWizard def.label = L("Export sources full pathnames to 3mf and amf"); def.type = coBool; @@ -76,7 +75,6 @@ void PreferencesDialog::build() def.set_default_value(new ConfigOptionBool(app_config->get("export_sources_full_pathnames") == "1")); option = Option(def, "export_sources_full_pathnames"); m_optgroup_general->append_single_option_line(option); -#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF // Please keep in sync with ConfigWizard def.label = L("Update built-in Presets automatically"); @@ -125,14 +123,12 @@ void PreferencesDialog::build() option = Option(def, "use_perspective_camera"); m_optgroup_camera->append_single_option_line(option); -#if ENABLE_6DOF_CAMERA def.label = L("Use free camera"); def.type = coBool; def.tooltip = L("If enabled, use free camera. If not enabled, use constrained camera."); def.set_default_value(new ConfigOptionBool(app_config->get("use_free_camera") == "1")); option = Option(def, "use_free_camera"); m_optgroup_camera->append_single_option_line(option); -#endif // ENABLE_6DOF_CAMERA m_optgroup_gui = std::make_shared(this, _(L("GUI"))); m_optgroup_gui->label_width = 40; diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 797fa28f87..6f379aa393 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -386,7 +386,8 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config) const std::vector& Preset::print_options() { static std::vector s_opts { - "layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "top_solid_layers", "bottom_solid_layers", + "layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", + "top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness", "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs", "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern", "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", @@ -505,6 +506,10 @@ const std::vector& Preset::sla_print_options() "pad_object_connector_stride", "pad_object_connector_width", "pad_object_connector_penetration", + "hollowing_enable", + "hollowing_min_thickness", + "hollowing_quality", + "hollowing_closing_distance", "output_filename_format", "default_sla_print_profile", "compatible_printers", @@ -546,7 +551,6 @@ const std::vector& Preset::sla_printer_options() s_opts = { "printer_technology", "bed_shape", "bed_custom_texture", "bed_custom_model", "max_print_height", - "bed_shape", "max_print_height", "display_width", "display_height", "display_pixels_x", "display_pixels_y", "display_mirror_x", "display_mirror_y", "display_orientation", @@ -600,6 +604,7 @@ void PresetCollection::reset(bool delete_files) m_presets.erase(m_presets.begin() + m_num_default_presets, m_presets.end()); this->select_preset(0); } + m_map_alias_to_profile_name.clear(); m_map_system_profile_renamed.clear(); } @@ -869,18 +874,14 @@ bool PresetCollection::delete_preset(const std::string& name) return true; } -void PresetCollection::load_bitmap_default(wxWindow *window, const std::string &file_name) +void PresetCollection::load_bitmap_default(const std::string &file_name) { - // XXX: See note in PresetBundle::load_compatible_bitmaps() - (void)window; - *m_bitmap_main_frame = create_scaled_bitmap(nullptr, file_name); + *m_bitmap_main_frame = create_scaled_bitmap(file_name); } -void PresetCollection::load_bitmap_add(wxWindow *window, const std::string &file_name) +void PresetCollection::load_bitmap_add(const std::string &file_name) { - // XXX: See note in PresetBundle::load_compatible_bitmaps() - (void)window; - *m_bitmap_add = create_scaled_bitmap(nullptr, file_name); + *m_bitmap_add = create_scaled_bitmap(file_name); } const Preset* PresetCollection::get_selected_preset_parent() const @@ -944,15 +945,15 @@ PresetWithVendorProfile PresetCollection::get_preset_with_vendor_profile(const P const std::string& PresetCollection::get_preset_name_by_alias(const std::string& alias) const { - for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) { - const Preset& preset = this->m_presets[i]; - if (!preset.is_visible || (!preset.is_compatible && i != m_idx_selected)) - continue; - - if (preset.alias == alias) - return preset.name; - } - + for ( + // Find the 1st profile name with the alias. + auto it = Slic3r::lower_bound_by_predicate(m_map_alias_to_profile_name.begin(), m_map_alias_to_profile_name.end(), [&alias](auto &l){ return l.first < alias; }); + // Continue over all profile names with the same alias. + it != m_map_alias_to_profile_name.end() && it->first == alias; ++ it) + if (auto it_preset = this->find_preset_internal(it->second); + it_preset != m_presets.end() && it_preset->name == it->second && + it_preset->is_visible && (it_preset->is_compatible || (it_preset - m_presets.begin()) == m_idx_selected)) + return it_preset->name; return alias; } @@ -1060,7 +1061,15 @@ void PresetCollection::update_plater_ui(GUI::PresetComboBox *ui) const Preset &preset = this->m_presets[i]; if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected)) continue; + std::string bitmap_key = ""; + // !!! Temporary solution, till refactoring: create and use "sla_printer" icon instead of m_bitmap_main_frame + wxBitmap main_bmp = m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap; + if (m_type == Preset::TYPE_PRINTER && preset.printer_technology()==ptSLA ) { + bitmap_key = "sla_printer"; + main_bmp = create_scaled_bitmap("sla_printer"); + } + // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left // to the filament color image. if (wide_icons) @@ -1075,7 +1084,7 @@ void PresetCollection::update_plater_ui(GUI::PresetComboBox *ui) bmps.emplace_back(preset.is_compatible ? m_bitmap_cache->mkclear(icon_width, icon_height) : *m_bitmap_incompatible); // Paint the color bars. bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); - bmps.emplace_back(*m_bitmap_main_frame); + bmps.emplace_back(main_bmp); // Paint a lock at the system presets. bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(icon_width, icon_height)); @@ -1085,7 +1094,7 @@ void PresetCollection::update_plater_ui(GUI::PresetComboBox *ui) const std::string name = preset.alias.empty() ? preset.name : preset.alias; if (preset.is_default || preset.is_system) { ui->Append(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), - (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); + (bmp == 0) ? main_bmp : *bmp); if (i == m_idx_selected || // just in case: mark selected_preset_item as a first added element selected_preset_item == INT_MAX) { @@ -1115,13 +1124,13 @@ void PresetCollection::update_plater_ui(GUI::PresetComboBox *ui) selected_preset_item = ui->GetCount() - 1; } } - if (m_type == Preset::TYPE_PRINTER) { + if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_SLA_MATERIAL) { std::string bitmap_key = ""; // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left // to the filament color image. if (wide_icons) bitmap_key += "wide,"; - bitmap_key += "add_printer"; + bitmap_key += "edit_preset_list"; wxBitmap *bmp = m_bitmap_cache->find(bitmap_key); if (bmp == nullptr) { // Create the bitmap with color bars. @@ -1134,12 +1143,14 @@ void PresetCollection::update_plater_ui(GUI::PresetComboBox *ui) bmps.emplace_back(*m_bitmap_main_frame); // Paint a lock at the system presets. bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); - bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap); +// bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap); + bmps.emplace_back(create_scaled_bitmap("edit_uni")); bmp = m_bitmap_cache->insert(bitmap_key, bmps); } - ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add a new printer")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_PRINTERS); - } else if (m_type == Preset::TYPE_SLA_MATERIAL) { - ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove materials")), wxNullBitmap), GUI::PresetComboBox::LABEL_ITEM_WIZARD_MATERIALS); + if (m_type == Preset::TYPE_SLA_MATERIAL) + ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove materials")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_MATERIALS); + else + ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove printers")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_PRINTERS); } /* But, if selected_preset_item is still equal to INT_MAX, it means that @@ -1184,6 +1195,14 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected)) continue; std::string bitmap_key = "tab"; + + // !!! Temporary solution, till refactoring: create and use "sla_printer" icon instead of m_bitmap_main_frame + wxBitmap main_bmp = m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap; + if (m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA) { + bitmap_key = "sla_printer"; + main_bmp = create_scaled_bitmap("sla_printer"); + } + bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; wxBitmap *bmp = m_bitmap_cache->find(bitmap_key); @@ -1191,7 +1210,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati // Create the bitmap with color bars. std::vector bmps; const wxBitmap* tmp_bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible; - bmps.emplace_back((tmp_bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *tmp_bmp); + bmps.emplace_back((tmp_bmp == 0) ? main_bmp : *tmp_bmp); // Paint a lock at the system presets. bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(icon_width, icon_height)); bmp = m_bitmap_cache->insert(bitmap_key, bmps); @@ -1199,7 +1218,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati if (preset.is_default || preset.is_system) { ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), - (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); + (bmp == 0) ? main_bmp : *bmp); if (i == m_idx_selected || // just in case: mark selected_preset_item as a first added element selected_preset_item == INT_MAX) @@ -1226,12 +1245,13 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati } } if (m_type == Preset::TYPE_PRINTER) { - wxBitmap *bmp = m_bitmap_cache->find("add_printer_tab"); + wxBitmap *bmp = m_bitmap_cache->find("edit_printer_list"); if (bmp == nullptr) { // Create the bitmap with color bars. std::vector bmps; bmps.emplace_back(*m_bitmap_main_frame); - bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap); +// bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap); + bmps.emplace_back(create_scaled_bitmap("edit_uni")); bmp = m_bitmap_cache->insert("add_printer_tab", bmps); } ui->Append(PresetCollection::separator("Add a new printer"), *bmp); @@ -1426,6 +1446,14 @@ std::vector PresetCollection::merge_presets(PresetCollection &&othe return duplicates; } +void PresetCollection::update_map_alias_to_profile_name() +{ + m_map_alias_to_profile_name.clear(); + for (const Preset &preset : m_presets) + m_map_alias_to_profile_name.emplace_back(preset.alias, preset.name); + std::sort(m_map_alias_to_profile_name.begin(), m_map_alias_to_profile_name.end(), [](auto &l, auto &r) { return l.first < r.first; }); +} + void PresetCollection::update_map_system_profile_renamed() { m_map_system_profile_renamed.clear(); diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp index 742d3e01d2..83970419cc 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -166,7 +166,7 @@ public: DynamicPrintConfig config; // Alias of the preset - std::string alias = ""; + std::string alias; // List of profile names, from which this profile was renamed at some point of time. // This list is then used to match profiles by their names when loaded from .gcode, .3mf, .amf, // and to match the "inherits" field of user profiles with updated system profiles. @@ -313,10 +313,10 @@ public: bool delete_preset(const std::string& name); // Load default bitmap to be placed at the wxBitmapComboBox of a MainFrame. - void load_bitmap_default(wxWindow *window, const std::string &file_name); + void load_bitmap_default(const std::string &file_name); // Load "add new printer" bitmap to be placed at the wxBitmapComboBox of a MainFrame. - void load_bitmap_add(wxWindow *window, const std::string &file_name); + void load_bitmap_add(const std::string &file_name); // Compatible & incompatible marks, to be placed at the wxBitmapComboBox items. void set_bitmap_compatible (const wxBitmap *bmp) { m_bitmap_compatible = bmp; } @@ -356,7 +356,7 @@ public: // used to update preset_choice from Tab const std::deque& get_presets() const { return m_presets; } - int get_idx_selected() { return m_idx_selected; } + int get_idx_selected() { return m_idx_selected; } static const std::string& get_suffix_modified(); // Return a preset possibly with modifications. @@ -475,6 +475,9 @@ protected: // Merge one vendor's presets with the other vendor's presets, report duplicates. std::vector merge_presets(PresetCollection &&other, const VendorMap &new_vendors); + // Update m_map_alias_to_profile_name from loaded system profiles. + void update_map_alias_to_profile_name(); + // Update m_map_system_profile_renamed from loaded system profiles. void update_map_system_profile_renamed(); @@ -522,6 +525,8 @@ private: // Use deque to force the container to allocate an object per each entry, // so that the addresses of the presets don't change during resizing of the container. std::deque m_presets; + // System profiles may have aliases. Map to the full profile name. + std::vector> m_map_alias_to_profile_name; // Map from old system profile name to a current system profile name. std::map m_map_system_profile_renamed; // Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user. diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index bb08ce2d07..ebfe6e40eb 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -288,11 +288,18 @@ std::string PresetBundle::load_system_presets() // No config bundle loaded, reset. this->reset(false); } + this->prints .update_map_system_profile_renamed(); this->sla_prints .update_map_system_profile_renamed(); this->filaments .update_map_system_profile_renamed(); this->sla_materials.update_map_system_profile_renamed(); this->printers .update_map_system_profile_renamed(); + + this->prints .update_map_alias_to_profile_name(); + this->sla_prints .update_map_alias_to_profile_name(); + this->filaments .update_map_alias_to_profile_name(); + this->sla_materials.update_map_alias_to_profile_name(); + return errors_cummulative; } @@ -473,19 +480,12 @@ void PresetBundle::export_selections(AppConfig &config) config.set("presets", "printer", printers.get_selected_preset_name()); } -void PresetBundle::load_compatible_bitmaps(wxWindow *window) +void PresetBundle::load_compatible_bitmaps() { - // We don't actually pass the window pointer here and instead generate - // a low DPI bitmap, because the wxBitmapComboBox and wxDataViewCtrl don't support - // high DPI bitmaps very well, they compute their dimensions wrong. - // TODO: Update this when fixed in wxWidgets - // See also PresetCollection::load_bitmap_default() and PresetCollection::load_bitmap_add() - - (void)window; - *m_bitmapCompatible = create_scaled_bitmap(nullptr, "flag_green"); - *m_bitmapIncompatible = create_scaled_bitmap(nullptr, "flag_red"); - *m_bitmapLock = create_scaled_bitmap(nullptr, "lock_closed"); - *m_bitmapLockOpen = create_scaled_bitmap(nullptr, "lock_open"); + *m_bitmapCompatible = create_scaled_bitmap("flag_green"); + *m_bitmapIncompatible = create_scaled_bitmap("flag_red"); + *m_bitmapLock = create_scaled_bitmap("lock_closed"); + *m_bitmapLockOpen = create_scaled_bitmap("lock_open"); prints .set_bitmap_compatible(m_bitmapCompatible); filaments .set_bitmap_compatible(m_bitmapCompatible); @@ -877,7 +877,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool // 4) Load the project config values (the per extruder wipe matrix etc). this->project_config.apply_only(config, s_project_options); - update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes, &this->project_config); + update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z, &this->project_config); break; } @@ -1529,31 +1529,7 @@ void PresetBundle::set_filament_preset(size_t idx, const std::string &name) filament_presets[idx] = Preset::remove_suffix_modified(name); } -static inline int hex_digit_to_int(const char c) -{ - return - (c >= '0' && c <= '9') ? int(c - '0') : - (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : - (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; -} - -bool PresetBundle::parse_color(const std::string &scolor, unsigned char *rgb_out) -{ - rgb_out[0] = rgb_out[1] = rgb_out[2] = 0; - if (scolor.size() != 7 || scolor.front() != '#') - return false; - const char *c = scolor.data() + 1; - for (size_t i = 0; i < 3; ++ i) { - int digit1 = hex_digit_to_int(*c ++); - int digit2 = hex_digit_to_int(*c ++); - if (digit1 == -1 || digit2 == -1) - return false; - rgb_out[i] = (unsigned char)(digit1 * 16 + digit2); - } - return true; -} - -void PresetBundle::load_default_preset_bitmaps(wxWindow *window) +void PresetBundle::load_default_preset_bitmaps() { // Clear bitmap cache, before load new scaled default preset bitmaps m_bitmapCache->clear(); @@ -1563,13 +1539,13 @@ void PresetBundle::load_default_preset_bitmaps(wxWindow *window) this->sla_materials.clear_bitmap_cache(); this->printers.clear_bitmap_cache(); - this->prints.load_bitmap_default(window, "cog"); - this->sla_prints.load_bitmap_default(window, "cog"); - this->filaments.load_bitmap_default(window, "spool.png"); - this->sla_materials.load_bitmap_default(window, "resin"); - this->printers.load_bitmap_default(window, "printer"); - this->printers.load_bitmap_add(window, "add.png"); - this->load_compatible_bitmaps(window); + this->prints.load_bitmap_default("cog"); + this->sla_prints.load_bitmap_default("cog"); + this->filaments.load_bitmap_default("spool.png"); + this->sla_materials.load_bitmap_default("resin"); + this->printers.load_bitmap_default("printer"); + this->printers.load_bitmap_add("add.png"); + this->load_compatible_bitmaps(); } void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui) @@ -1580,7 +1556,7 @@ void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::Pre unsigned char rgb[3]; std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); - if (! parse_color(extruder_color, rgb)) + if (!m_bitmapCache->parse_color(extruder_color, rgb)) // Extruder color is not defined. extruder_color.clear(); @@ -1616,7 +1592,12 @@ void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::Pre // To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size, so // set a bitmap height to m_bitmapLock->GetHeight() - const int icon_height = m_bitmapLock->GetHeight();//2 * icon_unit; //16 * scale_f + 0.5f; + // Note, under OSX we should use a ScaledHeight because of Retina scale +#ifdef __APPLE__ + const int icon_height = m_bitmapLock->GetScaledHeight(); +#else + const int icon_height = m_bitmapLock->GetHeight(); +#endif wxString tooltip = ""; @@ -1645,10 +1626,10 @@ void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::Pre // Paint a red flag for incompatible presets. bmps.emplace_back(preset.is_compatible ? m_bitmapCache->mkclear(normal_icon_width, icon_height) : *m_bitmapIncompatible); // Paint the color bars. - parse_color(filament_rgb, rgb); + m_bitmapCache->parse_color(filament_rgb, rgb); bmps.emplace_back(m_bitmapCache->mksolid(single_bar ? wide_icon_width : normal_icon_width, icon_height, rgb)); if (! single_bar) { - parse_color(extruder_rgb, rgb); + m_bitmapCache->parse_color(extruder_rgb, rgb); bmps.emplace_back(m_bitmapCache->mksolid(thin_icon_width, icon_height, rgb)); } // Paint a lock at the system presets. @@ -1697,7 +1678,23 @@ void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::Pre } } - ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove filaments")), wxNullBitmap), GUI::PresetComboBox::LABEL_ITEM_WIZARD_FILAMENTS); + std::string bitmap_key = ""; + if (wide_icons) + bitmap_key += "wide,"; + bitmap_key += "edit_preset_list"; + wxBitmap* bmp = m_bitmapCache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars. + std::vector bmps; + if (wide_icons) + // Paint a red flag for incompatible presets. + bmps.emplace_back(m_bitmapCache->mkclear(normal_icon_width, icon_height)); + // Paint the color bars + a lock at the system presets. + bmps.emplace_back(m_bitmapCache->mkclear(wide_icon_width+space_icon_width, icon_height)); + bmps.emplace_back(create_scaled_bitmap("edit_uni")); + bmp = m_bitmapCache->insert(bitmap_key, bmps); + } + ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove filaments")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_FILAMENTS); /* But, if selected_preset_item is still equal to INT_MAX, it means that * there is no presets added to the list. diff --git a/src/slic3r/GUI/PresetBundle.hpp b/src/slic3r/GUI/PresetBundle.hpp index ff27d0023d..b818107338 100644 --- a/src/slic3r/GUI/PresetBundle.hpp +++ b/src/slic3r/GUI/PresetBundle.hpp @@ -54,8 +54,7 @@ public: // There will be an entry for each system profile loaded, // and the system profiles will point to the VendorProfile instances owned by PresetBundle::vendors. - // std::set vendors; - VendorMap vendors; + VendorMap vendors; struct ObsoletePresets { std::vector prints; @@ -130,9 +129,7 @@ public: // preset if the current print or filament preset is not compatible. void update_compatible(bool select_other_if_incompatible); - static bool parse_color(const std::string &scolor, unsigned char *rgb_out); - - void load_default_preset_bitmaps(wxWindow *window); + void load_default_preset_bitmaps(); // Set the is_visible flag for printer vendors, printer models and printer variants // based on the user configuration. @@ -161,7 +158,7 @@ private: // If it is not an external config, then the config will be stored into the user profile directory. void load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config); void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree); - void load_compatible_bitmaps(wxWindow *window); + void load_compatible_bitmaps(); DynamicPrintConfig full_fff_config() const; DynamicPrintConfig full_sla_config() const; diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index f6281d7af4..cd3554bc40 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -1,6 +1,7 @@ #include #include "libslic3r/Flow.hpp" +#include "libslic3r/Slicing.hpp" #include "libslic3r/libslic3r.h" #include "PresetBundle.hpp" @@ -242,7 +243,7 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre float nozzle_diameter = float(printer_config.opt_float("nozzle_diameter", 0)); std::string out; - if (layer_height <= 0.f){ + if (layer_height <= 0.f) { out += _utf8(L("Recommended object thin wall thickness: Not available due to invalid layer height.")); return out; } @@ -272,4 +273,71 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre return out; } + +// Produce a textual explanation of the combined effects of the top/bottom_solid_layers +// versus top/bottom_min_shell_thickness. Which of the two values wins depends +// on the active layer height. +std::string PresetHints::top_bottom_shell_thickness_explanation(const PresetBundle &preset_bundle) +{ + const DynamicPrintConfig &print_config = preset_bundle.prints .get_edited_preset().config; + const DynamicPrintConfig &printer_config = preset_bundle.printers .get_edited_preset().config; + + std::string out; + + int top_solid_layers = print_config.opt_int("top_solid_layers"); + int bottom_solid_layers = print_config.opt_int("bottom_solid_layers"); + bool has_top_layers = top_solid_layers > 0; + bool has_bottom_layers = bottom_solid_layers > 0; + double top_solid_min_thickness = print_config.opt_float("top_solid_min_thickness"); + double bottom_solid_min_thickness = print_config.opt_float("bottom_solid_min_thickness"); + double layer_height = print_config.opt_float("layer_height"); + bool variable_layer_height = printer_config.opt_bool("variable_layer_height"); + //FIXME the following lines take into account the 1st extruder only. + double min_layer_height = variable_layer_height ? Slicing::min_layer_height_from_nozzle(printer_config, 1) : layer_height; + double max_layer_height = variable_layer_height ? Slicing::max_layer_height_from_nozzle(printer_config, 1) : layer_height; + + if (layer_height <= 0.f) { + out += _utf8(L("Top / bottom shell thickness hint: Not available due to invalid layer height.")); + return out; + } + + if (has_top_layers) { + double top_shell_thickness = top_solid_layers * layer_height; + if (top_shell_thickness < top_solid_min_thickness) { + // top_solid_min_shell_thickness triggers even in case of normal layer height. Round the top_shell_thickness up + // to an integer multiply of layer_height. + double n = ceil(top_solid_min_thickness / layer_height); + top_shell_thickness = n * layer_height; + } + double top_shell_thickness_minimum = std::max(top_solid_min_thickness, top_solid_layers * min_layer_height); + out += (boost::format(_utf8(L("Top shell is %1% mm thick for layer height %2% mm."))) % top_shell_thickness % layer_height).str(); + if (variable_layer_height && top_shell_thickness_minimum < top_shell_thickness) { + out += " "; + out += (boost::format(_utf8(L("Minimum top shell thickness is %1% mm."))) % top_shell_thickness_minimum).str(); + } + } else + out += _utf8(L("Top is open.")); + + out += "\n"; + + if (has_bottom_layers) { + double bottom_shell_thickness = bottom_solid_layers * layer_height; + if (bottom_shell_thickness < bottom_solid_min_thickness) { + // bottom_solid_min_shell_thickness triggers even in case of normal layer height. Round the bottom_shell_thickness up + // to an integer multiply of layer_height. + double n = ceil(bottom_solid_min_thickness / layer_height); + bottom_shell_thickness = n * layer_height; + } + double bottom_shell_thickness_minimum = std::max(bottom_solid_min_thickness, bottom_solid_layers * min_layer_height); + out += (boost::format(_utf8(L("Bottom shell is %1% mm thick for layer height %2% mm."))) % bottom_shell_thickness % layer_height).str(); + if (variable_layer_height && bottom_shell_thickness_minimum < bottom_shell_thickness) { + out += " "; + out += (boost::format(_utf8(L("Minimum bottom shell thickness is %1% mm."))) % bottom_shell_thickness_minimum).str(); + } + } else + out += _utf8(L("Bottom is open.")); + + return out; +} + }; // namespace Slic3r diff --git a/src/slic3r/GUI/PresetHints.hpp b/src/slic3r/GUI/PresetHints.hpp index 39bf0b100b..be049c2c87 100644 --- a/src/slic3r/GUI/PresetHints.hpp +++ b/src/slic3r/GUI/PresetHints.hpp @@ -23,6 +23,11 @@ public: // Produce a textual description of a recommended thin wall thickness // from the provided number of perimeters and the external / internal perimeter width. static std::string recommended_thin_wall_thickness(const PresetBundle &preset_bundle); + + // Produce a textual explanation of the combined effects of the top/bottom_solid_layers + // versus top/bottom_min_shell_thickness. Which of the two values wins depends + // on the active layer height. + static std::string top_bottom_shell_thickness_explanation(const PresetBundle &preset_bundle); }; } // namespace Slic3r diff --git a/src/slic3r/GUI/ProgressIndicator.hpp b/src/slic3r/GUI/ProgressIndicator.hpp index 280ba63ac8..674a81ba26 100644 --- a/src/slic3r/GUI/ProgressIndicator.hpp +++ b/src/slic3r/GUI/ProgressIndicator.hpp @@ -11,58 +11,17 @@ namespace Slic3r { */ class ProgressIndicator { public: - using CancelFn = std::function; // Cancel function signature. - -private: - float m_state = .0f, m_max = 1.f, m_step; - CancelFn m_cancelfunc = [](){}; - -public: - - inline virtual ~ProgressIndicator() {} - - /// Get the maximum of the progress range. - float max() const { return m_max; } - - /// Get the current progress state - float state() const { return m_state; } - - /// Set the maximum of the progress range - virtual void max(float maxval) { m_max = maxval; } - - /// Set the current state of the progress. - virtual void state(float val) { m_state = val; } - - /** - * @brief Number of states int the progress. Can be used instead of giving a - * maximum value. - */ - virtual void states(unsigned statenum) { - m_step = m_max / statenum; - } - - /// Message shown on the next status update. - virtual void message(const std::string&) = 0; - - /// Title of the operation. - virtual void title(const std::string&) = 0; - - /// Formatted message for the next status update. Works just like sprintf. - virtual void message_fmt(const std::string& fmt, ...); - - /// Set up a cancel callback for the operation if feasible. - virtual void on_cancel(CancelFn func = CancelFn()) { m_cancelfunc = func; } - - /** - * Explicitly shut down the progress indicator and call the associated - * callback. - */ - virtual void cancel() { m_cancelfunc(); } - - /// Convenience function to call message and status update in one function. - void update(float st, const std::string& msg) { - message(msg); state(st); - } + + /// Cancel callback function type + using CancelFn = std::function; + + virtual ~ProgressIndicator() = default; + + virtual void set_range(int range) = 0; + virtual void set_cancel_callback(CancelFn = CancelFn()) = 0; + virtual void set_progress(int pr) = 0; + virtual void set_status_text(const char *) = 0; // utf8 char array + virtual int get_range() const = 0; }; } diff --git a/src/slic3r/GUI/ProgressStatusBar.cpp b/src/slic3r/GUI/ProgressStatusBar.cpp index 33e4855cdd..1ec2b81930 100644 --- a/src/slic3r/GUI/ProgressStatusBar.cpp +++ b/src/slic3r/GUI/ProgressStatusBar.cpp @@ -15,8 +15,7 @@ namespace Slic3r { ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id) - : self{new wxStatusBar(parent ? parent : GUI::wxGetApp().mainframe, - id == -1 ? wxID_ANY : id)} + : self{new wxStatusBar(parent, id == -1 ? wxID_ANY : id)} , m_prog{new wxGauge(self, wxGA_HORIZONTAL, 100, @@ -32,7 +31,6 @@ ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id) m_prog->Hide(); m_cancelbutton->Hide(); - self->SetFont(GUI::wxGetApp().normal_font()); self->SetFieldsCount(3); int w[] = {-1, 150, 155}; self->SetStatusWidths(3, w); @@ -149,8 +147,7 @@ void ProgressStatusBar::run(int rate) void ProgressStatusBar::embed(wxFrame *frame) { - wxFrame* mf = frame ? frame : GUI::wxGetApp().mainframe; - if(mf) mf->SetStatusBar(self); + if(frame) frame->SetStatusBar(self); } void ProgressStatusBar::set_status_text(const wxString& txt) @@ -173,6 +170,11 @@ wxString ProgressStatusBar::get_status_text() const return self->GetStatusText(); } +void ProgressStatusBar::set_font(const wxFont &font) +{ + self->SetFont(font); +} + void ProgressStatusBar::show_cancel_button() { if(m_cancelbutton) m_cancelbutton->Show(); diff --git a/src/slic3r/GUI/ProgressStatusBar.hpp b/src/slic3r/GUI/ProgressStatusBar.hpp index 1aab67d5aa..faeb7a34ef 100644 --- a/src/slic3r/GUI/ProgressStatusBar.hpp +++ b/src/slic3r/GUI/ProgressStatusBar.hpp @@ -6,6 +6,8 @@ #include #include +#include "ProgressIndicator.hpp" + class wxTimer; class wxGauge; class wxButton; @@ -14,6 +16,7 @@ class wxStatusBar; class wxWindow; class wxFrame; class wxString; +class wxFont; namespace Slic3r { @@ -22,7 +25,7 @@ namespace Slic3r { * of the Slicer main window. It consists of a message area to the left and a * progress indication area to the right with an optional cancel button. */ -class ProgressStatusBar +class ProgressStatusBar : public ProgressIndicator { wxStatusBar *self; // we cheat! It should be the base class but: perl! wxGauge *m_prog; @@ -30,30 +33,29 @@ class ProgressStatusBar std::unique_ptr m_timer; public: - /// Cancel callback function type - using CancelFn = std::function; ProgressStatusBar(wxWindow *parent = nullptr, int id = -1); - ~ProgressStatusBar(); + ~ProgressStatusBar() override; int get_progress() const; // if the argument is less than 0 it shows the last state or // pulses if no state was set before. - void set_progress(int); - int get_range() const; - void set_range(int = 100); + void set_progress(int) override; + int get_range() const override; + void set_range(int = 100) override; void show_progress(bool); void start_busy(int = 100); void stop_busy(); inline bool is_busy() const { return m_busy; } - void set_cancel_callback(CancelFn = CancelFn()); + void set_cancel_callback(CancelFn = CancelFn()) override; inline void reset_cancel_callback() { set_cancel_callback(); } void run(int rate); void embed(wxFrame *frame = nullptr); void set_status_text(const wxString& txt); void set_status_text(const std::string& txt); - void set_status_text(const char *txt); + void set_status_text(const char *txt) override; wxString get_status_text() const; + void set_font(const wxFont &font); // Temporary methods to satisfy Perl side void show_cancel_button(); diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 4ed6d36a48..aa7a3d6a31 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -27,7 +27,9 @@ namespace Slic3r { namespace GUI { #if _WIN32 +/* currently not used, left for possible future use INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +*/ void RemovableDriveManager::search_for_drives() { m_current_drives.clear(); @@ -44,7 +46,7 @@ void RemovableDriveManager::search_for_drives() if (drive_type == DRIVE_REMOVABLE) { // get name of drive - std::wstring wpath = boost::nowide::widen(path);//std::wstring(path.begin(), path.end()); + std::wstring wpath = boost::nowide::widen(path); std::wstring volume_name; volume_name.resize(1024); std::wstring file_system_name; @@ -54,12 +56,6 @@ void RemovableDriveManager::search_for_drives() if(error != 0) { volume_name.erase(std::find(volume_name.begin(), volume_name.end(), '\0'), volume_name.end()); - /* - if (volume_name == L"") - { - volume_name = L"REMOVABLE DRIVE"; - } - */ if (file_system_name != L"") { ULARGE_INTEGER free_space; @@ -147,6 +143,7 @@ void RemovableDriveManager::register_window() { //creates new unvisible window that is recieving callbacks from system // structure to register + /* currently not used, left for possible future use WNDCLASSEX wndClass; wndClass.cbSize = sizeof(WNDCLASSEX); wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; @@ -182,13 +179,15 @@ void RemovableDriveManager::register_window() } //ShowWindow(hWnd, SW_SHOWNORMAL); UpdateWindow(hWnd); + */ } - +/* currently not used, left for possible future use INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { // here we need to catch messeges about device removal // problem is that when ejecting usb (how is it implemented above) there is no messege dispached. Only after physical removal of the device. //uncomment register_window() in init() to register and comment update() in GUI_App.cpp (only for windows!) to stop recieving periodical updates + LRESULT lRet = 1; static HDEVNOTIFY hDeviceNotify; @@ -221,8 +220,9 @@ INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP break; } return lRet; + } - +*/ #else void RemovableDriveManager::search_for_drives() { @@ -370,10 +370,15 @@ bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path) } return false; } -std::string RemovableDriveManager::get_drive_from_path(const std::string& path) +std::string RemovableDriveManager::get_drive_from_path(const std::string& path) { std::size_t found = path.find_last_of("/"); std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path; + + // trim the filename + found = new_path.find_last_of("/"); + new_path = new_path.substr(0, found); + //check if same filesystem for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it) { @@ -440,7 +445,7 @@ bool RemovableDriveManager::update(const long time,const bool check) return !m_current_drives.empty(); } -bool RemovableDriveManager::is_drive_mounted(const std::string &path) +bool RemovableDriveManager::is_drive_mounted(const std::string &path) const { for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it) { @@ -451,7 +456,7 @@ bool RemovableDriveManager::is_drive_mounted(const std::string &path) } return false; } -std::string RemovableDriveManager::get_drive_path() +std::string RemovableDriveManager::get_drive_path() { if (m_current_drives.size() == 0) { @@ -462,17 +467,17 @@ std::string RemovableDriveManager::get_drive_path() return m_last_save_path; return m_current_drives.back().path; } -std::string RemovableDriveManager::get_last_save_path() +std::string RemovableDriveManager::get_last_save_path() const { if (!m_last_save_path_verified) return ""; return m_last_save_path; } -std::string RemovableDriveManager::get_last_save_name() +std::string RemovableDriveManager::get_last_save_name() const { return m_last_save_name; } -std::vector RemovableDriveManager::get_all_drives() +std::vector RemovableDriveManager::get_all_drives() const { return m_current_drives; } @@ -482,7 +487,7 @@ void RemovableDriveManager::check_and_notify() { m_drive_count_changed_callback(m_plater_ready_to_slice); } - if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() /*&& m_last_save_path_verified */&& !is_drive_mounted(m_last_save_path)) + if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() && !is_drive_mounted(m_last_save_path)) { for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it) { @@ -533,7 +538,7 @@ void RemovableDriveManager::verify_last_save_path() reset_last_save_path(); } } -std::string RemovableDriveManager::get_drive_name(const std::string& path) +std::string RemovableDriveManager::get_drive_name(const std::string& path) const { if (m_current_drives.size() == 0) return ""; @@ -546,7 +551,7 @@ std::string RemovableDriveManager::get_drive_name(const std::string& path) } return ""; } -bool RemovableDriveManager::is_last_drive_removed() +bool RemovableDriveManager::is_last_drive_removed() { if(!m_last_save_path_verified) { @@ -578,27 +583,27 @@ void RemovableDriveManager::set_is_writing(const bool b) m_did_eject = false; } } -bool RemovableDriveManager::get_is_writing() +bool RemovableDriveManager::get_is_writing() const { return m_is_writing; } -bool RemovableDriveManager::get_did_eject() +bool RemovableDriveManager::get_did_eject() const { return m_did_eject; } -void RemovableDriveManager::set_did_eject(const bool b) +void RemovableDriveManager::set_did_eject(const bool b) { m_did_eject = b; } -size_t RemovableDriveManager::get_drives_count() +size_t RemovableDriveManager::get_drives_count() const { return m_current_drives.size(); } -std::string RemovableDriveManager::get_ejected_path() +std::string RemovableDriveManager::get_ejected_path() const { return m_ejected_path; } -std::string RemovableDriveManager::get_ejected_name() +std::string RemovableDriveManager::get_ejected_name() const { return m_ejected_name; } diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp index 6bd90e98cd..032eef682b 100644 --- a/src/slic3r/GUI/RemovableDriveManager.hpp +++ b/src/slic3r/GUI/RemovableDriveManager.hpp @@ -35,14 +35,14 @@ public: void init(); //update() searches for removable devices, returns false if empty. /time = 0 is forced update, time expects wxGetLocalTime() bool update(const long time = 0,const bool check = false); - bool is_drive_mounted(const std::string &path); + bool is_drive_mounted(const std::string &path) const; void eject_drive(const std::string &path); //returns path to last drive which was used, if none was used, returns device that was enumerated last - std::string get_last_save_path(); - std::string get_last_save_name(); + std::string get_last_save_path() const; + std::string get_last_save_name() const; //returns path to last drive which was used, if none was used, returns empty string std::string get_drive_path(); - std::vector get_all_drives(); + std::vector get_all_drives() const; bool is_path_on_removable_drive(const std::string &path); // callback will notify only if device with last save path was removed void add_remove_callback(std::function callback); @@ -59,13 +59,13 @@ public: // param as update() bool is_last_drive_removed_with_update(const long time = 0); void set_is_writing(const bool b); - bool get_is_writing(); - bool get_did_eject(); + bool get_is_writing() const; + bool get_did_eject() const; void set_did_eject(const bool b); - std::string get_drive_name(const std::string& path); - size_t get_drives_count(); - std::string get_ejected_path(); - std::string get_ejected_name(); + std::string get_drive_name(const std::string& path) const; + size_t get_drives_count() const; + std::string get_ejected_path() const; + std::string get_ejected_name() const; private: RemovableDriveManager(); void search_for_drives(); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 224f5007de..bb81b56c0d 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1328,6 +1328,7 @@ void Selection::copy_to_clipboard() static_cast(dst_object->config) = static_cast(src_object->config); dst_object->sla_support_points = src_object->sla_support_points; dst_object->sla_points_status = src_object->sla_points_status; + dst_object->sla_drain_holes = src_object->sla_drain_holes; dst_object->layer_config_ranges = src_object->layer_config_ranges; // #ys_FIXME_experiment dst_object->layer_height_profile = src_object->layer_height_profile; dst_object->origin_translation = src_object->origin_translation; @@ -2004,11 +2005,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co const float max_y = box.max(1) + Margin; // view dependend order of rendering to keep correct transparency -#if ENABLE_6DOF_CAMERA bool camera_on_top = wxGetApp().plater()->get_camera().is_looking_downward(); -#else - bool camera_on_top = wxGetApp().plater()->get_camera().get_theta() <= 90.0f; -#endif // ENABLE_6DOF_CAMERA float z1 = camera_on_top ? min_z : max_z; float z2 = camera_on_top ? max_z : min_z; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index e2f33f6dbd..c5c6dc4669 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -114,7 +114,7 @@ void Tab::create_preset_tab() #endif //__WXOSX__ // preset chooser - m_presets_choice = new wxBitmapComboBox(panel, wxID_ANY, "", wxDefaultPosition, wxSize(35 * m_em_unit, -1), 0, 0, wxCB_READONLY); + m_presets_choice = new PresetBitmapComboBox(panel, wxSize(35 * m_em_unit, -1)); auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); @@ -257,9 +257,9 @@ void Tab::create_preset_tab() // Fill cache for mode bitmaps m_mode_bitmap_cache.reserve(3); - m_mode_bitmap_cache.push_back(ScalableBitmap(this, "mode_simple_.png")); - m_mode_bitmap_cache.push_back(ScalableBitmap(this, "mode_advanced_.png")); - m_mode_bitmap_cache.push_back(ScalableBitmap(this, "mode_expert_.png")); + m_mode_bitmap_cache.push_back(ScalableBitmap(this, "mode_simple" , mode_icon_px_size())); + m_mode_bitmap_cache.push_back(ScalableBitmap(this, "mode_advanced", mode_icon_px_size())); + m_mode_bitmap_cache.push_back(ScalableBitmap(this, "mode_expert" , mode_icon_px_size())); // Initialize the DynamicPrintConfig by default keys/values. build(); @@ -518,6 +518,18 @@ void TabPrinter::init_options_list() m_options_list.emplace("extruders_count", m_opt_status_value); } +void TabPrinter::msw_rescale() +{ + Tab::msw_rescale(); + + // rescale missed options_groups + const std::vector& pages = m_printer_technology == ptFFF ? m_pages_sla : m_pages_fff; + for (auto page : pages) + page->msw_rescale(); + + Layout(); +} + void TabSLAMaterial::init_options_list() { if (!m_options_list.empty()) @@ -1056,6 +1068,16 @@ void TabPrint::build() line.append_option(optgroup->get_option("top_solid_layers")); line.append_option(optgroup->get_option("bottom_solid_layers")); optgroup->append_line(line); + line = { _(L("Minimum shell thickness")), "" }; + line.append_option(optgroup->get_option("top_solid_min_thickness")); + line.append_option(optgroup->get_option("bottom_solid_min_thickness")); + optgroup->append_line(line); + line = { "", "" }; + line.full_width = 1; + line.widget = [this](wxWindow* parent) { + return description_line_widget(parent, &m_top_bottom_shell_thickness_explanation); + }; + optgroup->append_line(line); optgroup = page->new_optgroup(_(L("Quality (slower slicing)"))); optgroup->append_single_option_line("extra_perimeters"); @@ -1211,18 +1233,14 @@ void TabPrint::build() optgroup = page->new_optgroup(_(L("Sequential printing"))); optgroup->append_single_option_line("complete_objects"); line = { _(L("Extruder clearance (mm)")), "" }; - Option option = optgroup->get_option("extruder_clearance_radius"); - option.opt.width = 6; - line.append_option(option); - option = optgroup->get_option("extruder_clearance_height"); - option.opt.width = 6; - line.append_option(option); + line.append_option(optgroup->get_option("extruder_clearance_radius")); + line.append_option(optgroup->get_option("extruder_clearance_height")); optgroup->append_line(line); optgroup = page->new_optgroup(_(L("Output file"))); optgroup->append_single_option_line("gcode_comments"); optgroup->append_single_option_line("gcode_label_objects"); - option = optgroup->get_option("output_filename_format"); + Option option = optgroup->get_option("output_filename_format"); option.opt.full_width = true; optgroup->append_single_option_line(option); @@ -1277,6 +1295,8 @@ void TabPrint::update() m_recommended_thin_wall_thickness_description_line->SetText( from_u8(PresetHints::recommended_thin_wall_thickness(*m_preset_bundle))); + m_top_bottom_shell_thickness_explanation->SetText( + from_u8(PresetHints::top_bottom_shell_thickness_explanation(*m_preset_bundle))); Layout(); // Thaw(); @@ -1295,6 +1315,8 @@ void TabPrint::OnActivate() { m_recommended_thin_wall_thickness_description_line->SetText( from_u8(PresetHints::recommended_thin_wall_thickness(*m_preset_bundle))); + m_top_bottom_shell_thickness_explanation->SetText( + from_u8(PresetHints::top_bottom_shell_thickness_explanation(*m_preset_bundle))); Tab::OnActivate(); } @@ -1449,7 +1471,10 @@ void TabFilament::build() page = add_options_page(_(L("Advanced")), "wrench"); optgroup = page->new_optgroup(_(L("Filament properties"))); - optgroup->append_single_option_line("filament_type"); + // Set size as all another fields for a better alignment + Option option = optgroup->get_option("filament_type"); + option.opt.width = Field::def_width(); + optgroup->append_single_option_line(option); optgroup->append_single_option_line("filament_soluble"); optgroup = page->new_optgroup(_(L("Print speed override"))); @@ -1503,7 +1528,7 @@ void TabFilament::build() page = add_options_page(_(L("Custom G-code")), "cog"); optgroup = page->new_optgroup(_(L("Start G-code")), 0); - Option option = optgroup->get_option("start_filament_gcode"); + option = optgroup->get_option("start_filament_gcode"); option.opt.full_width = true; option.opt.height = gcode_field_height;// 150; optgroup->append_single_option_line(option); @@ -1676,21 +1701,28 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) return sizer; }; - Line host_line = optgroup->create_single_option_line("print_host"); + // Set a wider width for a better alignment + Option option = optgroup->get_option("print_host"); + option.opt.width = Field::def_width_wider(); + Line host_line = optgroup->create_single_option_line(option); host_line.append_widget(printhost_browse); host_line.append_widget(print_host_test); optgroup->append_line(host_line); - optgroup->append_single_option_line("printhost_apikey"); + option = optgroup->get_option("printhost_apikey"); + option.opt.width = Field::def_width_wider(); + optgroup->append_single_option_line(option); const auto ca_file_hint = _(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.")); if (Http::ca_file_supported()) { - Line cafile_line = optgroup->create_single_option_line("printhost_cafile"); + option = optgroup->get_option("printhost_cafile"); + option.opt.width = Field::def_width_wider(); + Line cafile_line = optgroup->create_single_option_line(option); auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) { auto btn = new wxButton(parent, wxID_ANY, " " + _(L("Browse"))+" " +dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - btn->SetBitmap(create_scaled_bitmap(this, "browse")); + btn->SetBitmap(create_scaled_bitmap("browse")); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn); @@ -3483,7 +3515,6 @@ void TabSLAMaterial::build() for (auto& axis : axes) { auto opt = optgroup->get_option(opt_key, id); opt.opt.label = axis; - opt.opt.width = 6; line.append_option(opt); ++id; } @@ -3608,6 +3639,13 @@ void TabSLAPrint::build() optgroup->append_single_option_line("pad_object_connector_stride"); optgroup->append_single_option_line("pad_object_connector_width"); optgroup->append_single_option_line("pad_object_connector_penetration"); + + page = add_options_page(_(L("Hollowing")), "hollowing"); + optgroup = page->new_optgroup(_(L("Hollowing"))); + optgroup->append_single_option_line("hollowing_enable"); + optgroup->append_single_option_line("hollowing_min_thickness"); + optgroup->append_single_option_line("hollowing_quality"); + optgroup->append_single_option_line("hollowing_closing_distance"); page = add_options_page(_(L("Advanced")), "wrench"); optgroup = page->new_optgroup(_(L("Slicing"))); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 4345f196ce..09662bf814 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -120,7 +120,7 @@ protected: Preset::Type m_type; std::string m_name; const wxString m_title; - wxBitmapComboBox* m_presets_choice; + PresetBitmapComboBox* m_presets_choice; ScalableButton* m_btn_save_preset; ScalableButton* m_btn_delete_preset; ScalableButton* m_btn_hide_incompatible_presets; @@ -290,7 +290,7 @@ public: virtual void reload_config(); void update_mode(); void update_visibility(); - void msw_rescale(); + virtual void msw_rescale(); Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const; bool set_value(const t_config_option_key& opt_key, const boost::any& value); wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText); @@ -327,8 +327,9 @@ public: Tab(parent, _(L("Print Settings")), Slic3r::Preset::TYPE_PRINT) {} ~TabPrint() {} - ogStaticText* m_recommended_thin_wall_thickness_description_line; - bool m_support_material_overhangs_queried = false; + ogStaticText* m_recommended_thin_wall_thickness_description_line = nullptr; + ogStaticText* m_top_bottom_shell_thickness_explanation = nullptr; + bool m_support_material_overhangs_queried = false; void build() override; void reload_config() override; @@ -336,6 +337,7 @@ public: void OnActivate() override; bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; } }; + class TabFilament : public Tab { ogStaticText* m_volumetric_speed_description_line; @@ -401,6 +403,7 @@ public: void build_unregular_pages(); void on_preset_loaded() override; void init_options_list() override; + void msw_rescale() override; bool supports_printer_technology(const PrinterTechnology /* tech */) override { return true; } }; diff --git a/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp index 00e7725ac1..3581a55b12 100644 --- a/src/slic3r/GUI/UpdateDialogs.cpp +++ b/src/slic3r/GUI/UpdateDialogs.cpp @@ -142,6 +142,67 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector &updates) : MsgUpdateConfig::~MsgUpdateConfig() {} +//MsgUpdateForced + +MsgUpdateForced::MsgUpdateForced(const std::vector& updates) : + MsgDialog(nullptr, wxString::Format(_(L("%s incompatibility")), SLIC3R_APP_NAME), _(L("Configuration update is necessary to install")), wxID_NONE) +{ + auto* text = new wxStaticText(this, wxID_ANY, wxString::Format(_(L( + "%s will now start updates. Otherwise it won't be able to start.\n\n" + "Note that a full configuration snapshot will be created first. It can then be restored at any time " + "should there be a problem with the new version.\n\n" + "Updated configuration bundles:" + )), SLIC3R_APP_NAME)); + + logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192)); + + text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); + content_sizer->Add(text); + content_sizer->AddSpacer(VERT_SPACING); + + const auto lang_code = wxGetApp().current_language_code_safe().ToStdString(); + + auto* versions = new wxFlexGridSizer(2, 0, VERT_SPACING); + for (const auto& update : updates) { + auto* text_vendor = new wxStaticText(this, wxID_ANY, update.vendor); + text_vendor->SetFont(boldfont); + versions->Add(text_vendor); + versions->Add(new wxStaticText(this, wxID_ANY, update.version.to_string())); + + if (!update.comment.empty()) { + versions->Add(new wxStaticText(this, wxID_ANY, _(L("Comment:")))/*, 0, wxALIGN_RIGHT*/);//uncoment if align to right (might not look good if 1 vedor name is longer than other names) + auto* update_comment = new wxStaticText(this, wxID_ANY, from_u8(update.comment)); + update_comment->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); + versions->Add(update_comment); + } + + if (!update.changelog_url.empty() && update.version.prerelease() == nullptr) { + auto* line = new wxBoxSizer(wxHORIZONTAL); + auto changelog_url = (boost::format(update.changelog_url) % lang_code).str(); + line->AddSpacer(3 * VERT_SPACING); + line->Add(new wxHyperlinkCtrl(this, wxID_ANY, _(L("Open changelog page")), changelog_url)); + versions->Add(line); + } + } + + content_sizer->Add(versions); + content_sizer->AddSpacer(2 * VERT_SPACING); + + auto* btn_exit = new wxButton(this, wxID_EXIT, wxString::Format(_(L("Exit %s")), SLIC3R_APP_NAME)); + btn_sizer->Add(btn_exit); + btn_sizer->AddSpacer(HORIZ_SPACING); + auto* btn_ok = new wxButton(this, wxID_OK); + btn_sizer->Add(btn_ok); + btn_ok->SetFocus(); + + auto exiter = [this](const wxCommandEvent& evt) { this->EndModal(evt.GetId()); }; + btn_exit->Bind(wxEVT_BUTTON, exiter); + btn_ok->Bind(wxEVT_BUTTON, exiter); + + Fit(); +} + +MsgUpdateForced::~MsgUpdateForced() {} // MsgDataIncompatible @@ -149,7 +210,7 @@ MsgDataIncompatible::MsgDataIncompatible(const std::unordered_mapSetBitmap(create_scaled_bitmap(this, "PrusaSlicer_192px_grayscale.png", 192)); + logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192)); auto *text = new wxStaticText(this, wxID_ANY, wxString::Format(_(L( "This version of %s is not compatible with currently installed configuration bundles.\n" @@ -157,7 +218,7 @@ MsgDataIncompatible::MsgDataIncompatible(const std::unordered_mapWrap(CONTENT_WIDTH * wxGetApp().em_unit()); content_sizer->Add(text); @@ -236,5 +297,28 @@ MsgDataLegacy::MsgDataLegacy() : MsgDataLegacy::~MsgDataLegacy() {} +// MsgNoUpdate + +MsgNoUpdates::MsgNoUpdates() : + MsgDialog(nullptr, _(L("Configuration updates")), _(L("No updates aviable"))) +{ + + auto* text = new wxStaticText(this, wxID_ANY, wxString::Format( + _(L( + "%s has no configuration updates aviable." + )), + SLIC3R_APP_NAME, ConfigWizard::name() + )); + text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); + content_sizer->Add(text); + content_sizer->AddSpacer(VERT_SPACING); + + logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192)); + + Fit(); +} + +MsgNoUpdates::~MsgNoUpdates() {} + } } diff --git a/src/slic3r/GUI/UpdateDialogs.hpp b/src/slic3r/GUI/UpdateDialogs.hpp index a916e0145e..6d355065a1 100644 --- a/src/slic3r/GUI/UpdateDialogs.hpp +++ b/src/slic3r/GUI/UpdateDialogs.hpp @@ -62,6 +62,33 @@ public: ~MsgUpdateConfig(); }; +// Informs about currently installed bundles not being compatible with the running Slic3r. Asks about action. +class MsgUpdateForced : public MsgDialog +{ +public: + struct Update + { + std::string vendor; + Semver version; + std::string comment; + std::string changelog_url; + + Update(std::string vendor, Semver version, std::string comment, std::string changelog_url) + : vendor(std::move(vendor)) + , version(std::move(version)) + , comment(std::move(comment)) + , changelog_url(std::move(changelog_url)) + {} + }; + + MsgUpdateForced(const std::vector& updates); + MsgUpdateForced(MsgUpdateForced&&) = delete; + MsgUpdateForced(const MsgUpdateForced&) = delete; + MsgUpdateForced& operator=(MsgUpdateForced&&) = delete; + MsgUpdateForced& operator=(const MsgUpdateForced&) = delete; + ~MsgUpdateForced(); +}; + // Informs about currently installed bundles not being compatible with the running Slic3r. Asks about action. class MsgDataIncompatible : public MsgDialog { @@ -87,6 +114,17 @@ public: ~MsgDataLegacy(); }; +// Informs about absence of bundles requiring update. +class MsgNoUpdates : public MsgDialog +{ +public: + MsgNoUpdates(); + MsgNoUpdates(MsgNoUpdates&&) = delete; + MsgNoUpdates(const MsgNoUpdates&) = delete; + MsgNoUpdates& operator=(MsgNoUpdates&&) = delete; + MsgNoUpdates& operator=(const MsgNoUpdates&) = delete; + ~MsgNoUpdates(); +}; } } diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp index 5394225456..970cd85284 100644 --- a/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -1,7 +1,7 @@ #include #include #include "WipeTowerDialog.hpp" -#include "PresetBundle.hpp" +#include "BitmapCache.hpp" #include "GUI.hpp" #include "I18N.hpp" #include "GUI_App.hpp" @@ -191,7 +191,7 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector& matrix, con for (const std::string& color : extruder_colours) { unsigned char rgb[3]; - Slic3r::PresetBundle::parse_color(color, rgb); + Slic3r::GUI::BitmapCache::parse_color(color, rgb); m_colours.push_back(wxColor(rgb[0], rgb[1], rgb[2])); } diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 8f43f6c307..939c6d1dce 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -3,16 +3,7 @@ #include #include -#include "libslic3r/Utils.hpp" -#include "libslic3r/Model.hpp" -#include "libslic3r/Print.hpp" - #include -#include -#include -#include -#include -#include #include @@ -20,19 +11,10 @@ #include "GUI.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" -#include "libslic3r/GCode/PreviewData.hpp" #include "I18N.hpp" #include "GUI_Utils.hpp" -#include "PresetBundle.hpp" -#include "ExtruderSequenceDialog.hpp" #include "../Utils/MacDarkMode.hpp" -using Slic3r::GUI::from_u8; -using Slic3r::GUI::into_u8; - -wxDEFINE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); -wxDEFINE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent); - #ifndef __WXGTK__// msw_menuitem_bitmaps is used for MSW and OSX static std::map msw_menuitem_bitmaps; #ifdef __WXMSW__ @@ -42,7 +24,7 @@ void msw_rescale_menu(wxMenu* menu) static void run(wxMenuItem* item) { const auto it = msw_menuitem_bitmaps.find(item->GetId()); if (it != msw_menuitem_bitmaps.end()) { - const wxBitmap& item_icon = create_scaled_bitmap(nullptr, it->second); + const wxBitmap& item_icon = create_scaled_bitmap(it->second); if (item_icon.IsOk()) item->SetBitmap(item_icon); } @@ -67,7 +49,7 @@ void enable_menu_item(wxUpdateUIEvent& evt, std::function const cb_condi const auto it = msw_menuitem_bitmaps.find(item->GetId()); if (it != msw_menuitem_bitmaps.end()) { - const wxBitmap& item_icon = create_scaled_bitmap(win, it->second, 16, false, !enable); + const wxBitmap& item_icon = create_scaled_bitmap(it->second, win, 16, !enable); if (item_icon.IsOk()) item->SetBitmap(item_icon); } @@ -109,7 +91,7 @@ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const if (id == wxID_ANY) id = wxNewId(); - const wxBitmap& bmp = !icon.empty() ? create_scaled_bitmap(parent, icon) : wxNullBitmap; // FIXME: pass window ptr + const wxBitmap& bmp = !icon.empty() ? create_scaled_bitmap(icon) : wxNullBitmap; // FIXME: pass window ptr //#ifdef __WXMSW__ #ifndef __WXGTK__ if (bmp.IsOk()) @@ -127,7 +109,7 @@ wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxStrin wxMenuItem* item = new wxMenuItem(menu, id, string, description); if (!icon.empty()) { - item->SetBitmap(create_scaled_bitmap(parent, icon)); // FIXME: pass window ptr + item->SetBitmap(create_scaled_bitmap(icon)); // FIXME: pass window ptr //#ifdef __WXMSW__ #ifndef __WXGTK__ msw_menuitem_bitmaps[id] = icon; @@ -164,7 +146,8 @@ wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, } wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, const wxString& description, - std::function cb, wxEvtHandler* event_handler) + std::function cb, wxEvtHandler* event_handler, + std::function const enable_condition, std::function const check_condition, wxWindow* parent) { if (id == wxID_ANY) id = wxNewId(); @@ -178,6 +161,13 @@ wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, #endif // __WXMSW__ menu->Bind(wxEVT_MENU, cb, id); + if (parent) + parent->Bind(wxEVT_UPDATE_UI, [enable_condition, check_condition](wxUpdateUIEvent& evt) + { + evt.Enable(enable_condition()); + evt.Check(check_condition()); + }, id); + return item; } @@ -309,6 +299,94 @@ void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt) } +namespace Slic3r { +namespace GUI { + +// *** PresetBitmapComboBox *** + +/* For PresetBitmapComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina + * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean + * "please scale this to such and such" but rather + * "the wxImage is already sized for backing scale such and such". ) + * Unfortunately, the constructor changes the size of wxBitmap too. + * Thus We need to use unscaled size value for bitmaps that we use + * to avoid scaled size of control items. + * For this purpose control drawing methods and + * control size calculation methods (virtual) are overridden. + **/ + +PresetBitmapComboBox::PresetBitmapComboBox(wxWindow* parent, const wxSize& size) : + wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, size, 0, nullptr, wxCB_READONLY) +{} + +#ifdef __APPLE__ +bool PresetBitmapComboBox::OnAddBitmap(const wxBitmap& bitmap) +{ + if (bitmap.IsOk()) + { + // we should use scaled! size values of bitmap + int width = (int)bitmap.GetScaledWidth(); + int height = (int)bitmap.GetScaledHeight(); + + if (m_usedImgSize.x < 0) + { + // If size not yet determined, get it from this image. + m_usedImgSize.x = width; + m_usedImgSize.y = height; + + // Adjust control size to vertically fit the bitmap + wxWindow* ctrl = GetControl(); + ctrl->InvalidateBestSize(); + wxSize newSz = ctrl->GetBestSize(); + wxSize sz = ctrl->GetSize(); + if (newSz.y > sz.y) + ctrl->SetSize(sz.x, newSz.y); + else + DetermineIndent(); + } + + wxCHECK_MSG(width == m_usedImgSize.x && height == m_usedImgSize.y, + false, + "you can only add images of same size"); + + return true; + } + + return false; +} + +void PresetBitmapComboBox::OnDrawItem(wxDC& dc, + const wxRect& rect, + int item, + int flags) const +{ + const wxBitmap& bmp = *(wxBitmap*)m_bitmaps[item]; + if (bmp.IsOk()) + { + // we should use scaled! size values of bitmap + wxCoord w = bmp.GetScaledWidth(); + wxCoord h = bmp.GetScaledHeight(); + + const int imgSpacingLeft = 4; + + // Draw the image centered + dc.DrawBitmap(bmp, + rect.x + (m_usedImgSize.x - w) / 2 + imgSpacingLeft, + rect.y + (rect.height - h) / 2, + true); + } + + wxString text = GetString(item); + if (!text.empty()) + dc.DrawText(text, + rect.x + m_imgAreaWidth + 1, + rect.y + (rect.height - dc.GetCharHeight()) / 2); +} +#endif +} +} + + // *** wxDataViewTreeCtrlComboPopup *** const unsigned int wxDataViewTreeCtrlComboPopup::DefaultWidth = 270; @@ -408,56 +486,33 @@ int em_unit(wxWindow* win) return Slic3r::GUI::wxGetApp().em_unit(); } -static float get_svg_scale_factor(wxWindow *win) +int mode_icon_px_size() { #ifdef __APPLE__ - // Note: win->GetContentScaleFactor() is not used anymore here because it tends to - // return bogus results quite often (such as 1.0 on Retina or even 0.0). - // We're using the max scaling factor across all screens because it's very likely to be good enough. - - static float max_scaling_factor = NAN; - if (std::isnan(max_scaling_factor)) { - max_scaling_factor = Slic3r::GUI::mac_max_scaling_factor(); - } - return win != nullptr ? max_scaling_factor : 1.0f; + return 10; #else - return 1.0f; + return 12; #endif } -// If an icon has horizontal orientation (width > height) call this function with is_horizontal = true -wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in, - const int px_cnt/* = 16*/, const bool is_horizontal /* = false*/, const bool grayscale/* = false*/) +// win is used to get a correct em_unit value +// It's important for bitmaps of dialogs. +// if win == nullptr, em_unit value of MainFrame will be used +wxBitmap create_scaled_bitmap( const std::string& bmp_name_in, + wxWindow *win/* = nullptr*/, + const int px_cnt/* = 16*/, + const bool grayscale/* = false*/) { static Slic3r::GUI::BitmapCache cache; -#ifdef __APPLE__ - // Note: win->GetContentScaleFactor() is not used anymore here because it tends to - // return bogus results quite often (such as 1.0 on Retina or even 0.0). - // We're using the max scaling factor across all screens because it's very likely to be good enough. - - static float max_scaling_factor = NAN; - if (std::isnan(max_scaling_factor)) { - max_scaling_factor = Slic3r::GUI::mac_max_scaling_factor(); - } - const float scale_factor = win != nullptr ? max_scaling_factor : 1.0f; -#else - (void)(win); - const float scale_factor = 1.0f; -#endif - - unsigned int height, width = height = 0; - unsigned int& scale_base = is_horizontal ? width : height; - - scale_base = (unsigned int)(em_unit(win) * px_cnt * 0.1f + 0.5f); + unsigned int width = 0; + unsigned int height = (unsigned int)(em_unit(win) * px_cnt * 0.1f + 0.5f); std::string bmp_name = bmp_name_in; boost::replace_last(bmp_name, ".png", ""); -// std::string bmp_name = icon_name_respected_to_mode(bmp_name_in); - // Try loading an SVG first, then PNG if SVG is not found: - wxBitmap *bmp = cache.load_svg(bmp_name, width, height, scale_factor, grayscale, Slic3r::GUI::wxGetApp().dark_mode()); + wxBitmap *bmp = cache.load_svg(bmp_name, width, height, grayscale, Slic3r::GUI::wxGetApp().dark_mode()); if (bmp == nullptr) { bmp = cache.load_png(bmp_name, width, height, grayscale); } @@ -470,10 +525,10 @@ wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in, return *bmp; } - -Slic3r::GUI::BitmapCache* m_bitmap_cache = nullptr; std::vector get_extruder_color_icons(bool thin_icon/* = false*/) { + static Slic3r::GUI::BitmapCache bmp_cache; + // Create the bitmap with color bars. std::vector bmps; std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); @@ -495,11 +550,12 @@ std::vector get_extruder_color_icons(bool thin_icon/* = false*/) { std::string bitmap_key = color + "-h" + std::to_string(icon_height) + "-w" + std::to_string(icon_width); - wxBitmap* bitmap = m_bitmap_cache->find(bitmap_key); + wxBitmap* bitmap = bmp_cache.find(bitmap_key); if (bitmap == nullptr) { // Paint the color icon. - Slic3r::PresetBundle::parse_color(color, rgb); - bitmap = m_bitmap_cache->insert(bitmap_key, m_bitmap_cache->mksolid(icon_width, icon_height, rgb)); + Slic3r::GUI::BitmapCache::parse_color(color, rgb); + // there is no neede to scale created solid bitmap + bitmap = bmp_cache.insert(bitmap_key, bmp_cache.mksolid(icon_width, icon_height, rgb, true)); } bmps.emplace_back(bitmap); } @@ -508,16 +564,6 @@ std::vector get_extruder_color_icons(bool thin_icon/* = false*/) } -static wxBitmap get_extruder_color_icon(size_t extruder_idx, bool thin_icon = false) -{ - // Create the bitmap with color bars. - std::vector bmps = get_extruder_color_icons(thin_icon); - if (bmps.empty()) - return wxNullBitmap; - - return *bmps[extruder_idx >= bmps.size() ? 0 : extruder_idx]; -} - void apply_extruder_selector(wxBitmapComboBox** ctrl, wxWindow* parent, const std::string& first_item/* = ""*/, @@ -564,3399 +610,6 @@ void apply_extruder_selector(wxBitmapComboBox** ctrl, } -// ***************************************************************************** -// ---------------------------------------------------------------------------- -// ObjectDataViewModelNode -// ---------------------------------------------------------------------------- - -void ObjectDataViewModelNode::init_container() -{ -#ifdef __WXGTK__ - // it's necessary on GTK because of control have to know if this item will be container - // in another case you couldn't to add subitem for this item - // it will be produce "segmentation fault" - m_container = true; -#endif //__WXGTK__ -} - -#define LAYER_ROOT_ICON "edit_layers_all" -#define LAYER_ICON "edit_layers_some" - -ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type) : - m_parent(parent), - m_type(type), - m_extruder(wxEmptyString) -{ - if (type == itSettings) - m_name = "Settings to modified"; - else if (type == itInstanceRoot) - m_name = _(L("Instances")); - else if (type == itInstance) - { - m_idx = parent->GetChildCount(); - m_name = wxString::Format(_(L("Instance %d")), m_idx + 1); - - set_action_and_extruder_icons(); - } - else if (type == itLayerRoot) - { - m_bmp = create_scaled_bitmap(nullptr, LAYER_ROOT_ICON); // FIXME: pass window ptr - m_name = _(L("Layers")); - } - - if (type & (itInstanceRoot | itLayerRoot)) - init_container(); -} - -ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, - const t_layer_height_range& layer_range, - const int idx /*= -1 */, - const wxString& extruder) : - m_parent(parent), - m_type(itLayer), - m_idx(idx), - m_layer_range(layer_range), - m_extruder(extruder) -{ - const int children_cnt = parent->GetChildCount(); - if (idx < 0) - m_idx = children_cnt; - else - { - // update indexes for another Laeyr Nodes - for (int i = m_idx; i < children_cnt; i++) - parent->GetNthChild(i)->SetIdx(i + 1); - } - const std::string label_range = (boost::format(" %.2f-%.2f ") % layer_range.first % layer_range.second).str(); - m_name = _(L("Range")) + label_range + "(" + _(L("mm")) + ")"; - m_bmp = create_scaled_bitmap(nullptr, LAYER_ICON); // FIXME: pass window ptr - - set_action_and_extruder_icons(); - init_container(); -} - -#ifndef NDEBUG -bool ObjectDataViewModelNode::valid() -{ - // Verify that the object was not deleted yet. - assert(m_idx >= -1); - return m_idx >= -1; -} -#endif /* NDEBUG */ - -void ObjectDataViewModelNode::set_action_and_extruder_icons() -{ - m_action_icon_name = m_type & itObject ? "advanced_plus" : - m_type & (itVolume | itLayer) ? "cog" : /*m_type & itInstance*/ "set_separate_obj"; - m_action_icon = create_scaled_bitmap(nullptr, m_action_icon_name); // FIXME: pass window ptr - - if (m_type & itInstance) - return; // don't set colored bitmap for Instance - - // set extruder bitmap - int extruder_idx = atoi(m_extruder.c_str()); - if (extruder_idx > 0) --extruder_idx; - m_extruder_bmp = get_extruder_color_icon(extruder_idx); -} - -void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable) -{ - m_printable = printable; - m_printable_icon = m_printable == piUndef ? m_empty_bmp : - create_scaled_bitmap(nullptr, m_printable == piPrintable ? "eye_open.png" : "eye_closed.png"); -} - -void ObjectDataViewModelNode::update_settings_digest_bitmaps() -{ - m_bmp = m_empty_bmp; - - std::map& categories_icon = Slic3r::GUI::wxGetApp().obj_list()->CATEGORY_ICON; - - std::string scaled_bitmap_name = m_name.ToUTF8().data(); - scaled_bitmap_name += "-em" + std::to_string(Slic3r::GUI::wxGetApp().em_unit()); - - wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name); - if (bmp == nullptr) { - std::vector bmps; - for (auto& cat : m_opt_categories) - bmps.emplace_back( categories_icon.find(cat) == categories_icon.end() ? - wxNullBitmap : categories_icon.at(cat)); - bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); - } - - m_bmp = *bmp; -} - -bool ObjectDataViewModelNode::update_settings_digest(const std::vector& categories) -{ - if (m_type != itSettings || m_opt_categories == categories) - return false; - - m_opt_categories = categories; - m_name = wxEmptyString; - - for (auto& cat : m_opt_categories) - m_name += _(cat) + "; "; - if (!m_name.IsEmpty()) - m_name.erase(m_name.Length()-2, 2); // Delete last "; " - - update_settings_digest_bitmaps(); - - return true; -} - -void ObjectDataViewModelNode::msw_rescale() -{ - if (!m_action_icon_name.empty()) - m_action_icon = create_scaled_bitmap(nullptr, m_action_icon_name); - - if (m_printable != piUndef) - m_printable_icon = create_scaled_bitmap(nullptr, m_printable == piPrintable ? "eye_open.png" : "eye_closed.png"); - - if (!m_opt_categories.empty()) - update_settings_digest_bitmaps(); -} - -bool ObjectDataViewModelNode::SetValue(const wxVariant& variant, unsigned col) -{ - switch (col) - { - case colPrint: - m_printable_icon << variant; - return true; - case colName: { - DataViewBitmapText data; - data << variant; - m_bmp = data.GetBitmap(); - m_name = data.GetText(); - return true; } - case colExtruder: { - DataViewBitmapText data; - data << variant; - m_extruder_bmp = data.GetBitmap(); - m_extruder = data.GetText() == "0" ? _(L("default")) : data.GetText(); - return true; } - case colEditing: - m_action_icon << variant; - return true; - default: - printf("MyObjectTreeModel::SetValue: wrong column"); - } - return false; -} - -void ObjectDataViewModelNode::SetIdx(const int& idx) -{ - m_idx = idx; - // update name if this node is instance - if (m_type == itInstance) - m_name = wxString::Format(_(L("Instance %d")), m_idx + 1); -} - -// ***************************************************************************** -// ---------------------------------------------------------------------------- -// ObjectDataViewModel -// ---------------------------------------------------------------------------- - -static int get_root_idx(ObjectDataViewModelNode *parent_node, const ItemType root_type) -{ - // because of istance_root and layers_root are at the end of the list, so - // start locking from the end - for (int root_idx = parent_node->GetChildCount() - 1; root_idx >= 0; root_idx--) - { - // if there is SettingsItem or VolumeItem, then RootItems don't exist in current ObjectItem - if (parent_node->GetNthChild(root_idx)->GetType() & (itSettings | itVolume)) - break; - if (parent_node->GetNthChild(root_idx)->GetType() & root_type) - return root_idx; - } - - return -1; -} - -ObjectDataViewModel::ObjectDataViewModel() -{ - m_bitmap_cache = new Slic3r::GUI::BitmapCache; -} - -ObjectDataViewModel::~ObjectDataViewModel() -{ - for (auto object : m_objects) - delete object; - delete m_bitmap_cache; - m_bitmap_cache = nullptr; -} - -wxDataViewItem ObjectDataViewModel::Add(const wxString &name, - const int extruder, - const bool has_errors/* = false*/) -{ - const wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder); - auto root = new ObjectDataViewModelNode(name, extruder_str); - // Add error icon if detected auto-repaire - if (has_errors) - root->m_bmp = *m_warning_bmp; - - m_objects.push_back(root); - // notify control - wxDataViewItem child((void*)root); - wxDataViewItem parent((void*)NULL); - - ItemAdded(parent, child); - return child; -} - -wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent_item, - const wxString &name, - const Slic3r::ModelVolumeType volume_type, - const bool has_errors/* = false*/, - const int extruder/* = 0*/, - const bool create_frst_child/* = true*/) -{ - ObjectDataViewModelNode *root = (ObjectDataViewModelNode*)parent_item.GetID(); - if (!root) return wxDataViewItem(0); - - wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder); - - // get insertion position according to the existed Layers and/or Instances Items - int insert_position = get_root_idx(root, itLayerRoot); - if (insert_position < 0) - insert_position = get_root_idx(root, itInstanceRoot); - - const bool obj_errors = root->m_bmp.IsOk(); - - if (create_frst_child && root->m_volumes_cnt == 0) - { - const Slic3r::ModelVolumeType type = Slic3r::ModelVolumeType::MODEL_PART; - const auto node = new ObjectDataViewModelNode(root, root->m_name, GetVolumeIcon(type, obj_errors), extruder_str, 0); - node->m_volume_type = type; - - insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); - // notify control - const wxDataViewItem child((void*)node); - ItemAdded(parent_item, child); - - root->m_volumes_cnt++; - if (insert_position >= 0) insert_position++; - } - - const auto node = new ObjectDataViewModelNode(root, name, GetVolumeIcon(volume_type, has_errors), extruder_str, root->m_volumes_cnt); - insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); - - // if part with errors is added, but object wasn't marked, then mark it - if (!obj_errors && has_errors) - root->SetBitmap(*m_warning_bmp); - - // notify control - const wxDataViewItem child((void*)node); - ItemAdded(parent_item, child); - root->m_volumes_cnt++; - - node->m_volume_type = volume_type; - - return child; -} - -wxDataViewItem ObjectDataViewModel::AddSettingsChild(const wxDataViewItem &parent_item) -{ - ObjectDataViewModelNode *root = (ObjectDataViewModelNode*)parent_item.GetID(); - if (!root) return wxDataViewItem(0); - - const auto node = new ObjectDataViewModelNode(root, itSettings); - root->Insert(node, 0); - // notify control - const wxDataViewItem child((void*)node); - ItemAdded(parent_item, child); - return child; -} - -/* return values: - * true => root_node is created and added to the parent_root - * false => root node alredy exists -*/ -static bool append_root_node(ObjectDataViewModelNode *parent_node, - ObjectDataViewModelNode **root_node, - const ItemType root_type) -{ - const int inst_root_id = get_root_idx(parent_node, root_type); - - *root_node = inst_root_id < 0 ? - new ObjectDataViewModelNode(parent_node, root_type) : - parent_node->GetNthChild(inst_root_id); - - if (inst_root_id < 0) { - if ((root_type&itInstanceRoot) || - ( (root_type&itLayerRoot) && get_root_idx(parent_node, itInstanceRoot)<0) ) - parent_node->Append(*root_node); - else if (root_type&itLayerRoot) - parent_node->Insert(*root_node, static_cast(get_root_idx(parent_node, itInstanceRoot))); - return true; - } - - return false; -} - -wxDataViewItem ObjectDataViewModel::AddRoot(const wxDataViewItem &parent_item, ItemType root_type) -{ - ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID(); - if (!parent_node) return wxDataViewItem(0); - - // get InstanceRoot node - ObjectDataViewModelNode *root_node { nullptr }; - const bool appended = append_root_node(parent_node, &root_node, root_type); - if (!root_node) return wxDataViewItem(0); - - const wxDataViewItem root_item((void*)root_node); - - if (appended) - ItemAdded(parent_item, root_item);// notify control - return root_item; -} - -wxDataViewItem ObjectDataViewModel::AddInstanceRoot(const wxDataViewItem &parent_item) -{ - return AddRoot(parent_item, itInstanceRoot); -} - -wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num) -{ - std::vector print_indicator(num, true); - - // if InstanceRoot item isn't created for this moment - if (!GetInstanceRootItem(parent_item).IsOk()) - // use object's printable state to first instance - print_indicator[0] = IsPrintable(parent_item); - - return wxDataViewItem((void*)AddInstanceChild(parent_item, print_indicator)); -} - -wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem& parent_item, - const std::vector& print_indicator) -{ - const wxDataViewItem inst_root_item = AddInstanceRoot(parent_item); - if (!inst_root_item) return wxDataViewItem(0); - - ObjectDataViewModelNode* inst_root_node = (ObjectDataViewModelNode*)inst_root_item.GetID(); - - // Add instance nodes - ObjectDataViewModelNode *instance_node = nullptr; - size_t counter = 0; - while (counter < print_indicator.size()) { - instance_node = new ObjectDataViewModelNode(inst_root_node, itInstance); - - instance_node->set_printable_icon(print_indicator[counter] ? piPrintable : piUnprintable); - - inst_root_node->Append(instance_node); - // notify control - const wxDataViewItem instance_item((void*)instance_node); - ItemAdded(inst_root_item, instance_item); - ++counter; - } - - // update object_node printable property - UpdateObjectPrintable(parent_item); - - return wxDataViewItem((void*)instance_node); -} - -void ObjectDataViewModel::UpdateObjectPrintable(wxDataViewItem parent_item) -{ - const wxDataViewItem inst_root_item = GetInstanceRootItem(parent_item); - if (!inst_root_item) - return; - - ObjectDataViewModelNode* inst_root_node = (ObjectDataViewModelNode*)inst_root_item.GetID(); - - const size_t child_cnt = inst_root_node->GetChildren().Count(); - PrintIndicator obj_pi = piUnprintable; - for (size_t i=0; i < child_cnt; i++) - if (inst_root_node->GetNthChild(i)->IsPrintable() & piPrintable) { - obj_pi = piPrintable; - break; - } - // and set printable state for object_node to piUndef - ObjectDataViewModelNode* obj_node = (ObjectDataViewModelNode*)parent_item.GetID(); - obj_node->set_printable_icon(obj_pi); - ItemChanged(parent_item); -} - -// update printable property for all instances from object -void ObjectDataViewModel::UpdateInstancesPrintable(wxDataViewItem parent_item) -{ - const wxDataViewItem inst_root_item = GetInstanceRootItem(parent_item); - if (!inst_root_item) - return; - - ObjectDataViewModelNode* obj_node = (ObjectDataViewModelNode*)parent_item.GetID(); - const PrintIndicator obj_pi = obj_node->IsPrintable(); - - ObjectDataViewModelNode* inst_root_node = (ObjectDataViewModelNode*)inst_root_item.GetID(); - const size_t child_cnt = inst_root_node->GetChildren().Count(); - - for (size_t i=0; i < child_cnt; i++) - { - ObjectDataViewModelNode* inst_node = inst_root_node->GetNthChild(i); - // and set printable state for object_node to piUndef - inst_node->set_printable_icon(obj_pi); - ItemChanged(wxDataViewItem((void*)inst_node)); - } -} - -bool ObjectDataViewModel::IsPrintable(const wxDataViewItem& item) const -{ - ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID(); - if (!node) - return false; - - return node->IsPrintable() == piPrintable; -} - -wxDataViewItem ObjectDataViewModel::AddLayersRoot(const wxDataViewItem &parent_item) -{ - return AddRoot(parent_item, itLayerRoot); -} - -wxDataViewItem ObjectDataViewModel::AddLayersChild(const wxDataViewItem &parent_item, - const t_layer_height_range& layer_range, - const int extruder/* = 0*/, - const int index /* = -1*/) -{ - ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID(); - if (!parent_node) return wxDataViewItem(0); - - wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder); - - // get LayerRoot node - ObjectDataViewModelNode *layer_root_node; - wxDataViewItem layer_root_item; - - if (parent_node->GetType() & itLayerRoot) { - layer_root_node = parent_node; - layer_root_item = parent_item; - } - else { - const int root_idx = get_root_idx(parent_node, itLayerRoot); - if (root_idx < 0) return wxDataViewItem(0); - layer_root_node = parent_node->GetNthChild(root_idx); - layer_root_item = wxDataViewItem((void*)layer_root_node); - } - - // Add layer node - ObjectDataViewModelNode *layer_node = new ObjectDataViewModelNode(layer_root_node, layer_range, index, extruder_str); - if (index < 0) - layer_root_node->Append(layer_node); - else - layer_root_node->Insert(layer_node, index); - - // notify control - const wxDataViewItem layer_item((void*)layer_node); - ItemAdded(layer_root_item, layer_item); - - return layer_item; -} - -wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item) -{ - auto ret_item = wxDataViewItem(0); - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - if (!node) // happens if item.IsOk()==false - return ret_item; - - auto node_parent = node->GetParent(); - wxDataViewItem parent(node_parent); - - // first remove the node from the parent's array of children; - // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_ - // thus removing the node from it doesn't result in freeing it - if (node_parent) { - if (node->m_type & (itInstanceRoot|itLayerRoot)) - { - // node can be deleted by the Delete, let's check its type while we safely can - bool is_instance_root = (node->m_type & itInstanceRoot); - - for (int i = int(node->GetChildCount() - 1); i >= (is_instance_root ? 1 : 0); i--) - Delete(wxDataViewItem(node->GetNthChild(i))); - - return parent; - } - - auto id = node_parent->GetChildren().Index(node); - auto idx = node->GetIdx(); - - - if (node->m_type & (itVolume|itLayer)) { - node_parent->m_volumes_cnt--; - DeleteSettings(item); - } - node_parent->GetChildren().Remove(node); - - if (id > 0) { - if (size_t(id) == node_parent->GetChildCount()) id--; - ret_item = wxDataViewItem(node_parent->GetChildren().Item(id)); - } - - //update idx value for remaining child-nodes - auto children = node_parent->GetChildren(); - for (size_t i = 0; i < node_parent->GetChildCount() && idx>=0; i++) - { - auto cur_idx = children[i]->GetIdx(); - if (cur_idx > idx) - children[i]->SetIdx(cur_idx-1); - } - - // if there is last instance item, delete both of it and instance root item - if (node_parent->GetChildCount() == 1 && node_parent->GetNthChild(0)->m_type == itInstance) - { - delete node; - ItemDeleted(parent, item); - - ObjectDataViewModelNode *last_instance_node = node_parent->GetNthChild(0); - PrintIndicator last_instance_printable = last_instance_node->IsPrintable(); - node_parent->GetChildren().Remove(last_instance_node); - delete last_instance_node; - ItemDeleted(parent, wxDataViewItem(last_instance_node)); - - ObjectDataViewModelNode *obj_node = node_parent->GetParent(); - obj_node->set_printable_icon(last_instance_printable); - obj_node->GetChildren().Remove(node_parent); - delete node_parent; - ret_item = wxDataViewItem(obj_node); - -#ifndef __WXGTK__ - if (obj_node->GetChildCount() == 0) - obj_node->m_container = false; -#endif //__WXGTK__ - ItemDeleted(ret_item, wxDataViewItem(node_parent)); - return ret_item; - } - - if (node->m_type & itInstance) - UpdateObjectPrintable(wxDataViewItem(node_parent->GetParent())); - - // if there was last layer item, delete this one and layers root item - if (node_parent->GetChildCount() == 0 && node_parent->m_type == itLayerRoot) - { - ObjectDataViewModelNode *obj_node = node_parent->GetParent(); - obj_node->GetChildren().Remove(node_parent); - delete node_parent; - ret_item = wxDataViewItem(obj_node); - -#ifndef __WXGTK__ - if (obj_node->GetChildCount() == 0) - obj_node->m_container = false; -#endif //__WXGTK__ - ItemDeleted(ret_item, wxDataViewItem(node_parent)); - return ret_item; - } - - // if there is last volume item after deleting, delete this last volume too - if (node_parent->GetChildCount() <= 3) // 3??? #ys_FIXME - { - int vol_cnt = 0; - int vol_idx = 0; - for (size_t i = 0; i < node_parent->GetChildCount(); ++i) { - if (node_parent->GetNthChild(i)->GetType() == itVolume) { - vol_idx = i; - vol_cnt++; - } - if (vol_cnt > 1) - break; - } - - if (vol_cnt == 1) { - delete node; - ItemDeleted(parent, item); - - ObjectDataViewModelNode *last_child_node = node_parent->GetNthChild(vol_idx); - DeleteSettings(wxDataViewItem(last_child_node)); - node_parent->GetChildren().Remove(last_child_node); - node_parent->m_volumes_cnt = 0; - delete last_child_node; - -#ifndef __WXGTK__ - if (node_parent->GetChildCount() == 0) - node_parent->m_container = false; -#endif //__WXGTK__ - ItemDeleted(parent, wxDataViewItem(last_child_node)); - - wxCommandEvent event(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED); - auto it = find(m_objects.begin(), m_objects.end(), node_parent); - event.SetInt(it == m_objects.end() ? -1 : it - m_objects.begin()); - wxPostEvent(m_ctrl, event); - - ret_item = parent; - - return ret_item; - } - } - } - else - { - auto it = find(m_objects.begin(), m_objects.end(), node); - size_t id = it - m_objects.begin(); - if (it != m_objects.end()) - { - // Delete all sub-items - int i = m_objects[id]->GetChildCount() - 1; - while (i >= 0) { - Delete(wxDataViewItem(m_objects[id]->GetNthChild(i))); - i = m_objects[id]->GetChildCount() - 1; - } - m_objects.erase(it); - } - if (id > 0) { - if(id == m_objects.size()) id--; - ret_item = wxDataViewItem(m_objects[id]); - } - } - // free the node - delete node; - - // set m_containet to FALSE if parent has no child - if (node_parent) { -#ifndef __WXGTK__ - if (node_parent->GetChildCount() == 0) - node_parent->m_container = false; -#endif //__WXGTK__ - ret_item = parent; - } - - // notify control - ItemDeleted(parent, item); - return ret_item; -} - -wxDataViewItem ObjectDataViewModel::DeleteLastInstance(const wxDataViewItem &parent_item, size_t num) -{ - auto ret_item = wxDataViewItem(0); - ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID(); - if (!parent_node) return ret_item; - - const int inst_root_id = get_root_idx(parent_node, itInstanceRoot); - if (inst_root_id < 0) return ret_item; - - wxDataViewItemArray items; - ObjectDataViewModelNode *inst_root_node = parent_node->GetNthChild(inst_root_id); - const wxDataViewItem inst_root_item((void*)inst_root_node); - - const int inst_cnt = inst_root_node->GetChildCount(); - const bool delete_inst_root_item = inst_cnt - num < 2 ? true : false; - - PrintIndicator last_inst_printable = piUndef; - - int stop = delete_inst_root_item ? 0 : inst_cnt - num; - for (int i = inst_cnt - 1; i >= stop;--i) { - ObjectDataViewModelNode *last_instance_node = inst_root_node->GetNthChild(i); - if (i==0) last_inst_printable = last_instance_node->IsPrintable(); - inst_root_node->GetChildren().Remove(last_instance_node); - delete last_instance_node; - ItemDeleted(inst_root_item, wxDataViewItem(last_instance_node)); - } - - if (delete_inst_root_item) { - ret_item = parent_item; - parent_node->GetChildren().Remove(inst_root_node); - parent_node->set_printable_icon(last_inst_printable); - ItemDeleted(parent_item, inst_root_item); - ItemChanged(parent_item); -#ifndef __WXGTK__ - if (parent_node->GetChildCount() == 0) - parent_node->m_container = false; -#endif //__WXGTK__ - } - - // update object_node printable property - UpdateObjectPrintable(parent_item); - - return ret_item; -} - -void ObjectDataViewModel::DeleteAll() -{ - while (!m_objects.empty()) - { - auto object = m_objects.back(); -// object->RemoveAllChildren(); - Delete(wxDataViewItem(object)); - } -} - -void ObjectDataViewModel::DeleteChildren(wxDataViewItem& parent) -{ - ObjectDataViewModelNode *root = (ObjectDataViewModelNode*)parent.GetID(); - if (!root) // happens if item.IsOk()==false - return; - - // first remove the node from the parent's array of children; - // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_ - // thus removing the node from it doesn't result in freeing it - auto& children = root->GetChildren(); - for (int id = root->GetChildCount() - 1; id >= 0; --id) - { - auto node = children[id]; - auto item = wxDataViewItem(node); - children.RemoveAt(id); - - if (node->m_type == itVolume) - root->m_volumes_cnt--; - - // free the node - delete node; - - // notify control - ItemDeleted(parent, item); - } - - // set m_containet to FALSE if parent has no child -#ifndef __WXGTK__ - root->m_container = false; -#endif //__WXGTK__ -} - -void ObjectDataViewModel::DeleteVolumeChildren(wxDataViewItem& parent) -{ - ObjectDataViewModelNode *root = (ObjectDataViewModelNode*)parent.GetID(); - if (!root) // happens if item.IsOk()==false - return; - - // first remove the node from the parent's array of children; - // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_ - // thus removing the node from it doesn't result in freeing it - auto& children = root->GetChildren(); - for (int id = root->GetChildCount() - 1; id >= 0; --id) - { - auto node = children[id]; - if (node->m_type != itVolume) - continue; - - auto item = wxDataViewItem(node); - DeleteSettings(item); - children.RemoveAt(id); - - // free the node - delete node; - - // notify control - ItemDeleted(parent, item); - } - root->m_volumes_cnt = 0; - - // set m_containet to FALSE if parent has no child -#ifndef __WXGTK__ - root->m_container = false; -#endif //__WXGTK__ -} - -void ObjectDataViewModel::DeleteSettings(const wxDataViewItem& parent) -{ - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)parent.GetID(); - if (!node) return; - - // if volume has a "settings"item, than delete it before volume deleting - if (node->GetChildCount() > 0 && node->GetNthChild(0)->GetType() == itSettings) { - auto settings_node = node->GetNthChild(0); - auto settings_item = wxDataViewItem(settings_node); - node->GetChildren().RemoveAt(0); - delete settings_node; - ItemDeleted(parent, settings_item); - } -} - -wxDataViewItem ObjectDataViewModel::GetItemById(int obj_idx) -{ - if (size_t(obj_idx) >= m_objects.size()) - { - printf("Error! Out of objects range.\n"); - return wxDataViewItem(0); - } - return wxDataViewItem(m_objects[obj_idx]); -} - - -wxDataViewItem ObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_idx) -{ - if (size_t(obj_idx) >= m_objects.size()) { - printf("Error! Out of objects range.\n"); - return wxDataViewItem(0); - } - - auto parent = m_objects[obj_idx]; - if (parent->GetChildCount() == 0 || - (parent->GetChildCount() == 1 && parent->GetNthChild(0)->GetType() & itSettings )) { - if (volume_idx == 0) - return GetItemById(obj_idx); - - printf("Error! Object has no one volume.\n"); - return wxDataViewItem(0); - } - - for (size_t i = 0; i < parent->GetChildCount(); i++) - if (parent->GetNthChild(i)->m_idx == volume_idx && parent->GetNthChild(i)->GetType() & itVolume) - return wxDataViewItem(parent->GetNthChild(i)); - - return wxDataViewItem(0); -} - -wxDataViewItem ObjectDataViewModel::GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type) -{ - if (size_t(obj_idx) >= m_objects.size()) { - printf("Error! Out of objects range.\n"); - return wxDataViewItem(0); - } - - auto item = GetItemByType(wxDataViewItem(m_objects[obj_idx]), parent_type); - if (!item) - return wxDataViewItem(0); - - auto parent = (ObjectDataViewModelNode*)item.GetID(); - for (size_t i = 0; i < parent->GetChildCount(); i++) - if (parent->GetNthChild(i)->m_idx == sub_obj_idx) - return wxDataViewItem(parent->GetNthChild(i)); - - return wxDataViewItem(0); -} - -wxDataViewItem ObjectDataViewModel::GetItemByInstanceId(int obj_idx, int inst_idx) -{ - return GetItemById(obj_idx, inst_idx, itInstanceRoot); -} - -wxDataViewItem ObjectDataViewModel::GetItemByLayerId(int obj_idx, int layer_idx) -{ - return GetItemById(obj_idx, layer_idx, itLayerRoot); -} - -wxDataViewItem ObjectDataViewModel::GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range) -{ - if (size_t(obj_idx) >= m_objects.size()) { - printf("Error! Out of objects range.\n"); - return wxDataViewItem(0); - } - - auto item = GetItemByType(wxDataViewItem(m_objects[obj_idx]), itLayerRoot); - if (!item) - return wxDataViewItem(0); - - auto parent = (ObjectDataViewModelNode*)item.GetID(); - for (size_t i = 0; i < parent->GetChildCount(); i++) - if (parent->GetNthChild(i)->m_layer_range == layer_range) - return wxDataViewItem(parent->GetNthChild(i)); - - return wxDataViewItem(0); -} - -int ObjectDataViewModel::GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range) -{ - wxDataViewItem item = GetItemByLayerRange(obj_idx, layer_range); - if (!item) - return -1; - - return GetLayerIdByItem(item); -} - -int ObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) const -{ - if(!item.IsOk()) - return -1; - - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - auto it = find(m_objects.begin(), m_objects.end(), node); - if (it == m_objects.end()) - return -1; - - return it - m_objects.begin(); -} - -int ObjectDataViewModel::GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const -{ - wxASSERT(item.IsOk()); - - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - if (!node || node->m_type != type) - return -1; - return node->GetIdx(); -} - -int ObjectDataViewModel::GetObjectIdByItem(const wxDataViewItem& item) const -{ - return GetIdByItem(GetTopParent(item)); -} - -int ObjectDataViewModel::GetVolumeIdByItem(const wxDataViewItem& item) const -{ - return GetIdByItemAndType(item, itVolume); -} - -int ObjectDataViewModel::GetInstanceIdByItem(const wxDataViewItem& item) const -{ - return GetIdByItemAndType(item, itInstance); -} - -int ObjectDataViewModel::GetLayerIdByItem(const wxDataViewItem& item) const -{ - return GetIdByItemAndType(item, itLayer); -} - -t_layer_height_range ObjectDataViewModel::GetLayerRangeByItem(const wxDataViewItem& item) const -{ - wxASSERT(item.IsOk()); - - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - if (!node || node->m_type != itLayer) - return { 0.0f, 0.0f }; - return node->GetLayerRange(); -} - -bool ObjectDataViewModel::UpdateColumValues(unsigned col) -{ - switch (col) - { - case colPrint: - case colName: - case colEditing: - return true; - case colExtruder: - { - wxDataViewItemArray items; - GetAllChildren(wxDataViewItem(nullptr), items); - - if (items.IsEmpty()) return false; - - for (auto item : items) - UpdateExtruderBitmap(item); - - return true; - } - default: - printf("MyObjectTreeModel::SetValue: wrong column"); - } - return false; -} - - -void ObjectDataViewModel::UpdateExtruderBitmap(wxDataViewItem item) -{ - wxString extruder = GetExtruder(item); - if (extruder.IsEmpty()) - return; - - // set extruder bitmap - int extruder_idx = atoi(extruder.c_str()); - if (extruder_idx > 0) --extruder_idx; - - const DataViewBitmapText extruder_val(extruder, get_extruder_color_icon(extruder_idx)); - - wxVariant value; - value << extruder_val; - - SetValue(value, item, colExtruder); -} - -void ObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx) -{ - wxASSERT(item.IsOk()); - type = itUndef; - - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - if (!node || - node->GetIdx() <-1 || - ( node->GetIdx() == -1 && - !(node->GetType() & (itObject | itSettings | itInstanceRoot | itLayerRoot/* | itLayer*/)) - ) - ) - return; - - idx = node->GetIdx(); - type = node->GetType(); - - ObjectDataViewModelNode *parent_node = node->GetParent(); - if (!parent_node) return; - - // get top parent (Object) node - while (parent_node->m_type != itObject) - parent_node = parent_node->GetParent(); - - auto it = find(m_objects.begin(), m_objects.end(), parent_node); - if (it != m_objects.end()) - obj_idx = it - m_objects.begin(); - else - type = itUndef; -} - -int ObjectDataViewModel::GetRowByItem(const wxDataViewItem& item) const -{ - if (m_objects.empty()) - return -1; - - int row_num = 0; - - for (size_t i = 0; i < m_objects.size(); i++) - { - row_num++; - if (item == wxDataViewItem(m_objects[i])) - return row_num; - - for (size_t j = 0; j < m_objects[i]->GetChildCount(); j++) - { - row_num++; - ObjectDataViewModelNode* cur_node = m_objects[i]->GetNthChild(j); - if (item == wxDataViewItem(cur_node)) - return row_num; - - if (cur_node->m_type == itVolume && cur_node->GetChildCount() == 1) - row_num++; - if (cur_node->m_type == itInstanceRoot) - { - row_num++; - for (size_t t = 0; t < cur_node->GetChildCount(); t++) - { - row_num++; - if (item == wxDataViewItem(cur_node->GetNthChild(t))) - return row_num; - } - } - } - } - - return -1; -} - -bool ObjectDataViewModel::InvalidItem(const wxDataViewItem& item) -{ - if (!item) - return true; - - ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID(); - if (!node || node->invalid()) - return true; - - return false; -} - -wxString ObjectDataViewModel::GetName(const wxDataViewItem &item) const -{ - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - if (!node) // happens if item.IsOk()==false - return wxEmptyString; - - return node->m_name; -} - -wxBitmap& ObjectDataViewModel::GetBitmap(const wxDataViewItem &item) const -{ - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - return node->m_bmp; -} - -wxString ObjectDataViewModel::GetExtruder(const wxDataViewItem& item) const -{ - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - if (!node) // happens if item.IsOk()==false - return wxEmptyString; - - return node->m_extruder; -} - -int ObjectDataViewModel::GetExtruderNumber(const wxDataViewItem& item) const -{ - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - if (!node) // happens if item.IsOk()==false - return 0; - - return atoi(node->m_extruder.c_str()); -} - -void ObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const -{ - wxASSERT(item.IsOk()); - - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - switch (col) - { - case colPrint: - variant << node->m_printable_icon; - break; - case colName: - variant << DataViewBitmapText(node->m_name, node->m_bmp); - break; - case colExtruder: - variant << DataViewBitmapText(node->m_extruder, node->m_extruder_bmp); - break; - case colEditing: - variant << node->m_action_icon; - break; - default: - ; - } -} - -bool ObjectDataViewModel::SetValue(const wxVariant &variant, const wxDataViewItem &item, unsigned int col) -{ - wxASSERT(item.IsOk()); - - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - return node->SetValue(variant, col); -} - -bool ObjectDataViewModel::SetValue(const wxVariant &variant, const int item_idx, unsigned int col) -{ - if (size_t(item_idx) >= m_objects.size()) - return false; - - return m_objects[item_idx]->SetValue(variant, col); -} - -void ObjectDataViewModel::SetExtruder(const wxString& extruder, wxDataViewItem item) -{ - DataViewBitmapText extruder_val; - extruder_val.SetText(extruder); - - // set extruder bitmap - int extruder_idx = atoi(extruder.c_str()); - if (extruder_idx > 0) --extruder_idx; - extruder_val.SetBitmap(get_extruder_color_icon(extruder_idx)); - - wxVariant value; - value << extruder_val; - - SetValue(value, item, colExtruder); -} - -wxDataViewItem ObjectDataViewModel::ReorganizeChildren( const int current_volume_id, - const int new_volume_id, - const wxDataViewItem &parent) -{ - auto ret_item = wxDataViewItem(0); - if (current_volume_id == new_volume_id) - return ret_item; - wxASSERT(parent.IsOk()); - ObjectDataViewModelNode *node_parent = (ObjectDataViewModelNode*)parent.GetID(); - if (!node_parent) // happens if item.IsOk()==false - return ret_item; - - const size_t shift = node_parent->GetChildren().Item(0)->m_type == itSettings ? 1 : 0; - - ObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id+shift); - node_parent->GetChildren().Remove(deleted_node); - ItemDeleted(parent, wxDataViewItem(deleted_node)); - node_parent->Insert(deleted_node, new_volume_id+shift); - ItemAdded(parent, wxDataViewItem(deleted_node)); - const auto settings_item = GetSettingsItem(wxDataViewItem(deleted_node)); - if (settings_item) - ItemAdded(wxDataViewItem(deleted_node), settings_item); - - //update volume_id value for child-nodes - auto children = node_parent->GetChildren(); - int id_frst = current_volume_id < new_volume_id ? current_volume_id : new_volume_id; - int id_last = current_volume_id > new_volume_id ? current_volume_id : new_volume_id; - for (int id = id_frst; id <= id_last; ++id) - children[id+shift]->SetIdx(id); - - return wxDataViewItem(node_parent->GetNthChild(new_volume_id+shift)); -} - -bool ObjectDataViewModel::IsEnabled(const wxDataViewItem &item, unsigned int col) const -{ - wxASSERT(item.IsOk()); - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - - // disable extruder selection for the non "itObject|itVolume" item - return !(col == colExtruder && node->m_extruder.IsEmpty()); -} - -wxDataViewItem ObjectDataViewModel::GetParent(const wxDataViewItem &item) const -{ - // the invisible root node has no parent - if (!item.IsOk()) - return wxDataViewItem(0); - - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - assert(node != nullptr && node->valid()); - - // objects nodes has no parent too - if (node->m_type == itObject) - return wxDataViewItem(0); - - return wxDataViewItem((void*)node->GetParent()); -} - -wxDataViewItem ObjectDataViewModel::GetTopParent(const wxDataViewItem &item) const -{ - // the invisible root node has no parent - if (!item.IsOk()) - return wxDataViewItem(0); - - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - if (node->m_type == itObject) - return item; - - ObjectDataViewModelNode *parent_node = node->GetParent(); - while (parent_node->m_type != itObject) - parent_node = parent_node->GetParent(); - - return wxDataViewItem((void*)parent_node); -} - -bool ObjectDataViewModel::IsContainer(const wxDataViewItem &item) const -{ - // the invisible root node can have children - if (!item.IsOk()) - return true; - - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - return node->IsContainer(); -} - -unsigned int ObjectDataViewModel::GetChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const -{ - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)parent.GetID(); - if (!node) - { - for (auto object : m_objects) - array.Add(wxDataViewItem((void*)object)); - return m_objects.size(); - } - - if (node->GetChildCount() == 0) - { - return 0; - } - - unsigned int count = node->GetChildren().GetCount(); - for (unsigned int pos = 0; pos < count; pos++) - { - ObjectDataViewModelNode *child = node->GetChildren().Item(pos); - array.Add(wxDataViewItem((void*)child)); - } - - return count; -} - -void ObjectDataViewModel::GetAllChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const -{ - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)parent.GetID(); - if (!node) { - for (auto object : m_objects) - array.Add(wxDataViewItem((void*)object)); - } - else if (node->GetChildCount() == 0) - return; - else { - const size_t count = node->GetChildren().GetCount(); - for (size_t pos = 0; pos < count; pos++) { - ObjectDataViewModelNode *child = node->GetChildren().Item(pos); - array.Add(wxDataViewItem((void*)child)); - } - } - - wxDataViewItemArray new_array = array; - for (const auto item : new_array) - { - wxDataViewItemArray children; - GetAllChildren(item, children); - WX_APPEND_ARRAY(array, children); - } -} - -ItemType ObjectDataViewModel::GetItemType(const wxDataViewItem &item) const -{ - if (!item.IsOk()) - return itUndef; - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - return node->m_type < 0 ? itUndef : node->m_type; -} - -wxDataViewItem ObjectDataViewModel::GetItemByType(const wxDataViewItem &parent_item, ItemType type) const -{ - if (!parent_item.IsOk()) - return wxDataViewItem(0); - - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)parent_item.GetID(); - if (node->GetChildCount() == 0) - return wxDataViewItem(0); - - for (size_t i = 0; i < node->GetChildCount(); i++) { - if (node->GetNthChild(i)->m_type == type) - return wxDataViewItem((void*)node->GetNthChild(i)); - } - - return wxDataViewItem(0); -} - -wxDataViewItem ObjectDataViewModel::GetSettingsItem(const wxDataViewItem &item) const -{ - return GetItemByType(item, itSettings); -} - -wxDataViewItem ObjectDataViewModel::GetInstanceRootItem(const wxDataViewItem &item) const -{ - return GetItemByType(item, itInstanceRoot); -} - -wxDataViewItem ObjectDataViewModel::GetLayerRootItem(const wxDataViewItem &item) const -{ - return GetItemByType(item, itLayerRoot); -} - -bool ObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const -{ - if (!item.IsOk()) - return false; - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - return node->m_type == itSettings; -} - -void ObjectDataViewModel::UpdateSettingsDigest(const wxDataViewItem &item, - const std::vector& categories) -{ - if (!item.IsOk()) return; - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - if (!node->update_settings_digest(categories)) - return; - ItemChanged(item); -} - -void ObjectDataViewModel::SetVolumeType(const wxDataViewItem &item, const Slic3r::ModelVolumeType type) -{ - if (!item.IsOk() || GetItemType(item) != itVolume) - return; - - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - node->SetBitmap(*m_volume_bmps[int(type)]); - ItemChanged(item); -} - -wxDataViewItem ObjectDataViewModel::SetPrintableState( - PrintIndicator printable, - int obj_idx, - int subobj_idx /* = -1*/, - ItemType subobj_type/* = itInstance*/) -{ - wxDataViewItem item = wxDataViewItem(0); - if (subobj_idx < 0) - item = GetItemById(obj_idx); - else - item = subobj_type&itInstance ? GetItemByInstanceId(obj_idx, subobj_idx) : - GetItemByVolumeId(obj_idx, subobj_idx); - - ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID(); - if (!node) - return wxDataViewItem(0); - node->set_printable_icon(printable); - ItemChanged(item); - - if (subobj_idx >= 0) - UpdateObjectPrintable(GetItemById(obj_idx)); - - return item; -} - -wxDataViewItem ObjectDataViewModel::SetObjectPrintableState( - PrintIndicator printable, - wxDataViewItem obj_item) -{ - ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)obj_item.GetID(); - if (!node) - return wxDataViewItem(0); - node->set_printable_icon(printable); - ItemChanged(obj_item); - - UpdateInstancesPrintable(obj_item); - - return obj_item; -} - -void ObjectDataViewModel::Rescale() -{ - wxDataViewItemArray all_items; - GetAllChildren(wxDataViewItem(0), all_items); - - for (wxDataViewItem item : all_items) - { - if (!item.IsOk()) - continue; - - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - node->msw_rescale(); - - switch (node->m_type) - { - case itObject: - if (node->m_bmp.IsOk()) node->m_bmp = *m_warning_bmp; - break; - case itVolume: - node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_bmp.GetWidth() != node->m_bmp.GetHeight()); - break; - case itLayerRoot: - node->m_bmp = create_scaled_bitmap(nullptr, LAYER_ROOT_ICON); // FIXME: pass window ptr - break; - case itLayer: - node->m_bmp = create_scaled_bitmap(nullptr, LAYER_ICON); // FIXME: pass window ptr - break; - default: break; - } - - ItemChanged(item); - } -} - -wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const bool is_marked/* = false*/) -{ - if (!is_marked) - return *m_volume_bmps[static_cast(vol_type)]; - - std::string scaled_bitmap_name = "warning" + std::to_string(static_cast(vol_type)); - scaled_bitmap_name += "-em" + std::to_string(Slic3r::GUI::wxGetApp().em_unit()); - - wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name); - if (bmp == nullptr) { - std::vector bmps; - - bmps.emplace_back(*m_warning_bmp); - bmps.emplace_back(*m_volume_bmps[static_cast(vol_type)]); - - bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); - } - - return *bmp; -} - -void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object/* = false*/) -{ - if (!item.IsOk()) - return; - - ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - - if (!node->GetBitmap().IsOk() || !(node->GetType() & (itVolume | itObject))) - return; - - if (node->GetType() & itVolume) { - node->SetBitmap(*m_volume_bmps[static_cast(node->volume_type())]); - return; - } - - node->SetBitmap(wxNullBitmap); - if (unmark_object) - { - wxDataViewItemArray children; - GetChildren(item, children); - for (const wxDataViewItem& child : children) - DeleteWarningIcon(child); - } -} - -//----------------------------------------------------------------------------- -// DataViewBitmapText -//----------------------------------------------------------------------------- - -wxIMPLEMENT_DYNAMIC_CLASS(DataViewBitmapText, wxObject) - -IMPLEMENT_VARIANT_OBJECT(DataViewBitmapText) - -// --------------------------------------------------------- -// BitmapTextRenderer -// --------------------------------------------------------- - -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING -BitmapTextRenderer::BitmapTextRenderer(wxDataViewCellMode mode /*= wxDATAVIEW_CELL_EDITABLE*/, - int align /*= wxDVR_DEFAULT_ALIGNMENT*/): -wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align) -{ - SetMode(mode); - SetAlignment(align); -} -#endif // ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - -bool BitmapTextRenderer::SetValue(const wxVariant &value) -{ - m_value << value; - return true; -} - -bool BitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const -{ - return false; -} - -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY -wxString BitmapTextRenderer::GetAccessibleDescription() const -{ - return m_value.GetText(); -} -#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - -bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) -{ - int xoffset = 0; - - const wxBitmap& icon = m_value.GetBitmap(); - if (icon.IsOk()) - { - dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); - xoffset = icon.GetWidth() + 4; - } - - RenderText(m_value.GetText(), xoffset, rect, dc, state); - - return true; -} - -wxSize BitmapTextRenderer::GetSize() const -{ - if (!m_value.GetText().empty()) - { - wxSize size = GetTextExtent(m_value.GetText()); - - if (m_value.GetBitmap().IsOk()) - size.x += m_value.GetBitmap().GetWidth() + 4; - return size; - } - return wxSize(80, 20); -} - - -wxWindow* BitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) -{ - wxDataViewCtrl* const dv_ctrl = GetOwner()->GetOwner(); - ObjectDataViewModel* const model = dynamic_cast(dv_ctrl->GetModel()); - - if ( !(model->GetItemType(dv_ctrl->GetSelection()) & (itVolume | itObject)) ) - return nullptr; - - DataViewBitmapText data; - data << value; - - m_was_unusable_symbol = false; - - wxPoint position = labelRect.GetPosition(); - if (data.GetBitmap().IsOk()) { - const int bmp_width = data.GetBitmap().GetWidth(); - position.x += bmp_width; - labelRect.SetWidth(labelRect.GetWidth() - bmp_width); - } - - wxTextCtrl* text_editor = new wxTextCtrl(parent, wxID_ANY, data.GetText(), - position, labelRect.GetSize(), wxTE_PROCESS_ENTER); - text_editor->SetInsertionPointEnd(); - text_editor->SelectAll(); - - return text_editor; -} - -bool BitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) -{ - wxTextCtrl* text_editor = wxDynamicCast(ctrl, wxTextCtrl); - if (!text_editor || text_editor->GetValue().IsEmpty()) - return false; - - std::string chosen_name = Slic3r::normalize_utf8_nfc(text_editor->GetValue().ToUTF8()); - const char* unusable_symbols = "<>:/\\|?*\""; - for (size_t i = 0; i < std::strlen(unusable_symbols); i++) { - if (chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) { - m_was_unusable_symbol = true; - return false; - } - } - - // The icon can't be edited so get its old value and reuse it. - wxVariant valueOld; - GetView()->GetModel()->GetValue(valueOld, m_item, colName); - - DataViewBitmapText bmpText; - bmpText << valueOld; - - // But replace the text with the value entered by user. - bmpText.SetText(text_editor->GetValue()); - - value << bmpText; - return true; -} - -// ---------------------------------------------------------------------------- -// BitmapChoiceRenderer -// ---------------------------------------------------------------------------- - -bool BitmapChoiceRenderer::SetValue(const wxVariant& value) -{ - m_value << value; - return true; -} - -bool BitmapChoiceRenderer::GetValue(wxVariant& value) const -{ - value << m_value; - return true; -} - -bool BitmapChoiceRenderer::Render(wxRect rect, wxDC* dc, int state) -{ - int xoffset = 0; - - const wxBitmap& icon = m_value.GetBitmap(); - if (icon.IsOk()) - { - dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); - xoffset = icon.GetWidth() + 4; - } - - if (rect.height==0) - rect.height= icon.GetHeight(); - RenderText(m_value.GetText(), xoffset, rect, dc, state); - - return true; -} - -wxSize BitmapChoiceRenderer::GetSize() const -{ - wxSize sz = GetTextExtent(m_value.GetText()); - - if (m_value.GetBitmap().IsOk()) - sz.x += m_value.GetBitmap().GetWidth() + 4; - - return sz; -} - - -wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) -{ - wxDataViewCtrl* const dv_ctrl = GetOwner()->GetOwner(); - ObjectDataViewModel* const model = dynamic_cast(dv_ctrl->GetModel()); - - if (!(model->GetItemType(dv_ctrl->GetSelection()) & (itVolume | itLayer | itObject))) - return nullptr; - - std::vector icons = get_extruder_color_icons(); - if (icons.empty()) - return nullptr; - - DataViewBitmapText data; - data << value; - - auto c_editor = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, - labelRect.GetTopLeft(), wxSize(labelRect.GetWidth(), -1), - 0, nullptr , wxCB_READONLY); - - int i=0; - for (wxBitmap* bmp : icons) { - if (i==0) { - c_editor->Append(_(L("default")), *bmp); - ++i; - } - - c_editor->Append(wxString::Format("%d", i), *bmp); - ++i; - } - c_editor->SetSelection(atoi(data.GetText().c_str())); - - // to avoid event propagation to other sidebar items - c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) { - evt.StopPropagation(); - // FinishEditing grabs new selection and triggers config update. We better call - // it explicitly, automatic update on KILL_FOCUS didn't work on Linux. - this->FinishEditing(); - }); - - return c_editor; -} - -bool BitmapChoiceRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) -{ - wxBitmapComboBox* c = (wxBitmapComboBox*)ctrl; - int selection = c->GetSelection(); - if (selection < 0) - return false; - - DataViewBitmapText bmpText; - - bmpText.SetText(c->GetString(selection)); - bmpText.SetBitmap(c->GetItemBitmap(selection)); - - value << bmpText; - return true; -} - -// ---------------------------------------------------------------------------- -// DoubleSlider -// ---------------------------------------------------------------------------- -DoubleSlider::DoubleSlider( wxWindow *parent, - wxWindowID id, - int lowerValue, - int higherValue, - int minValue, - int maxValue, - const wxPoint& pos, - const wxSize& size, - long style, - const wxValidator& val, - const wxString& name) : - wxControl(parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE), - m_lower_value(lowerValue), m_higher_value (higherValue), - m_min_value(minValue), m_max_value(maxValue), - m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL) -{ -#ifdef __WXOSX__ - is_osx = true; -#endif //__WXOSX__ - if (!is_osx) - SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX - - const float scale_factor = get_svg_scale_factor(this); - - m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "right_half_circle.png") : ScalableBitmap(this, "thumb_up")); - m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "left_half_circle.png" ) : ScalableBitmap(this, "thumb_down")); - m_thumb_size = m_bmp_thumb_lower.bmp().GetSize()*(1.0/scale_factor); - - m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add"); - m_bmp_add_tick_off = ScalableBitmap(this, "colorchange_add_f"); - m_bmp_del_tick_on = ScalableBitmap(this, "colorchange_del"); - m_bmp_del_tick_off = ScalableBitmap(this, "colorchange_del_f"); - m_tick_icon_dim = int((float)m_bmp_add_tick_on.bmp().GetSize().x / scale_factor); - - m_bmp_one_layer_lock_on = ScalableBitmap(this, "lock_closed"); - m_bmp_one_layer_lock_off = ScalableBitmap(this, "lock_closed_f"); - m_bmp_one_layer_unlock_on = ScalableBitmap(this, "lock_open"); - m_bmp_one_layer_unlock_off = ScalableBitmap(this, "lock_open_f"); - m_lock_icon_dim = int((float)m_bmp_one_layer_lock_on.bmp().GetSize().x / scale_factor); - - m_bmp_revert = ScalableBitmap(this, "undo"); - m_revert_icon_dim = int((float)m_bmp_revert.bmp().GetSize().x / scale_factor); - m_bmp_cog = ScalableBitmap(this, "cog"); - m_cog_icon_dim = int((float)m_bmp_cog.bmp().GetSize().x / scale_factor); - - m_selection = ssUndef; - m_ticks.set_pause_print_msg(_utf8(L("Place bearings in slots and resume"))); - - // slider events - Bind(wxEVT_PAINT, &DoubleSlider::OnPaint, this); - Bind(wxEVT_CHAR, &DoubleSlider::OnChar, this); - Bind(wxEVT_LEFT_DOWN, &DoubleSlider::OnLeftDown, this); - Bind(wxEVT_MOTION, &DoubleSlider::OnMotion, this); - Bind(wxEVT_LEFT_UP, &DoubleSlider::OnLeftUp, this); - Bind(wxEVT_MOUSEWHEEL, &DoubleSlider::OnWheel, this); - Bind(wxEVT_ENTER_WINDOW,&DoubleSlider::OnEnterWin, this); - Bind(wxEVT_LEAVE_WINDOW,&DoubleSlider::OnLeaveWin, this); - Bind(wxEVT_KEY_DOWN, &DoubleSlider::OnKeyDown, this); - Bind(wxEVT_KEY_UP, &DoubleSlider::OnKeyUp, this); - Bind(wxEVT_RIGHT_DOWN, &DoubleSlider::OnRightDown,this); - Bind(wxEVT_RIGHT_UP, &DoubleSlider::OnRightUp, this); - - // control's view variables - SLIDER_MARGIN = 4 + Slic3r::GUI::wxGetApp().em_unit(); - - DARK_ORANGE_PEN = wxPen(wxColour(237, 107, 33)); - ORANGE_PEN = wxPen(wxColour(253, 126, 66)); - LIGHT_ORANGE_PEN = wxPen(wxColour(254, 177, 139)); - - DARK_GREY_PEN = wxPen(wxColour(128, 128, 128)); - GREY_PEN = wxPen(wxColour(164, 164, 164)); - LIGHT_GREY_PEN = wxPen(wxColour(204, 204, 204)); - - m_line_pens = { &DARK_GREY_PEN, &GREY_PEN, &LIGHT_GREY_PEN }; - m_segm_pens = { &DARK_ORANGE_PEN, &ORANGE_PEN, &LIGHT_ORANGE_PEN }; - - const wxFont& font = GetFont(); - m_font = is_osx ? font.Smaller().Smaller() : font.Smaller(); -} - -void DoubleSlider::msw_rescale() -{ - const wxFont& font = Slic3r::GUI::wxGetApp().normal_font(); - m_font = is_osx ? font.Smaller().Smaller() : font.Smaller(); - - m_bmp_thumb_higher.msw_rescale(); - m_bmp_thumb_lower .msw_rescale(); - m_thumb_size = m_bmp_thumb_lower.bmp().GetSize(); - - m_bmp_add_tick_on .msw_rescale(); - m_bmp_add_tick_off.msw_rescale(); - m_bmp_del_tick_on .msw_rescale(); - m_bmp_del_tick_off.msw_rescale(); - m_tick_icon_dim = m_bmp_add_tick_on.bmp().GetSize().x; - - m_bmp_one_layer_lock_on .msw_rescale(); - m_bmp_one_layer_lock_off .msw_rescale(); - m_bmp_one_layer_unlock_on .msw_rescale(); - m_bmp_one_layer_unlock_off.msw_rescale(); - m_lock_icon_dim = m_bmp_one_layer_lock_on.bmp().GetSize().x; - - m_bmp_revert.msw_rescale(); - m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x; - m_bmp_cog.msw_rescale(); - m_cog_icon_dim = m_bmp_cog.bmp().GetSize().x; - - SLIDER_MARGIN = 4 + Slic3r::GUI::wxGetApp().em_unit(); - - SetMinSize(get_min_size()); - GetParent()->Layout(); -} - -int DoubleSlider::GetActiveValue() const -{ - return m_selection == ssLower ? - m_lower_value : m_selection == ssHigher ? - m_higher_value : -1; -} - -wxSize DoubleSlider::get_min_size() const -{ - const int min_side = is_horizontal() ? - (is_osx ? 8 : 6) * Slic3r::GUI::wxGetApp().em_unit() : - /*(is_osx ? 10 : 8)*/10 * Slic3r::GUI::wxGetApp().em_unit(); - - return wxSize(min_side, min_side); -} - -wxSize DoubleSlider::DoGetBestSize() const -{ - const wxSize size = wxControl::DoGetBestSize(); - if (size.x > 1 && size.y > 1) - return size; - return get_min_size(); -} - -void DoubleSlider::SetLowerValue(const int lower_val) -{ - m_selection = ssLower; - m_lower_value = lower_val; - correct_lower_value(); - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::SetHigherValue(const int higher_val) -{ - m_selection = ssHigher; - m_higher_value = higher_val; - correct_higher_value(); - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::SetSelectionSpan(const int lower_val, const int higher_val) -{ - m_lower_value = std::max(lower_val, m_min_value); - m_higher_value = std::max(std::min(higher_val, m_max_value), m_lower_value); - if (m_lower_value < m_higher_value) - m_is_one_layer = false; - - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::SetMaxValue(const int max_value) -{ - m_max_value = max_value; - Refresh(); - Update(); -} - -void DoubleSlider::draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos) -{ - int width; - int height; - get_size(&width, &height); - - wxCoord line_beg_x = is_horizontal() ? SLIDER_MARGIN : width*0.5 - 1; - wxCoord line_beg_y = is_horizontal() ? height*0.5 - 1 : SLIDER_MARGIN; - wxCoord line_end_x = is_horizontal() ? width - SLIDER_MARGIN + 1 : width*0.5 - 1; - wxCoord line_end_y = is_horizontal() ? height*0.5 - 1 : height - SLIDER_MARGIN + 1; - - wxCoord segm_beg_x = is_horizontal() ? lower_pos : width*0.5 - 1; - wxCoord segm_beg_y = is_horizontal() ? height*0.5 - 1 : lower_pos/*-1*/; - wxCoord segm_end_x = is_horizontal() ? higher_pos : width*0.5 - 1; - wxCoord segm_end_y = is_horizontal() ? height*0.5 - 1 : higher_pos-1; - - for (size_t id = 0; id < m_line_pens.size(); id++) - { - dc.SetPen(*m_line_pens[id]); - dc.DrawLine(line_beg_x, line_beg_y, line_end_x, line_end_y); - dc.SetPen(*m_segm_pens[id]); - dc.DrawLine(segm_beg_x, segm_beg_y, segm_end_x, segm_end_y); - if (is_horizontal()) - line_beg_y = line_end_y = segm_beg_y = segm_end_y += 1; - else - line_beg_x = line_end_x = segm_beg_x = segm_end_x += 1; - } -} - -double DoubleSlider::get_scroll_step() -{ - const wxSize sz = get_size(); - const int& slider_len = m_style == wxSL_HORIZONTAL ? sz.x : sz.y; - return double(slider_len - SLIDER_MARGIN * 2) / (m_max_value - m_min_value); -} - -// get position on the slider line from entered value -wxCoord DoubleSlider::get_position_from_value(const int value) -{ - const double step = get_scroll_step(); - const int val = is_horizontal() ? value : m_max_value - value; - return wxCoord(SLIDER_MARGIN + int(val*step + 0.5)); -} - -wxSize DoubleSlider::get_size() -{ - int w, h; - get_size(&w, &h); - return wxSize(w, h); -} - -void DoubleSlider::get_size(int *w, int *h) -{ - GetSize(w, h); - is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim; -} - -double DoubleSlider::get_double_value(const SelectedSlider& selection) -{ - if (m_values.empty() || m_lower_value<0) - return 0.0; - if (m_values.size() <= m_higher_value) { - correct_higher_value(); - return m_values.back(); - } - return m_values[selection == ssLower ? m_lower_value : m_higher_value]; -} - -using t_custom_code = Slic3r::Model::CustomGCode; -Slic3r::Model::CustomGCodeInfo DoubleSlider::GetTicksValues() const -{ - Slic3r::Model::CustomGCodeInfo custom_gcode_per_print_z; - std::vector& values = custom_gcode_per_print_z.gcodes; - - const int val_size = m_values.size(); - if (!m_values.empty()) - for (const TICK_CODE& tick : m_ticks.ticks) { - if (tick.tick > val_size) - break; - values.emplace_back(t_custom_code{m_values[tick.tick], tick.gcode, tick.extruder, tick.color}); - } - - custom_gcode_per_print_z.mode = m_force_mode_apply ? m_mode : m_ticks.mode; - - return custom_gcode_per_print_z; -} - -void DoubleSlider::SetTicksValues(const Slic3r::Model::CustomGCodeInfo& custom_gcode_per_print_z) -{ - if (m_values.empty()) - { - m_ticks.mode = m_mode; - return; - } - - const bool was_empty = m_ticks.empty(); - - m_ticks.ticks.clear(); - const std::vector& heights = custom_gcode_per_print_z.gcodes; - for (auto h : heights) { - auto it = std::lower_bound(m_values.begin(), m_values.end(), h.print_z - epsilon()); - - if (it == m_values.end()) - continue; - - m_ticks.ticks.emplace(TICK_CODE{int(it-m_values.begin()), h.gcode, h.extruder, h.color}); - } - - if (!was_empty && m_ticks.empty()) - // Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one - post_ticks_changed_event(); - - m_ticks.mode = custom_gcode_per_print_z.mode; - - Refresh(); - Update(); -} - -void DoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos) -{ - const double step = get_scroll_step(); - if (is_horizontal()) { - lower_pos = SLIDER_MARGIN + int(m_lower_value*step + 0.5); - higher_pos = SLIDER_MARGIN + int(m_higher_value*step + 0.5); - } - else { - lower_pos = SLIDER_MARGIN + int((m_max_value - m_lower_value)*step + 0.5); - higher_pos = SLIDER_MARGIN + int((m_max_value - m_higher_value)*step + 0.5); - } -} - -void DoubleSlider::draw_focus_rect() -{ - if (!m_is_focused) - return; - const wxSize sz = GetSize(); - wxPaintDC dc(this); - const wxPen pen = wxPen(wxColour(128, 128, 10), 1, wxPENSTYLE_DOT); - dc.SetPen(pen); - dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT)); - dc.DrawRectangle(1, 1, sz.x - 2, sz.y - 2); -} - -void DoubleSlider::render() -{ - SetBackgroundColour(GetParent()->GetBackgroundColour()); - draw_focus_rect(); - - wxPaintDC dc(this); - dc.SetFont(m_font); - - const wxCoord lower_pos = get_position_from_value(m_lower_value); - const wxCoord higher_pos = get_position_from_value(m_higher_value); - - // draw colored band on the background of a scroll line - // and only in a case of no-empty m_values - draw_colored_band(dc); - - // draw line - draw_scroll_line(dc, lower_pos, higher_pos); - - //draw color print ticks - draw_ticks(dc); - - // draw both sliders - draw_thumbs(dc, lower_pos, higher_pos); - - //draw lock/unlock - draw_one_layer_icon(dc); - - //draw revert bitmap (if it's shown) - draw_revert_icon(dc); - - //draw cog bitmap (if it's shown) - draw_cog_icon(dc); -} - -void DoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end) -{ - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - - // suppress add tick on first layer - if (tick == 0) - return; - - wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); - if (m_ticks.ticks.find(TICK_CODE{tick}) != m_ticks.ticks.end()) - icon = m_is_action_icon_focesed ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp(); - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = pt_beg.x - 0.5*m_tick_icon_dim : y_draw = pt_beg.y - 0.5*m_tick_icon_dim; - if (m_selection == ssLower) - is_horizontal() ? y_draw = pt_end.y + 3 : x_draw = pt_beg.x - m_tick_icon_dim-2; - else - is_horizontal() ? y_draw = pt_beg.y - m_tick_icon_dim-2 : x_draw = pt_end.x + 3; - - dc.DrawBitmap(*icon, x_draw, y_draw); - - //update rect of the tick action icon - m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim); -} - -void DoubleSlider::draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, const SelectedSlider selection) -{ - if (m_selection == selection) { - //draw info line - dc.SetPen(DARK_ORANGE_PEN); - const wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x, pos.y - m_thumb_size.y) : wxPoint(pos.x - m_thumb_size.x, pos.y/* - 1*/); - const wxPoint pt_end = is_horizontal() ? wxPoint(pos.x, pos.y + m_thumb_size.y) : wxPoint(pos.x + m_thumb_size.x, pos.y/* - 1*/); - dc.DrawLine(pt_beg, pt_end); - - //draw action icon - if (m_is_enabled_tick_manipulation) - draw_action_icon(dc, pt_beg, pt_end); - } -} - -wxString DoubleSlider::get_label(const SelectedSlider& selection) const -{ - const int value = selection == ssLower ? m_lower_value : m_higher_value; - - if (m_label_koef == 1.0 && m_values.empty()) - return wxString::Format("%d", value); - if (value >= m_values.size()) - return "ErrVal"; - - const wxString str = m_values.empty() ? - wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) : - wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None); - return wxString::Format("%s\n(%d)", str, m_values.empty() ? value : value+1); -} - -void DoubleSlider::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const -{ - if ( selection == ssUndef || - ((m_is_one_layer || m_higher_value==m_lower_value) && selection != m_selection) ) - return; - wxCoord text_width, text_height; - const wxString label = get_label(selection); - dc.GetMultiLineTextExtent(label, &text_width, &text_height); - wxPoint text_pos; - if (selection ==ssLower) - text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x) : - wxPoint(pos.x + m_thumb_size.x+1, pos.y - 0.5*text_height - 1); - else - text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x - text_height) : - wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5*text_height + 1); - dc.DrawText(label, text_pos); -} - -void DoubleSlider::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) -{ - wxCoord x_draw, y_draw; - if (selection == ssLower) { - if (is_horizontal()) { - x_draw = pos.x - m_thumb_size.x; - y_draw = pos.y - int(0.5*m_thumb_size.y); - } - else { - x_draw = pos.x - int(0.5*m_thumb_size.x); - y_draw = pos.y - int(0.5*m_thumb_size.y); - } - } - else{ - if (is_horizontal()) { - x_draw = pos.x; - y_draw = pos.y - int(0.5*m_thumb_size.y); - } - else { - x_draw = pos.x - int(0.5*m_thumb_size.x); - y_draw = pos.y - int(0.5*m_thumb_size.y); - } - } - dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower.bmp() : m_bmp_thumb_higher.bmp(), x_draw, y_draw); - - // Update thumb rect - update_thumb_rect(x_draw, y_draw, selection); -} - -void DoubleSlider::draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection) -{ - //calculate thumb position on slider line - int width, height; - get_size(&width, &height); - const wxPoint pos = is_horizontal() ? wxPoint(pos_coord, height*0.5) : wxPoint(0.5*width, pos_coord); - - // Draw thumb - draw_thumb_item(dc, pos, selection); - - // Draw info_line - draw_info_line_with_icon(dc, pos, selection); - - // Draw thumb text - draw_thumb_text(dc, pos, selection); -} - -void DoubleSlider::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos) -{ - //calculate thumb position on slider line - int width, height; - get_size(&width, &height); - const wxPoint pos_l = is_horizontal() ? wxPoint(lower_pos, height*0.5) : wxPoint(0.5*width, lower_pos); - const wxPoint pos_h = is_horizontal() ? wxPoint(higher_pos, height*0.5) : wxPoint(0.5*width, higher_pos); - - // Draw lower thumb - draw_thumb_item(dc, pos_l, ssLower); - // Draw lower info_line - draw_info_line_with_icon(dc, pos_l, ssLower); - - // Draw higher thumb - draw_thumb_item(dc, pos_h, ssHigher); - // Draw higher info_line - draw_info_line_with_icon(dc, pos_h, ssHigher); - // Draw higher thumb text - draw_thumb_text(dc, pos_h, ssHigher); - - // Draw lower thumb text - draw_thumb_text(dc, pos_l, ssLower); -} - -void DoubleSlider::draw_ticks(wxDC& dc) -{ - if (!m_is_enabled_tick_manipulation) - return; - - dc.SetPen(m_is_enabled_tick_manipulation ? DARK_GREY_PEN : LIGHT_GREY_PEN ); - int height, width; - get_size(&width, &height); - const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width; - for (auto tick : m_ticks.ticks) - { - const wxCoord pos = get_position_from_value(tick.tick); - - is_horizontal() ? dc.DrawLine(pos, mid-14, pos, mid-9) : - dc.DrawLine(mid - 14, pos/* - 1*/, mid - 9, pos/* - 1*/); - is_horizontal() ? dc.DrawLine(pos, mid+14, pos, mid+9) : - dc.DrawLine(mid + 14, pos/* - 1*/, mid + 9, pos/* - 1*/); - - wxBitmap icon = wxNullBitmap; - - // Draw icon for "Pause print" or "Custom Gcode" - if (tick.gcode != Slic3r::ColorChangeCode && tick.gcode != Slic3r::ToolChangeCode) - icon = create_scaled_bitmap(this, tick.gcode == Slic3r::PausePrintCode ? "pause_print" : "edit_gcode"); - else - { - if ((tick.gcode == Slic3r::ColorChangeCode && ( - (m_ticks.mode == t_mode::SingleExtruder && m_mode == t_mode::MultiExtruder ) || - (m_ticks.mode == t_mode::MultiExtruder && m_mode == t_mode::SingleExtruder) )) || - (tick.gcode == Slic3r::ToolChangeCode && - (m_ticks.mode == t_mode::MultiAsSingle && m_mode != t_mode::MultiAsSingle ) )) - icon = create_scaled_bitmap(this, "error_tick"); - } - - if (!icon.IsNull()) - { - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = pos - 0.5 * m_tick_icon_dim : y_draw = pos - 0.5 * m_tick_icon_dim; - is_horizontal() ? y_draw = mid + 22 : x_draw = mid + m_thumb_size.x + 3; - - dc.DrawBitmap(icon, x_draw, y_draw); - } - } -} - -std::string DoubleSlider::get_color_for_tool_change_tick(std::set::const_iterator it) const -{ - const int current_extruder = it->extruder == 0 ? std::max(m_only_extruder, 1) : it->extruder; - - auto it_n = it; - while (it_n != m_ticks.ticks.begin()) { - --it_n; - if (it_n->gcode == Slic3r::ColorChangeCode && it_n->extruder == current_extruder) - return it_n->color; - } - - return it->color; -} - -std::string DoubleSlider::get_color_for_color_change_tick(std::set::const_iterator it) const -{ - const int def_extruder = std::max(1, m_only_extruder); - auto it_n = it; - bool is_tool_change = false; - while (it_n != m_ticks.ticks.begin()) { - --it_n; - if (it_n->gcode == Slic3r::ToolChangeCode) { - is_tool_change = true; - if (it_n->extruder == it->extruder) - return it->color; - break; - } - } - if (!is_tool_change && it->extruder == def_extruder) - return it->color; - - return ""; -} - -void DoubleSlider::draw_colored_band(wxDC& dc) -{ - if (!m_is_enabled_tick_manipulation) - return; - - int height, width; - get_size(&width, &height); - - const wxCoord mid = is_horizontal() ? 0.5 * height : 0.5 * width; - - wxRect main_band = is_horizontal() ? - wxRect(SLIDER_MARGIN, lround(mid - 0.375 * m_thumb_size.y), - width - 2 * SLIDER_MARGIN + 1, lround(0.75 * m_thumb_size.y)) : - wxRect(lround(mid - 0.375 * m_thumb_size.x), SLIDER_MARGIN, - lround(0.75 * m_thumb_size.x), height - 2 * SLIDER_MARGIN + 1); - - auto draw_band = [](wxDC& dc, const wxColour& clr, const wxRect& band_rc) { - dc.SetPen(clr); - dc.SetBrush(clr); - dc.DrawRectangle(band_rc); - }; - - // don't color a band for MultiExtruder mode - if (m_ticks.empty() || m_mode == t_mode::MultiExtruder) - { - draw_band(dc, GetParent()->GetBackgroundColour(), main_band); - return; - } - - const int default_color_idx = m_mode==t_mode::MultiAsSingle ? std::max(m_only_extruder - 1, 0) : 0; - draw_band(dc, wxColour(Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config()[default_color_idx]), main_band); - - std::set::const_iterator tick_it = m_ticks.ticks.begin(); - - while (tick_it != m_ticks.ticks.end()) - { - if ( (m_mode == t_mode::SingleExtruder && tick_it->gcode == Slic3r::ColorChangeCode ) || - (m_mode == t_mode::MultiAsSingle && (tick_it->gcode == Slic3r::ToolChangeCode || tick_it->gcode == Slic3r::ColorChangeCode)) ) - { - const wxCoord pos = get_position_from_value(tick_it->tick); - is_horizontal() ? main_band.SetLeft(SLIDER_MARGIN + pos) : - main_band.SetBottom(pos - 1); - - const std::string clr_str = m_mode == t_mode::SingleExtruder ? tick_it->color : - tick_it->gcode == Slic3r::ToolChangeCode ? - get_color_for_tool_change_tick(tick_it) : - get_color_for_color_change_tick(tick_it); - - if (!clr_str.empty()) - draw_band(dc, wxColour(clr_str), main_band); - } - ++tick_it; - } -} - -void DoubleSlider::draw_one_layer_icon(wxDC& dc) -{ - const wxBitmap& icon = m_is_one_layer ? - m_is_one_layer_icon_focesed ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() : - m_is_one_layer_icon_focesed ? m_bmp_one_layer_unlock_off.bmp() : m_bmp_one_layer_unlock_on.bmp(); - - int width, height; - get_size(&width, &height); - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = width-2 : x_draw = 0.5*width - 0.5*m_lock_icon_dim; - is_horizontal() ? y_draw = 0.5*height - 0.5*m_lock_icon_dim : y_draw = height-2; - - dc.DrawBitmap(icon, x_draw, y_draw); - - //update rect of the lock/unlock icon - m_rect_one_layer_icon = wxRect(x_draw, y_draw, m_lock_icon_dim, m_lock_icon_dim); -} - -void DoubleSlider::draw_revert_icon(wxDC& dc) -{ - if (m_ticks.empty() || !m_is_enabled_tick_manipulation) - return; - - int width, height; - get_size(&width, &height); - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = width-2 : x_draw = 0.25*SLIDER_MARGIN; - is_horizontal() ? y_draw = 0.25*SLIDER_MARGIN: y_draw = height-2; - - dc.DrawBitmap(m_bmp_revert.bmp(), x_draw, y_draw); - - //update rect of the lock/unlock icon - m_rect_revert_icon = wxRect(x_draw, y_draw, m_revert_icon_dim, m_revert_icon_dim); -} - -void DoubleSlider::draw_cog_icon(wxDC& dc) -{ - if (m_mode != t_mode::MultiExtruder) - return; - - int width, height; - get_size(&width, &height); - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = width-2 : x_draw = width - m_cog_icon_dim - 2; - is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height-2; - - dc.DrawBitmap(m_bmp_cog.bmp(), x_draw, y_draw); - - //update rect of the lock/unlock icon - m_rect_cog_icon = wxRect(x_draw, y_draw, m_cog_icon_dim, m_cog_icon_dim); -} - -void DoubleSlider::update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection) -{ - const wxRect& rect = wxRect(begin_x, begin_y, m_thumb_size.x, int(m_thumb_size.y*0.5)); - if (selection == ssLower) - m_rect_lower_thumb = rect; - else - m_rect_higher_thumb = rect; -} - -int DoubleSlider::get_value_from_position(const wxCoord x, const wxCoord y) -{ - const int height = get_size().y; - const double step = get_scroll_step(); - - if (is_horizontal()) - return int(double(x - SLIDER_MARGIN) / step + 0.5); - - return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5); -} - -void DoubleSlider::detect_selected_slider(const wxPoint& pt) -{ - m_selection = is_point_in_rect(pt, m_rect_lower_thumb) ? ssLower : - is_point_in_rect(pt, m_rect_higher_thumb) ? ssHigher : ssUndef; -} - -bool DoubleSlider::is_point_in_rect(const wxPoint& pt, const wxRect& rect) -{ - return rect.GetLeft() <= pt.x && pt.x <= rect.GetRight() && - rect.GetTop() <= pt.y && pt.y <= rect.GetBottom(); -} - -int DoubleSlider::is_point_near_tick(const wxPoint& pt) -{ - for (auto tick : m_ticks.ticks) { - const wxCoord pos = get_position_from_value(tick.tick); - - if (is_horizontal()) { - if (pos - 4 <= pt.x && pt.x <= pos + 4) - return tick.tick; - } - else { - if (pos - 4 <= pt.y && pt.y <= pos + 4) - return tick.tick; - } - } - return -1; -} - -void DoubleSlider::ChangeOneLayerLock() -{ - m_is_one_layer = !m_is_one_layer; - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - if (!m_selection) m_selection = ssHigher; - - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::OnLeftDown(wxMouseEvent& event) -{ - if (HasCapture()) - return; - this->CaptureMouse(); - - wxClientDC dc(this); - wxPoint pos = event.GetLogicalPosition(dc); - if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) - { - const auto it = m_ticks.ticks.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value }); - if (it == m_ticks.ticks.end()) - m_force_add_tick = true; - else - m_force_delete_tick = true; - return; - } - - m_is_left_down = true; - if (is_point_in_rect(pos, m_rect_one_layer_icon)) { - // switch on/off one layer mode - m_is_one_layer = !m_is_one_layer; - if (!m_is_one_layer) { - SetLowerValue(m_min_value); - SetHigherValue(m_max_value); - } - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - if (!m_selection) m_selection = ssHigher; - } - else if (is_point_in_rect(pos, m_rect_revert_icon) && m_is_enabled_tick_manipulation) { - // discard all color changes - SetLowerValue(m_min_value); - SetHigherValue(m_max_value); - - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - if (!m_selection) m_selection = ssHigher; - - m_ticks.ticks.clear(); - post_ticks_changed_event(); - } - else if (is_point_in_rect(pos, m_rect_cog_icon) && m_mode == t_mode::MultiExtruder) { - // show dialog for set extruder sequence - m_force_edit_extruder_sequence = true; - return; - } - else - detect_selected_slider(pos); - - if (!m_selection) { - const int tick_val = is_point_near_tick(pos); - /* Set current thumb position to the nearest tick (if it is) - * OR to a value corresponding to the mouse click - * */ - const int mouse_val = tick_val >= 0 && m_is_enabled_tick_manipulation ? tick_val : - get_value_from_position(pos.x, pos.y); - if (mouse_val >= 0) - { - // if (abs(mouse_val - m_lower_value) < abs(mouse_val - m_higher_value)) { - if ( mouse_val <= m_lower_value ) { - SetLowerValue(mouse_val); - correct_lower_value(); - m_selection = ssLower; - } - else { - SetHigherValue(mouse_val); - correct_higher_value(); - m_selection = ssHigher; - } - } - } - - Refresh(); - Update(); - event.Skip(); -} - -void DoubleSlider::correct_lower_value() -{ - if (m_lower_value < m_min_value) - m_lower_value = m_min_value; - else if (m_lower_value > m_max_value) - m_lower_value = m_max_value; - - if ((m_lower_value >= m_higher_value && m_lower_value <= m_max_value) || m_is_one_layer) - m_higher_value = m_lower_value; -} - -void DoubleSlider::correct_higher_value() -{ - if (m_higher_value > m_max_value) - m_higher_value = m_max_value; - else if (m_higher_value < m_min_value) - m_higher_value = m_min_value; - - if ((m_higher_value <= m_lower_value && m_higher_value >= m_min_value) || m_is_one_layer) - m_lower_value = m_higher_value; -} - -wxString DoubleSlider::get_tooltip(IconFocus icon_focus) -{ - wxString tooltip(wxEmptyString); - if (m_is_one_layer_icon_focesed) - tooltip = _(L("One layer mode")); - - if (icon_focus == ifRevert) - tooltip = _(L("Discard all custom changes")); - if (icon_focus == ifCog) - tooltip = _(L("Set extruder sequence for whole print")); - else if (m_is_action_icon_focesed) - { - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - const auto tick_code_it = m_ticks.ticks.find(TICK_CODE{tick}); - tooltip = tick_code_it == m_ticks.ticks.end() ? (m_mode == t_mode::MultiAsSingle ? - _(L("For add change extruder use left mouse button click")) : - _(L("For add color change use left mouse button click")) ) + "\n" + - _(L("For add another code use right mouse button click")) : - tick_code_it->gcode == Slic3r::ColorChangeCode ? ( m_mode == t_mode::SingleExtruder ? - _(L("For Delete color change use left mouse button click\n" - "For Edit color use right mouse button click")) : - from_u8((boost::format(_utf8(L("Delete color change for Extruder %1%"))) % tick_code_it->extruder).str()) ): - tick_code_it->gcode == Slic3r::PausePrintCode ? - _(L("Delete pause")) : - tick_code_it->gcode == Slic3r::ToolChangeCode ? - from_u8((boost::format(_utf8(L("Delete extruder change to \"%1%\""))) % tick_code_it->extruder).str()) : - from_u8((boost::format(_utf8(L("For Delete \"%1%\" code use left mouse button click\n" - "For Edit \"%1%\" code use right mouse button click"))) % tick_code_it->gcode ).str()); - } - - return tooltip; -} - -void DoubleSlider::OnMotion(wxMouseEvent& event) -{ - bool action = false; - - const wxClientDC dc(this); - const wxPoint pos = event.GetLogicalPosition(dc); - - m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon); - IconFocus icon_focus = ifNone; - - if (!m_is_left_down && !m_is_one_layer) { - m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action); - if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon)) - icon_focus = ifRevert; - else if (is_point_in_rect(pos, m_rect_cog_icon)) - icon_focus = ifCog; - } - else if (m_is_left_down || m_is_right_down) { - if (m_selection == ssLower) { - int current_value = m_lower_value; - m_lower_value = get_value_from_position(pos.x, pos.y); - correct_lower_value(); - action = (current_value != m_lower_value); - } - else if (m_selection == ssHigher) { - int current_value = m_higher_value; - m_higher_value = get_value_from_position(pos.x, pos.y); - correct_higher_value(); - action = (current_value != m_higher_value); - } - } - Refresh(); - Update(); - event.Skip(); - - // Set tooltips with information for each icon - this->SetToolTip(get_tooltip(icon_focus)); - - if (action) - { - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - e.SetString("moving"); - ProcessWindowEvent(e); - } -} - -void DoubleSlider::append_change_extruder_menu_item(wxMenu* menu) -{ - const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt(); - if (extruders_cnt > 1) - { - const int initial_extruder = std::max(1 , get_extruder_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value)); - - wxMenu* change_extruder_menu = new wxMenu(); - - for (int i = 1; i <= extruders_cnt; i++) - { - const bool is_active_extruder = i == initial_extruder; - const wxString item_name = wxString::Format(_(L("Extruder %d")), i) + - (is_active_extruder ? " (" + _(L("active")) + ")" : ""); - - if (m_mode == t_mode::MultiAsSingle) - append_menu_item(change_extruder_menu, wxID_ANY, item_name, "", - [this, i](wxCommandEvent&) { add_code_as_tick(Slic3r::ToolChangeCode, i); }, "", menu, - [is_active_extruder]() { return !is_active_extruder; }, Slic3r::GUI::wxGetApp().plater()); - } - - const wxString change_extruder_menu_name = m_mode == t_mode::MultiAsSingle ? _(L("Change extruder")) : _(L("Change extruder (N/A)")); - - wxMenuItem* change_extruder_menu_item = menu->AppendSubMenu(change_extruder_menu, change_extruder_menu_name, _(L("Use another extruder"))); - change_extruder_menu_item->SetBitmap(create_scaled_bitmap(this, "change_extruder")); - - Slic3r::GUI::wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this, change_extruder_menu_item](wxUpdateUIEvent& evt) { - enable_menu_item(evt, [this]() {return m_mode == t_mode::MultiAsSingle; }, change_extruder_menu_item, this); }, - change_extruder_menu_item->GetId()); - } -} - -void DoubleSlider::append_add_color_change_menu_item(wxMenu* menu) -{ - const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt(); - if (extruders_cnt > 1) - { - std::set used_extruders_for_tick = get_used_extruders_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); - - wxMenu* add_color_change_menu = new wxMenu(); - - for (int i = 1; i <= extruders_cnt; i++) - { - const bool is_used_extruder = used_extruders_for_tick.empty() ? true : // #ys_FIXME till used_extruders_for_tick doesn't filled correct for mmMultiExtruder - used_extruders_for_tick.find(i) != used_extruders_for_tick.end(); - const wxString item_name = wxString::Format(_(L("Extruder %d")), i) + - (is_used_extruder ? " (" + _(L("used")) + ")" : ""); - - append_menu_item(add_color_change_menu, wxID_ANY, item_name, "", - [this, i](wxCommandEvent&) { add_code_as_tick(Slic3r::ColorChangeCode, i); }, "", menu, - [is_used_extruder]() { return is_used_extruder; }, Slic3r::GUI::wxGetApp().plater()); - } - - const wxString menu_name = from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % Slic3r::ColorChangeCode).str()); - wxMenuItem* add_color_change_menu_item = menu->AppendSubMenu(add_color_change_menu, menu_name, ""); - add_color_change_menu_item->SetBitmap(create_scaled_bitmap(this, "colorchange_add_m")); - } -} - -void DoubleSlider::OnLeftUp(wxMouseEvent& event) -{ - if (!HasCapture()) - return; - this->ReleaseMouse(); - m_is_left_down = false; - - if (m_force_delete_tick) - { - delete_current_tick(); - m_force_delete_tick = false; - } - else - if (m_force_add_tick) - { - add_current_tick(); - m_force_add_tick = false; - } - else - if (m_force_edit_extruder_sequence) { - edit_extruder_sequence(); - m_force_edit_extruder_sequence = false; - } - - Refresh(); - Update(); - event.Skip(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::enter_window(wxMouseEvent& event, const bool enter) -{ - m_is_focused = enter; - Refresh(); - Update(); - event.Skip(); -} - -// "condition" have to be true for: -// - value increase (if wxSL_VERTICAL) -// - value decrease (if wxSL_HORIZONTAL) -void DoubleSlider::move_current_thumb(const bool condition) -{ -// m_is_one_layer = wxGetKeyState(WXK_CONTROL); - int delta = condition ? -1 : 1; - if (is_horizontal()) - delta *= -1; - - if (m_selection == ssLower) { - m_lower_value -= delta; - correct_lower_value(); - } - else if (m_selection == ssHigher) { - m_higher_value -= delta; - correct_higher_value(); - } - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::OnWheel(wxMouseEvent& event) -{ - // Set nearest to the mouse thumb as a selected, if there is not selected thumb - if (m_selection == ssUndef) - { - const wxPoint& pt = event.GetLogicalPosition(wxClientDC(this)); - - if (is_horizontal()) - m_selection = abs(pt.x - m_rect_lower_thumb.GetRight()) <= - abs(pt.x - m_rect_higher_thumb.GetLeft()) ? - ssLower : ssHigher; - else - m_selection = abs(pt.y - m_rect_lower_thumb.GetTop()) <= - abs(pt.y - m_rect_higher_thumb.GetBottom()) ? - ssLower : ssHigher; - } - - move_current_thumb(event.GetWheelRotation() > 0); -} - -void DoubleSlider::OnKeyDown(wxKeyEvent &event) -{ - const int key = event.GetKeyCode(); - if (key == WXK_NUMPAD_ADD) { - // OnChar() is called immediately after OnKeyDown(), which can cause call of add_tick() twice. - // To avoid this case we should suppress second add_tick() call. - m_ticks.suppress_plus(true); - add_current_tick(true); - } - else if (key == 390 || key == WXK_DELETE || key == WXK_BACK) { - // OnChar() is called immediately after OnKeyDown(), which can cause call of delete_tick() twice. - // To avoid this case we should suppress second delete_tick() call. - m_ticks.suppress_minus(true); - delete_current_tick(); - } - else if (is_horizontal()) - { - if (key == WXK_LEFT || key == WXK_RIGHT) - move_current_thumb(key == WXK_LEFT); - else if (key == WXK_UP || key == WXK_DOWN) { - m_selection = key == WXK_UP ? ssHigher : ssLower; - Refresh(); - } - } - else { - if (key == WXK_LEFT || key == WXK_RIGHT) { - m_selection = key == WXK_LEFT ? ssHigher : ssLower; - Refresh(); - } - else if (key == WXK_UP || key == WXK_DOWN) - move_current_thumb(key == WXK_UP); - } - - event.Skip(); // !Needed to have EVT_CHAR generated as well -} - -void DoubleSlider::OnKeyUp(wxKeyEvent &event) -{ - if (event.GetKeyCode() == WXK_CONTROL) - m_is_one_layer = false; - Refresh(); - Update(); - event.Skip(); -} - -void DoubleSlider::OnChar(wxKeyEvent& event) -{ - const int key = event.GetKeyCode(); - if (key == '+' && !m_ticks.suppressed_plus()) { - add_current_tick(true); - m_ticks.suppress_plus(false); - } - else if (key == '-' && !m_ticks.suppressed_minus()) { - delete_current_tick(); - m_ticks.suppress_minus(false); - } -} - -void DoubleSlider::OnRightDown(wxMouseEvent& event) -{ - if (HasCapture()) return; - this->CaptureMouse(); - - const wxClientDC dc(this); - - wxPoint pos = event.GetLogicalPosition(dc); - if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) - { - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - // if on this Z doesn't exist tick - auto it = m_ticks.ticks.find(TICK_CODE{ tick }); - if (it == m_ticks.ticks.end()) - { - // show context menu on OnRightUp() - m_show_context_menu = true; - return; - } - if (it->gcode != Slic3r::ToolChangeCode) - { - // show "Edit" and "Delete" menu on OnRightUp() - m_show_edit_menu = true; - return; - } - } - - detect_selected_slider(event.GetLogicalPosition(dc)); - if (!m_selection) - return; - - if (m_selection == ssLower) - m_higher_value = m_lower_value; - else - m_lower_value = m_higher_value; - - // set slider to "one layer" mode - m_is_right_down = m_is_one_layer = true; - - Refresh(); - Update(); - event.Skip(); -} - -int DoubleSlider::get_extruder_for_tick(int tick) -{ - int default_initial_extruder = m_mode == t_mode::MultiAsSingle ? m_only_extruder : 0; - if (m_ticks.empty()) - return default_initial_extruder; - - auto it = m_ticks.ticks.lower_bound(TICK_CODE{tick}); - while (it != m_ticks.ticks.begin()) { - --it; - if(it->gcode == Slic3r::ToolChangeCode) - return it->extruder; - } - - return default_initial_extruder; -} - -std::set DoubleSlider::get_used_extruders_for_tick(int tick) -{ - if (m_mode == t_mode::MultiExtruder) - { - // #ys_FIXME: get tool ordering from _correct_ place - const Slic3r::ToolOrdering& tool_ordering = Slic3r::GUI::wxGetApp().plater()->fff_print().get_tool_ordering(); - - if (tool_ordering.empty()) - return {}; - - std::set used_extruders; - - auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), Slic3r::LayerTools(m_values[tick])); - for (; it_layer_tools != tool_ordering.end(); it_layer_tools++) - { - const std::vector& extruders = it_layer_tools->extruders; - for (const auto& extruder : extruders) - used_extruders.emplace(extruder+1); - } - - return used_extruders; - } - - const int default_initial_extruder = m_mode == t_mode::MultiAsSingle ? std::max(m_only_extruder, 1) : 1; - if (m_ticks.empty()) - return {default_initial_extruder}; - - std::set used_extruders; - const std::set& ticks = m_ticks.ticks; - - auto it_start = ticks.lower_bound(TICK_CODE{tick}); - auto it = it_start; - if (it == ticks.begin() && it->gcode == Slic3r::ToolChangeCode) { - used_extruders.emplace(it->extruder); - if (tick < it->tick) - used_extruders.emplace(default_initial_extruder); - } - - while (it != ticks.begin()) { - --it; - if(it->gcode == Slic3r::ToolChangeCode) - { - used_extruders.emplace(it->extruder); - break; - } - } - - if (it == ticks.begin() && used_extruders.empty()) - used_extruders.emplace(default_initial_extruder); - - it = it_start; - while (it != ticks.end()) { - if(it->gcode == Slic3r::ToolChangeCode) - used_extruders.emplace(it->extruder); - ++it; - } - - return used_extruders; -} - -void DoubleSlider::OnRightUp(wxMouseEvent& event) -{ - if (!HasCapture()) - return; - this->ReleaseMouse(); - m_is_right_down = m_is_one_layer = false; - - if (m_show_context_menu) { - wxMenu menu; - - if (m_mode == t_mode::SingleExtruder) - append_menu_item(&menu, wxID_ANY, _(L("Add color change")) + " (M600)", "", - [this](wxCommandEvent&) { add_code_as_tick(Slic3r::ColorChangeCode); }, "colorchange_add_m", &menu, - [](){return true;}, this); - else - { - append_change_extruder_menu_item(&menu); - append_add_color_change_menu_item(&menu); - } - - append_menu_item(&menu, wxID_ANY, _(L("Add pause print")) + " (M601)", "", - [this](wxCommandEvent&) { add_code_as_tick(Slic3r::PausePrintCode); }, "pause_print", &menu, - []() {return true; }, this); - - append_menu_item(&menu, wxID_ANY, _(L("Add custom G-code")), "", - [this](wxCommandEvent&) { add_code_as_tick(""); }, "edit_gcode", &menu, - []() {return true; }, this); - - Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu); - - m_show_context_menu = false; - } - else if (m_show_edit_menu) { - wxMenu menu; - - std::set::iterator it = m_ticks.ticks.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value }); - const bool is_color_change = it->gcode == Slic3r::ColorChangeCode; - - append_menu_item(&menu, wxID_ANY, it->gcode == Slic3r::ColorChangeCode ? _(L("Edit color")) : - it->gcode == Slic3r::PausePrintCode ? _(L("Edit pause print message")) : - _(L("Edit custom G-code")), "", - [this](wxCommandEvent&) { edit_tick(); }, "edit_uni", &menu); - - append_menu_item(&menu, wxID_ANY, it->gcode == Slic3r::ColorChangeCode ? _(L("Delete color change")) : - it->gcode == Slic3r::PausePrintCode ? _(L("Delete pause print")) : - _(L("Delete custom G-code")), "", - [this](wxCommandEvent&) { delete_current_tick();}, "colorchange_del_f", &menu); - - Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu); - - m_show_edit_menu = false; - } - - Refresh(); - Update(); - event.Skip(); -} - -static std::string get_new_color(const std::string& color) -{ - wxColour clr(color); - if (!clr.IsOk()) - clr = wxColour(0, 0, 0); // Don't set alfa to transparence - - auto data = new wxColourData(); - data->SetChooseFull(1); - data->SetColour(clr); - - wxColourDialog dialog(nullptr, data); - dialog.CenterOnParent(); - if (dialog.ShowModal() == wxID_OK) - return dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); - return ""; -} - -static std::string get_custom_code(const std::string& code_in, double height) -{ - wxString msg_text = from_u8(_utf8(L("Enter custom G-code used on current layer"))) + ":"; - wxString msg_header = from_u8((boost::format(_utf8(L("Custom Gcode on current layer (%1% mm)."))) % height).str()); - - // get custom gcode - wxTextEntryDialog dlg(nullptr, msg_text, msg_header, code_in, - wxTextEntryDialogStyle | wxTE_MULTILINE); - if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) - return ""; - - return dlg.GetValue().ToStdString(); -} - -static std::string get_pause_print_msg(const std::string& msg_in, double height) -{ - wxString msg_text = from_u8(_utf8(L("Enter short message shown on Printer display during pause print"))) + ":"; - wxString msg_header = from_u8((boost::format(_utf8(L("Message for pause print on current layer (%1% mm)."))) % height).str()); - - // get custom gcode - wxTextEntryDialog dlg(nullptr, msg_text, msg_header, from_u8(msg_in), - wxTextEntryDialogStyle); - if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) - return ""; - - return into_u8(dlg.GetValue()); -} - -void DoubleSlider::add_code_as_tick(std::string code, int selected_extruder/* = -1*/) -{ - if (m_selection == ssUndef) - return; - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - - if (m_ticks.ticks.find(TICK_CODE{ tick }) != m_ticks.ticks.end() || // if on this Z doesn't exist tick - !check_ticks_changed_event(code)) - return; - - const int extruder = selected_extruder > 0 ? selected_extruder : std::max(1, m_only_extruder); - - if (!m_ticks.add_tick(tick, code, extruder, m_values[tick])) - return; - - post_ticks_changed_event(code); -} - -void DoubleSlider::add_current_tick(bool call_from_keyboard /*= false*/) -{ - if (m_selection == ssUndef) - return; - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - auto it = m_ticks.ticks.find(TICK_CODE{ tick }); - - if (it != m_ticks.ticks.end() || // this tick is already exist - !check_ticks_changed_event(m_mode == t_mode::MultiAsSingle ? Slic3r::ToolChangeCode : Slic3r::ColorChangeCode)) - return; - - if (m_mode == t_mode::SingleExtruder) - add_code_as_tick(Slic3r::ColorChangeCode); - else - { - wxMenu menu; - - if (m_mode == t_mode::MultiAsSingle) - append_change_extruder_menu_item(&menu); - else - append_add_color_change_menu_item(&menu); - - wxPoint pos = wxDefaultPosition; - if (call_from_keyboard) - { - int width, height; - get_size(&width, &height); - - const wxCoord coord = 0.75 * (is_horizontal() ? height : width); - this->GetPosition(&width, &height); - - pos = is_horizontal() ? - wxPoint(get_position_from_value(tick), height + coord) : - wxPoint(width + coord, get_position_from_value(tick)); - } - - Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu, pos); - } -} - -void DoubleSlider::delete_current_tick() -{ - if (m_selection == ssUndef) - return; - auto it = m_ticks.ticks.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value }); - - if (it != m_ticks.ticks.end()) - { - if (!check_ticks_changed_event(it->gcode)) - return; - - const std::string code = it->gcode; - m_ticks.ticks.erase(it); - post_ticks_changed_event(code); - } -} - -void DoubleSlider::edit_tick() -{ - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - const std::set::iterator it = m_ticks.ticks.find(TICK_CODE{ tick }); - - if (it == m_ticks.ticks.end() || // if on this Z exists tick - !check_ticks_changed_event(it->gcode)) - return; - - const std::string code = it->gcode; - if (m_ticks.edit_tick(it, m_values[it->tick])) - post_ticks_changed_event(code); -} - -void DoubleSlider::edit_extruder_sequence() -{ - if (!check_ticks_changed_event(Slic3r::ToolChangeCode)) - return; - - Slic3r::GUI::ExtruderSequenceDialog dlg(m_extruders_sequence); - if (dlg.ShowModal() != wxID_OK) - return; - - const ExtrudersSequence& from_dlg_val = dlg.GetValue(); - if (m_extruders_sequence == from_dlg_val) - return; - - m_extruders_sequence = from_dlg_val; - - m_ticks.erase_all_ticks_with_code(Slic3r::ToolChangeCode); - - int tick = 0; - double value = 0.0; - int extruder = 0; - const int extr_cnt = m_extruders_sequence.extruders.size(); - - std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); - - while (tick <= m_max_value) - { - const int cur_extruder = m_extruders_sequence.extruders[extruder]; - m_ticks.ticks.emplace(TICK_CODE{tick, Slic3r::ToolChangeCode, cur_extruder + 1, colors[cur_extruder]}); - - extruder++; - if (extruder == extr_cnt) - extruder = 0; - if (m_extruders_sequence.is_mm_intervals) - { - value += m_extruders_sequence.interval_by_mm; - auto val_it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon()); - - if (val_it == m_values.end()) - break; - - tick = val_it - m_values.begin(); - } - else - tick += m_extruders_sequence.interval_by_layers; - } - - post_ticks_changed_event(Slic3r::ToolChangeCode); -} - -void DoubleSlider::post_ticks_changed_event(const std::string& gcode /*= ""*/) -{ - m_force_mode_apply = (gcode.empty() || gcode == Slic3r::ColorChangeCode || gcode == Slic3r::ToolChangeCode); - - wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); -} - -bool DoubleSlider::check_ticks_changed_event(const std::string& gcode) -{ - if ( m_ticks.mode == m_mode || - (gcode != Slic3r::ColorChangeCode && gcode != Slic3r::ToolChangeCode) || - (m_ticks.mode == t_mode::SingleExtruder && m_mode == t_mode::MultiAsSingle) || // All ColorChanges will be applied for 1st extruder - (m_ticks.mode == t_mode::MultiExtruder && m_mode == t_mode::MultiAsSingle) ) // Just mark ColorChanges for all unused extruders - return true; - - if ((m_ticks.mode == t_mode::SingleExtruder && m_mode == t_mode::MultiExtruder ) || - (m_ticks.mode == t_mode::MultiExtruder && m_mode == t_mode::SingleExtruder) ) - { - if (!m_ticks.has_tick_with_code(Slic3r::ColorChangeCode)) - return true; - - wxString message = (m_ticks.mode == t_mode::SingleExtruder ? - _(L("The last color change data was saved for a single extruder printer profile.")) : - _(L("The last color change data was saved for a multiple extruder printer profile.")) - ) + "\n" + - _(L("Your current changes will cause a deletion of all saved color changes.")) + "\n\n\t" + - _(L("Are you sure you want to continue?")); - - wxMessageDialog msg(this, message, _(L("Notice")), wxYES_NO); - if (msg.ShowModal() == wxID_YES) { - m_ticks.erase_all_ticks_with_code(Slic3r::ColorChangeCode); - post_ticks_changed_event(Slic3r::ColorChangeCode); - } - return false; - } - // m_ticks_mode == t_mode::MultiAsSingle - if( m_ticks.has_tick_with_code(Slic3r::ToolChangeCode) ) - { - wxString message = m_mode == t_mode::SingleExtruder ? ( - _(L("The last color change data was saved for a multi extruder printing.")) + "\n\n" + - _(L("Select YES if you want to delete all saved tool changes, \n\t" - "NO if you want all tool changes switch to color changes, \n\t" - "or CANCEL for do nothing")) + "\n\n\t" + - _(L("Do you want to delete all saved tool changes?")) - ) : ( // t_mode::MultiExtruder - _(L("The last color change data was saved for a multi extruder printing with tool changes for whole print.")) + "\n\n" + - _(L("Your current changes will cause a deletion of all saved tool changes.")) + "\n\n\t" + - _(L("Are you sure you want to continue?")) ) ; - - wxMessageDialog msg(this, message, _(L("Notice")), wxYES_NO | (m_mode == t_mode::SingleExtruder ? wxCANCEL : 0)); - const int answer = msg.ShowModal(); - if (answer == wxID_YES) { - m_ticks.erase_all_ticks_with_code(Slic3r::ToolChangeCode); - post_ticks_changed_event(Slic3r::ToolChangeCode); - } - else if (m_mode == t_mode::SingleExtruder && answer == wxID_NO) { - m_ticks.switch_code(Slic3r::ToolChangeCode, Slic3r::ColorChangeCode); - post_ticks_changed_event(Slic3r::ColorChangeCode); - } - return false; - } - - return true; -} - -bool DoubleSlider::TICK_CODE_INFO::add_tick(const int tick, std::string& code, const int extruder, double print_z) -{ - std::string color; - if (code.empty()) // custom Gcode - { - code = get_custom_code(custom_gcode, print_z); - if (code.empty()) - return false; - custom_gcode = code; - } - else if (code == Slic3r::PausePrintCode) - { - /* PausePrintCode doesn't need a color, so - * this field is used for save a short message shown on Printer display - * */ - color = get_pause_print_msg(pause_print_msg, print_z); - if (color.empty()) - return false; - pause_print_msg = color; - } - else - { - std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); - color = colors[extruder - 1]; - - if (code == Slic3r::ColorChangeCode) - { - if (!ticks.empty()) - { - auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), TICK_CODE{ tick }); - while (before_tick_it != ticks.begin()) { - --before_tick_it; - if (before_tick_it->gcode == Slic3r::ColorChangeCode && before_tick_it->extruder == extruder) { - color = before_tick_it->color; - break; - } - } - } - - color = get_new_color(color); - if (color.empty()) - return false; - } - } - - ticks.emplace(TICK_CODE{ tick, code, extruder, color }); - return true; -} - -bool DoubleSlider::TICK_CODE_INFO::edit_tick(std::set::iterator it, double print_z) -{ - std::string edited_value; - if (it->gcode == Slic3r::ColorChangeCode) - edited_value = get_new_color(it->color); - else if (it->gcode == Slic3r::PausePrintCode) - edited_value = get_pause_print_msg(it->color, print_z); - else - edited_value = get_custom_code(it->gcode, print_z); - - if (edited_value.empty()) - return false; - - TICK_CODE changed_tick = *it; - if (it->gcode == Slic3r::ColorChangeCode || it->gcode == Slic3r::PausePrintCode) { - if (it->color == edited_value) - return false; - changed_tick.color = edited_value; - } - else { - if (it->gcode == edited_value) - return false; - changed_tick.gcode = edited_value; - } - - ticks.erase(it); - ticks.emplace(changed_tick); - - return true; -} - -void DoubleSlider::TICK_CODE_INFO::switch_code(const std::string& code_from, const std::string& code_to) -{ - for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) - if (it->gcode == code_from) - { - TICK_CODE tick = *it; - tick.gcode = code_to; - tick.extruder = 1; - ticks.erase(it); - it = ticks.emplace(tick).first; - } - else - ++it; -} - -void DoubleSlider::TICK_CODE_INFO::erase_all_ticks_with_code(const std::string& gcode) -{ - for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) { - if (it->gcode == gcode) - it = ticks.erase(it); - else - ++it; - } -} - -bool DoubleSlider::TICK_CODE_INFO::has_tick_with_code(const std::string& gcode) -{ - for (const TICK_CODE& tick : ticks) - if (tick.gcode == gcode) - return true; - - return false; -} - - // ---------------------------------------------------------------------------- // LockButton // ---------------------------------------------------------------------------- @@ -4032,6 +685,20 @@ ModeButton::ModeButton( wxWindow * parent, const wxSize& size /* = wxDefaultSize*/, const wxPoint& pos /* = wxDefaultPosition*/) : ScalableButton(parent, id, icon_name, mode, size, pos, wxBU_EXACTFIT) +{ + Init(mode); +} + +ModeButton::ModeButton( wxWindow* parent, + const wxString& mode/* = wxEmptyString*/, + const std::string& icon_name/* = ""*/, + int px_cnt/* = 16*/) : + ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, icon_name, px_cnt), mode, wxBU_EXACTFIT) +{ + Init(mode); +} + +void ModeButton::Init(const wxString &mode) { m_tt_focused = wxString::Format(_(L("Switch to the %s mode")), mode); m_tt_selected = wxString::Format(_(L("Current mode is %s")), mode); @@ -4083,9 +750,9 @@ ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 0*/) : SetFlexibleDirection(wxHORIZONTAL); std::vector < std::pair < wxString, std::string >> buttons = { - {_(L("Simple")), "mode_simple_sq.png"}, - {_(L("Advanced")), "mode_advanced_sq.png"}, - {_(L("Expert")), "mode_expert_sq.png"} + {_(L("Simple")), "mode_simple"}, + {_(L("Advanced")), "mode_advanced"}, + {_(L("Expert")), "mode_expert"}, }; auto modebtnfn = [](wxCommandEvent &event, int mode_id) { @@ -4095,7 +762,7 @@ ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 0*/) : m_mode_btns.reserve(3); for (const auto& button : buttons) { - m_mode_btns.push_back(new ModeButton(parent, wxID_ANY, button.second, button.first)); + m_mode_btns.push_back(new ModeButton(parent, button.first, button.second, mode_icon_px_size())); m_mode_btns.back()->Bind(wxEVT_BUTTON, std::bind(modebtnfn, std::placeholders::_1, int(m_mode_btns.size() - 1))); Add(m_mode_btns.back()); @@ -4147,18 +814,44 @@ void MenuWithSeparators::SetSecondSeparator() // ---------------------------------------------------------------------------- ScalableBitmap::ScalableBitmap( wxWindow *parent, const std::string& icon_name/* = ""*/, - const int px_cnt/* = 16*/, - const bool is_horizontal/* = false*/): + const int px_cnt/* = 16*/): m_parent(parent), m_icon_name(icon_name), - m_px_cnt(px_cnt), m_is_horizontal(is_horizontal) + m_px_cnt(px_cnt) { - m_bmp = create_scaled_bitmap(parent, icon_name, px_cnt, is_horizontal); + m_bmp = create_scaled_bitmap(icon_name, parent, px_cnt); +} + +wxSize ScalableBitmap::GetBmpSize() const +{ +#ifdef __APPLE__ + return m_bmp.GetScaledSize(); +#else + return m_bmp.GetSize(); +#endif +} + +int ScalableBitmap::GetBmpWidth() const +{ +#ifdef __APPLE__ + return m_bmp.GetScaledWidth(); +#else + return m_bmp.GetWidth(); +#endif +} + +int ScalableBitmap::GetBmpHeight() const +{ +#ifdef __APPLE__ + return m_bmp.GetScaledHeight(); +#else + return m_bmp.GetHeight(); +#endif } void ScalableBitmap::msw_rescale() { - m_bmp = create_scaled_bitmap(m_parent, m_icon_name, m_px_cnt, m_is_horizontal); + m_bmp = create_scaled_bitmap(m_icon_name, m_parent, m_px_cnt); } // ---------------------------------------------------------------------------- @@ -4181,7 +874,7 @@ ScalableButton::ScalableButton( wxWindow * parent, SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); #endif // __WXMSW__ - SetBitmap(create_scaled_bitmap(parent, icon_name)); + SetBitmap(create_scaled_bitmap(icon_name, parent)); if (size != wxDefaultSize) { @@ -4199,8 +892,7 @@ ScalableButton::ScalableButton( wxWindow * parent, long style /*= wxBU_EXACTFIT | wxNO_BORDER*/) : m_parent(parent), m_current_icon_name(bitmap.name()), - m_px_cnt(bitmap.px_cnt()), - m_is_horizontal(bitmap.is_horizontal()) + m_px_cnt(bitmap.px_cnt()) { Create(parent, id, label, wxDefaultPosition, wxDefaultSize, style); #ifdef __WXMSW__ @@ -4225,15 +917,18 @@ void ScalableButton::SetBitmapDisabled_(const ScalableBitmap& bmp) int ScalableButton::GetBitmapHeight() { - const float scale_factor = get_svg_scale_factor(m_parent); - return int((float)GetBitmap().GetHeight() / scale_factor); +#ifdef __APPLE__ + return GetBitmap().GetScaledHeight(); +#else + return GetBitmap().GetHeight(); +#endif } void ScalableButton::msw_rescale() { - SetBitmap(create_scaled_bitmap(m_parent, m_current_icon_name, m_px_cnt, m_is_horizontal)); + SetBitmap(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt)); if (!m_disabled_icon_name.empty()) - SetBitmapDisabled(create_scaled_bitmap(m_parent, m_disabled_icon_name, m_px_cnt, m_is_horizontal)); + SetBitmapDisabled(create_scaled_bitmap(m_disabled_icon_name, m_parent, m_px_cnt)); if (m_width > 0 || m_height>0) { diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 8cae495ef3..38d726ad39 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -4,27 +4,14 @@ #include #include #include -#include -#include -#include #include #include -#include #include -#include +#include #include -#include #include -#include "libslic3r/Model.hpp" -#include "libslic3r/GCodeWriter.hpp" -namespace Slic3r { - enum class ModelVolumeType : int; -}; - -typedef double coordf_t; -typedef std::pair t_layer_height_range; #ifdef __WXMSW__ void msw_rescale_menu(wxMenu* menu); @@ -47,17 +34,21 @@ wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, std::function cb, wxEvtHandler* event_handler); wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, const wxString& description, - std::function cb, wxEvtHandler* event_handler); + std::function cb, wxEvtHandler* event_handler, + std::function const enable_condition = []() { return true; }, + std::function const check_condition = []() { return true; }, wxWindow* parent = nullptr); + +void enable_menu_item(wxUpdateUIEvent& evt, std::function const cb_condition, wxMenuItem* item, wxWindow* win); class wxDialog; -class wxBitmapComboBox; void edit_tooltip(wxString& tooltip); void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector& btn_ids); int em_unit(wxWindow* win); +int mode_icon_px_size(); -wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name, - const int px_cnt = 16, const bool is_horizontal = false, const bool grayscale = false); +wxBitmap create_scaled_bitmap(const std::string& bmp_name, wxWindow *win = nullptr, + const int px_cnt = 16, const bool grayscale = false); std::vector get_extruder_color_icons(bool thin_icon = false); void apply_extruder_selector(wxBitmapComboBox** ctrl, @@ -103,6 +94,37 @@ public: void OnListBoxSelection(wxCommandEvent& evt); }; +namespace Slic3r { +namespace GUI { +// *** PresetBitmapComboBox *** + +// BitmapComboBox used to presets list on Sidebar and Tabs +class PresetBitmapComboBox: public wxBitmapComboBox +{ +public: + PresetBitmapComboBox(wxWindow* parent, const wxSize& size = wxDefaultSize); + ~PresetBitmapComboBox() {} + +#ifdef __APPLE__ +protected: + /* For PresetBitmapComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina + * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean + * "please scale this to such and such" but rather + * "the wxImage is already sized for backing scale such and such". ) + * Unfortunately, the constructor changes the size of wxBitmap too. + * Thus We need to use unscaled size value for bitmaps that we use + * to avoid scaled size of control items. + * For this purpose control drawing methods and + * control size calculation methods (virtual) are overridden. + **/ + virtual bool OnAddBitmap(const wxBitmap& bitmap) override; + virtual void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override; +#endif +}; + +} +} + // *** wxDataViewTreeCtrlComboBox *** @@ -128,587 +150,6 @@ public: }; -// ---------------------------------------------------------------------------- -// DataViewBitmapText: helper class used by PrusaBitmapTextRenderer -// ---------------------------------------------------------------------------- - -class DataViewBitmapText : public wxObject -{ -public: - DataViewBitmapText( const wxString &text = wxEmptyString, - const wxBitmap& bmp = wxNullBitmap) : - m_text(text), - m_bmp(bmp) - { } - - DataViewBitmapText(const DataViewBitmapText &other) - : wxObject(), - m_text(other.m_text), - m_bmp(other.m_bmp) - { } - - void SetText(const wxString &text) { m_text = text; } - wxString GetText() const { return m_text; } - void SetBitmap(const wxBitmap &bmp) { m_bmp = bmp; } - const wxBitmap &GetBitmap() const { return m_bmp; } - - bool IsSameAs(const DataViewBitmapText& other) const { - return m_text == other.m_text && m_bmp.IsSameAs(other.m_bmp); - } - - bool operator==(const DataViewBitmapText& other) const { - return IsSameAs(other); - } - - bool operator!=(const DataViewBitmapText& other) const { - return !IsSameAs(other); - } - -private: - wxString m_text; - wxBitmap m_bmp; - - wxDECLARE_DYNAMIC_CLASS(DataViewBitmapText); -}; -DECLARE_VARIANT_OBJECT(DataViewBitmapText) - - -// ---------------------------------------------------------------------------- -// ObjectDataViewModelNode: a node inside ObjectDataViewModel -// ---------------------------------------------------------------------------- - -enum ItemType { - itUndef = 0, - itObject = 1, - itVolume = 2, - itInstanceRoot = 4, - itInstance = 8, - itSettings = 16, - itLayerRoot = 32, - itLayer = 64, -}; - -enum ColumnNumber -{ - colName = 0, // item name - colPrint , // printable property - colExtruder , // extruder selection - colEditing , // item editing -}; - -enum PrintIndicator -{ - piUndef = 0, // no print indicator - piPrintable , // printable - piUnprintable , // unprintable -}; - -class ObjectDataViewModelNode; -WX_DEFINE_ARRAY_PTR(ObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray); - -class ObjectDataViewModelNode -{ - ObjectDataViewModelNode* m_parent; - MyObjectTreeModelNodePtrArray m_children; - wxBitmap m_empty_bmp; - size_t m_volumes_cnt = 0; - std::vector< std::string > m_opt_categories; - t_layer_height_range m_layer_range = { 0.0f, 0.0f }; - - wxString m_name; - wxBitmap& m_bmp = m_empty_bmp; - ItemType m_type; - int m_idx = -1; - bool m_container = false; - wxString m_extruder = "default"; - wxBitmap m_extruder_bmp; - wxBitmap m_action_icon; - PrintIndicator m_printable {piUndef}; - wxBitmap m_printable_icon; - - std::string m_action_icon_name = ""; - Slic3r::ModelVolumeType m_volume_type; - -public: - ObjectDataViewModelNode(const wxString &name, - const wxString& extruder): - m_parent(NULL), - m_name(name), - m_type(itObject), - m_extruder(extruder) - { - set_action_and_extruder_icons(); - init_container(); - } - - ObjectDataViewModelNode(ObjectDataViewModelNode* parent, - const wxString& sub_obj_name, - const wxBitmap& bmp, - const wxString& extruder, - const int idx = -1 ) : - m_parent (parent), - m_name (sub_obj_name), - m_type (itVolume), - m_idx (idx), - m_extruder (extruder) - { - m_bmp = bmp; - set_action_and_extruder_icons(); - init_container(); - } - - ObjectDataViewModelNode(ObjectDataViewModelNode* parent, - const t_layer_height_range& layer_range, - const int idx = -1, - const wxString& extruder = wxEmptyString ); - - ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type); - - ~ObjectDataViewModelNode() - { - // free all our children nodes - size_t count = m_children.GetCount(); - for (size_t i = 0; i < count; i++) - { - ObjectDataViewModelNode *child = m_children[i]; - delete child; - } -#ifndef NDEBUG - // Indicate that the object was deleted. - m_idx = -2; -#endif /* NDEBUG */ - } - - void init_container(); - bool IsContainer() const - { - return m_container; - } - - ObjectDataViewModelNode* GetParent() - { - assert(m_parent == nullptr || m_parent->valid()); - return m_parent; - } - MyObjectTreeModelNodePtrArray& GetChildren() - { - return m_children; - } - ObjectDataViewModelNode* GetNthChild(unsigned int n) - { - return m_children.Item(n); - } - void Insert(ObjectDataViewModelNode* child, unsigned int n) - { - if (!m_container) - m_container = true; - m_children.Insert(child, n); - } - void Append(ObjectDataViewModelNode* child) - { - if (!m_container) - m_container = true; - m_children.Add(child); - } - void RemoveAllChildren() - { - if (GetChildCount() == 0) - return; - for (int id = int(GetChildCount()) - 1; id >= 0; --id) - { - if (m_children.Item(id)->GetChildCount() > 0) - m_children[id]->RemoveAllChildren(); - auto node = m_children[id]; - m_children.RemoveAt(id); - delete node; - } - } - - size_t GetChildCount() const - { - return m_children.GetCount(); - } - - bool SetValue(const wxVariant &variant, unsigned int col); - - void SetBitmap(const wxBitmap &icon) { m_bmp = icon; } - const wxBitmap& GetBitmap() const { return m_bmp; } - const wxString& GetName() const { return m_name; } - ItemType GetType() const { return m_type; } - void SetIdx(const int& idx); - int GetIdx() const { return m_idx; } - t_layer_height_range GetLayerRange() const { return m_layer_range; } - PrintIndicator IsPrintable() const { return m_printable; } - - // use this function only for childrens - void AssignAllVal(ObjectDataViewModelNode& from_node) - { - // ! Don't overwrite other values because of equality of this values for all children -- - m_name = from_node.m_name; - m_bmp = from_node.m_bmp; - m_idx = from_node.m_idx; - m_extruder = from_node.m_extruder; - m_type = from_node.m_type; - } - - bool SwapChildrens(int frst_id, int scnd_id) { - if (GetChildCount() < 2 || - frst_id < 0 || (size_t)frst_id >= GetChildCount() || - scnd_id < 0 || (size_t)scnd_id >= GetChildCount()) - return false; - - ObjectDataViewModelNode new_scnd = *GetNthChild(frst_id); - ObjectDataViewModelNode new_frst = *GetNthChild(scnd_id); - - new_scnd.m_idx = m_children.Item(scnd_id)->m_idx; - new_frst.m_idx = m_children.Item(frst_id)->m_idx; - - m_children.Item(frst_id)->AssignAllVal(new_frst); - m_children.Item(scnd_id)->AssignAllVal(new_scnd); - return true; - } - - // Set action icons for node - void set_action_and_extruder_icons(); - // Set printable icon for node - void set_printable_icon(PrintIndicator printable); - - void update_settings_digest_bitmaps(); - bool update_settings_digest(const std::vector& categories); - int volume_type() const { return int(m_volume_type); } - void msw_rescale(); - -#ifndef NDEBUG - bool valid(); -#endif /* NDEBUG */ - bool invalid() const { return m_idx < -1; } - -private: - friend class ObjectDataViewModel; -}; - -// ---------------------------------------------------------------------------- -// ObjectDataViewModel -// ---------------------------------------------------------------------------- - -// custom message the model sends to associated control to notify a last volume deleted from the object: -wxDECLARE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent); - -class ObjectDataViewModel :public wxDataViewModel -{ - std::vector m_objects; - std::vector m_volume_bmps; - wxBitmap* m_warning_bmp { nullptr }; - - wxDataViewCtrl* m_ctrl { nullptr }; - -public: - ObjectDataViewModel(); - ~ObjectDataViewModel(); - - wxDataViewItem Add( const wxString &name, - const int extruder, - const bool has_errors = false); - wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item, - const wxString &name, - const Slic3r::ModelVolumeType volume_type, - const bool has_errors = false, - const int extruder = 0, - const bool create_frst_child = true); - wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); - wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num); - wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, const std::vector& print_indicator); - wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item); - wxDataViewItem AddLayersChild( const wxDataViewItem &parent_item, - const t_layer_height_range& layer_range, - const int extruder = 0, - const int index = -1); - wxDataViewItem Delete(const wxDataViewItem &item); - wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num); - void DeleteAll(); - void DeleteChildren(wxDataViewItem& parent); - void DeleteVolumeChildren(wxDataViewItem& parent); - void DeleteSettings(const wxDataViewItem& parent); - wxDataViewItem GetItemById(int obj_idx); - wxDataViewItem GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type); - wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx); - wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx); - wxDataViewItem GetItemByLayerId(int obj_idx, int layer_idx); - wxDataViewItem GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range); - int GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range); - int GetIdByItem(const wxDataViewItem& item) const; - int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const; - int GetObjectIdByItem(const wxDataViewItem& item) const; - int GetVolumeIdByItem(const wxDataViewItem& item) const; - int GetInstanceIdByItem(const wxDataViewItem& item) const; - int GetLayerIdByItem(const wxDataViewItem& item) const; - void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx); - int GetRowByItem(const wxDataViewItem& item) const; - bool IsEmpty() { return m_objects.empty(); } - bool InvalidItem(const wxDataViewItem& item); - - // helper method for wxLog - - wxString GetName(const wxDataViewItem &item) const; - wxBitmap& GetBitmap(const wxDataViewItem &item) const; - wxString GetExtruder(const wxDataViewItem &item) const; - int GetExtruderNumber(const wxDataViewItem &item) const; - - // helper methods to change the model - - virtual unsigned int GetColumnCount() const override { return 3;} - virtual wxString GetColumnType(unsigned int col) const override{ return wxT("string"); } - - virtual void GetValue( wxVariant &variant, - const wxDataViewItem &item, - unsigned int col) const override; - virtual bool SetValue( const wxVariant &variant, - const wxDataViewItem &item, - unsigned int col) override; - bool SetValue( const wxVariant &variant, - const int item_idx, - unsigned int col); - - void SetExtruder(const wxString& extruder, wxDataViewItem item); - - // For parent move child from cur_volume_id place to new_volume_id - // Remaining items will moved up/down accordingly - wxDataViewItem ReorganizeChildren( const int cur_volume_id, - const int new_volume_id, - const wxDataViewItem &parent); - - virtual bool IsEnabled(const wxDataViewItem &item, unsigned int col) const override; - - virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override; - // get object item - wxDataViewItem GetTopParent(const wxDataViewItem &item) const; - virtual bool IsContainer(const wxDataViewItem &item) const override; - virtual unsigned int GetChildren(const wxDataViewItem &parent, - wxDataViewItemArray &array) const override; - void GetAllChildren(const wxDataViewItem &parent,wxDataViewItemArray &array) const; - // Is the container just a header or an item with all columns - // In our case it is an item with all columns - virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } - - ItemType GetItemType(const wxDataViewItem &item) const ; - wxDataViewItem GetItemByType( const wxDataViewItem &parent_item, - ItemType type) const; - wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const; - wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const; - wxDataViewItem GetLayerRootItem(const wxDataViewItem &item) const; - bool IsSettingsItem(const wxDataViewItem &item) const; - void UpdateSettingsDigest( const wxDataViewItem &item, - const std::vector& categories); - - bool IsPrintable(const wxDataViewItem &item) const; - void UpdateObjectPrintable(wxDataViewItem parent_item); - void UpdateInstancesPrintable(wxDataViewItem parent_item); - - void SetVolumeBitmaps(const std::vector& volume_bmps) { m_volume_bmps = volume_bmps; } - void SetWarningBitmap(wxBitmap* bitmap) { m_warning_bmp = bitmap; } - void SetVolumeType(const wxDataViewItem &item, const Slic3r::ModelVolumeType type); - wxDataViewItem SetPrintableState( PrintIndicator printable, int obj_idx, - int subobj_idx = -1, - ItemType subobj_type = itInstance); - wxDataViewItem SetObjectPrintableState(PrintIndicator printable, wxDataViewItem obj_item); - - void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; } - // Rescale bitmaps for existing Items - void Rescale(); - - wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, - const bool is_marked = false); - void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false); - t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const; - - bool UpdateColumValues(unsigned col); - void UpdateExtruderBitmap(wxDataViewItem item); - -private: - wxDataViewItem AddRoot(const wxDataViewItem& parent_item, const ItemType root_type); - wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item); -}; - -// ---------------------------------------------------------------------------- -// BitmapTextRenderer -// ---------------------------------------------------------------------------- -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING -class BitmapTextRenderer : public wxDataViewRenderer -#else -class BitmapTextRenderer : public wxDataViewCustomRenderer -#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING -{ -public: - BitmapTextRenderer(wxDataViewCellMode mode = -#ifdef __WXOSX__ - wxDATAVIEW_CELL_INERT -#else - wxDATAVIEW_CELL_EDITABLE -#endif - - ,int align = wxDVR_DEFAULT_ALIGNMENT -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - ); -#else - ) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) {} -#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - - bool SetValue(const wxVariant &value); - bool GetValue(wxVariant &value) const; -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY - virtual wxString GetAccessibleDescription() const override; -#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - - virtual bool Render(wxRect cell, wxDC *dc, int state); - virtual wxSize GetSize() const; - - bool HasEditorCtrl() const override - { -#ifdef __WXOSX__ - return false; -#else - return true; -#endif - } - wxWindow* CreateEditorCtrl(wxWindow* parent, - wxRect labelRect, - const wxVariant& value) override; - bool GetValueFromEditorCtrl( wxWindow* ctrl, - wxVariant& value) override; - bool WasCanceled() const { return m_was_unusable_symbol; } - -private: - DataViewBitmapText m_value; - bool m_was_unusable_symbol {false}; -}; - - -// ---------------------------------------------------------------------------- -// BitmapChoiceRenderer -// ---------------------------------------------------------------------------- - -class BitmapChoiceRenderer : public wxDataViewCustomRenderer -{ -public: - BitmapChoiceRenderer(wxDataViewCellMode mode = -#ifdef __WXOSX__ - wxDATAVIEW_CELL_INERT -#else - wxDATAVIEW_CELL_EDITABLE -#endif - ,int align = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL - ) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) {} - - bool SetValue(const wxVariant& value); - bool GetValue(wxVariant& value) const; - - virtual bool Render(wxRect cell, wxDC* dc, int state); - virtual wxSize GetSize() const; - - bool HasEditorCtrl() const override { return true; } - wxWindow* CreateEditorCtrl(wxWindow* parent, - wxRect labelRect, - const wxVariant& value) override; - bool GetValueFromEditorCtrl( wxWindow* ctrl, - wxVariant& value) override; - -private: - DataViewBitmapText m_value; -}; - - -// ---------------------------------------------------------------------------- -// MyCustomRenderer -// ---------------------------------------------------------------------------- - -class MyCustomRenderer : public wxDataViewCustomRenderer -{ -public: - // This renderer can be either activatable or editable, for demonstration - // purposes. In real programs, you should select whether the user should be - // able to activate or edit the cell and it doesn't make sense to switch - // between the two -- but this is just an example, so it doesn't stop us. - explicit MyCustomRenderer(wxDataViewCellMode mode) - : wxDataViewCustomRenderer("string", mode, wxALIGN_CENTER) - { } - - virtual bool Render(wxRect rect, wxDC *dc, int state) override/*wxOVERRIDE*/ - { - dc->SetBrush(*wxLIGHT_GREY_BRUSH); - dc->SetPen(*wxTRANSPARENT_PEN); - - rect.Deflate(2); - dc->DrawRoundedRectangle(rect, 5); - - RenderText(m_value, - 0, // no offset - wxRect(dc->GetTextExtent(m_value)).CentreIn(rect), - dc, - state); - return true; - } - - virtual bool ActivateCell(const wxRect& WXUNUSED(cell), - wxDataViewModel *WXUNUSED(model), - const wxDataViewItem &WXUNUSED(item), - unsigned int WXUNUSED(col), - const wxMouseEvent *mouseEvent) override/*wxOVERRIDE*/ - { - wxString position; - if (mouseEvent) - position = wxString::Format("via mouse at %d, %d", mouseEvent->m_x, mouseEvent->m_y); - else - position = "from keyboard"; -// wxLogMessage("MyCustomRenderer ActivateCell() %s", position); - return false; - } - - virtual wxSize GetSize() const override/*wxOVERRIDE*/ - { - return wxSize(60, 20); - } - - virtual bool SetValue(const wxVariant &value) override/*wxOVERRIDE*/ - { - m_value = value.GetString(); - return true; - } - - virtual bool GetValue(wxVariant &WXUNUSED(value)) const override/*wxOVERRIDE*/{ return true; } - - virtual bool HasEditorCtrl() const override/*wxOVERRIDE*/{ return true; } - - virtual wxWindow* - CreateEditorCtrl(wxWindow* parent, - wxRect labelRect, - const wxVariant& value) override/*wxOVERRIDE*/ - { - wxTextCtrl* text = new wxTextCtrl(parent, wxID_ANY, value, - labelRect.GetPosition(), - labelRect.GetSize(), - wxTE_PROCESS_ENTER); - text->SetInsertionPointEnd(); - - return text; - } - - virtual bool - GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override/*wxOVERRIDE*/ - { - wxTextCtrl* text = wxDynamicCast(ctrl, wxTextCtrl); - if (!text) - return false; - - value = text->GetValue(); - - return true; - } - -private: - wxString m_value; -}; - - // ---------------------------------------------------------------------------- // ScalableBitmap // ---------------------------------------------------------------------------- @@ -719,11 +160,14 @@ public: ScalableBitmap() {}; ScalableBitmap( wxWindow *parent, const std::string& icon_name = "", - const int px_cnt = 16, - const bool is_horizontal = false); + const int px_cnt = 16); ~ScalableBitmap() {} + wxSize GetBmpSize() const; + int GetBmpWidth() const; + int GetBmpHeight() const; + void msw_rescale(); const wxBitmap& bmp() const { return m_bmp; } @@ -731,316 +175,12 @@ public: const std::string& name() const{ return m_icon_name; } int px_cnt()const {return m_px_cnt;} - bool is_horizontal()const {return m_is_horizontal;} private: wxWindow* m_parent{ nullptr }; wxBitmap m_bmp = wxBitmap(); std::string m_icon_name = ""; int m_px_cnt {16}; - bool m_is_horizontal {false}; -}; - - -// ---------------------------------------------------------------------------- -// DoubleSlider -// ---------------------------------------------------------------------------- - -// custom message the slider sends to its parent to notify a tick-change: -wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); - -enum SelectedSlider { - ssUndef, - ssLower, - ssHigher -}; -enum TicksAction{ - taOnIcon, - taAdd, - taDel -}; - -class DoubleSlider : public wxControl -{ - enum IconFocus { - ifNone, - ifRevert, - ifCog - }; -public: - DoubleSlider( - wxWindow *parent, - wxWindowID id, - int lowerValue, - int higherValue, - int minValue, - int maxValue, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxSL_VERTICAL, - const wxValidator& val = wxDefaultValidator, - const wxString& name = wxEmptyString); - ~DoubleSlider() {} - - using t_mode = Slic3r::Model::CustomGCodeInfo::MODE; - - /* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values. - * So, let use same value as a permissible error for layer height. - */ - static double epsilon() { return 0.0011;} - - void msw_rescale(); - - int GetMinValue() const { return m_min_value; } - int GetMaxValue() const { return m_max_value; } - double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; } - double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; } - int GetLowerValue() const { return m_lower_value; } - int GetHigherValue() const { return m_higher_value; } - int GetActiveValue() const; - wxSize get_min_size() const ; - double GetLowerValueD() { return get_double_value(ssLower); } - double GetHigherValueD() { return get_double_value(ssHigher); } - wxSize DoGetBestSize() const override; - void SetLowerValue(const int lower_val); - void SetHigherValue(const int higher_val); - // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. - void SetSelectionSpan(const int lower_val, const int higher_val); - void SetMaxValue(const int max_value); - void SetKoefForLabels(const double koef) { m_label_koef = koef; } - void SetSliderValues(const std::vector& values) { m_values = values; } - void ChangeOneLayerLock(); - Slic3r::Model::CustomGCodeInfo GetTicksValues() const; - void SetTicksValues(const Slic3r::Model::CustomGCodeInfo &custom_gcode_per_print_z); - void EnableTickManipulation(bool enable = true) { m_is_enabled_tick_manipulation = enable; } - void DisableTickManipulation() { EnableTickManipulation(false); } - - void SetManipulationMode(t_mode mode) { m_mode = mode; } - t_mode GetManipulationMode() const { return m_mode; } - - void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder) - { - m_mode = !is_one_extruder_printed_model ? t_mode::MultiExtruder : - only_extruder < 0 ? t_mode::SingleExtruder : - t_mode::MultiAsSingle; - m_only_extruder = only_extruder; - } - - bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } - bool is_one_layer() const { return m_is_one_layer; } - bool is_lower_at_min() const { return m_lower_value == m_min_value; } - bool is_higher_at_max() const { return m_higher_value == m_max_value; } - bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } - - void OnPaint(wxPaintEvent& ) { render();} - void OnLeftDown(wxMouseEvent& event); - void OnMotion(wxMouseEvent& event); - void OnLeftUp(wxMouseEvent& event); - void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); } - void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); } - void OnWheel(wxMouseEvent& event); - void OnKeyDown(wxKeyEvent &event); - void OnKeyUp(wxKeyEvent &event); - void OnChar(wxKeyEvent &event); - void OnRightDown(wxMouseEvent& event); - void OnRightUp(wxMouseEvent& event); - - void add_code_as_tick(std::string code, int selected_extruder = -1); - // add default action for tick, when press "+" - void add_current_tick(bool call_from_keyboard = false); - // delete current tick, when press "-" - void delete_current_tick(); - void edit_tick(); - void edit_extruder_sequence(); - - struct TICK_CODE - { - bool operator<(const TICK_CODE& other) const { return other.tick > this->tick; } - bool operator>(const TICK_CODE& other) const { return other.tick < this->tick; } - - int tick = 0; - std::string gcode = Slic3r::ColorChangeCode; - int extruder = 0; - std::string color; - }; - -protected: - - void render(); - void draw_focus_rect(); - void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end); - void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos); - void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection); - void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos); - void draw_ticks(wxDC& dc); - void draw_colored_band(wxDC& dc); - void draw_one_layer_icon(wxDC& dc); - void draw_revert_icon(wxDC& dc); - void draw_cog_icon(wxDC &dc); - void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection); - void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection); - void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const; - - void update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection); - void detect_selected_slider(const wxPoint& pt); - void correct_lower_value(); - void correct_higher_value(); - void move_current_thumb(const bool condition); - void enter_window(wxMouseEvent& event, const bool enter); - -private: - - bool is_point_in_rect(const wxPoint& pt, const wxRect& rect); - int is_point_near_tick(const wxPoint& pt); - - double get_scroll_step(); - wxString get_label(const SelectedSlider& selection) const; - void get_lower_and_higher_position(int& lower_pos, int& higher_pos); - int get_value_from_position(const wxCoord x, const wxCoord y); - wxCoord get_position_from_value(const int value); - wxSize get_size(); - void get_size(int *w, int *h); - double get_double_value(const SelectedSlider& selection); - wxString get_tooltip(IconFocus icon_focus); - - std::string get_color_for_tool_change_tick(std::set::const_iterator it) const; - std::string get_color_for_color_change_tick(std::set::const_iterator it) const; - int get_extruder_for_tick(int tick); - std::set get_used_extruders_for_tick(int tick); - - void post_ticks_changed_event(const std::string& gcode = ""); - bool check_ticks_changed_event(const std::string& gcode); - void append_change_extruder_menu_item(wxMenu*); - void append_add_color_change_menu_item(wxMenu*); - - bool is_osx { false }; - wxFont m_font; - int m_min_value; - int m_max_value; - int m_lower_value; - int m_higher_value; - ScalableBitmap m_bmp_thumb_higher; - ScalableBitmap m_bmp_thumb_lower; - ScalableBitmap m_bmp_add_tick_on; - ScalableBitmap m_bmp_add_tick_off; - ScalableBitmap m_bmp_del_tick_on; - ScalableBitmap m_bmp_del_tick_off; - ScalableBitmap m_bmp_one_layer_lock_on; - ScalableBitmap m_bmp_one_layer_lock_off; - ScalableBitmap m_bmp_one_layer_unlock_on; - ScalableBitmap m_bmp_one_layer_unlock_off; - ScalableBitmap m_bmp_revert; - ScalableBitmap m_bmp_cog; - SelectedSlider m_selection; - bool m_is_left_down = false; - bool m_is_right_down = false; - bool m_is_one_layer = false; - bool m_is_focused = false; - bool m_is_action_icon_focesed = false; - bool m_is_one_layer_icon_focesed = false; - bool m_is_enabled_tick_manipulation = true; - bool m_show_context_menu = false; - bool m_show_edit_menu = false; - bool m_force_edit_extruder_sequence = false; - bool m_force_mode_apply = true; - bool m_force_add_tick = false; - bool m_force_delete_tick = false; - t_mode m_mode = t_mode::SingleExtruder; - int m_only_extruder = -1; - - wxRect m_rect_lower_thumb; - wxRect m_rect_higher_thumb; - wxRect m_rect_tick_action; - wxRect m_rect_one_layer_icon; - wxRect m_rect_revert_icon; - wxRect m_rect_cog_icon; - wxSize m_thumb_size; - int m_tick_icon_dim; - int m_lock_icon_dim; - int m_revert_icon_dim; - int m_cog_icon_dim; - long m_style; - float m_label_koef = 1.0; - -// control's view variables - wxCoord SLIDER_MARGIN; // margin around slider - - wxPen DARK_ORANGE_PEN; - wxPen ORANGE_PEN; - wxPen LIGHT_ORANGE_PEN; - - wxPen DARK_GREY_PEN; - wxPen GREY_PEN; - wxPen LIGHT_GREY_PEN; - - std::vector m_line_pens; - std::vector m_segm_pens; - std::vector m_values; - - struct TICK_CODE_INFO - { - std::set ticks; - t_mode mode = t_mode::SingleExtruder; - - bool empty() const { return ticks.empty(); } - void set_pause_print_msg(const std::string& message) { pause_print_msg = message; } - - bool add_tick (const int tick, std::string &code, int extruder, double print_z); - bool edit_tick (std::set::iterator it, double print_z); - void switch_code(const std::string& code_from, const std::string& code_to); - void erase_all_ticks_with_code (const std::string& gcode); - bool has_tick_with_code (const std::string& gcode); - - void suppress_plus (bool suppress) { m_suppress_plus = suppress;} - void suppress_minus(bool suppress) { m_suppress_minus = suppress;} - bool suppressed_plus () { return m_suppress_plus ; } - bool suppressed_minus() { return m_suppress_minus; } - - private: - - std::string custom_gcode = ""; - std::string pause_print_msg = ""; - bool m_suppress_plus = false; - bool m_suppress_minus = false; - } - m_ticks; - -public: - struct ExtrudersSequence - { - bool is_mm_intervals = true; - double interval_by_mm = 3.0; - int interval_by_layers = 10; - std::vector extruders = { 0 }; - - bool operator==(const ExtrudersSequence& other) const - { - return (other.is_mm_intervals == this->is_mm_intervals ) && - (other.interval_by_mm == this->interval_by_mm ) && - (other.interval_by_layers == this->interval_by_layers ) && - (other.extruders == this->extruders ) ; - } - bool operator!=(const ExtrudersSequence& other) const - { - return (other.is_mm_intervals != this->is_mm_intervals ) && - (other.interval_by_mm != this->interval_by_mm ) && - (other.interval_by_layers != this->interval_by_layers ) && - (other.extruders != this->extruders ) ; - } - - void add_extruder(size_t pos) - { - extruders.insert(extruders.begin() + pos+1, size_t(0)); - } - - void delete_extruder(size_t pos) - { - if (extruders.size() == 1) - return;// last item can't be deleted - extruders.erase(extruders.begin() + pos); - } - } - m_extruders_sequence; }; @@ -1116,15 +256,14 @@ public: void msw_rescale(); private: - wxWindow* m_parent; - std::string m_current_icon_name = ""; - std::string m_disabled_icon_name = ""; + wxWindow* m_parent { nullptr }; + std::string m_current_icon_name; + std::string m_disabled_icon_name; int m_width {-1}; // should be multiplied to em_unit int m_height{-1}; // should be multiplied to em_unit // bitmap dimensions int m_px_cnt{ 16 }; - bool m_is_horizontal{ false }; }; @@ -1142,8 +281,17 @@ public: const wxString& mode = wxEmptyString, const wxSize& size = wxDefaultSize, const wxPoint& pos = wxDefaultPosition); + + ModeButton( + wxWindow* parent, + const wxString& mode = wxEmptyString, + const std::string& icon_name = "", + int px_cnt = 16); + ~ModeButton() {} + void Init(const wxString& mode); + void OnButton(wxCommandEvent& event); void OnEnterBtn(wxMouseEvent& event) { focus_button(true); event.Skip(); } void OnLeaveBtn(wxMouseEvent& event) { focus_button(m_is_selected); event.Skip(); } diff --git a/src/slic3r/Utils/FixModelByWin10.cpp b/src/slic3r/Utils/FixModelByWin10.cpp index 28f8b54d5e..0de526432b 100644 --- a/src/slic3r/Utils/FixModelByWin10.cpp +++ b/src/slic3r/Utils/FixModelByWin10.cpp @@ -363,17 +363,10 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx) ModelObject *model_object = model.add_object(); model_object->add_volume(*volumes[ivolume]); model_object->add_instance(); -#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF if (!Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr, false)) { boost::filesystem::remove(path_src); throw std::runtime_error(L("Export of a temporary 3mf file failed")); } -#else - if (! Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr)) { - boost::filesystem::remove(path_src); - throw std::runtime_error(L("Export of a temporary 3mf file failed")); - } -#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF model.clear_objects(); model.clear_materials(); boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 55e3a5a738..4e33e9d6bc 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -73,13 +73,16 @@ struct Update std::string vendor; std::string changelog_url; + bool forced_update; + Update() {} - Update(fs::path &&source, fs::path &&target, const Version &version, std::string vendor, std::string changelog_url) + Update(fs::path &&source, fs::path &&target, const Version &version, std::string vendor, std::string changelog_url, bool forced = false) : source(std::move(source)) , target(std::move(target)) , version(version) , vendor(std::move(vendor)) , changelog_url(std::move(changelog_url)) + , forced_update(forced) {} void install() const @@ -297,6 +300,12 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors) const auto idx_url = vendor.config_update_url + "/" + INDEX_FILENAME; const std::string idx_path = (cache_path / (vendor.id + ".idx")).string(); const std::string idx_path_temp = idx_path + "-update"; + //check if idx_url is leading to our site + if (! boost::starts_with(idx_url, "http://files.prusa3d.com/wp-content/uploads/repository/")) + { + BOOST_LOG_TRIVIAL(warning) << "unsafe url path for vendor \"" << vendor.name << "\" rejected: " << idx_url; + continue; + } if (!get_file(idx_url, idx_path_temp)) { continue; } if (cancel) { return; } @@ -418,11 +427,16 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version continue; } - if (ver_current_found && !ver_current->is_current_slic3r_supported()) { - // "Reconfigure" situation. - BOOST_LOG_TRIVIAL(warning) << "Current Slic3r incompatible with installed bundle: " << bundle_path.string(); - updates.incompats.emplace_back(std::move(bundle_path), *ver_current, vp.name); - continue; + bool current_not_supported = false; //if slcr is incompatible but situation is not downgrade, we do forced updated and this bool is information to do it + + if (ver_current_found && !ver_current->is_current_slic3r_supported()){ + if(ver_current->is_current_slic3r_downgrade()) { + // "Reconfigure" situation. + BOOST_LOG_TRIVIAL(warning) << "Current Slic3r incompatible with installed bundle: " << bundle_path.string(); + updates.incompats.emplace_back(std::move(bundle_path), *ver_current, vp.name); + continue; + } + current_not_supported = true; } if (recommended->config_version < vp.config_version) { @@ -462,7 +476,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version if (new_vp.config_version == recommended->config_version) { // The config bundle from the cache directory matches the recommended version of the index from the cache directory. // This is the newest known recommended config. Use it. - new_update = Update(std::move(path_in_cache), std::move(bundle_path), *recommended, vp.name, vp.changelog_url); + new_update = Update(std::move(path_in_cache), std::move(bundle_path), *recommended, vp.name, vp.changelog_url, current_not_supported); // and install the config index from the cache into vendor's directory. bundle_path_idx_to_install = idx.path(); found = true; @@ -492,7 +506,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version } recommended = rsrc_idx.recommended(); if (recommended != rsrc_idx.end() && recommended->config_version == rsrc_vp.config_version && recommended->config_version > vp.config_version) { - new_update = Update(std::move(path_in_rsrc), std::move(bundle_path), *recommended, vp.name, vp.changelog_url); + new_update = Update(std::move(path_in_rsrc), std::move(bundle_path), *recommended, vp.name, vp.changelog_url, current_not_supported); bundle_path_idx_to_install = path_idx_in_rsrc; found = true; } else { @@ -513,24 +527,27 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version // Find a recommended config bundle version for the slic3r version last executed. This makes sure that a config bundle update will not be missed // when upgrading an application. On the other side, the user will be bugged every time he will switch between slic3r versions. const auto existing_recommended = existing_idx.recommended(old_slic3r_version); - if (existing_recommended != existing_idx.end() && recommended->config_version == existing_recommended->config_version) { + /*if (existing_recommended != existing_idx.end() && recommended->config_version == existing_recommended->config_version) { // The user has already seen (and presumably rejected) this update BOOST_LOG_TRIVIAL(info) << boost::format("Downloaded index for `%1%` is the same as installed one, not offering an update.") % idx.vendor(); continue; - } + }*/ } catch (const std::exception &err) { BOOST_LOG_TRIVIAL(error) << boost::format("Cannot load the installed index at `%1%`: %2%") % bundle_path_idx % err.what(); } } // Check if the update is already present in a snapshot - const auto recommended_snap = SnapshotDB::singleton().snapshot_with_vendor_preset(vp.name, recommended->config_version); - if (recommended_snap != SnapshotDB::singleton().end()) { - BOOST_LOG_TRIVIAL(info) << boost::format("Bundle update %1% %2% already found in snapshot %3%, skipping...") - % vp.name - % recommended->config_version.to_string() - % recommended_snap->id; - continue; + if(!current_not_supported) + { + const auto recommended_snap = SnapshotDB::singleton().snapshot_with_vendor_preset(vp.name, recommended->config_version); + if (recommended_snap != SnapshotDB::singleton().end()) { + BOOST_LOG_TRIVIAL(info) << boost::format("Bundle update %1% %2% already found in snapshot %3%, skipping...") + % vp.name + % recommended->config_version.to_string() + % recommended_snap->id; + continue; + } } updates.updates.emplace_back(std::move(new_update)); @@ -554,14 +571,17 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons BOOST_LOG_TRIVIAL(info) << "Taking a snapshot..."; SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_DOWNGRADE); } - + BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% incompatible bundles") % updates.incompats.size(); for (auto &incompat : updates.incompats) { BOOST_LOG_TRIVIAL(info) << '\t' << incompat; incompat.remove(); } + + } else if (updates.updates.size() > 0) { + if (snapshot) { BOOST_LOG_TRIVIAL(info) << "Taking a snapshot..."; SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_UPGRADE); @@ -688,6 +708,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3 ); } else if (min_slic3r != Semver::zero()) { restrictions = wxString::Format(_(L("requires min. %s")), min_slic3r.to_string()); + BOOST_LOG_TRIVIAL(debug) << "Bundle is not downgrade, user will now have to do whole wizard. This should not happen."; } else { restrictions = wxString::Format(_(L("requires max. %s")), max_slic3r.to_string()); } @@ -704,16 +725,59 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3 // (snapshot is taken beforehand) p->perform_updates(std::move(updates)); - if (! GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) { + if (!GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) { return R_INCOMPAT_EXIT; } return R_INCOMPAT_CONFIGURED; - } else { + } + else { BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye..."; return R_INCOMPAT_EXIT; } + } else if (updates.updates.size() > 0) { + + bool incompatible_version = false; + for (const auto& update : updates.updates) { + incompatible_version = (update.forced_update ? true : incompatible_version); + //td::cout << update.forced_update << std::endl; + //BOOST_LOG_TRIVIAL(info) << boost::format("Update requires higher version."); + } + + //forced update + if(incompatible_version) + { + BOOST_LOG_TRIVIAL(info) << boost::format("Update of %1% bundles available. At least one requires higher version of Slicer.") % updates.updates.size(); + + std::vector updates_msg; + for (const auto& update : updates.updates) { + std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string(); + updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url)); + } + + GUI::MsgUpdateForced dlg(updates_msg); + + const auto res = dlg.ShowModal(); + if (res == wxID_OK) { + BOOST_LOG_TRIVIAL(info) << "User wants to update..."; + + p->perform_updates(std::move(updates)); + + // Reload global configuration + auto* app_config = GUI::wxGetApp().app_config; + GUI::wxGetApp().preset_bundle->load_presets(*app_config); + GUI::wxGetApp().load_current_presets(); + GUI::wxGetApp().plater()->set_bed_shape(); + return R_UPDATE_INSTALLED; + } + else { + BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye..."; + return R_INCOMPAT_EXIT; + } + } + + // regular update BOOST_LOG_TRIVIAL(info) << boost::format("Update of %1% bundles available. Asking for confirmation ...") % updates.updates.size(); std::vector updates_msg; diff --git a/t/gcode.t b/t/gcode.t index 126c621b7e..a43b5049ce 100644 --- a/t/gcode.t +++ b/t/gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 25; +use Test::More tests => 24; use strict; use warnings; @@ -199,22 +199,24 @@ use Slic3r::Test; like $gcode, qr/START:20mm_cube/, '[input_filename] is also available in custom G-code'; } -{ - my $config = Slic3r::Config::new_from_defaults; - $config->set('spiral_vase', 1); - my $print = Slic3r::Test::init_print('cube_with_hole', config => $config); - - my $spiral = 0; - Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { - my ($self, $cmd, $args, $info) = @_; - - if ($cmd eq 'G1' && exists $args->{E} && exists $args->{Z}) { - $spiral = 1; - } - }); - - ok !$spiral, 'spiral vase is correctly disabled on layers with multiple loops'; -} +# The current Spiral Vase slicing code removes the holes and all but the largest contours from each slice, +# therefore the following test is no more valid. +#{ +# my $config = Slic3r::Config::new_from_defaults; +# $config->set('spiral_vase', 1); +# my $print = Slic3r::Test::init_print('cube_with_hole', config => $config); +# +# my $spiral = 0; +# Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { +# my ($self, $cmd, $args, $info) = @_; +# +# if ($cmd eq 'G1' && exists $args->{E} && exists $args->{Z}) { +# $spiral = 1; +# } +# }); +# +# ok !$spiral, 'spiral vase is correctly disabled on layers with multiple loops'; +#} { diff --git a/t/shells.t b/t/shells.t index dc38ff4aa3..c401315908 100644 --- a/t/shells.t +++ b/t/shells.t @@ -1,4 +1,4 @@ -use Test::More tests => 21; +use Test::More tests => 20; use strict; use warnings; @@ -293,31 +293,33 @@ use Slic3r::Test; ok !$horizontal_extrusions, 'no horizontal extrusions'; } -{ - my $config = Slic3r::Config::new_from_defaults; - $config->set('perimeters', 1); - $config->set('fill_density', 0); - $config->set('top_solid_layers', 0); - $config->set('spiral_vase', 1); - $config->set('bottom_solid_layers', 0); - $config->set('skirts', 0); - $config->set('first_layer_height', '100%'); - $config->set('start_gcode', ''); - - my $print = Slic3r::Test::init_print('two_hollow_squares', config => $config); - my $diagonal_moves = 0; - Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { - my ($self, $cmd, $args, $info) = @_; - - if ($cmd eq 'G1') { - if ($info->{extruding} && $info->{dist_XY} > 0) { - if ($info->{dist_Z} > 0) { - $diagonal_moves++; - } - } - } - }); - is $diagonal_moves, 0, 'no spiral moves on two-island object'; -} +# The current Spiral Vase slicing code removes the holes and all but the largest contours from each slice, +# therefore the following test is no more valid. +#{ +# my $config = Slic3r::Config::new_from_defaults; +# $config->set('perimeters', 1); +# $config->set('fill_density', 0); +# $config->set('top_solid_layers', 0); +# $config->set('spiral_vase', 1); +# $config->set('bottom_solid_layers', 0); +# $config->set('skirts', 0); +# $config->set('first_layer_height', '100%'); +# $config->set('start_gcode', ''); +# +# my $print = Slic3r::Test::init_print('two_hollow_squares', config => $config); +# my $diagonal_moves = 0; +# Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { +# my ($self, $cmd, $args, $info) = @_; +# +# if ($cmd eq 'G1') { +# if ($info->{extruding} && $info->{dist_XY} > 0) { +# if ($info->{dist_Z} > 0) { +# $diagonal_moves++; +# } +# } +# } +# }); +# is $diagonal_moves, 0, 'no spiral moves on two-island object'; +#} __END__ diff --git a/tests/data/extruder_idler_quads.obj b/tests/data/extruder_idler_quads.obj new file mode 100644 index 0000000000..266cb242d1 --- /dev/null +++ b/tests/data/extruder_idler_quads.obj @@ -0,0 +1,7348 @@ +#### +# +# OBJ File Generated by Meshlab +# +#### +# Object extruder_idler_quads.obj +# +# Vertices: 3660 +# Faces: 3672 +# +#### +v 61.288807 37.917557 6.068595 +v 61.312790 37.917618 6.193261 +v 61.356049 37.917774 6.504920 +v 46.677547 37.917866 9.519013 +v 55.420834 37.917618 7.326775 +v 55.396851 37.917557 7.202109 +v 70.876183 37.682098 2.970614 +v 46.216084 37.450668 7.120349 +v 61.171124 37.838562 5.456883 +v 70.404060 35.864510 0.516525 +v 70.345604 35.376511 0.212692 +v 54.692173 35.864510 3.539228 +v 54.633720 35.376511 3.235396 +v 61.171051 37.731289 7.795076 +v 55.864639 37.839470 8.386023 +v 55.279167 37.838562 6.590397 +v 46.326366 37.682098 7.693588 +v 70.566055 36.778572 1.358596 +v 70.661987 37.147907 1.857247 +v 54.854176 36.778572 4.381299 +v 46.112171 37.147907 6.580221 +v 55.054016 37.450668 5.420078 +v 60.659683 36.348526 2.798444 +v 54.767727 36.348526 3.931957 +v 45.929794 36.348526 5.632228 +v 50.387226 39.697979 28.801762 +v 50.382210 39.699635 28.775690 +v 48.877922 40.196674 20.956472 +v 48.879765 39.946739 20.966045 +v 48.897690 38.450691 21.059217 +v 48.672623 38.559273 19.889320 +v 48.627918 34.768574 19.656961 +v 48.967503 37.518620 21.422112 +v 49.148136 36.364048 22.361021 +v 49.308113 35.827209 23.192574 +v 49.224934 36.068989 22.760210 +v 50.379288 38.947979 28.760509 +v 50.332268 37.976688 28.516096 +v 50.291908 37.522064 28.306307 +v 50.557526 37.884193 29.686979 +v 49.863396 35.643738 26.078924 +v 49.771500 35.518578 25.601242 +v 50.513439 34.145573 29.457813 +v 46.316734 33.337154 7.643516 +v 45.795788 35.376511 4.935667 +v 46.366432 33.896751 7.901852 +v 45.854240 35.864510 5.239500 +v 46.306992 32.926773 7.592876 +v 46.316822 32.516430 7.643979 +v 46.420128 31.655691 8.180969 +v 46.451801 31.534010 8.345595 +v 46.486115 31.434301 8.523966 +v 46.599277 31.280785 9.112178 +v 46.099930 28.231632 6.516577 +v 46.638424 31.280876 9.315653 +v 47.221455 27.861065 12.346222 +v 46.715168 31.358620 9.714571 +v 46.785843 31.534855 10.081941 +v 47.732357 31.056692 15.001865 +v 46.930408 32.928387 10.833368 +v 46.927967 32.721592 10.820692 +v 47.124451 37.150532 11.842004 +v 47.220444 36.781719 12.340965 +v 46.817513 31.656748 10.246544 +v 46.870964 31.958406 10.524391 +v 46.891937 32.133430 10.633411 +v 47.306953 36.352104 12.790643 +v 46.391373 34.056538 8.031486 +v 46.016239 36.778572 6.081570 +v 46.441235 37.838562 8.290668 +v 46.598972 34.574272 9.110590 +v 46.560146 34.548199 8.908764 +v 46.558918 37.917557 8.902380 +v 46.714870 34.497074 9.713024 +v 47.020462 37.452759 11.301476 +v 46.910164 37.683620 10.728139 +v 46.870785 33.898132 10.523453 +v 46.891777 33.723171 10.632566 +v 46.751282 34.420887 9.902280 +v 51.630699 27.764736 35.265278 +v 52.147446 28.245863 37.951313 +v 46.795250 37.839470 10.130821 +v 71.227364 37.917866 4.796038 +v 76.205231 29.860657 30.670778 +v 70.878639 33.534252 2.983394 +v 70.916252 33.896751 3.178878 +v 70.969704 34.198410 3.456725 +v 71.001366 34.320305 3.621328 +v 70.991051 37.838562 3.567693 +v 71.108734 37.917557 4.179405 +v 71.109962 34.548199 4.185790 +v 71.226776 34.548508 4.792975 +v 71.345062 37.839470 5.407847 +v 71.459976 37.683620 6.005164 +v 71.301102 34.420887 5.179305 +v 71.441589 33.723171 5.909592 +v 70.479614 36.348526 0.909254 +v 70.866547 33.337154 2.920542 +v 70.765900 37.450668 2.397374 +v 70.916428 31.957125 3.179823 +v 70.304688 31.906664 0.000000 +v 71.001617 31.534010 3.622621 +v 71.035934 31.434301 3.800991 +v 71.149094 31.280785 4.389203 +v 71.110252 31.306643 4.187318 +v 71.227066 31.306923 4.794502 +v 71.301376 31.434967 5.180758 +v 71.264984 31.358620 4.991596 +v 71.458290 33.535702 5.996392 +v 71.477760 33.135204 6.097586 +v 71.480225 32.928387 6.110393 +v 72.579239 34.928131 11.823022 +v 73.058540 33.686779 14.314425 +v 71.367325 31.656748 5.523570 +v 73.568642 32.857552 16.965889 +v 73.831314 32.604336 18.331276 +v 75.124626 28.113617 25.053829 +v 74.363899 32.428364 21.099625 +v 74.895103 32.695507 23.860798 +v 75.156105 32.993713 25.217482 +v 75.149338 30.209538 25.182302 +v 76.679512 26.740294 33.136051 +v 76.697266 28.245863 33.228336 +v 76.180519 27.764736 30.542305 +v 71.996040 35.338497 8.791578 +v 72.012505 35.153580 8.877157 +v 56.978554 37.452759 9.385705 +v 57.088562 37.406780 9.449879 +v 71.674271 37.150532 7.119029 +v 60.551853 37.406780 8.783600 +v 59.598507 37.150532 9.442204 +v 47.382626 35.868492 13.183980 +v 71.856773 36.352104 8.067668 +v 71.770264 36.781719 7.617991 +v 59.439774 37.126163 9.506907 +v 58.300529 37.132675 9.716898 +v 58.834583 37.093670 9.668919 +v 71.570282 37.452759 6.578501 +v 71.932442 35.868492 8.461005 +v 47.446220 35.338497 13.514553 +v 44.992771 27.189064 13.242427 +v 51.780609 26.868217 17.170813 +v 61.232605 27.449877 5.863266 +v 58.929241 27.283329 9.023430 +v 58.809258 27.282513 9.059827 +v 58.359135 27.139223 11.484049 +v 58.309364 27.285778 9.102742 +v 55.616589 27.411655 7.567233 +v 47.938751 27.189064 12.675670 +v 54.076530 26.062111 29.879890 +v 54.026432 26.058466 29.948986 +v 53.712753 26.029385 30.483768 +v 53.552719 25.999935 30.994987 +v 53.514042 25.981487 31.303373 +v 53.514153 25.976261 31.388626 +v 50.928787 26.201115 28.217762 +v 53.514442 25.962425 31.614302 +v 68.806427 26.062111 27.046103 +v 68.623718 26.048828 27.297941 +v 68.756279 26.058466 27.115208 +v 68.515053 26.037975 27.495895 +v 62.093616 26.073893 28.145342 +v 68.338547 26.012682 27.942495 +v 68.254539 25.986547 28.385019 +v 68.244034 25.976261 28.554842 +v 68.218460 26.326206 22.850769 +v 73.165169 25.937754 28.236309 +v 73.194099 25.909863 28.685766 +v 73.183594 25.899576 28.855589 +v 72.995468 25.856739 29.590630 +v 72.923080 25.848146 29.744713 +v 72.631706 25.824013 30.194506 +v 71.988434 25.798077 30.741371 +v 71.553574 25.790627 30.946589 +v 71.338646 25.788862 31.016699 +v 70.807259 25.790627 31.090166 +v 70.411186 25.796120 31.076721 +v 70.327293 25.798077 31.060946 +v 70.105293 25.803261 31.019094 +v 58.193188 25.848146 32.578499 +v 68.283722 25.943062 29.088823 +v 68.361572 25.923698 29.389732 +v 68.626915 25.886189 29.950634 +v 68.759964 25.873442 30.132986 +v 58.265583 25.856739 32.424416 +v 68.965179 25.856739 30.365990 +v 69.023651 25.852247 30.428022 +v 69.089577 25.848146 30.482216 +v 69.263924 25.837296 30.625723 +v 69.527039 25.824013 30.791792 +v 69.604355 25.820885 30.827938 +v 68.476524 25.904634 29.678623 +v 58.435280 25.937754 31.070093 +v 58.463875 25.923698 31.293884 +v 58.464321 25.904634 31.604805 +v 58.369694 25.873442 32.131901 +v 68.810265 25.868631 30.201771 +v 69.454826 25.827658 30.746227 +v 57.951962 25.827658 32.959187 +v 69.808907 25.812611 30.923559 +v 57.688858 25.812611 33.255257 +v 70.023972 25.805826 30.992867 +v 57.514957 25.805826 33.399391 +v 57.109299 25.794794 33.657429 +v 56.904839 25.791292 33.753883 +v 56.823685 25.790627 33.780373 +v 56.608757 25.788862 33.850483 +v 56.302967 25.788862 33.909313 +v 56.077370 25.790627 33.923950 +v 55.375401 25.803261 33.852879 +v 54.874439 25.820885 33.661728 +v 54.080334 25.868631 33.035564 +v 52.746826 25.600410 37.667839 +v 54.359653 25.848146 33.316006 +v 54.533997 25.837296 33.459515 +v 54.797138 25.824013 33.625580 +v 55.079014 25.812611 33.757343 +v 55.766525 25.794794 33.915756 +v 55.681297 25.796120 33.910507 +v 73.076561 25.962425 27.850878 +v 72.993141 25.976261 27.641191 +v 72.811218 25.999935 27.289974 +v 71.911095 26.062111 26.448816 +v 71.833778 26.065239 26.412672 +v 71.629128 26.073511 26.317068 +v 70.716064 26.094831 26.144928 +v 70.630875 26.095497 26.150444 +v 69.598946 26.091330 26.416964 +v 69.521751 26.090002 26.453463 +v 66.578064 26.868217 14.324030 +v 66.011253 26.886908 14.128163 +v 60.892159 27.567686 4.006828 +v 61.173477 27.469414 5.555912 +v 61.073132 26.073893 28.341667 +v 58.403152 25.948370 30.903088 +v 58.263256 25.976261 30.474977 +v 58.081326 25.999935 30.123760 +v 57.948280 26.012682 29.941408 +v 57.618668 26.037975 29.592178 +v 57.684593 26.033876 29.646372 +v 57.444317 26.048828 29.448671 +v 57.181206 26.062111 29.282602 +v 57.103886 26.065239 29.246456 +v 56.684273 26.080296 29.081528 +v 56.899239 26.073511 29.150852 +v 55.986176 26.094831 28.978714 +v 55.675388 26.097260 28.998867 +v 54.869053 26.091330 29.250750 +v 54.791859 26.090002 29.287249 +v 54.719810 26.088047 29.333023 +v 54.463398 26.080296 29.508787 +v 53.421013 26.326206 25.697552 +v 54.529179 26.082863 29.454258 +v 67.902382 34.970417 12.655975 +v 47.462685 35.153580 13.600132 +v 54.822617 34.805954 17.855391 +v 58.905815 34.974163 14.325680 +v 54.142002 34.777916 18.443687 +v 56.180191 34.357391 24.912003 +v 64.640221 42.441532 22.165798 +v 64.498436 42.393948 21.428791 +v 64.108391 38.000809 19.401363 +v 65.105515 36.364048 19.291088 +v 64.965973 41.759834 23.859028 +v 65.182312 36.068989 19.690277 +v 64.884438 37.973030 18.141928 +v 64.924889 37.518620 18.352179 +v 64.831230 39.445396 17.865381 +v 64.038155 39.445889 19.036266 +v 65.353737 35.642532 20.581331 +v 65.445679 35.517838 21.059244 +v 65.633987 43.439590 22.038057 +v 64.077850 40.550362 19.242599 +v 64.837143 39.946739 17.896112 +v 65.265495 35.827209 20.122641 +v 64.330994 39.446640 20.558413 +v 64.055885 40.191998 19.128443 +v 64.350174 39.980480 20.658138 +v 64.243858 37.134884 20.105499 +v 64.108124 40.891331 19.399967 +v 64.364799 40.145279 20.734161 +v 64.042656 39.069904 19.059660 +v 64.078056 38.341614 19.243677 +v 64.339630 39.086071 20.603313 +v 64.350266 38.912914 20.658621 +v 64.405319 38.454250 20.944765 +v 64.488396 38.135056 21.376616 +v 64.430038 36.593735 21.073254 +v 64.301460 36.913589 20.404915 +v 64.520393 38.068111 21.542925 +v 64.587852 38.000175 21.893555 +v 64.569603 36.453186 21.798729 +v 64.640778 36.453377 22.168688 +v 64.711395 36.500771 22.535736 +v 64.721664 38.135670 22.589117 +v 64.846489 36.733505 23.237947 +v 64.804688 38.455292 23.020670 +v 64.826614 38.595646 23.134649 +v 65.063599 37.685196 24.366478 +v 64.845032 38.749439 23.230366 +v 64.243446 41.757961 20.103352 +v 64.405144 40.439426 20.943855 +v 64.300987 41.979553 20.402464 +v 64.458015 40.671249 21.218702 +v 64.488167 40.759056 21.375410 +v 64.429504 42.300076 21.070503 +v 64.553452 40.871548 21.714767 +v 64.569054 42.441341 21.795839 +v 64.656105 40.871815 22.248354 +v 64.689438 40.826614 22.421604 +v 64.845985 42.162468 23.235317 +v 64.908371 41.981129 23.559612 +v 64.804512 40.440468 23.019760 +v 64.844902 40.146526 23.229715 +v 64.859566 39.981804 23.305906 +v 64.876671 39.629799 23.394819 +v 64.751572 40.672024 22.744589 +v 64.779427 40.565052 22.889357 +v 65.101707 38.003387 24.564560 +v 65.153946 38.702721 24.836084 +v 65.131981 38.344357 24.721928 +v 65.167244 39.072823 24.905212 +v 65.153801 40.194855 24.835358 +v 64.363846 36.732250 20.729210 +v 64.966385 37.136757 23.861176 +v 66.068886 36.366562 24.298624 +v 65.820778 35.643738 23.008991 +v 66.289383 40.921688 25.444761 +v 66.318748 40.444027 25.597404 +v 64.975334 41.796913 18.614428 +v 64.146233 41.209522 19.598049 +v 64.363342 42.161217 20.726580 +v 65.728134 43.376877 22.527445 +v 64.779793 42.300983 22.891273 +v 64.056030 38.699863 19.129168 +v 64.975761 37.094627 18.616638 +v 64.146568 37.682812 19.599808 +v 64.191963 37.392635 19.835770 +v 64.042587 39.821896 19.059315 +v 64.498993 36.500225 21.431665 +v 64.780319 36.594643 22.894024 +v 66.137520 42.186985 24.655401 +v 65.101440 40.893909 24.563162 +v 64.191582 41.499920 19.833786 +v 65.035782 42.184120 18.928610 +v 64.924530 41.372654 18.350315 +v 65.353035 43.250977 20.577698 +v 65.991501 42.825726 23.896412 +v 66.068298 42.530670 24.295601 +v 64.710838 42.394493 22.532862 +v 65.171677 39.448830 24.928261 +v 66.336670 38.947979 25.690577 +v 65.017868 41.502083 24.128757 +v 65.063263 41.211906 24.364719 +v 66.318924 38.454502 25.598343 +v 65.018250 37.394798 24.130741 +v 64.908844 36.915165 23.562061 +v 65.992126 36.071102 23.899647 +v 65.634720 35.455376 22.041878 +v 65.131775 40.553104 24.720850 +v 65.167175 39.824814 24.904867 +v 66.336571 39.950649 25.690052 +v 65.539093 43.439342 21.544811 +v 68.963753 42.184120 18.172934 +v 65.264824 43.065830 20.119164 +v 65.181694 42.823616 19.687042 +v 68.852501 41.372654 17.594639 +v 68.782860 40.440216 17.232672 +v 64.884171 40.918030 18.140526 +v 70.264542 39.950649 24.934376 +v 70.267563 39.699635 24.950079 +v 65.444939 43.376144 21.055380 +v 65.104935 42.528164 19.288065 +v 69.032906 42.528164 18.532389 +v 64.854889 40.440216 17.988346 +v 68.903305 41.796913 17.858751 +v 69.748047 43.252190 22.249683 +v 65.820076 43.252190 23.005358 +v 65.908325 43.067505 23.464048 +v 70.217354 40.921688 24.689085 +v 70.246719 40.444027 24.841728 +v 66.198051 41.800091 24.970051 +v 70.065491 42.186985 23.899725 +v 66.248932 41.376099 25.234510 +v 70.176903 41.376099 24.478834 +v 65.539833 35.455128 21.548632 +v 65.733398 35.391171 22.554787 +v 65.737389 35.300861 22.575521 +v 65.728882 35.518578 22.531309 +v 65.908989 35.828888 23.467525 +v 66.514908 37.884193 26.617046 +v 66.289650 37.976688 25.446163 +v 66.249290 37.522064 25.236374 +v 66.138039 36.710598 24.658079 +v 66.198479 37.097805 24.972261 +v 66.339592 39.699635 25.705755 +v 66.342583 39.449322 25.721308 +v 64.837250 38.944069 17.896637 +v 64.855072 38.450691 17.989285 +v 65.036293 36.707745 18.931288 +v 64.597069 35.766628 16.648207 +v 64.835304 40.196674 17.886539 +v 70.272575 39.697979 24.976152 +v 68.773964 40.193138 17.186436 +v 64.845993 40.193138 17.942112 +v 69.192795 43.065830 19.363487 +v 69.274765 40.145279 19.789566 +v 69.315109 40.439426 19.999262 +v 69.367981 40.671249 20.274107 +v 69.398132 40.759056 20.430815 +v 69.467064 43.439342 20.789135 +v 69.372910 43.376144 20.299704 +v 69.430099 40.826176 20.596979 +v 69.497551 40.894455 20.947596 +v 69.566071 40.871815 21.303759 +v 69.531944 40.894543 21.126377 +v 69.561958 43.439590 21.282381 +v 69.656105 43.376877 21.771769 +v 69.599403 40.826614 21.477009 +v 69.836296 43.067505 22.708372 +v 69.919472 42.825726 23.140736 +v 69.689392 40.565052 21.944761 +v 69.996269 42.530670 23.539925 +v 69.714478 40.440468 22.075167 +v 69.754868 40.146526 22.285120 +v 70.126022 41.800091 24.214375 +v 69.736404 40.300220 22.189165 +v 68.763275 40.196674 17.130863 +v 68.812134 40.918030 17.384850 +v 69.249519 39.807266 19.658356 +v 69.243126 39.264919 19.625113 +v 69.109665 42.823616 18.931366 +v 69.281006 43.250977 19.822021 +v 69.240952 39.446640 19.613817 +v 69.315285 38.454250 20.000172 +v 69.340370 38.329666 20.130575 +v 68.904175 32.857552 17.863255 +v 68.646049 33.219078 16.521528 +v 68.513268 34.768574 15.831351 +v 69.368217 38.222706 20.275343 +v 69.166855 32.604336 19.228642 +v 69.699440 32.428364 21.996990 +v 69.566338 38.023182 21.305166 +v 70.442879 37.884193 25.861370 +v 69.736580 38.595646 22.190054 +v 69.769524 39.981804 22.361311 +v 69.769615 38.914249 22.361794 +v 69.780167 39.808659 22.416618 +v 69.786629 39.629799 22.450226 +v 70.230637 32.695507 24.758162 +v 67.914772 34.928131 12.720387 +v 68.394073 33.686779 15.211790 +v 66.470818 34.145573 26.387878 +v 64.642441 35.766621 16.639477 +v 64.630005 38.559273 16.819387 +v 44.275478 27.861065 12.912979 +v 46.509041 38.329666 24.522942 +v 46.462021 38.594498 24.278538 +v 46.418194 39.807266 24.050722 +v 45.931946 40.196674 21.523230 +v 46.428902 38.912914 24.106392 +v 45.951530 40.440216 21.625038 +v 45.942635 40.193138 21.578802 +v 46.443562 38.748192 24.182585 +v 46.483952 38.454250 24.392538 +v 46.567032 38.135056 24.824387 +v 47.555691 33.147514 29.963390 +v 46.800297 38.135670 26.036888 +v 46.948906 39.087452 26.809347 +v 47.088142 42.825726 27.533102 +v 46.938290 38.914249 26.754160 +v 47.234161 42.186985 28.292091 +v 47.345573 41.376099 28.871201 +v 46.883320 38.455292 26.468443 +v 47.611549 37.884193 30.253736 +v 47.415386 40.444027 29.234095 +v 46.957474 39.448078 26.853886 +v 47.441246 39.697979 29.368519 +v 46.132423 42.184120 22.565300 +v 46.566799 40.759056 24.823181 +v 46.598766 40.826176 24.989346 +v 46.632088 40.871548 25.162539 +v 46.541576 43.376144 24.692070 +v 46.666222 40.894455 25.339962 +v 46.700615 40.894543 25.518744 +v 46.734741 40.871815 25.696125 +v 46.858059 40.565052 26.337128 +v 46.905079 40.300220 26.581532 +v 46.923538 40.146526 26.677486 +v 46.948837 39.808659 26.808985 +v 47.004963 43.067505 27.100739 +v 45.655373 32.515556 20.085615 +v 48.601349 32.515556 19.518858 +v 46.635735 43.439342 25.181501 +v 49.581715 43.439342 24.614744 +v 50.034122 42.825726 26.966345 +v 46.361465 43.065830 23.755854 +v 46.449680 43.250977 24.214388 +v 48.967144 41.372654 21.420248 +v 48.926785 40.918030 21.210459 +v 45.980808 40.918030 21.777216 +v 49.017956 41.796913 21.684361 +v 46.824780 43.376877 26.164135 +v 47.433212 39.950649 29.326742 +v 47.436234 39.699635 29.342445 +v 49.487556 43.376144 24.125313 +v 46.201576 42.528164 22.924755 +v 46.278332 42.823616 23.323732 +v 48.897511 40.440216 21.058281 +v 48.888615 40.193138 21.012045 +v 46.071980 41.796913 22.251118 +v 46.021168 41.372654 21.987005 +v 46.730629 43.439590 25.674747 +v 47.164940 42.530670 27.932291 +v 46.916721 43.252190 26.642050 +v 49.950943 43.067505 26.533981 +v 50.361366 40.444027 28.667337 +v 50.379189 39.950649 28.759985 +v 50.180138 42.186985 27.725334 +v 50.240673 41.800091 28.039984 +v 47.294693 41.800091 28.606741 +v 50.291550 41.376099 28.304443 +v 47.386021 40.921688 29.081451 +v 50.331997 40.921688 28.514694 +v 49.676605 43.439590 25.107990 +v 49.677341 35.455376 25.111813 +v 49.582447 35.455128 24.618565 +v 49.862698 43.252190 26.075293 +v 50.607594 36.733505 25.977272 +v 50.541428 36.594643 25.633348 +v 49.396355 35.642532 23.651264 +v 50.669476 41.981129 26.298937 +v 50.094238 39.628387 23.308878 +v 49.799259 39.445889 21.775591 +v 49.838955 40.550362 21.981924 +v 49.869232 40.891331 22.139292 +v 50.144321 40.299072 23.569202 +v 50.166248 40.439426 23.683182 +v 50.004551 41.757961 22.842676 +v 49.817135 38.699863 21.868494 +v 50.219124 40.671249 23.958029 +v 50.100666 39.807266 23.342276 +v 50.111282 39.980480 23.397465 +v 50.249271 40.759056 24.114737 +v 50.062096 41.979553 23.141790 +v 50.191307 40.564144 23.813421 +v 50.281239 40.826176 24.280899 +v 50.190613 42.300076 23.809828 +v 50.314560 40.871548 24.454092 +v 50.401329 42.441532 24.905123 +v 50.417213 40.871815 24.987679 +v 50.471947 42.394493 25.272188 +v 50.450542 40.826614 25.160929 +v 50.482540 40.759663 25.327238 +v 50.540531 40.565052 25.628683 +v 50.862545 40.893909 27.302488 +v 50.824368 41.211906 27.104046 +v 49.839165 38.341614 21.983002 +v 50.111374 38.912914 23.397947 +v 49.869499 38.000809 22.140690 +v 50.004963 37.134884 22.844826 +v 50.191513 38.329666 23.814495 +v 50.166424 38.454250 23.684092 +v 50.219364 38.222706 23.959265 +v 50.281502 38.068111 24.282249 +v 50.260098 36.500225 24.170990 +v 50.383350 38.000263 24.811661 +v 50.401886 36.453377 24.908012 +v 50.330711 36.453186 24.538054 +v 50.472500 36.500771 25.275061 +v 50.450802 38.068542 25.162279 +v 50.482769 38.135670 25.328442 +v 50.669949 36.915165 26.301388 +v 50.540737 38.330574 25.629755 +v 50.892879 40.553104 27.460176 +v 50.637775 39.629799 26.134146 +v 50.779354 37.394798 26.870068 +v 50.914909 40.194855 27.574684 +v 50.587723 38.595646 25.873976 +v 50.862812 38.003387 27.303885 +v 50.620762 38.914249 26.045713 +v 50.631378 39.087452 26.100901 +v 50.932781 39.448830 27.667585 +v 50.727493 37.136757 26.600500 +v 50.111500 36.366562 27.368559 +v 50.361546 38.454502 28.668276 +v 50.893085 38.344357 27.461254 +v 50.915051 38.702721 27.575411 +v 49.907337 41.209522 22.337376 +v 48.879868 38.944069 20.966570 +v 49.953072 37.392635 22.575094 +v 49.907677 37.682812 22.339132 +v 49.078915 36.707745 22.001223 +v 50.062565 36.913589 23.144239 +v 50.191143 36.593735 23.812578 +v 50.124954 36.732250 23.468536 +v 49.770756 43.376877 25.597380 +v 50.607086 42.162468 25.974642 +v 50.540901 42.300983 25.630598 +v 50.778973 41.502083 26.868084 +v 50.034744 36.071102 26.969580 +v 49.951611 35.828888 26.537458 +v 50.180653 36.710598 27.728012 +v 50.928349 39.072823 27.644539 +v 49.078400 42.184120 21.998545 +v 48.927055 37.973030 21.211861 +v 49.018383 37.094627 21.686571 +v 49.488297 35.517838 24.129177 +v 50.110920 42.530670 27.365534 +v 50.727081 41.759834 26.598352 +v 50.928280 39.824814 27.644192 +v 50.385201 39.449322 28.791241 +v 50.824703 37.685196 27.105803 +v 50.241096 37.097805 28.042196 +v 49.803696 39.821896 21.798639 +v 48.873852 39.445396 20.935314 +v 49.307442 43.065830 23.189098 +v 49.952690 41.499920 22.573111 +v 49.803761 39.069904 21.798986 +v 49.395657 43.250977 23.647631 +v 49.147552 42.528164 22.357998 +v 49.224308 42.823616 22.756975 +v 50.124451 42.161217 23.465906 +v 49.816994 40.191998 21.867767 +v 50.330158 42.441341 24.535164 +v 50.259544 42.393948 24.168116 +v 68.149811 34.257607 13.942123 +v 72.566849 34.970417 11.758610 +v 72.814278 34.257607 13.044758 +v 73.310516 33.219078 15.624163 +v 74.630486 32.506702 22.485336 +v 69.432487 32.460957 20.609421 +v 74.096954 32.460957 19.712055 +v 69.966026 32.506702 23.382702 +v 70.491646 32.993713 26.114845 +v 64.621979 40.894543 22.070972 +v 64.587585 40.894455 21.892191 +v 50.348694 40.894455 24.631517 +v 50.383087 40.894543 24.810297 +v 69.788803 39.448078 22.461519 +v 46.955303 39.629799 26.842592 +v 69.340164 40.564144 20.129501 +v 64.430199 40.564144 21.074097 +v 46.483776 40.439426 24.391628 +v 64.430405 38.329666 21.075171 +v 64.364929 38.748192 20.734812 +v 69.274887 38.748192 19.790218 +v 50.126034 38.748192 23.474138 +v 69.661537 40.672024 21.799994 +v 64.339561 39.807266 20.602951 +v 69.243095 39.628387 19.624958 +v 46.411766 39.628387 24.017324 +v 46.443436 40.145279 24.181932 +v 64.458260 38.222706 21.219938 +v 69.398361 38.135056 20.432020 +v 50.249504 38.135056 24.115940 +v 46.536892 38.222706 24.667709 +v 69.463684 38.022903 20.771578 +v 69.430359 38.068111 20.598330 +v 50.314831 38.022903 24.455498 +v 69.293350 38.594498 19.886171 +v 50.144493 38.594498 23.570091 +v 69.249596 39.086071 19.658720 +v 69.260231 38.912914 19.714025 +v 50.100735 39.086071 23.342640 +v 64.870201 39.808659 23.361214 +v 64.520134 40.826176 21.541573 +v 69.463417 40.871548 20.770172 +v 46.508835 40.564144 24.521868 +v 46.536652 40.671249 24.666473 +v 64.333130 39.628387 20.569553 +v 46.409622 39.446640 24.006184 +v 50.092098 39.446640 23.297737 +v 69.260139 39.980480 19.713545 +v 50.125908 40.145279 23.473488 +v 46.428810 39.980480 24.105911 +v 64.383217 40.299072 20.829878 +v 69.293182 40.299072 19.885283 +v 46.461849 40.299072 24.277649 +v 46.599026 38.068111 24.990696 +v 64.553726 38.022903 21.716173 +v 50.348957 38.000175 24.632881 +v 46.632359 38.022903 25.163944 +v 64.383385 38.594498 20.830767 +v 64.333160 39.264919 20.569706 +v 46.418262 39.086071 24.051086 +v 46.411797 39.264919 24.017479 +v 50.094269 39.264919 23.309032 +v 46.883144 40.440468 26.467533 +v 50.565620 40.440468 25.759087 +v 69.714653 38.455292 22.076077 +v 46.905251 38.595646 26.582420 +v 50.565792 38.455292 25.759996 +v 50.620667 39.981804 26.045231 +v 46.938194 39.981804 26.753677 +v 69.497810 38.000175 20.948959 +v 46.666485 38.000175 25.341326 +v 50.587551 40.300220 25.873085 +v 50.606010 40.146526 25.969040 +v 64.779633 38.330574 22.890430 +v 69.599663 38.068542 21.478359 +v 69.631630 38.135670 21.644522 +v 46.768330 38.068542 25.870726 +v 50.631310 39.808659 26.100538 +v 64.721436 40.759663 22.587912 +v 69.631393 40.759663 21.643318 +v 46.768070 40.826614 25.869375 +v 46.830208 40.672024 26.192360 +v 50.512680 40.672024 25.483913 +v 46.800064 40.759663 26.035685 +v 64.826447 40.300220 23.133760 +v 69.786659 39.266331 22.450378 +v 64.878838 39.448078 23.406115 +v 50.637802 39.266331 26.134300 +v 50.639946 39.448078 26.145439 +v 64.876694 39.266331 23.394974 +v 46.955330 39.266331 26.842745 +v 64.751808 38.223469 22.745825 +v 69.661774 38.223469 21.801229 +v 69.689598 38.330574 21.945835 +v 46.830444 38.223469 26.193596 +v 46.858265 38.330574 26.338202 +v 50.512917 38.223469 25.485149 +v 64.656380 38.023182 22.249760 +v 64.689697 38.068542 22.422955 +v 46.735012 38.023182 25.697533 +v 64.622246 38.000263 22.072336 +v 69.532204 38.000263 21.127741 +v 46.700878 38.000263 25.520107 +v 50.417484 38.023182 24.989086 +v 69.754990 38.749439 22.285770 +v 64.859657 38.914249 23.306389 +v 50.606136 38.749439 25.969690 +v 46.923664 38.749439 26.678137 +v 64.870270 39.087452 23.361576 +v 69.780235 39.087452 22.416981 +v 61.316620 34.574280 6.299973 +v 61.312920 34.574375 6.490880 +v 55.456875 34.574280 7.427290 +v 61.036880 32.926773 4.759091 +v 61.039318 33.133568 4.771767 +v 70.859245 33.133568 2.882577 +v 55.144924 32.926773 5.892605 +v 60.995525 31.783306 7.656045 +v 60.987606 31.798721 7.674161 +v 71.395844 31.798721 5.671790 +v 55.257820 34.198410 6.479429 +v 70.866638 32.516430 2.921004 +v 71.072044 34.496571 3.988700 +v 46.522228 34.496571 8.711675 +v 46.485836 34.420193 8.522511 +v 70.895279 33.721729 3.069858 +v 46.328823 33.534252 7.706368 +v 55.183392 33.721729 6.092562 +v 55.166759 33.534252 6.006098 +v 70.941185 34.056538 3.308511 +v 61.096321 33.896751 5.068068 +v 61.058811 32.319458 4.873092 +v 70.878738 32.319458 2.983902 +v 46.328922 32.319458 7.706877 +v 70.859268 32.719955 2.882709 +v 61.046711 32.516430 4.810194 +v 46.908474 33.535702 10.719367 +v 56.492821 33.535702 8.875499 +v 56.404926 33.723171 8.802396 +v 71.420784 31.958406 5.801417 +v 60.925636 31.958406 7.820507 +v 46.846027 31.798721 10.394764 +v 61.272781 34.524712 5.985285 +v 61.256676 34.524712 5.988383 +v 55.360161 34.496571 7.011404 +v 61.312759 34.574272 6.279903 +v 71.148788 34.574272 4.387615 +v 61.273930 34.548199 6.078078 +v 55.453011 34.574272 7.407220 +v 55.414185 34.548199 7.205395 +v 46.419884 34.198410 8.179700 +v 71.035652 34.420193 3.799536 +v 55.323772 34.420193 6.822240 +v 46.451553 34.320305 8.344303 +v 46.309429 33.133568 7.605552 +v 55.147362 33.133568 5.905281 +v 61.075348 33.721729 4.959048 +v 46.345459 33.721729 7.792832 +v 61.274227 31.306643 6.079606 +v 55.414478 31.306643 7.206923 +v 61.181690 31.534010 5.511811 +v 55.289734 31.534010 6.645325 +v 70.941391 31.797485 3.309555 +v 70.969948 31.655691 3.457995 +v 46.391571 31.797485 8.032529 +v 55.258064 31.655691 6.480699 +v 61.235847 31.392776 5.793306 +v 71.072342 31.358046 3.990242 +v 46.522526 31.358046 8.713217 +v 55.376564 31.358046 7.009848 +v 61.274078 31.280785 6.288991 +v 55.418308 31.304075 7.226835 +v 46.560440 31.306643 8.910293 +v 70.895439 32.131989 3.070702 +v 46.366615 31.957125 7.902797 +v 46.345619 32.131989 7.793677 +v 55.183556 32.131989 6.093406 +v 70.856804 32.926773 2.869901 +v 46.309456 32.719955 7.605683 +v 56.676979 32.928387 8.958290 +v 46.927944 33.135204 10.820560 +v 71.470390 33.338730 6.059290 +v 60.891953 33.535702 8.029180 +v 46.920574 33.338730 10.782265 +v 60.946449 33.723171 7.928683 +v 71.367081 34.199467 5.522300 +v 46.845825 34.057674 10.393715 +v 61.258160 34.420887 7.111399 +v 71.335411 34.321148 5.357674 +v 55.812225 34.420887 8.159105 +v 46.785595 34.321148 10.080648 +v 71.441757 32.133430 5.910437 +v 56.384117 31.958406 8.694220 +v 56.476284 32.133430 8.789543 +v 71.470482 32.518005 6.059753 +v 60.814278 32.320908 8.044650 +v 46.920666 32.518005 10.782727 +v 56.570694 32.320908 8.861045 +v 60.751659 32.928387 8.174390 +v 71.335663 31.534855 5.358966 +v 61.176445 31.434967 7.128626 +v 46.751560 31.434967 9.903733 +v 55.776115 31.358620 7.971396 +v 71.188240 31.280876 4.592679 +v 61.270115 31.280876 6.500761 +v 61.266422 31.306923 6.710764 +v 56.582695 33.338730 8.923434 +v 71.420601 33.898132 5.800478 +v 61.088646 34.057674 7.653634 +v 71.395645 34.057674 5.670740 +v 61.060886 33.999672 7.707832 +v 46.817268 34.199467 10.245275 +v 56.036705 34.199467 8.471608 +v 55.928535 34.321148 8.321699 +v 71.264687 34.497074 4.990050 +v 55.613091 34.548508 7.796787 +v 46.676956 34.548508 9.515950 +v 61.303711 34.490608 6.923106 +v 55.693523 34.497074 7.985682 +v 71.187935 34.574375 4.591091 +v 46.638119 34.574375 9.314066 +v 71.458389 32.320908 5.996901 +v 60.868248 32.168861 7.961548 +v 46.908573 32.320908 10.719875 +v 71.477783 32.721592 6.097717 +v 60.770199 32.518005 8.118308 +v 56.674541 32.721592 8.945614 +v 56.638954 32.518005 8.913091 +v 61.266220 31.311365 6.728227 +v 55.655907 31.306923 7.790134 +v 46.677250 31.306923 9.517477 +v 55.662575 31.311365 7.806274 +v 45.754868 31.906664 4.722975 +v 46.792843 27.567686 6.719300 +v 70.601196 28.748648 1.541240 +v 60.484760 31.906664 1.889190 +v 54.592804 31.906664 3.022704 +v 51.725163 32.743256 35.756310 +v 76.274979 32.743256 31.033335 +v 75.544533 25.600410 33.281940 +v 52.123806 26.241262 37.828434 +v 72.279839 27.965517 28.017206 +v 71.904739 28.001427 27.503540 +v 71.559708 28.020782 27.254135 +v 71.366455 28.028603 27.163761 +v 70.953598 28.039904 27.058821 +v 70.527489 28.044874 27.059690 +v 67.170486 28.113617 26.584072 +v 69.045937 27.965517 28.639355 +v 69.125305 27.990200 28.221403 +v 69.203674 28.001427 28.023180 +v 69.306305 28.011675 27.836245 +v 69.577461 28.028603 27.507933 +v 69.921913 28.039904 27.257298 +v 69.741806 28.035006 27.371834 +v 70.114883 28.043211 27.166206 +v 72.439278 27.912836 28.845966 +v 72.412743 27.900188 29.057392 +v 72.359901 27.888153 29.263918 +v 72.178909 27.866678 29.649078 +v 71.743401 27.843346 30.113489 +v 71.370232 27.835142 30.319136 +v 71.167282 27.833479 30.385315 +v 69.073219 27.939177 29.063824 +v 68.226372 27.764736 32.072548 +v 69.205376 27.912836 29.468115 +v 70.531616 27.838449 30.426502 +v 70.744606 27.835142 30.439495 +v 70.321869 27.843346 30.386969 +v 69.925507 27.857569 30.231188 +v 69.745140 27.866678 30.117294 +v 69.580475 27.876926 29.981783 +v 69.434158 27.888153 29.826784 +v 67.195198 30.209538 26.712547 +v 71.908768 30.209538 25.805735 +v 72.964653 29.860657 31.294212 +v 72.943474 28.064154 31.184092 +v 68.251091 29.860657 32.201023 +v 68.569992 30.209538 26.448061 +v 71.887581 28.413034 25.695614 +v 57.682102 27.939177 31.255283 +v 57.321167 27.990200 30.492323 +v 57.174850 28.001427 30.337324 +v 57.010181 28.011675 30.201813 +v 56.829819 28.020782 30.087919 +v 56.433453 28.035006 29.932138 +v 55.797596 28.044874 29.893476 +v 55.588039 28.044874 29.933792 +v 55.385090 28.043211 29.999971 +v 54.316307 27.952452 31.686220 +v 54.316055 27.965517 31.473139 +v 54.395416 27.990200 31.055189 +v 54.576412 28.011675 30.670029 +v 54.701645 28.020782 30.497345 +v 55.011921 28.035006 30.205618 +v 55.192024 28.039904 30.091084 +v 57.709126 27.925900 31.466671 +v 57.709385 27.912836 31.679752 +v 57.630016 27.888153 32.097702 +v 57.551651 27.876926 32.295929 +v 59.584839 27.764736 33.735035 +v 57.449020 27.866678 32.482864 +v 56.640343 27.835142 33.152920 +v 56.437393 27.833479 33.219101 +v 56.227837 27.833479 33.259415 +v 54.343330 27.939177 31.897608 +v 50.574806 28.113617 29.776804 +v 54.578556 27.900188 32.488392 +v 55.015247 27.866678 32.951080 +v 58.234764 29.860657 34.127998 +v 53.840099 30.209538 29.281845 +v 50.599525 30.209538 29.905277 +v 53.818913 28.413034 29.171724 +v 58.528950 28.113617 28.246559 +v 57.157688 28.413034 28.529400 +v 58.553665 30.209538 28.375034 +v 58.213581 28.064154 34.017876 +v 59.609554 29.860657 33.863510 +v 51.655415 29.860657 35.393753 +v 69.604698 28.064154 31.826416 +v 72.962326 28.057924 31.282099 +v 69.623550 28.057924 31.924423 +v 69.104996 30.633932 29.229004 +v 68.780930 30.440424 27.544506 +v 69.422005 30.228603 30.876797 +v 69.625877 29.860657 31.936537 +v 68.548805 28.413034 26.337940 +v 69.648270 30.153843 32.052898 +v 72.760780 30.228603 30.234472 +v 71.893440 30.515184 25.726080 +v 68.554665 30.515184 26.368404 +v 68.529945 28.419264 26.239931 +v 71.868721 28.419264 25.597607 +v 72.119705 30.440424 26.902182 +v 72.443771 30.633932 28.586679 +v 72.987045 30.153843 31.410572 +v 72.764313 30.528021 30.252825 +v 72.471054 30.607592 29.011148 +v 72.470795 30.620655 28.798069 +v 72.391685 30.582907 29.429100 +v 72.210686 30.561432 29.814259 +v 72.313316 30.571682 29.627323 +v 71.775185 30.538101 30.278669 +v 71.595078 30.533205 30.393206 +v 69.425537 30.528021 30.895151 +v 69.776917 30.561432 30.282476 +v 69.340240 30.594944 29.819784 +v 72.123238 30.739841 26.920534 +v 72.390434 30.647207 28.380356 +v 72.208527 30.672918 27.995901 +v 71.771851 30.706430 27.533209 +v 71.398232 30.723356 27.328941 +v 71.195122 30.729761 27.263535 +v 70.772285 30.737967 27.211027 +v 68.784454 30.739841 27.562859 +v 69.773590 30.729761 27.537014 +v 69.609238 30.723356 27.673115 +v 69.235451 30.696182 28.188360 +v 69.338081 30.706430 28.001427 +v 69.077972 30.647207 29.017616 +v 58.232433 28.057924 34.115883 +v 54.051037 30.440424 30.378290 +v 53.824776 30.515184 29.202190 +v 53.800060 28.419264 29.073715 +v 54.692116 30.228603 33.710583 +v 54.893661 28.057924 34.758209 +v 54.895992 29.860657 34.770321 +v 54.918377 30.153843 34.886681 +v 54.874805 28.064154 34.660202 +v 58.257153 30.153843 34.244358 +v 57.389812 30.440424 29.735966 +v 57.163551 30.515184 28.559864 +v 57.138832 28.419264 28.431391 +v 57.178875 30.209538 28.639521 +v 57.661793 30.582907 32.262886 +v 57.583431 30.571682 32.461109 +v 57.355564 30.552324 32.820728 +v 58.034420 30.528021 33.086609 +v 56.672123 30.529896 33.318100 +v 54.695644 30.528021 33.728935 +v 55.420643 30.544506 33.320526 +v 55.047028 30.561432 33.116261 +v 54.736042 30.582907 32.825748 +v 54.882362 30.571682 32.980751 +v 54.507275 30.607592 32.467079 +v 54.375107 30.633932 32.062790 +v 57.581722 30.660273 31.016172 +v 55.829376 30.739630 30.058657 +v 57.393345 30.739841 29.754320 +v 55.223801 30.734657 30.256264 +v 54.374340 30.672918 31.426899 +v 54.054569 30.739841 30.396645 +v 54.348083 30.647207 31.851400 +v 58.030888 30.228603 33.068256 +v 72.358658 27.952452 28.215174 +v 72.411995 27.939177 28.421499 +v 69.126556 27.925900 29.270149 +v 69.067329 27.440147 29.033234 +v 71.161400 27.334450 30.354727 +v 70.957726 27.833479 30.425631 +v 70.951836 27.334450 30.395042 +v 69.919617 27.358541 30.200598 +v 69.739250 27.367649 30.086706 +v 69.780449 30.860849 30.300829 +v 69.431534 28.020782 27.663559 +v 69.777115 31.029179 27.555368 +v 69.302582 27.401161 29.624014 +v 69.469467 30.882324 30.010319 +v 70.947708 27.540874 27.028231 +v 71.157455 27.535978 27.067764 +v 70.985374 30.734657 27.224001 +v 70.988907 31.034075 27.242355 +v 69.072472 27.978165 28.427931 +v 69.157089 30.684956 28.386585 +v 69.104248 30.672918 28.593111 +v 69.735924 27.535978 27.341244 +v 69.953697 30.734657 27.422480 +v 70.150192 31.037384 27.349741 +v 71.907753 27.849751 29.977388 +v 70.315987 27.344318 30.356379 +v 70.563393 30.533205 30.591682 +v 70.154068 30.843924 30.505096 +v 69.960815 30.851742 30.414722 +v 69.957283 30.552324 30.396368 +v 69.199493 27.413807 29.437527 +v 69.308464 27.900188 29.654604 +v 72.311615 30.660273 28.182388 +v 71.740074 28.011675 27.368027 +v 71.734192 27.512646 27.337439 +v 71.591484 30.715538 27.419315 +v 71.898857 27.502398 27.472950 +v 72.045174 27.491171 27.627949 +v 72.051056 27.990200 27.658539 +v 71.936516 30.696182 27.668720 +v 72.082832 30.684956 27.823719 +v 70.734627 27.544182 27.015257 +v 70.740509 28.043211 27.045845 +v 70.775818 31.037384 27.229380 +v 70.317932 28.044874 27.100006 +v 70.349709 30.739630 27.265188 +v 70.559265 30.739630 27.224873 +v 71.163345 28.035006 27.098354 +v 71.401764 31.022774 27.347296 +v 69.040314 27.453424 28.821846 +v 69.081505 30.946625 29.035971 +v 69.160614 30.984373 28.404938 +v 69.040054 27.466488 28.608767 +v 69.077713 30.660273 28.804537 +v 69.046196 27.952452 28.852436 +v 69.463310 30.715538 27.828741 +v 69.466843 31.014956 27.847094 +v 69.612770 31.022774 27.691467 +v 70.108994 27.544182 27.135616 +v 70.146660 30.737967 27.331387 +v 71.402016 30.529896 30.484316 +v 71.202591 30.827650 30.568850 +v 71.199059 30.528233 30.550497 +v 71.778709 30.837519 30.297024 +v 72.047791 27.358541 29.791174 +v 72.085457 30.552324 29.986944 +v 72.088989 30.851742 30.005297 +v 71.943062 30.843924 30.160923 +v 71.939529 30.544506 30.142570 +v 72.173027 27.367649 29.618488 +v 72.316849 30.871098 29.645678 +v 72.444519 30.594944 29.222574 +v 70.118759 27.849751 30.321562 +v 70.112869 27.350721 30.290972 +v 70.353645 30.538101 30.552149 +v 70.150536 30.544506 30.486742 +v 70.566925 30.832623 30.610037 +v 70.989502 30.528233 30.590813 +v 70.776382 30.529896 30.604677 +v 69.465935 30.582907 29.991964 +v 69.574585 27.377897 29.951195 +v 69.612251 30.571682 30.146965 +v 69.120674 27.426872 29.239559 +v 69.158333 30.620655 29.435329 +v 69.240685 30.907009 29.651649 +v 69.237152 30.607592 29.633297 +v 72.170860 27.479136 27.800131 +v 72.176750 27.978165 27.830719 +v 72.212059 30.972336 28.014254 +v 71.775383 31.005848 27.551561 +v 70.562798 31.039047 27.243225 +v 71.563293 27.838449 30.228024 +v 71.598602 30.832623 30.411558 +v 72.053680 27.857569 29.821762 +v 72.275658 27.377897 29.431553 +v 72.281540 27.876926 29.462143 +v 72.439018 27.925900 28.632887 +v 57.622879 27.453424 31.018370 +v 57.676220 27.440147 31.224695 +v 54.396667 27.925900 32.103931 +v 56.469170 30.528233 33.384281 +v 56.263145 30.827650 33.442951 +v 56.259613 30.528233 33.424599 +v 55.189728 27.358541 33.034382 +v 55.227390 30.552324 33.230152 +v 54.736954 31.014956 30.680880 +v 55.006035 27.535978 30.175030 +v 55.227333 31.034075 30.274618 +v 55.043697 30.729761 30.370800 +v 54.704266 27.888153 32.660568 +v 54.610332 30.594944 32.653572 +v 56.223705 28.039904 29.892605 +v 56.217823 27.540874 29.862017 +v 56.427567 27.535978 29.901550 +v 56.468761 31.029179 30.115673 +v 56.255486 30.734657 30.057787 +v 56.465229 30.729761 30.097321 +v 56.630680 27.529573 29.966957 +v 54.342564 27.978165 31.261719 +v 54.336678 27.479136 31.231129 +v 54.427197 30.684956 31.220369 +v 54.847572 28.028603 30.341719 +v 54.841686 27.529573 30.311129 +v 54.879349 30.723356 30.506899 +v 55.186138 27.540874 30.060493 +v 55.379204 27.544182 29.969383 +v 55.416866 30.737967 30.165154 +v 57.013512 27.843346 32.947273 +v 57.213169 30.843924 32.994709 +v 57.048820 30.837519 33.130810 +v 57.209641 30.544506 32.976357 +v 55.586098 27.344318 33.190163 +v 55.833504 30.533205 33.425468 +v 55.627289 30.837519 33.404289 +v 55.623760 30.538101 33.385933 +v 55.195614 27.857569 33.064972 +v 55.230923 30.851742 33.248508 +v 54.469612 27.413807 32.271309 +v 54.510807 30.907009 32.485432 +v 57.549946 27.965517 30.850992 +v 57.628765 27.952452 31.048958 +v 57.660542 30.647207 31.214140 +v 57.585255 30.959690 31.034527 +v 57.356476 30.984373 30.675858 +v 57.478638 30.672918 30.829685 +v 56.861599 30.715538 30.253101 +v 57.041962 30.706430 30.366993 +v 57.210159 30.995600 30.520859 +v 57.352943 30.684956 30.657505 +v 55.619820 30.739630 30.098972 +v 56.636566 28.028603 29.997545 +v 56.668343 30.723356 30.162727 +v 54.347836 30.660273 31.638319 +v 54.467896 27.502398 30.826376 +v 54.389534 27.491171 31.024599 +v 54.505558 30.696182 31.022146 +v 54.473782 28.001427 30.856964 +v 54.608192 30.706430 30.835211 +v 54.310173 27.466488 31.442549 +v 54.351364 30.959690 31.656673 +v 54.695759 27.521755 30.466755 +v 54.733421 30.715538 30.662525 +v 55.623348 31.039047 30.117327 +v 57.713882 30.633932 31.420465 +v 57.740902 30.620655 31.631853 +v 56.431507 27.334450 33.188511 +v 56.833408 27.838449 33.061810 +v 57.045292 30.538101 33.112453 +v 56.865185 30.533205 33.226990 +v 57.177860 27.849751 32.811172 +v 57.359097 30.851742 32.839081 +v 57.682850 27.900188 31.891176 +v 57.714626 30.594944 32.056358 +v 57.676964 27.401161 31.860588 +v 57.744694 30.907009 31.863287 +v 57.718159 30.894361 32.074711 +v 55.591980 27.843346 33.220753 +v 55.388866 27.849751 33.155346 +v 55.424175 30.843924 33.338882 +v 55.801727 27.838449 33.260288 +v 56.014816 27.835142 33.273262 +v 56.050125 30.829313 33.456795 +v 56.046593 30.529896 33.438442 +v 54.850582 27.876926 32.815567 +v 54.844696 27.377897 32.784981 +v 55.050560 30.860849 33.134613 +v 54.475498 27.912836 32.301899 +v 54.428448 30.620655 32.269115 +v 57.440971 27.479136 30.633915 +v 57.446857 27.978165 30.664505 +v 57.206627 30.696182 30.502504 +v 55.791714 27.545847 29.862886 +v 56.010616 28.043211 29.879631 +v 56.004734 27.544182 29.849041 +v 56.045925 31.037384 30.063166 +v 56.042397 30.737967 30.044811 +v 56.675652 30.829313 33.336456 +v 57.323788 27.857569 32.655548 +v 57.317902 27.358541 32.624958 +v 57.480797 30.561432 32.648045 +v 57.484329 30.860849 32.666397 +v 57.545769 27.377897 32.265339 +v 57.703499 27.413807 31.649162 +v 57.741161 30.607592 31.844933 +v 73.133041 25.948370 28.069304 +v 68.864822 26.065239 26.983849 +v 72.354019 27.389124 29.233330 +v 72.406853 27.401161 29.026802 +v 73.155472 25.886189 29.079416 +v 72.433388 27.413807 28.815378 +v 72.352768 27.453424 28.184586 +v 71.360573 27.529573 27.133171 +v 71.414162 26.080296 26.247744 +v 71.026947 26.090002 26.163887 +v 71.110840 26.088047 26.179663 +v 72.418747 25.812611 30.421471 +v 73.194214 25.904634 28.771019 +v 72.678169 26.012682 27.107624 +v 72.920380 25.986547 27.487391 +v 71.983307 26.058466 26.494383 +v 69.803406 26.094831 26.320511 +v 70.099487 26.097260 26.223911 +v 70.521606 27.545847 27.029102 +v 70.312042 27.545847 27.069418 +v 70.405281 26.097260 26.165081 +v 68.272964 25.948370 29.004301 +v 68.243927 25.981487 28.469589 +v 68.442665 26.029385 27.649981 +v 68.359734 26.017492 27.859955 +v 69.119423 27.491171 28.190815 +v 70.525734 27.339420 30.395912 +v 70.496414 25.794794 31.081974 +v 71.032852 25.788862 31.075527 +v 68.517754 25.899576 29.753220 +v 68.305092 25.937754 29.171307 +v 72.244850 25.805826 30.565607 +v 72.179062 25.803261 30.620136 +v 72.814415 25.837296 29.942667 +v 71.901863 27.350721 29.946800 +v 72.964188 25.852247 29.669930 +v 73.099586 25.873442 29.298115 +v 73.078400 25.868631 29.380653 +v 72.414482 26.033876 26.812588 +v 72.348557 26.037975 26.758394 +v 72.472954 26.029385 26.874619 +v 72.627869 26.017492 27.038837 +v 72.174210 26.048828 26.614885 +v 71.332840 26.082863 26.221514 +v 71.553825 27.521755 27.223545 +v 69.449699 26.088047 26.499239 +v 69.916031 27.540874 27.226709 +v 69.884560 26.095497 26.294022 +v 70.941620 26.091330 26.158655 +v 68.244370 25.962425 28.780510 +v 69.425652 27.521755 27.632971 +v 68.473946 26.033876 27.570681 +v 69.197784 27.502398 27.992590 +v 68.282661 25.999935 28.161194 +v 69.066589 27.479136 28.397341 +v 70.738724 27.336113 30.408907 +v 69.428268 27.389124 29.796194 +v 68.444992 25.909863 29.599417 +v 71.364349 27.336113 30.288546 +v 71.634727 25.791292 30.920099 +v 71.839088 25.794794 30.823664 +v 71.557411 27.339420 30.197435 +v 71.916382 25.796120 30.787148 +v 71.737518 27.344318 30.082899 +v 73.193764 25.923698 28.460100 +v 72.406113 27.440147 28.390909 +v 73.154411 25.943062 28.151785 +v 72.433128 27.426872 28.602299 +v 72.961609 25.981487 27.561985 +v 72.273949 27.466488 27.986618 +v 69.193283 26.080296 26.675001 +v 69.571579 27.529573 27.477345 +v 69.019287 26.073511 26.819159 +v 69.300415 27.512646 27.805655 +v 70.722069 25.791292 31.095680 +v 72.573311 25.820885 30.256760 +v 72.681854 25.827658 30.125401 +v 69.259071 26.082863 26.620474 +v 58.424519 25.943062 30.985571 +v 57.258545 25.798077 33.575157 +v 55.597404 25.798077 33.894730 +v 58.348507 25.868631 32.214439 +v 58.453705 25.899576 31.689375 +v 57.544060 27.466488 30.820402 +v 58.346668 25.962425 30.684662 +v 56.602951 26.082863 29.055300 +v 56.380947 26.088047 29.013447 +v 55.369595 26.097260 29.057695 +v 55.382980 27.350721 33.124756 +v 57.171974 27.350721 32.780586 +v 58.190487 25.986547 30.321175 +v 57.004299 27.512646 30.171223 +v 57.253414 26.058466 29.328167 +v 55.073513 26.094831 29.154295 +v 53.744095 26.033876 30.404459 +v 53.608658 26.012682 30.776279 +v 53.629807 26.017492 30.693748 +v 53.896976 25.886189 32.784428 +v 54.337444 27.440147 31.867020 +v 54.310421 27.453424 31.655630 +v 53.543034 25.948370 31.838093 +v 53.575165 25.937754 32.005100 +v 54.390781 27.426872 32.073345 +v 53.631645 25.923698 32.223526 +v 57.449173 25.803261 33.453922 +v 57.007629 27.344318 32.916683 +v 57.443134 27.367649 32.452274 +v 58.084530 25.837296 32.776451 +v 57.624130 27.389124 32.067116 +v 58.425587 25.886189 31.913200 +v 58.464214 25.909863 31.519552 +v 57.315281 27.491171 30.461735 +v 57.743065 26.029385 29.708405 +v 57.168964 27.502398 30.306734 +v 56.823933 27.521755 30.057331 +v 55.154671 26.095497 29.127806 +v 55.582157 27.545847 29.903202 +v 56.211830 26.091330 28.992420 +v 56.297058 26.090002 28.997673 +v 55.900986 26.095497 28.984228 +v 53.893787 26.048828 30.131733 +v 53.785137 26.037975 30.329685 +v 54.134899 26.065239 29.817640 +v 53.524647 25.986547 31.218805 +v 55.795841 27.339420 33.229698 +v 53.787865 25.899576 32.587002 +v 54.572670 27.401161 32.457802 +v 54.698380 27.389124 32.629978 +v 54.030071 25.873442 32.966770 +v 54.235268 25.856739 33.199780 +v 54.293797 25.852247 33.261799 +v 55.009365 27.367649 32.920490 +v 54.724979 25.827658 33.580006 +v 53.715111 25.909863 32.433201 +v 53.746639 25.904634 32.512405 +v 53.553837 25.943062 31.922609 +v 57.901814 25.824013 33.028290 +v 56.634460 27.336113 33.122330 +v 57.186497 25.796120 33.620930 +v 56.827522 27.339420 33.031219 +v 58.234299 25.852247 32.503712 +v 57.703239 27.426872 31.436083 +v 58.231724 25.981487 30.395771 +v 57.897980 26.017492 29.872623 +v 54.289398 26.073511 29.652943 +v 54.570530 27.512646 30.639441 +v 56.221951 27.334450 33.228828 +v 56.008930 27.336113 33.242672 +v 55.992176 25.791292 33.929466 +v 57.843426 25.820885 33.090546 +v 55.294083 25.805826 33.826653 +v 72.689682 32.936100 28.577045 +v 69.957222 31.034075 27.440832 +v 69.108528 30.933350 29.247358 +v 68.913231 32.922829 29.520058 +v 68.855606 32.944691 29.174501 +v 68.873070 32.936100 29.311298 +v 72.655136 32.944691 28.443537 +v 69.657249 32.846298 30.625416 +v 72.717247 32.884747 29.409529 +v 69.370560 33.026978 27.733027 +v 69.653427 33.039387 27.476149 +v 72.393967 30.946625 28.398708 +v 69.237755 32.876957 30.205971 +v 72.448051 30.894361 29.240927 +v 69.547142 33.035244 27.564184 +v 69.770012 33.042557 27.401987 +v 70.803802 32.810127 30.994938 +v 72.447304 30.933350 28.605034 +v 72.474327 30.920073 28.816422 +v 72.656609 32.870937 29.646420 +v 71.832893 32.816372 30.695118 +v 69.081245 30.959690 28.822889 +v 68.855453 32.953144 29.036612 +v 68.886566 32.929462 29.416945 +v 69.238983 30.995600 28.206715 +v 69.234924 33.018517 27.897144 +v 70.353241 31.039047 27.283541 +v 70.314301 33.050713 27.164234 +v 72.134529 33.000874 27.627140 +v 71.415482 33.035244 27.204746 +v 70.454575 32.816372 30.960283 +v 70.357178 30.837519 30.570503 +v 69.864136 32.835857 30.755957 +v 69.468384 32.858055 30.469965 +v 69.038193 32.899254 29.880606 +v 69.343773 30.894361 29.838139 +v 72.474586 30.907009 29.029501 +v 72.747452 32.905785 29.060493 +v 72.605949 32.863670 29.774776 +v 72.214218 30.860849 29.832613 +v 72.566765 32.858055 29.873888 +v 72.395218 30.882324 29.447453 +v 72.682976 32.876957 29.543167 +v 69.107780 30.972336 28.611465 +v 69.341614 31.005848 28.019779 +v 69.297539 33.023071 27.810801 +v 70.661209 33.049877 27.111113 +v 71.198654 31.029179 27.281889 +v 70.779915 30.829313 30.623030 +v 69.615784 30.871098 30.165318 +v 72.137970 32.826893 30.464764 +v 71.949486 32.819542 30.620956 +v 72.055763 32.823685 30.532921 +v 72.367989 32.840412 30.199961 +v 72.086365 30.984373 27.842073 +v 71.595016 31.014956 27.437670 +v 71.738770 33.023071 27.341148 +v 71.945656 33.012627 27.471689 +v 71.043518 33.045006 27.117043 +v 71.148338 33.042557 27.136820 +v 69.161865 30.920073 29.453684 +v 70.559395 32.813923 30.980062 +v 69.550751 32.852936 30.537661 +v 71.405540 30.829313 30.502670 +v 71.646355 32.812271 30.797882 +v 71.390076 32.809052 30.899731 +v 71.048218 32.808216 30.979116 +v 72.305374 32.835857 30.286304 +v 72.315147 30.959690 28.200741 +v 71.940048 30.995600 27.687075 +v 70.941696 32.809052 30.985991 +v 70.993034 30.827650 30.609165 +v 54.357590 33.005993 30.963581 +v 54.156677 32.929462 32.250729 +v 54.378639 30.933350 32.081142 +v 54.431976 30.920073 32.287468 +v 57.635265 32.981972 30.724920 +v 54.143177 32.936100 32.145084 +v 57.874226 32.953144 31.149218 +v 57.987358 32.884747 32.243313 +v 54.817253 33.035244 30.397968 +v 55.047230 31.029179 30.389153 +v 56.685593 33.035244 30.038530 +v 54.426495 32.884747 32.928364 +v 58.004532 32.892925 32.106560 +v 54.189991 32.981972 31.387732 +v 54.138645 32.966003 31.658104 +v 55.040123 33.042557 30.235771 +v 55.584408 33.050713 29.998020 +v 55.824802 33.050713 29.951773 +v 55.832905 31.039047 30.077011 +v 56.211807 32.809052 33.819775 +v 54.739574 30.882324 32.844105 +v 54.570660 32.870937 33.125854 +v 57.717411 30.933350 31.438818 +v 57.744434 30.920073 31.650206 +v 54.125698 32.944691 32.008289 +v 54.351616 30.946625 31.869755 +v 54.216408 32.987988 31.284470 +v 54.267094 32.995258 31.156109 +v 54.430725 30.984373 31.238724 +v 54.505032 33.018517 30.730928 +v 54.611721 31.005848 30.853563 +v 54.882881 31.022774 30.525253 +v 56.069214 33.048801 29.935953 +v 56.671875 31.022774 30.181080 +v 55.134247 32.835857 33.589741 +v 54.885891 30.871098 32.999104 +v 54.820824 32.852936 33.371452 +v 54.613865 30.894361 32.671925 +v 54.359818 32.892925 32.807743 +v 54.308266 32.899254 32.714397 +v 58.017681 32.899254 32.000771 +v 56.660187 32.809052 33.733517 +v 57.103004 32.816372 33.528904 +v 57.785500 32.852936 32.801102 +v 57.665325 30.882324 32.281239 +v 57.586960 30.871098 32.479462 +v 57.836872 32.858055 32.707672 +v 57.953087 32.876957 32.376953 +v 57.926720 32.870937 32.480206 +v 54.125408 32.959675 31.763912 +v 54.377872 30.972336 31.445253 +v 54.509090 30.995600 31.040499 +v 54.567650 33.023071 30.644585 +v 55.420399 31.037384 30.183506 +v 57.572468 32.987988 30.638821 +v 57.045490 31.005848 30.385347 +v 56.259014 31.034075 30.076139 +v 55.931320 33.049877 29.944899 +v 54.217865 32.914238 32.487358 +v 55.837036 30.832623 33.443821 +v 55.936039 32.812271 33.820282 +v 55.829506 32.813923 33.813847 +v 55.230915 32.831951 33.634872 +v 54.738510 32.858055 33.303745 +v 57.719166 32.846298 32.922104 +v 57.477795 32.995258 30.538424 +v 57.783360 32.966003 30.956921 +v 57.482166 30.972336 30.848040 +v 56.912216 33.026978 30.129803 +v 56.865128 31.014956 30.271454 +v 57.008881 33.023071 30.174932 +v 56.313625 33.045006 29.950829 +v 56.418449 33.042557 29.970606 +v 55.457539 32.823685 33.726143 +v 55.355911 32.826893 33.693352 +v 56.868717 30.832623 33.245342 +v 57.013000 32.813923 33.586163 +v 56.916462 32.812271 33.631668 +v 56.472702 30.827650 33.402634 +v 56.318329 32.808216 33.812901 +v 57.834824 32.959675 31.050283 +v 57.664074 30.946625 31.232492 +v 57.404636 33.000874 30.460924 +v 66.344604 39.697979 25.731829 +v 45.726643 38.559273 20.456078 +v 68.557976 38.559273 16.063711 +v 59.756031 34.946129 14.619476 +v 57.905457 27.120533 11.876236 +v 65.778191 34.374424 22.787634 +v 65.364769 34.357391 23.145042 +v 55.329880 34.385426 24.618225 +v 64.885788 34.768574 16.529219 +v 65.773750 35.392830 22.519989 +v 65.713318 35.397808 22.450407 +v 64.642441 35.766628 16.639479 +v 61.281570 34.189182 26.674751 +v 64.857506 34.777916 16.382204 +v 61.639938 26.055202 28.537531 +v 47.359364 27.719242 12.310676 +v 50.426838 26.801964 29.007669 +v 69.590553 27.567686 2.333402 +v 70.554688 33.050713 27.117987 +v 70.212830 33.049877 27.197374 +v 70.081459 33.048801 27.240231 +v 69.956558 33.046658 27.299223 +v 69.860016 33.045006 27.344728 +v 69.464935 33.032036 27.632341 +v 57.215771 33.012627 30.305473 +v 69.153854 33.012627 28.008785 +v 68.919930 32.981972 28.553938 +v 68.855339 32.959675 28.930120 +v 57.986450 32.929462 31.513945 +v 57.925243 32.944691 31.277321 +v 68.885658 32.974182 28.687576 +v 57.716671 32.974182 30.836304 +v 68.946297 32.987988 28.450686 +v 69.036140 33.000874 28.223217 +v 69.087517 33.005993 28.129789 +v 57.959789 32.936100 31.410831 +v 68.868484 32.966003 28.824327 +v 68.996964 32.995258 28.322329 +v 57.322266 33.005993 30.393229 +v 57.099064 33.018517 30.231880 +v 56.787216 33.032036 30.071323 +v 56.554234 33.039387 29.996216 +v 70.387024 33.147514 25.571024 +v 56.207088 33.046658 29.944391 +v 55.226669 33.046658 30.133007 +v 54.155811 32.974182 31.521353 +v 54.125526 32.953144 31.870403 +v 50.501671 33.147514 29.396635 +v 54.306274 33.000874 31.056999 +v 54.423965 33.012627 30.842569 +v 54.735046 33.032036 30.466125 +v 54.640671 33.026978 30.566811 +v 54.923534 33.039387 30.309935 +v 55.130127 33.045006 30.178513 +v 55.351574 33.048801 30.074015 +v 55.482941 33.049877 30.031158 +v 72.564713 32.959675 28.216497 +v 72.604118 32.953144 28.315432 +v 72.446564 32.974182 28.002518 +v 72.513252 32.966003 28.123137 +v 72.302361 32.987988 27.805037 +v 72.365158 32.981972 27.891134 +v 75.218338 33.092384 25.540936 +v 72.207687 32.995258 27.704639 +v 72.052155 33.005993 27.559443 +v 71.828957 33.018517 27.398094 +v 71.642105 33.026978 27.296019 +v 71.517105 33.032036 27.237537 +v 70.553871 33.092384 26.438301 +v 71.284119 33.039387 27.162430 +v 70.936981 33.046658 27.110605 +v 70.799103 33.048801 27.102167 +v 72.729843 32.922829 28.785805 +v 72.716339 32.929462 28.680161 +v 72.747299 32.914238 28.922602 +v 72.734421 32.892925 29.272776 +v 72.747574 32.899254 29.166985 +v 72.515388 32.852936 29.967316 +v 72.449051 32.846298 30.088320 +v 72.232353 32.831951 30.364079 +v 71.742889 32.813923 30.752377 +v 71.521446 32.810127 30.856874 +v 71.288612 32.808216 30.932869 +v 57.575481 32.835857 33.120090 +v 57.999950 32.922829 31.619591 +v 68.947769 32.914238 29.653568 +v 58.017410 32.914238 31.756388 +v 68.998795 32.905785 29.781673 +v 58.017563 32.905785 31.894278 +v 69.089661 32.892925 29.973969 +v 69.156349 32.884747 30.094587 +v 69.300552 32.870937 30.292068 +v 69.395226 32.863670 30.392466 +v 57.876057 32.863670 32.608562 +v 57.638096 32.840412 33.033745 +v 69.773956 32.840412 30.699009 +v 69.960800 32.831951 30.801086 +v 57.502460 32.831951 33.197865 +v 70.085800 32.826893 30.859566 +v 70.187431 32.823685 30.892359 +v 57.325878 32.823685 33.366707 +v 70.318787 32.819542 30.934675 +v 57.408081 32.826893 33.298550 +v 57.219593 32.819542 33.454739 +v 70.665932 32.812271 30.986498 +v 56.791557 32.810127 33.690659 +v 56.558720 32.808216 33.766655 +v 54.507816 32.876957 33.039764 +v 54.183338 32.922829 32.353844 +v 54.268864 32.905785 32.615463 +v 56.073917 32.810127 33.828720 +v 55.724682 32.816372 33.794067 +v 55.588898 32.819542 33.768459 +v 55.044064 32.840412 33.532795 +v 54.927361 32.846298 33.459202 +v 54.665352 32.863670 33.226246 +v 47.791321 32.554409 23.248281 +v 48.639805 32.274055 27.658665 +v 47.329750 34.144943 21.907705 +v 47.370945 37.638145 22.121828 +v 48.502254 37.264343 28.002338 +v 48.698654 37.264343 27.964556 +v 47.567341 37.638145 22.084045 +v 48.443409 32.274055 27.696447 +v 69.065254 32.274055 23.729149 +v 67.933945 32.647858 17.848639 +v 69.124100 37.264343 24.035040 +v 69.320503 37.264343 23.997257 +v 68.189186 37.638145 18.116745 +v 69.261650 32.274055 23.691366 +v 67.992790 37.638145 18.154530 +v 68.130341 32.647858 17.810854 +v 59.409355 27.286600 8.877703 +v 55.492302 31.280785 7.401309 +v 55.574558 31.280876 7.596490 +v 55.372860 27.449877 6.990583 +v 55.496300 37.917774 7.632237 +v 55.570572 37.917866 7.808144 +v 55.531143 34.574375 7.603197 +v 55.739853 37.864441 8.207960 +v 55.700066 34.490608 8.001154 +v 61.077744 27.375963 7.098868 +v 61.134232 31.534855 7.321551 +v 61.127060 31.557924 7.355211 +v 61.165653 34.199467 7.484885 +v 61.200024 37.745140 7.734471 +v 56.505417 33.498474 8.885402 +v 56.646233 33.135204 8.950924 +v 56.489738 32.168861 8.803900 +v 56.142513 31.656748 8.452569 +v 56.272274 31.798721 8.581312 +v 56.432846 27.344353 8.508171 +v 55.975113 27.375963 8.080529 +v 56.553570 37.581589 9.135686 +v 56.316475 37.683620 8.918522 +v 56.170826 34.057674 8.599740 +v 56.297031 33.898132 8.710000 +v 55.313728 27.469414 6.683230 +v 55.359997 31.392776 6.923721 +v 55.436939 37.917618 7.323677 +v 58.218220 37.150532 9.707748 +v 57.562119 27.298674 9.036112 +v 57.679352 37.239975 9.645487 +v 56.969593 27.318205 8.831476 +v 58.184422 27.286600 9.113360 +v 55.894501 31.434967 8.144784 +v 56.005283 31.534855 8.308274 +v 56.024429 31.557924 8.336872 +v 56.097393 37.745140 8.716132 +v 56.055264 34.172634 8.497147 +v 58.924877 37.087070 9.660819 +v 60.630196 37.452759 8.683191 +v 60.826279 33.338730 8.107039 +v 60.777477 33.135204 8.156141 +v 60.432880 27.318205 8.165197 +v 60.749218 32.721592 8.161714 +v 61.236313 31.358046 5.882530 +v 61.296688 37.917618 6.196359 +v 61.060333 31.656748 7.506463 +v 61.157894 34.172634 7.515486 +v 60.075809 37.239975 9.184449 +v 59.958572 27.298674 8.575074 +v 59.525467 37.132675 9.481241 +v 61.278057 31.304075 6.099518 +v 61.220230 27.411655 6.489185 +v 61.352345 37.917866 6.695827 +v 61.308647 34.548508 6.701057 +v 61.304043 34.497074 6.906312 +v 61.343494 37.864441 7.129913 +v 61.293682 37.839470 7.341565 +v 61.210480 34.321148 7.305542 +v 61.207512 34.312927 7.317633 +v 61.215351 37.760574 7.670162 +v 61.222050 31.358620 6.923690 +v 61.191738 27.404522 6.611038 +v 61.200790 31.394100 7.019108 +v 60.864689 27.350676 7.552396 +v 60.811356 27.344353 7.665818 +v 60.875416 32.133430 7.943223 +v 60.932079 37.581589 8.293333 +v 61.012363 33.898132 7.802849 +v 60.883930 33.498474 8.043050 +v 61.071674 37.683620 8.003700 +v 55.154667 33.337154 5.943245 +v 55.204365 33.896751 6.201581 +v 55.229305 34.056538 6.331215 +v 55.289486 34.320305 6.644032 +v 54.950108 37.147907 4.879951 +v 55.164299 37.682098 5.993318 +v 55.147388 32.719955 5.905413 +v 55.154755 32.516430 5.943708 +v 55.166855 32.319458 6.006606 +v 55.204548 31.957125 6.202527 +v 55.000202 27.567686 5.140342 +v 55.229507 31.797485 6.332258 +v 55.324051 31.434301 6.823695 +v 61.058716 33.534252 4.872584 +v 61.046623 33.337154 4.809731 +v 60.525677 35.376511 2.101882 +v 60.584129 35.864510 2.405715 +v 61.039345 32.719955 4.771899 +v 61.075512 32.131989 4.959892 +v 61.121464 31.797485 5.198744 +v 61.096504 31.957125 5.069013 +v 61.150021 31.655691 5.347185 +v 61.189579 27.469414 5.552814 +v 61.216007 31.434301 5.690181 +v 61.121262 34.056538 5.197701 +v 60.746132 36.778572 3.247786 +v 61.149776 34.198410 5.345915 +v 60.842064 37.147907 3.746436 +v 61.181442 34.320305 5.510518 +v 60.945972 37.450668 4.286564 +v 61.056255 37.682098 4.859804 +v 61.252117 34.496571 5.877890 +v 61.215725 34.420193 5.688726 +v 61.219742 31.392776 5.796404 +v 55.343891 31.392776 6.926820 +v 55.297623 27.469414 6.686328 +v 55.396927 34.524712 7.115701 +v 55.380825 34.524712 7.118799 +v 44.786377 31.056692 15.568623 +v 48.252270 30.884907 17.704353 +v 45.306290 30.884907 18.271109 +v 44.413383 27.719242 12.877433 +v 47.231552 28.717300 12.398706 +v 44.285572 28.717300 12.965464 +v 47.480862 26.801964 29.574427 +v 47.982811 26.201115 28.784519 +v 47.594925 32.554409 23.286064 +v 47.526150 34.144943 21.869921 +v 66.210762 37.917587 5.186333 +v 61.326370 37.917694 6.350639 +v 66.291702 37.917820 5.650479 +v 55.466621 37.917694 7.477957 +v 50.989876 37.917587 8.114577 +v 51.086922 37.917820 8.575625 +v 65.612869 36.563549 2.078520 +v 50.391983 36.563549 5.006763 +v 65.911079 37.566383 3.628589 +v 50.690193 37.566383 6.556833 +v 66.139931 37.878059 4.818144 +v 50.919044 37.878059 7.746388 +v 65.464867 35.620510 1.309204 +v 50.243980 35.620510 4.237448 +v 66.376831 37.761543 6.673365 +v 61.207687 37.752857 7.702316 +v 61.185539 37.738213 7.764773 +v 66.265823 37.683620 7.004432 +v 51.387402 37.761543 9.557081 +v 51.613319 37.683620 9.823330 +v 66.023651 37.760330 4.213748 +v 50.802765 37.760330 7.141993 +v 65.704056 36.963242 2.552516 +v 50.483173 36.963242 5.480761 +v 65.803986 37.299286 3.071905 +v 50.583092 37.299286 6.000150 +v 65.531876 36.106518 1.657485 +v 50.310982 36.106518 4.585728 +v 50.383705 39.574478 28.783466 +v 50.382244 39.198650 28.775875 +v 50.472374 38.791084 29.244370 +v 48.883270 40.194908 20.984259 +v 48.775272 39.377975 20.422897 +v 48.773239 39.002335 20.412317 +v 48.785156 38.504982 20.474270 +v 48.820061 38.038948 20.655716 +v 48.823151 35.931602 20.671766 +v 48.888027 35.566311 21.008991 +v 49.266525 35.948097 22.976391 +v 49.352234 35.734871 23.421919 +v 49.442326 35.580185 23.890221 +v 49.535370 35.486481 24.373871 +v 49.629894 35.455254 24.865189 +v 50.459534 38.169350 29.177628 +v 50.312088 37.749374 28.411201 +v 50.399311 37.490997 28.864588 +v 50.347046 35.428085 28.592913 +v 49.570679 34.457073 24.557388 +v 49.724419 35.486977 25.356527 +v 49.817448 35.581158 25.840084 +v 49.907501 35.736313 26.308191 +v 50.274094 35.108337 28.213696 +v 45.775330 33.641586 4.829321 +v 46.322777 33.435703 7.674942 +v 46.337143 33.627991 7.749600 +v 46.081108 34.636631 6.418759 +v 46.245750 30.014559 7.274553 +v 46.308212 33.030170 7.599214 +v 46.313080 33.235359 7.624534 +v 46.308224 32.823364 7.599279 +v 46.313141 32.618195 7.624831 +v 46.322872 32.417946 7.675428 +v 46.337273 32.225723 7.750277 +v 46.356117 32.044556 7.848237 +v 46.379093 31.877304 7.967663 +v 46.435966 31.594851 8.263283 +v 46.468956 31.484156 8.434780 +v 46.504318 31.396173 8.618591 +v 46.541481 31.332344 8.811754 +v 46.579857 31.293713 9.011235 +v 46.618851 31.280830 9.213915 +v 46.660690 28.046349 9.431399 +v 47.226501 28.289183 12.372464 +v 46.657837 31.293900 9.416565 +v 46.733364 31.396793 9.809153 +v 46.768700 31.484911 9.992837 +v 47.331383 31.992538 12.917617 +v 47.072456 37.301643 11.571739 +v 46.924316 32.619797 10.801710 +v 47.172447 36.966125 12.091484 +v 46.914619 32.419456 10.751301 +v 47.263699 36.566910 12.565804 +v 47.312149 31.595062 12.817638 +v 46.696209 31.332771 9.616024 +v 46.801678 31.595802 10.164242 +v 46.831772 31.727734 10.320654 +v 46.858498 31.878563 10.459578 +v 46.881451 32.045918 10.578901 +v 47.454453 35.246040 13.557343 +v 47.344788 36.110298 12.987311 +v 47.414421 35.603493 13.349266 +v 47.857475 33.019241 15.652243 +v 48.426811 31.700232 18.611607 +v 46.148113 35.122639 6.767040 +v 46.203804 35.417557 7.056528 +v 46.266029 35.673157 7.379961 +v 46.333817 35.885487 7.732326 +v 46.406101 36.051147 8.108049 +v 46.481731 36.167564 8.501171 +v 46.559532 36.232880 8.905572 +v 46.598518 36.245964 9.108223 +v 46.677254 36.233185 9.517482 +v 46.755058 36.168274 9.921923 +v 46.830723 36.052254 10.315209 +v 46.768440 34.371017 9.991465 +v 46.801430 34.260307 10.162962 +v 46.831547 34.128571 10.319494 +v 46.858307 33.977905 10.458584 +v 46.881279 33.810654 10.578010 +v 46.900124 33.629436 10.675966 +v 46.914524 33.437218 10.750816 +v 46.924259 33.236969 10.801413 +v 46.929176 33.031796 10.826964 +v 50.587166 29.161577 29.841042 +v 51.102753 27.939177 32.521042 +v 51.643059 28.812696 35.329514 +v 51.275322 26.521614 33.418053 +v 52.135628 27.243563 37.889874 +v 51.936302 30.494560 36.853813 +v 50.464256 29.974739 29.202152 +v 51.162346 31.476397 32.830795 +v 51.182911 37.878670 8.969482 +v 55.802246 37.851955 8.296991 +v 66.348701 37.878670 6.051837 +v 61.318588 37.851955 7.235739 +v 75.183838 31.650961 25.361618 +v 76.486122 30.494560 32.130836 +v 75.746658 32.917820 28.287136 +v 70.606079 34.356834 1.566617 +v 70.872589 33.435703 2.951968 +v 70.886963 33.627991 3.026626 +v 70.905762 33.809242 3.124368 +v 70.928719 33.976646 3.243695 +v 71.031548 36.167564 3.778196 +v 70.955444 34.127472 3.382618 +v 70.985535 34.259357 3.539026 +v 70.900772 35.935432 3.098455 +v 71.109344 36.232880 4.182598 +v 71.053848 34.458382 3.894118 +v 71.188080 36.246071 4.591826 +v 71.227066 36.233185 4.794507 +v 71.304871 36.168274 5.198948 +v 71.380539 36.052254 5.592235 +v 71.452850 35.886955 5.968088 +v 71.520676 35.674999 6.320664 +v 71.582954 35.419697 6.644365 +v 71.638687 35.125118 6.934073 +v 71.687012 34.795830 7.185299 +v 70.673080 34.842842 1.914898 +v 70.764267 35.242531 2.388895 +v 70.325150 33.641586 0.106346 +v 70.858025 33.030170 2.876239 +v 70.858032 32.823364 2.876305 +v 70.862953 32.618195 2.901856 +v 70.872688 32.417946 2.952453 +v 70.887085 32.225723 3.027302 +v 70.610558 31.931894 1.589911 +v 70.928909 31.877304 3.244689 +v 70.955673 31.726589 3.383775 +v 70.985779 31.594851 3.540308 +v 71.018776 31.484156 3.711806 +v 71.054138 31.396173 3.895617 +v 71.091293 31.332344 4.088780 +v 71.129669 31.293713 4.288260 +v 71.168671 31.280830 4.490941 +v 71.207657 31.293900 4.693590 +v 71.246025 31.332771 4.893049 +v 71.283180 31.396793 5.086177 +v 71.318520 31.484911 5.269862 +v 71.727051 34.438377 7.393374 +v 71.464340 33.437218 6.027841 +v 71.474075 33.236969 6.078438 +v 71.478989 33.031796 6.103990 +v 71.479004 32.824989 6.104055 +v 71.474136 32.619797 6.078735 +v 71.464432 32.419456 6.028327 +v 72.295868 35.040855 10.350090 +v 72.696762 34.592869 12.433889 +v 71.450073 32.227169 5.953669 +v 71.431274 32.045918 5.855927 +v 72.936409 33.972191 13.679592 +v 71.408310 31.878563 5.736604 +v 71.381584 31.727734 5.597680 +v 73.184525 33.452927 14.969294 +v 71.351494 31.595802 5.441268 +v 73.439575 33.038315 16.295025 +v 72.084915 30.803101 9.253565 +v 73.699982 32.730942 17.648582 +v 73.964134 32.532646 19.021666 +v 74.230423 32.444660 20.405840 +v 74.497192 32.467533 21.792480 +v 74.762794 32.601105 23.173067 +v 75.025604 32.844612 24.539139 +v 75.136978 29.161577 25.118065 +v 76.430016 27.252514 31.839178 +v 76.192871 28.812696 30.606541 +v 73.640350 27.744471 17.338646 +v 59.737595 35.153580 11.238645 +v 57.033558 37.429771 9.417791 +v 57.383957 37.323380 9.547683 +v 57.948784 37.195251 9.676618 +v 66.152237 37.301643 7.901110 +v 60.313831 37.323380 8.984024 +v 65.636391 37.150532 8.280617 +v 63.723949 36.029697 9.904218 +v 59.561989 37.141602 9.461723 +v 59.482620 37.129417 9.494074 +v 59.182327 37.106617 9.583863 +v 58.879730 37.090370 9.664869 +v 52.760487 36.957199 11.028932 +v 59.495354 36.781719 9.979478 +v 61.001877 37.632607 8.148517 +v 60.781136 37.517174 8.488262 +v 56.435020 37.632607 9.027103 +v 53.517529 37.495701 9.940955 +v 63.791565 35.515163 10.255713 +v 48.386688 27.028641 15.206619 +v 61.211090 27.459644 5.708040 +v 61.226418 27.430767 6.176226 +v 61.205986 27.408089 6.550112 +v 61.134743 27.390244 6.854953 +v 60.971214 27.363319 7.325632 +v 60.838020 27.347515 7.609107 +v 60.622116 27.331280 7.915508 +v 60.195724 27.308439 8.370136 +v 62.185196 27.013065 12.806107 +v 59.683964 27.292637 8.726389 +v 59.169296 27.284966 8.950566 +v 58.869247 27.282921 9.041628 +v 58.559311 27.284145 9.081285 +v 58.246895 27.286190 9.108051 +v 57.960625 27.218948 10.260080 +v 57.265854 27.308439 8.933794 +v 56.701218 27.331280 8.669824 +v 52.922104 27.154799 12.275953 +v 56.203979 27.360157 8.294350 +v 55.343292 27.459644 6.836906 +v 55.795853 27.393810 7.823881 +v 51.655807 27.319469 9.833126 +v 52.696091 26.140705 28.863274 +v 52.477608 26.129791 29.083374 +v 52.356964 26.119545 29.273724 +v 52.320770 26.115250 29.350765 +v 53.619232 26.015087 30.735014 +v 53.580688 26.006310 30.885633 +v 53.538681 25.993240 31.106895 +v 53.519344 25.984016 31.261089 +v 53.514099 25.978874 31.346001 +v 53.514297 25.969343 31.501465 +v 53.548435 25.945717 31.880352 +v 53.564499 25.940407 31.963856 +v 53.528740 25.955399 31.726196 +v 50.701912 26.263660 27.241035 +v 51.045235 27.518551 6.702814 +v 68.942055 26.069374 26.901505 +v 68.835625 26.063675 27.014977 +v 68.781357 26.060287 27.080654 +v 68.690002 26.053646 27.206573 +v 68.569382 26.043402 27.396919 +v 68.494499 26.035927 27.533287 +v 68.458305 26.031631 27.610331 +v 65.226677 26.045692 28.002647 +v 65.188141 26.036915 28.153267 +v 65.168770 26.027691 28.307465 +v 65.168991 26.018158 28.462925 +v 65.156036 26.200050 25.498055 +v 65.188667 26.008476 28.617083 +v 73.143723 25.945717 28.110544 +v 73.159790 25.940407 28.194046 +v 73.179466 25.930725 28.348206 +v 73.193932 25.916780 28.572933 +v 73.194153 25.907249 28.728394 +v 73.188904 25.902105 28.813305 +v 73.169533 25.892883 28.967503 +v 73.127533 25.879814 29.188766 +v 73.088989 25.871037 29.339384 +v 73.036934 25.862686 29.485641 +v 72.979828 25.854492 29.630280 +v 72.943634 25.850197 29.707321 +v 74.179474 25.718853 31.612305 +v 74.088120 25.712212 31.738224 +v 73.981644 25.706511 31.851706 +v 73.861801 25.701836 31.951038 +v 71.952408 25.797098 30.764259 +v 71.877731 25.795456 30.805405 +v 71.736908 25.793043 30.871881 +v 71.594147 25.790958 30.933344 +v 71.446106 25.789745 30.981644 +v 71.185745 25.788862 31.046112 +v 70.920059 25.789745 31.082848 +v 70.764664 25.790958 31.092922 +v 70.609238 25.793043 31.088827 +v 70.453796 25.795456 31.079348 +v 70.369240 25.797098 31.068832 +v 70.216293 25.800669 31.040020 +v 66.496857 25.701836 33.367931 +v 70.064636 25.804543 31.005981 +v 69.706635 25.816748 30.875748 +v 63.728554 25.842720 31.602112 +v 68.333328 25.930725 29.280519 +v 68.403282 25.916780 29.494576 +v 65.285072 25.989264 28.911983 +v 68.497139 25.902105 29.715921 +v 68.572334 25.892883 29.851927 +v 68.693436 25.879814 30.041809 +v 65.225098 25.961918 29.369652 +v 63.656845 25.862686 31.290215 +v 63.644615 25.854492 31.426220 +v 63.608421 25.850197 31.503262 +v 69.359375 25.832478 30.685974 +v 69.490936 25.825836 30.769009 +v 69.565697 25.822449 30.809864 +v 57.482063 25.804543 33.426659 +v 59.748825 26.008476 29.663620 +v 58.429901 25.940407 31.027832 +v 58.449577 25.930725 31.181988 +v 58.464043 25.916780 31.406719 +v 58.464268 25.907249 31.562180 +v 58.459015 25.902105 31.647091 +v 58.439644 25.892883 31.801289 +v 58.397640 25.879814 32.022552 +v 58.359100 25.871037 32.173172 +v 58.018246 25.832478 32.867821 +v 57.926888 25.825836 32.993736 +v 57.872620 25.822449 33.059418 +v 57.766144 25.816748 33.172901 +v 57.601906 25.809219 33.327324 +v 69.916443 25.809219 30.958214 +v 57.222519 25.797098 33.598045 +v 64.145676 25.600410 35.474892 +v 57.147896 25.795456 33.639179 +v 57.007069 25.793043 33.705658 +v 56.864262 25.790958 33.767128 +v 56.716221 25.789745 33.815430 +v 56.455864 25.788862 33.879898 +v 56.190170 25.789745 33.916634 +v 55.879349 25.793043 33.922611 +v 55.639351 25.797098 33.902618 +v 54.020454 25.703117 35.747246 +v 53.810631 25.710648 35.664783 +v 53.640411 25.718853 35.563675 +v 54.055202 25.871037 33.001167 +v 53.842422 25.892883 32.685715 +v 53.603405 25.930725 32.114311 +v 51.837807 25.900763 32.942802 +v 53.673378 25.916780 32.328362 +v 53.730873 25.907249 32.472801 +v 53.767250 25.902105 32.549706 +v 53.963524 25.879814 32.875599 +v 54.157799 25.862686 33.117672 +v 53.520309 25.726330 35.464821 +v 54.761059 25.825836 33.602791 +v 55.486404 25.800669 33.873802 +v 55.723911 25.795456 33.913132 +v 56.034775 25.790958 33.926708 +v 73.104797 25.955399 27.960091 +v 72.567543 26.584049 17.807671 +v 73.034851 25.969343 27.746035 +v 72.977371 25.978874 27.601589 +v 72.940994 25.984016 27.524689 +v 72.865799 25.993240 27.388683 +v 72.744690 26.006310 27.198799 +v 72.653015 26.015087 27.073231 +v 72.550415 26.023438 26.956728 +v 72.443718 26.031631 26.843603 +v 72.381516 26.035927 26.785492 +v 72.261383 26.043402 26.686640 +v 72.078758 26.053646 26.554634 +v 71.947205 26.060287 26.471600 +v 71.872437 26.063675 26.430744 +v 71.731453 26.069374 26.364870 +v 71.521645 26.076904 26.282406 +v 71.373505 26.081579 26.234629 +v 71.221840 26.085455 26.200588 +v 71.068893 26.089024 26.171776 +v 70.984283 26.090666 26.161270 +v 70.828842 26.093081 26.151791 +v 70.673470 26.095165 26.147686 +v 70.518082 26.096378 26.157763 +v 69.158974 26.211733 24.537340 +v 69.010933 26.210518 24.585640 +v 68.870102 26.208103 24.652115 +v 68.738770 26.204535 24.735622 +v 69.106285 26.076904 26.747080 +v 67.398262 26.597212 18.587399 +v 68.084305 27.217953 8.328716 +v 65.597847 27.286371 7.690868 +v 61.040871 27.518551 4.779821 +v 59.709900 26.018158 29.513165 +v 59.652428 26.027691 29.368719 +v 59.577229 26.036915 29.232714 +v 59.485558 26.045692 29.107145 +v 57.713829 26.031631 29.677387 +v 57.651630 26.035927 29.619274 +v 57.531494 26.043402 29.520424 +v 57.348866 26.053646 29.388420 +v 57.217308 26.060287 29.305386 +v 57.142548 26.063675 29.264530 +v 57.001564 26.069374 29.198654 +v 56.791756 26.076904 29.116190 +v 56.643612 26.081579 29.068413 +v 56.491951 26.085455 29.034374 +v 56.339005 26.089024 29.005560 +v 56.254444 26.090666 28.995047 +v 56.099003 26.093081 28.985567 +v 55.943581 26.095165 28.981472 +v 55.788185 26.096378 28.991547 +v 54.145035 26.208767 27.474152 +v 54.070412 26.207127 27.515287 +v 57.247070 26.200050 27.019609 +v 54.395302 26.211733 27.377625 +v 55.114090 26.095165 29.141050 +v 54.496288 26.081579 29.481522 +v 52.531845 26.133177 29.017700 +v 69.957443 35.061996 10.766566 +v 66.685387 34.773247 16.106777 +v 62.306770 34.862022 15.500840 +v 59.330925 34.960144 14.472578 +v 56.864216 34.890060 16.090534 +v 50.802345 34.965748 16.021910 +v 66.124504 34.259998 24.587757 +v 63.323170 34.273285 24.909897 +v 54.735939 34.581673 21.530956 +v 58.492126 34.145573 27.922846 +v 58.730881 34.273285 25.793377 +v 55.755035 34.371407 24.765114 +v 65.089661 42.940437 21.855305 +v 65.018768 42.916645 21.486801 +v 64.496414 37.986919 18.771645 +v 65.488335 42.255898 23.927607 +v 64.648743 36.878342 19.563429 +v 65.007645 35.985512 21.428986 +v 64.858185 42.706097 20.652138 +v 65.543083 42.016376 24.212179 +v 65.344055 42.684242 23.177662 +v 64.783478 36.370399 20.263779 +v 64.437698 39.194977 18.466452 +v 64.847763 36.210472 20.597946 +v 64.937859 36.055786 21.066250 +v 65.543564 36.880680 24.214684 +v 64.466370 40.495289 18.615473 +v 64.583458 41.648415 19.224106 +v 64.535385 41.291088 18.974182 +v 64.772522 42.492416 20.206810 +v 64.925735 42.822464 21.003244 +v 65.184174 42.909203 22.346622 +v 64.535728 37.600716 18.975994 +v 64.470230 38.336449 18.635548 +v 64.437653 39.696312 18.466190 +v 64.186829 39.258270 19.809036 +v 64.186790 39.634270 19.808865 +v 64.194595 38.982391 19.849438 +v 64.208847 38.713844 19.923496 +v 64.194504 39.910194 19.848999 +v 64.229324 38.456863 20.029991 +v 64.255753 38.215500 20.167309 +v 64.208710 40.178814 19.922775 +v 64.287674 37.993568 20.333267 +v 64.324585 37.794567 20.525131 +v 64.229149 40.435905 20.029053 +v 64.365936 37.621628 20.740044 +v 64.411057 37.477478 20.974575 +v 64.459213 37.364395 21.224934 +v 64.509689 37.284168 21.487295 +v 64.561661 37.238045 21.757450 +v 64.595924 37.226723 21.935532 +v 64.648575 37.238281 22.209225 +v 64.700546 37.284657 22.479345 +v 64.750992 37.365158 22.741570 +v 64.799149 37.478485 22.991886 +v 64.844238 37.622871 23.226246 +v 64.885536 37.796024 23.440922 +v 64.922432 37.995224 23.632694 +v 64.954315 38.217316 23.798422 +v 64.255516 40.677399 20.166105 +v 64.287399 40.899498 20.331833 +v 64.324295 41.098694 20.523603 +v 64.365593 41.271851 20.738281 +v 64.410675 41.416233 20.972641 +v 64.458832 41.529564 21.222958 +v 64.509285 41.610062 21.485182 +v 64.561249 41.656445 21.755302 +v 64.595520 41.667942 21.933407 +v 64.648163 41.656673 22.207077 +v 64.700134 41.610554 22.477234 +v 64.750610 41.530323 22.739594 +v 64.798782 41.417244 22.989952 +v 64.843903 41.273090 23.224483 +v 64.885239 41.100151 23.439394 +v 64.922157 40.901154 23.631260 +v 64.954086 40.679214 23.797218 +v 64.980499 40.437859 23.934534 +v 65.000992 40.180882 24.041031 +v 65.015236 39.912327 24.115089 +v 65.023010 39.636444 24.155491 +v 64.980682 38.458817 23.935474 +v 65.001129 38.715904 24.041752 +v 65.015320 38.984528 24.115528 +v 65.023041 39.260452 24.155663 +v 65.608368 37.246300 24.551502 +v 65.479256 36.603928 23.880411 +v 65.333633 36.188622 23.123470 +v 65.266083 36.072254 22.772364 +v 65.721588 40.558273 25.140060 +v 65.745186 40.072754 25.262705 +v 64.501190 40.961510 18.796457 +v 64.712570 42.290787 19.895197 +v 65.253967 42.838928 22.709358 +v 64.446640 38.821968 18.512901 +v 64.591431 37.195278 19.265549 +v 64.444290 40.007515 18.500713 +v 64.850441 40.316677 17.965229 +v 64.703491 36.638817 19.848001 +v 65.690636 37.933212 24.979151 +v 65.173058 35.978073 22.288807 +v 65.600388 41.699448 24.510059 +v 65.695412 40.907799 25.003962 +v 64.648254 42.014042 19.560925 +v 65.408348 42.524315 23.511829 +v 65.754913 39.261070 25.313259 +v 65.743088 38.763664 25.251778 +v 65.725449 38.399429 25.160135 +v 65.656448 37.603630 24.801426 +v 65.419312 36.402306 23.568798 +v 65.102158 35.954281 21.920303 +v 65.656097 41.294003 24.799614 +v 65.753387 39.762222 25.305311 +v 66.341087 39.574478 25.713531 +v 67.550522 43.439468 21.413597 +v 67.034348 42.356140 18.730499 +v 67.272919 43.158401 19.970592 +v 67.187241 42.944725 19.525265 +v 66.868332 41.145340 17.867582 +v 66.833511 40.679123 17.686600 +v 66.969543 41.990517 18.393681 +v 68.302063 39.825142 25.320065 +v 67.362976 43.313560 20.438702 +v 67.456001 43.407745 20.922256 +v 67.107300 42.675888 19.109715 +v 68.778412 40.316677 17.209553 +v 66.913918 41.584785 18.104534 +v 67.645050 43.408234 21.904913 +v 67.738091 43.314533 22.388565 +v 68.066895 42.358826 24.097664 +v 67.828186 43.159847 22.856865 +v 68.268051 40.682858 25.143246 +v 68.233139 41.148895 24.961798 +v 67.993881 42.678200 23.718168 +v 68.291641 40.197338 25.265890 +v 68.131775 41.993538 24.434887 +v 68.187477 41.588097 24.724442 +v 67.913895 42.946617 23.302391 +v 65.674019 35.426590 22.246143 +v 65.723358 35.394489 22.502598 +v 65.735397 35.346016 22.565155 +v 65.757790 34.837643 22.681578 +v 65.761017 35.517830 22.698362 +v 65.843590 35.101654 23.127579 +v 66.269852 35.256065 25.343250 +v 66.416916 38.169350 26.107695 +v 66.382095 37.703129 25.926710 +v 66.492859 36.014885 26.502462 +v 66.269112 35.984661 25.339407 +v 66.428741 38.666756 26.169178 +v 64.613541 37.162949 16.733797 +v 64.730621 39.002335 17.342384 +v 64.742538 38.504982 17.404335 +v 64.760979 36.642624 17.500193 +v 64.816681 36.237186 17.789747 +v 64.889694 35.917809 18.169243 +v 64.975403 35.704582 18.614769 +v 65.068451 35.610878 19.098419 +v 64.732651 39.377975 17.352962 +v 64.840652 40.194908 17.914326 +v 68.306084 39.698807 25.340954 +v 66.799286 40.196674 17.508701 +v 69.246307 39.717827 19.641657 +v 69.254829 39.893875 19.685951 +v 69.267456 40.062881 19.751556 +v 69.283974 40.222176 19.837425 +v 69.304146 40.369247 19.942272 +v 69.401505 42.101158 20.448341 +v 69.327637 40.501785 20.064381 +v 69.354073 40.617699 20.201805 +v 69.295464 41.912445 19.897152 +v 69.414116 40.792618 20.513897 +v 69.465240 42.155445 20.779654 +v 69.529755 42.167023 21.114988 +v 69.564011 42.155701 21.293070 +v 69.627754 42.101746 21.624390 +v 69.689720 42.005928 21.946501 +v 69.748917 41.869766 22.254183 +v 69.804428 41.695389 22.542747 +v 69.855377 41.485569 22.807545 +v 69.900948 41.243603 23.044445 +v 69.940445 40.973309 23.249748 +v 68.936630 38.506760 18.031940 +v 68.665970 39.376205 16.625072 +v 69.030823 40.362648 18.521603 +v 69.076416 40.802090 18.758553 +v 69.246361 39.175495 19.641916 +v 69.141212 41.167717 19.095371 +v 69.183998 41.898907 19.317736 +v 69.242020 39.537514 19.619389 +v 69.242035 39.355782 19.619465 +v 69.254913 38.999493 19.686373 +v 69.267563 38.830551 19.752121 +v 69.284119 38.671345 19.838196 +v 69.327827 38.391960 20.065372 +v 68.940742 36.495640 18.053347 +v 68.775116 33.038315 17.192390 +v 69.151268 35.496304 19.147636 +v 69.414360 38.101585 20.515175 +v 69.315269 35.313622 20.000111 +v 69.480743 38.011539 20.860268 +v 69.482346 35.230610 20.868580 +v 69.549271 38.011723 21.216454 +v 69.649551 35.248451 21.737675 +v 69.615646 38.102104 21.561440 +v 69.646698 38.179569 21.722876 +v 69.675690 38.277023 21.873531 +v 69.702126 38.392933 22.010956 +v 69.725616 38.525467 22.133064 +v 69.745789 38.672543 22.237911 +v 70.106247 38.399223 24.111582 +v 69.774925 39.000851 22.389387 +v 69.783447 39.176891 22.433680 +v 69.973213 40.678951 23.420073 +v 69.998764 40.365173 23.552853 +v 70.016678 40.036911 23.645977 +v 70.027100 39.664719 23.700153 +v 69.787735 39.357204 22.455948 +v 70.204453 35.195450 24.622036 +v 70.357727 38.791084 25.418762 +v 70.414948 35.515854 25.716198 +v 70.361145 32.844612 25.436504 +v 70.522758 33.043049 26.276573 +v 67.908577 34.949272 12.688181 +v 68.032288 34.592869 13.331255 +v 68.271942 33.972191 14.576957 +v 68.520065 33.452927 15.866659 +v 47.518276 29.974739 29.768909 +v 49.062492 36.014885 29.855774 +v 49.523602 33.480202 29.605947 +v 60.444347 33.147514 27.483829 +v 66.600204 37.162949 16.351595 +v 66.577850 35.267597 16.235415 +v 44.535973 29.886997 14.267043 +v 45.046333 30.970798 16.919865 +v 45.691010 35.537415 20.270847 +v 46.452789 38.671345 24.230560 +v 46.180412 40.000202 22.814762 +v 45.829292 39.377975 20.989655 +v 46.072453 38.822670 22.253582 +v 46.415031 39.175495 24.034283 +v 46.410694 39.537514 24.011753 +v 46.199501 40.362648 22.913969 +v 46.224991 40.676567 23.046459 +v 46.257706 40.971096 23.216526 +v 46.410709 39.355782 24.011831 +v 46.436234 38.830551 24.144489 +v 46.472984 38.524376 24.335537 +v 46.496498 38.391960 24.457741 +v 46.522964 38.276184 24.595325 +v 46.551964 38.178879 24.746048 +v 46.583031 38.101585 24.907541 +v 46.615692 38.045509 25.077320 +v 46.649422 38.011539 25.252636 +v 47.111088 35.573845 27.652359 +v 46.717945 38.011723 25.608820 +v 46.751671 38.045860 25.784130 +v 46.784313 38.102104 25.953808 +v 47.193069 35.685493 28.078493 +v 47.196854 39.573856 28.098166 +v 47.046555 42.946617 27.316921 +v 46.952118 39.176891 26.826046 +v 47.126541 42.678200 27.732697 +v 47.187263 39.306942 28.048302 +v 47.199551 42.358826 28.112190 +v 46.930977 38.831844 26.716148 +v 47.264427 41.993538 28.449417 +v 46.914459 38.672543 26.630280 +v 47.320133 41.588097 28.738972 +v 46.894287 38.525467 26.525433 +v 47.365799 41.148895 28.976326 +v 46.870792 38.392933 26.403322 +v 47.400703 40.682858 29.157772 +v 45.480831 31.700232 19.178362 +v 47.424301 40.197338 29.280418 +v 46.844353 38.277023 26.265900 +v 45.878170 27.331514 21.243702 +v 47.526398 38.791084 29.811127 +v 46.297134 41.241596 23.421474 +v 46.342674 41.483795 23.658192 +v 46.393585 41.693878 23.922800 +v 46.449059 41.868538 24.211163 +v 46.508240 42.005016 24.518784 +v 46.570171 42.101158 24.840708 +v 46.633911 42.155445 25.172020 +v 46.668175 42.166943 25.350122 +v 46.732685 42.155701 25.685436 +v 46.796425 42.101746 26.016754 +v 46.858391 42.005928 26.338867 +v 46.815136 40.715843 26.114021 +v 46.844131 40.618538 26.264744 +v 46.870602 40.502762 26.402330 +v 46.894112 40.370346 26.524532 +v 46.914307 40.223373 26.629509 +v 46.930866 40.064163 26.715582 +v 46.943516 39.895233 26.781330 +v 46.952072 39.719231 26.825788 +v 46.956390 39.538940 26.848240 +v 48.911728 39.698807 29.072104 +v 47.415627 40.193138 21.295424 +v 47.675732 37.295708 20.000786 +v 47.628216 33.266563 19.753813 +v 48.156170 43.439468 25.144745 +v 47.639988 42.356140 22.461651 +v 48.477951 43.067505 26.817360 +v 47.878563 43.158401 23.701742 +v 47.792885 42.944725 23.256416 +v 47.473976 41.145340 21.598732 +v 47.439159 40.679123 21.417747 +v 47.575188 41.990517 22.124830 +v 48.343739 43.314533 26.119713 +v 48.907711 39.825142 29.051216 +v 50.346680 40.682858 28.591015 +v 47.968620 43.313560 24.169849 +v 48.061646 43.407745 24.653408 +v 47.712944 42.675888 22.840866 +v 48.893063 40.316677 21.035164 +v 47.519562 41.584785 21.835682 +v 48.250694 43.408234 25.636063 +v 50.145531 42.358826 27.545433 +v 49.906822 43.159847 26.304638 +v 50.072521 42.678200 27.165939 +v 50.370277 40.197338 28.713661 +v 50.210403 41.993538 27.882660 +v 50.266113 41.588097 28.172215 +v 50.311775 41.148895 28.409569 +v 49.991524 42.940437 24.759933 +v 50.086044 42.909203 25.251251 +v 50.155830 42.838928 25.613989 +v 49.550606 36.878342 22.468058 +v 50.930565 39.260826 27.656063 +v 50.445427 36.880680 27.119312 +v 50.801670 41.356995 26.986065 +v 50.202412 36.119190 25.856136 +v 50.381119 36.603928 26.785040 +v 50.577362 37.762726 27.805096 +v 50.627316 38.399429 28.064766 +v 49.674377 42.492416 23.111441 +v 49.437592 37.600716 21.880623 +v 50.310211 42.524315 26.416458 +v 50.612438 40.737396 27.987434 +v 49.955616 39.910194 22.588322 +v 49.946747 39.537140 22.542234 +v 49.969810 40.178814 22.662100 +v 49.990257 40.435905 22.768379 +v 49.947929 39.258270 22.548363 +v 50.016624 40.677399 22.905432 +v 50.048508 40.899498 23.071156 +v 49.955704 38.982391 22.588764 +v 50.085400 41.098694 23.262928 +v 50.126701 41.271851 23.477606 +v 49.969948 38.713844 22.662821 +v 50.171787 41.416233 23.711967 +v 50.219940 41.529564 23.962282 +v 50.270393 41.610062 24.224506 +v 50.322357 41.656445 24.494629 +v 50.375011 41.667992 24.768320 +v 50.409271 41.656673 24.946400 +v 50.461243 41.610554 25.216558 +v 50.511719 41.530323 25.478918 +v 50.559883 41.417244 25.729279 +v 50.605003 41.273090 25.963810 +v 50.646351 41.100151 26.178719 +v 50.576584 40.370346 25.816086 +v 50.715187 40.679214 26.536543 +v 50.741608 40.437859 26.673859 +v 49.990437 38.456863 22.769318 +v 50.016853 38.215500 22.906635 +v 50.048782 37.993568 23.072594 +v 50.085693 37.794567 23.264458 +v 50.127037 37.621628 23.479366 +v 50.172157 37.477478 23.713902 +v 50.220322 37.364395 23.964260 +v 50.270798 37.284168 24.226620 +v 50.304527 37.250198 24.401936 +v 50.357033 37.226723 24.674858 +v 50.409683 37.238281 24.948549 +v 50.461651 37.284657 25.218670 +v 50.512100 37.365158 25.480896 +v 50.560257 37.478485 25.731211 +v 50.605343 37.622871 25.965572 +v 50.646645 37.796024 26.180248 +v 50.683540 37.995224 26.372021 +v 50.715420 38.217316 26.537746 +v 50.741787 38.458817 26.674799 +v 50.762230 38.715904 26.781078 +v 50.776428 38.984528 26.854855 +v 50.762093 40.180882 26.780357 +v 50.776344 39.912327 26.854416 +v 50.784111 39.636444 26.894815 +v 50.638874 39.357204 26.139870 +v 50.532898 37.391502 27.573999 +v 49.485321 41.648415 22.128735 +v 49.760056 42.706097 23.556768 +v 49.339561 39.194977 21.371080 +v 49.372093 38.336449 21.540176 +v 49.493294 37.195278 22.170177 +v 49.605350 36.638817 22.752630 +v 49.716534 36.279732 23.330555 +v 50.366314 42.510319 26.046408 +v 50.659489 41.160637 27.570328 +v 50.039612 35.954376 25.009912 +v 50.321167 36.402306 26.473427 +v 50.400372 37.067734 27.546759 +v 50.644947 38.763664 28.156406 +v 49.368233 40.495289 21.520103 +v 49.614429 42.290787 22.799826 +v 49.348503 38.821968 21.417532 +v 49.874199 36.009033 24.150084 +v 50.418999 42.145252 26.981943 +v 50.653732 39.887733 28.202087 +v 50.747757 39.657757 28.029158 +v 49.403049 40.961510 21.701086 +v 49.338776 39.633644 21.366978 +v 49.550121 42.014042 22.465553 +v 49.398277 37.986919 21.676275 +v 49.437241 41.291088 21.878811 +v 49.920631 42.916645 24.391430 +v 49.827599 42.822464 23.907873 +v 49.346153 40.007515 21.405342 +v 71.016159 34.956318 12.079202 +v 71.431984 32.480591 22.621675 +v 70.634117 32.688740 18.474390 +v 70.898766 32.508747 19.850039 +v 71.165276 32.439228 21.235346 +v 71.697258 32.632572 24.000555 +v 73.622032 33.026604 25.624420 +v 67.059769 40.894501 21.509285 +v 48.524654 40.894501 25.075130 +v 65.968369 39.386066 20.247313 +v 48.866055 39.325497 23.541418 +v 67.332733 39.538940 22.928169 +v 49.411007 39.569225 26.374060 +v 66.885178 40.564144 20.601799 +v 48.337540 40.501785 24.102524 +v 66.885391 38.329666 20.602873 +v 48.325188 38.454250 24.038315 +v 65.991806 38.858006 20.369154 +v 48.893658 38.803097 23.684891 +v 67.220482 40.618538 22.344675 +v 49.303757 40.600708 25.816574 +v 66.943146 40.759056 20.903112 +v 48.424019 40.792618 24.552040 +v 65.983292 39.922745 20.324879 +v 48.264740 39.893875 23.724094 +v 65.974068 39.747639 20.276953 +v 48.256218 39.717827 23.679800 +v 66.013733 40.247807 20.483109 +v 48.293877 40.222176 23.875568 +v 66.928314 38.178879 20.825979 +v 49.001919 38.193485 24.247637 +v 66.085625 38.258358 20.856817 +v 48.973309 38.294010 24.098902 +v 66.992035 38.045509 21.157251 +v 49.065121 38.053043 24.576147 +v 66.849335 38.524376 20.415468 +v 65.979828 39.028351 20.306885 +v 48.264816 38.999493 23.724516 +v 67.328415 39.719231 22.905720 +v 49.405975 39.749039 26.347891 +v 66.991776 40.848862 21.155872 +v 48.456665 40.848862 24.721718 +v 67.025497 40.883003 21.331181 +v 48.490391 40.883003 24.897026 +v 48.363979 40.617699 24.239948 +v 66.104713 40.700520 20.956072 +v 48.392960 40.715153 24.390606 +v 65.969078 39.567806 20.250975 +v 48.865322 39.507221 23.537600 +v 65.996582 40.090347 20.393955 +v 48.277359 40.062881 23.789700 +v 66.034492 40.392639 20.590998 +v 48.314049 40.369247 23.980415 +v 66.146385 38.090427 21.172623 +v 49.032681 38.112743 24.407526 +v 67.042831 38.000175 21.421257 +v 49.098713 38.015327 24.750772 +v 66.007729 38.696960 20.451933 +v 48.910847 38.645729 23.774256 +v 65.971977 39.205303 20.266043 +v 48.871090 39.145687 23.567587 +v 67.094025 40.883179 21.687366 +v 48.558914 40.883179 25.253212 +v 67.246948 40.502762 22.482262 +v 49.329762 40.481998 25.951767 +v 66.448647 38.502075 22.743797 +v 49.352921 38.548859 26.072130 +v 67.307213 40.064163 22.795513 +v 49.383404 40.091618 26.230585 +v 66.247437 38.000233 21.697876 +v 48.524918 38.000221 25.076492 +v 67.290657 40.223373 22.709440 +v 49.366215 40.248989 26.141220 +v 67.234619 38.330574 22.418133 +v 49.329952 38.413719 25.952730 +v 66.347664 38.113293 22.218864 +v 49.233967 38.090916 25.453814 +v 67.319870 39.895233 22.861263 +v 49.396725 39.924091 26.299814 +v 67.160416 40.793137 22.032459 +v 48.625305 40.793137 25.598307 +v 67.191483 40.715843 22.193954 +v 49.275143 40.701237 25.667837 +v 67.127754 40.849213 21.862682 +v 48.592644 40.849213 25.428528 +v 67.270462 40.370346 22.604462 +v 66.514061 39.326916 23.083822 +v 48.796566 39.266331 26.488522 +v 66.509064 39.147079 23.057844 +v 48.671680 38.223469 25.839373 +v 66.378418 38.194202 22.378723 +v 49.265331 38.164936 25.616827 +v 67.128021 38.045860 21.864059 +v 49.201099 38.038300 25.282965 +v 66.281654 38.015545 21.875753 +v 49.167236 38.007904 25.106951 +v 67.307327 38.831844 22.796080 +v 48.779526 38.914249 26.399937 +v 66.469406 38.646912 22.851690 +v 49.372509 38.698177 26.173935 +v 66.499847 38.971985 23.009918 +v 49.400349 39.029720 26.318655 +v 66.232704 34.574276 5.343794 +v 66.250427 34.574375 5.540986 +v 51.084633 34.574375 8.458632 +v 51.027924 34.574276 8.268940 +v 65.949280 33.133568 3.827172 +v 55.146141 33.030170 5.898943 +v 60.991566 31.791014 7.665103 +v 61.027931 31.720028 7.581254 +v 56.207394 31.727734 8.516941 +v 61.135521 34.127472 5.271808 +v 50.824596 34.127472 7.255457 +v 66.006470 31.957125 4.124418 +v 55.217026 31.877304 6.267393 +v 61.052761 32.417946 4.841643 +v 55.160805 32.417946 5.975157 +v 66.125687 34.420193 4.744131 +v 50.923000 34.458382 7.766957 +v 61.067032 33.627991 4.915816 +v 55.175076 33.627991 6.049330 +v 61.108791 33.976646 5.132885 +v 50.797867 33.976646 7.116533 +v 61.198849 31.484156 5.600996 +v 55.306892 31.484156 6.734510 +v 61.067162 32.225723 4.916492 +v 55.175205 32.225723 6.050006 +v 61.043030 32.618195 4.791047 +v 55.151070 32.618195 5.924561 +v 66.202370 33.629436 6.962537 +v 56.448875 33.629436 8.838947 +v 60.956619 31.878563 7.747334 +v 56.328194 31.878563 8.637766 +v 66.172409 34.510643 4.986992 +v 61.273354 34.536453 6.031681 +v 55.405556 34.536453 7.160548 +v 50.987167 34.548199 8.057079 +v 50.960152 34.522385 7.960084 +v 66.211365 34.561234 5.232846 +v 55.433598 34.561234 7.306308 +v 61.165611 34.259357 5.428216 +v 50.854687 34.259357 7.411866 +v 61.198586 34.370247 5.599622 +v 50.887661 34.370247 7.583271 +v 61.052670 33.435703 4.841157 +v 55.160713 33.435703 5.974671 +v 61.042969 33.235359 4.790749 +v 55.151016 33.235359 5.924263 +v 61.085835 33.809242 5.013558 +v 50.774910 33.809242 6.997207 +v 61.255272 31.332344 5.981068 +v 55.395523 31.332344 7.108386 +v 61.165855 31.594851 5.429498 +v 50.839096 31.655691 7.330834 +v 61.135742 31.726589 5.272964 +v 55.243786 31.726589 6.406479 +v 61.225929 31.413540 5.741743 +v 61.228027 31.375412 5.839467 +v 64.514832 31.369623 5.222026 +v 55.333969 31.413540 6.875257 +v 55.368279 31.375412 6.966784 +v 52.414326 31.369623 7.549962 +v 61.276142 31.305359 6.089562 +v 61.276070 31.292431 6.194254 +v 55.455307 31.292431 7.314072 +v 55.416393 31.305359 7.216879 +v 61.086006 32.044556 5.014452 +v 55.194054 32.044556 6.147966 +v 61.038113 32.823364 4.765495 +v 55.146156 32.823364 5.899009 +v 60.764568 33.031796 8.165266 +v 51.803696 32.928387 9.895829 +v 60.887939 33.517090 8.036115 +v 60.855103 33.418602 8.075045 +v 56.499119 33.517090 8.880450 +v 56.544056 33.418602 8.904418 +v 66.226974 33.810654 6.856220 +v 56.350979 33.810654 8.756198 +v 66.280647 34.128571 6.577813 +v 66.242142 34.057674 6.662187 +v 56.113045 34.115154 8.548443 +v 56.045982 34.186050 8.484377 +v 66.296783 34.371017 6.234537 +v 55.870380 34.371017 8.240402 +v 60.900528 32.045918 7.881865 +v 51.684113 32.133430 9.711477 +v 60.792236 32.419456 8.081479 +v 56.604824 32.419456 8.887068 +v 66.113503 32.721592 7.129715 +v 51.801254 32.721592 9.883153 +v 61.155338 31.484911 7.225088 +v 55.949890 31.484911 8.226529 +v 61.211418 31.376360 6.971399 +v 61.188618 31.414534 7.073867 +v 55.835308 31.396793 8.058090 +v 61.268269 31.293900 6.605762 +v 55.615234 31.293900 7.693312 +v 61.272095 31.280830 6.394876 +v 55.533432 31.280830 7.498899 +v 60.801880 33.236969 8.131590 +v 56.614464 33.236969 8.937180 +v 61.074768 34.028671 7.680733 +v 61.036625 33.948902 7.755341 +v 56.233929 33.977905 8.654870 +v 66.288780 34.260307 6.413921 +v 61.186584 34.256195 7.401259 +v 55.982620 34.260307 8.396654 +v 66.286667 34.522789 5.845553 +v 51.163979 34.522789 8.754906 +v 66.302574 34.458981 6.042809 +v 61.280937 34.455750 7.017253 +v 55.756145 34.455750 8.080130 +v 51.222404 34.458981 8.943981 +v 61.310783 34.561440 6.595968 +v 55.572117 34.561440 7.699992 +v 60.841263 32.244884 8.003099 +v 60.871834 32.151146 7.952385 +v 56.530216 32.244884 8.832472 +v 56.483009 32.151146 8.796721 +v 61.130646 31.546391 7.338381 +v 61.093697 31.607336 7.430837 +v 56.014854 31.546391 8.322573 +v 56.083473 31.607336 8.394720 +v 61.266319 31.309143 6.719496 +v 61.244133 31.334991 6.825958 +v 55.659241 31.309143 7.798204 +v 55.719345 31.334991 7.888835 +v 50.692825 29.737175 4.871002 +v 46.215881 29.235327 5.986284 +v 69.947617 29.737175 1.166701 +v 60.688461 29.737175 2.948009 +v 54.613262 33.641586 3.129050 +v 60.505219 33.641586 1.995536 +v 76.120895 26.923138 33.255138 +v 74.296257 27.851955 29.481903 +v 72.385330 27.945814 28.318336 +v 72.319244 27.958984 28.116190 +v 72.228294 27.971840 27.923962 +v 72.113907 27.984182 27.744629 +v 71.977898 27.995813 27.581039 +v 71.822403 28.006550 27.435783 +v 71.649887 28.016228 27.311081 +v 71.463081 28.024693 27.208948 +v 71.264900 28.031803 27.131058 +v 71.058472 28.037455 27.078587 +v 70.847054 28.041557 27.052334 +v 70.634003 28.044043 27.052769 +v 71.147552 28.113617 25.818951 +v 70.422714 28.044874 27.079849 +v 69.059708 27.945814 28.958130 +v 69.046066 27.958984 28.745895 +v 69.059204 27.971840 28.533642 +v 69.098892 27.984182 28.324667 +v 69.164490 27.995813 28.122292 +v 69.254990 28.006550 27.929712 +v 69.368919 28.016228 27.749901 +v 69.504501 28.024693 27.585747 +v 69.659637 28.031803 27.439884 +v 69.831863 28.037455 27.314566 +v 70.018402 28.041557 27.211752 +v 70.216408 28.044043 27.133106 +v 72.439148 27.919369 28.739426 +v 72.426010 27.906513 28.951679 +v 72.386322 27.894171 29.160656 +v 72.320724 27.882540 29.363029 +v 72.230225 27.871803 29.555611 +v 72.116295 27.862123 29.735420 +v 71.980713 27.853661 29.899574 +v 71.825577 27.846548 30.045439 +v 71.653351 27.840897 30.170757 +v 71.466766 27.836796 30.273579 +v 71.268753 27.834311 30.352226 +v 71.062500 27.833479 30.405472 +v 70.851166 27.834311 30.432564 +v 67.698425 27.939177 29.328310 +v 69.099884 27.932537 29.166986 +v 69.165970 27.919369 29.369133 +v 72.203445 27.764736 31.307426 +v 70.638107 27.836796 30.432999 +v 70.426743 27.840897 30.406734 +v 70.220314 27.846548 30.354265 +v 70.022133 27.853661 30.276375 +v 69.835327 27.862123 30.174240 +v 69.662811 27.871803 30.049538 +v 69.507317 27.882540 29.904284 +v 69.371307 27.894171 29.740694 +v 69.256920 27.906513 29.561359 +v 67.182846 29.161577 26.648310 +v 68.410538 30.035097 29.324543 +v 74.056992 30.035097 28.238258 +v 72.954063 28.962406 31.239151 +v 68.927895 28.962406 32.013718 +v 71.274086 28.064154 31.505253 +v 68.559402 29.311287 26.393002 +v 70.218193 28.413034 26.016777 +v 71.898178 29.311287 25.750675 +v 59.056892 27.939177 30.990797 +v 57.655434 27.945814 31.152121 +v 57.589355 27.958984 30.949974 +v 57.498402 27.971840 30.757748 +v 57.384010 27.984182 30.578415 +v 57.248009 27.995813 30.414824 +v 57.092514 28.006550 30.269569 +v 56.919998 28.016228 30.144867 +v 56.733192 28.024693 30.042732 +v 56.535011 28.031803 29.964842 +v 56.328579 28.037455 29.912373 +v 56.117161 28.041557 29.886118 +v 55.904106 28.044043 29.886555 +v 55.692818 28.044874 29.913635 +v 54.551880 28.113617 29.011681 +v 55.486565 28.044043 29.966881 +v 54.329819 27.945814 31.791914 +v 54.316181 27.958984 31.579679 +v 54.329308 27.971840 31.367428 +v 54.368988 27.984182 31.158455 +v 54.434601 27.995813 30.956078 +v 54.525097 28.006550 30.763496 +v 54.639030 28.016228 30.583687 +v 54.774609 28.024693 30.419533 +v 54.929749 28.031803 30.273668 +v 55.101974 28.037455 30.148350 +v 55.288559 28.041557 30.045528 +v 57.695614 27.932537 31.360977 +v 57.709255 27.919369 31.573212 +v 57.696117 27.906513 31.785465 +v 57.656433 27.894171 31.994438 +v 57.590836 27.882540 32.196815 +v 57.500336 27.871803 32.389397 +v 57.386406 27.862123 32.569206 +v 57.250824 27.853661 32.733360 +v 57.095688 27.846548 32.879223 +v 56.923462 27.840897 33.004539 +v 56.736877 27.836796 33.107365 +v 56.538868 27.834311 33.186012 +v 56.332615 27.833479 33.239258 +v 56.121326 27.834311 33.266338 +v 55.908272 27.836796 33.266777 +v 54.369999 27.932537 32.000771 +v 54.436081 27.919369 32.202915 +v 54.527027 27.906513 32.395145 +v 54.641411 27.894171 32.574478 +v 54.777424 27.882540 32.738068 +v 55.607769 27.764736 34.500156 +v 55.696854 27.840897 33.240520 +v 55.490425 27.846548 33.188049 +v 55.292240 27.853661 33.110161 +v 55.105431 27.862123 33.008026 +v 54.932915 27.871803 32.883324 +v 58.394215 30.035097 31.251516 +v 52.747757 30.035097 32.337799 +v 53.829506 29.311287 29.226784 +v 57.855675 29.311287 28.452217 +v 55.488300 28.413034 28.850563 +v 59.597198 28.812696 33.799271 +v 58.224174 28.962406 34.072937 +v 56.544193 28.064154 34.339039 +v 54.885399 28.962406 34.715263 +v 71.292938 28.057924 31.603260 +v 70.199333 28.419264 25.918770 +v 68.942963 30.537178 28.386755 +v 68.562332 30.362362 26.408234 +v 69.423767 30.378311 30.885975 +v 68.996002 30.219070 28.662430 +v 69.535141 30.191223 31.464848 +v 69.624710 28.959290 31.930481 +v 72.873917 30.191223 30.822521 +v 70.337189 30.477804 26.635292 +v 69.659355 29.816544 26.111471 +v 71.901108 30.362362 25.765907 +v 72.281738 30.537178 27.744431 +v 72.334778 30.219070 28.020103 +v 72.974686 29.105885 31.346336 +v 72.762543 30.378311 30.243649 +v 72.457283 30.627293 28.692375 +v 72.470924 30.614124 28.904610 +v 72.457787 30.601269 29.116861 +v 72.418106 30.588924 29.325836 +v 72.352501 30.577293 29.528212 +v 72.262001 30.566557 29.720791 +v 72.148071 30.556877 29.900600 +v 72.012497 30.548416 30.064758 +v 71.857361 30.541304 30.210621 +v 71.685135 30.535652 30.335938 +v 71.498550 30.531551 30.438761 +v 71.300537 30.529064 30.517406 +v 71.094284 30.528233 30.570656 +v 70.207520 30.528126 30.742981 +v 70.669891 30.531551 30.598179 +v 70.458519 30.535652 30.571915 +v 70.252090 30.541304 30.519445 +v 70.053909 30.548416 30.441555 +v 69.867096 30.556877 30.339422 +v 69.694580 30.566557 30.214722 +v 69.539093 30.577293 30.069466 +v 69.403091 30.588924 29.905874 +v 69.288696 30.601269 29.726540 +v 69.197739 30.614124 29.534313 +v 69.131668 30.627293 29.332167 +v 72.417099 30.640570 28.483517 +v 72.351028 30.653740 28.281372 +v 72.260071 30.666595 28.089146 +v 72.145676 30.678936 27.909809 +v 72.009674 30.690569 27.746220 +v 71.854187 30.701305 27.600964 +v 71.681671 30.710983 27.476261 +v 71.494858 30.719448 27.374128 +v 71.296677 30.726559 27.296238 +v 71.090248 30.732208 27.243767 +v 70.878830 30.736313 27.217514 +v 70.665771 30.738798 27.217949 +v 70.454483 30.739630 27.245029 +v 70.453842 30.739841 27.241695 +v 70.248184 30.738798 27.298286 +v 70.050179 30.736313 27.376934 +v 69.863647 30.732208 27.479748 +v 69.691414 30.726559 27.605064 +v 69.536270 30.719448 27.750927 +v 69.400696 30.710983 27.915085 +v 69.286766 30.701305 28.094894 +v 69.196274 30.690569 28.287472 +v 69.130669 30.678936 28.489849 +v 69.090981 30.666595 28.698824 +v 69.077843 30.653740 28.911076 +v 69.091484 30.640570 29.123310 +v 71.007957 30.540230 27.122408 +v 56.563049 28.057924 34.437046 +v 55.469444 28.419264 28.752552 +v 54.213074 30.537178 31.220539 +v 53.832436 30.362362 29.242018 +v 54.693878 30.378311 33.719757 +v 54.266106 30.219070 31.496214 +v 54.805244 30.191223 34.298630 +v 54.902679 29.357473 34.805073 +v 58.144020 30.191223 33.656307 +v 55.607292 30.477804 29.469078 +v 54.929462 29.816544 28.945257 +v 57.158852 29.314400 28.535456 +v 57.284344 30.324982 29.187744 +v 57.551849 30.537178 30.578217 +v 57.814846 30.099600 31.945259 +v 58.244793 29.105885 34.180122 +v 58.032654 30.378311 33.077431 +v 57.727394 30.627293 31.526159 +v 57.741032 30.614124 31.738392 +v 57.727894 30.601269 31.950645 +v 57.688210 30.588924 32.159622 +v 57.622612 30.577293 32.362000 +v 57.532112 30.566557 32.554577 +v 57.418182 30.556877 32.734386 +v 57.282600 30.548416 32.898544 +v 57.127464 30.541304 33.044403 +v 56.955238 30.535652 33.169724 +v 56.768654 30.531551 33.272545 +v 56.570648 30.529064 33.351189 +v 56.364391 30.528233 33.404442 +v 55.477631 30.528126 33.576767 +v 55.940048 30.531551 33.431953 +v 55.728630 30.535652 33.405701 +v 55.522202 30.541304 33.353230 +v 55.324017 30.548416 33.275337 +v 55.137207 30.556877 33.173206 +v 54.964695 30.566557 33.048508 +v 54.809204 30.577293 32.903252 +v 54.673187 30.588924 32.739662 +v 54.558804 30.601269 32.560326 +v 54.467861 30.614124 32.368095 +v 54.401779 30.627293 32.165955 +v 57.687210 30.640570 31.317303 +v 57.621132 30.653740 31.115156 +v 57.530182 30.666595 30.922928 +v 57.415791 30.678936 30.743595 +v 57.279785 30.690569 30.580006 +v 57.124294 30.701305 30.434750 +v 56.951782 30.710983 30.310047 +v 56.764969 30.719448 30.207914 +v 56.566788 30.726559 30.130024 +v 56.360359 30.732208 30.077553 +v 56.148941 30.736313 30.051300 +v 55.935886 30.738798 30.051735 +v 55.724598 30.739630 30.078815 +v 55.723957 30.739841 30.075481 +v 55.518341 30.738798 30.132063 +v 55.320335 30.736313 30.210709 +v 55.133751 30.732208 30.313532 +v 54.961525 30.726559 30.438850 +v 54.806385 30.719448 30.584713 +v 54.670807 30.710983 30.748867 +v 54.556877 30.701305 30.928679 +v 54.466377 30.690569 31.121258 +v 54.400768 30.678936 31.323635 +v 54.361088 30.666595 31.532608 +v 54.347961 30.653740 31.744860 +v 54.361595 30.640570 31.957096 +v 56.278065 30.540230 29.956192 +v 72.420639 30.939987 28.501871 +v 72.379440 27.446785 28.287746 +v 69.135193 30.926712 29.350521 +v 69.094002 27.433510 29.136396 +v 71.056618 27.334450 30.374886 +v 71.097809 30.827650 30.589008 +v 69.742195 27.617165 30.102001 +v 69.870636 30.856297 30.357777 +v 69.363037 27.517200 27.719313 +v 69.339844 30.856140 28.010603 +v 69.825974 27.538425 27.283978 +v 69.867172 31.031628 27.498100 +v 69.365425 27.395142 29.710104 +v 69.406616 30.888344 29.924229 +v 71.052582 27.538425 27.047997 +v 71.093781 31.031628 27.262123 +v 71.457199 27.525665 27.178358 +v 71.498390 31.018864 27.392483 +v 69.093002 27.485153 28.294079 +v 69.134201 30.978355 28.508202 +v 69.653748 27.532776 27.409294 +v 69.694946 31.025976 27.623417 +v 70.012512 27.542528 27.181164 +v 70.053711 31.035728 27.395287 +v 71.819687 27.347519 30.014851 +v 71.860886 30.840721 30.228973 +v 70.420860 27.341869 30.376144 +v 70.462051 30.835072 30.590271 +v 70.016243 27.354631 30.245785 +v 70.057442 30.847832 30.459909 +v 69.251038 27.407484 29.530769 +v 69.292229 30.900684 29.744894 +v 72.313354 27.459957 28.085602 +v 72.354553 30.953157 28.299725 +v 72.108017 27.485153 27.714039 +v 72.149216 30.978355 27.928164 +v 71.737137 27.762161 27.352734 +v 71.685196 31.010403 27.494616 +v 71.972015 27.496784 27.550449 +v 72.013206 30.989986 27.764574 +v 70.841171 27.542528 27.021744 +v 70.882362 31.035728 27.235867 +v 70.416824 27.545847 27.049259 +v 70.458023 31.039047 27.263382 +v 71.259018 27.532776 27.100468 +v 71.300209 31.025976 27.314592 +v 69.040184 27.459957 28.715305 +v 69.081375 30.953157 28.929430 +v 69.158600 27.496784 28.091702 +v 69.199799 30.989986 28.305826 +v 69.249100 27.507523 27.899122 +v 69.053322 27.472813 28.503054 +v 69.094513 30.966013 28.717178 +v 69.095016 30.939987 29.141665 +v 69.053818 27.446785 28.927540 +v 69.498611 27.525665 27.555157 +v 69.539810 31.018864 27.769279 +v 70.210518 27.545013 27.102516 +v 70.251717 31.038216 27.316641 +v 71.262878 27.335281 30.321636 +v 71.304062 30.828482 30.535759 +v 71.647461 27.341869 30.140167 +v 71.688660 30.835072 30.354290 +v 71.974823 27.354631 29.868988 +v 72.016022 30.847832 30.083111 +v 72.224342 27.372772 29.525021 +v 72.212448 30.711140 29.823437 +v 72.380432 27.395142 29.130066 +v 72.421631 30.888344 29.344189 +v 72.420120 27.407484 28.921089 +v 72.461319 30.900684 29.135214 +v 72.460815 30.926712 28.710728 +v 72.422562 27.683025 28.511898 +v 70.214432 27.347519 30.323675 +v 70.255623 30.840721 30.537800 +v 70.632233 27.337767 30.402409 +v 70.778152 30.679604 30.613853 +v 70.845276 27.335281 30.401974 +v 70.886475 30.828482 30.616096 +v 69.501427 27.383511 29.873695 +v 69.542625 30.876711 30.087818 +v 69.698120 30.865974 30.233074 +v 69.160080 27.420340 29.338543 +v 69.201279 30.913540 29.552666 +v 72.222404 27.472813 27.893375 +v 72.263603 30.966013 28.107498 +v 71.857712 31.000725 27.619318 +v 70.628113 27.545013 27.022179 +v 70.669312 31.038216 27.236301 +v 71.460876 27.337767 30.242990 +v 71.502075 30.830967 30.457115 +v 72.110413 27.363094 29.704830 +v 72.314835 27.383511 29.332441 +v 72.356033 30.876711 29.546566 +v 72.433258 27.420340 28.708839 +v 72.474457 30.913540 28.922962 +v 57.690742 30.939987 31.335655 +v 57.649551 27.446785 31.121532 +v 54.405308 30.926712 32.184303 +v 54.364113 27.433510 31.970182 +v 56.326729 27.334450 33.208672 +v 56.367924 30.827650 33.422791 +v 55.012306 27.617165 32.935783 +v 55.140739 30.856297 33.191559 +v 54.633144 27.517200 30.553097 +v 54.609955 30.856140 30.844387 +v 55.096085 27.538425 30.117762 +v 55.137283 31.031628 30.331886 +v 54.635525 27.395142 32.543892 +v 54.676720 30.888344 32.758015 +v 56.322693 27.538425 29.881783 +v 56.363888 31.031628 30.095905 +v 56.727306 27.525665 30.012144 +v 56.768501 31.018864 30.226267 +v 54.363106 27.485153 31.127865 +v 54.404297 30.978355 31.341988 +v 54.923859 27.532776 30.243080 +v 54.965057 31.025976 30.457203 +v 55.282669 27.542528 30.014938 +v 55.323868 31.035728 30.229061 +v 57.089802 27.347519 32.848633 +v 57.130997 30.840721 33.062759 +v 55.690971 27.341869 33.209930 +v 55.732162 30.835072 33.424057 +v 55.286354 27.354631 33.079567 +v 55.327549 30.847832 33.293694 +v 54.521141 27.407484 32.364555 +v 54.562336 30.900684 32.578678 +v 57.583469 27.459957 30.919386 +v 57.624664 30.953157 31.133511 +v 57.378128 27.485153 30.547825 +v 57.419319 30.978355 30.761948 +v 57.007240 27.762161 30.186518 +v 56.955307 31.010403 30.328400 +v 57.242123 27.496784 30.384235 +v 57.283318 30.989986 30.598358 +v 56.111279 27.542528 29.855530 +v 56.152470 31.035728 30.069653 +v 55.686935 27.545847 29.883045 +v 55.728127 31.039047 30.097168 +v 56.529121 27.532776 29.934254 +v 56.570320 31.025976 30.148376 +v 54.310295 27.459957 31.549089 +v 54.351490 30.953157 31.763214 +v 54.428715 27.496784 30.925488 +v 54.469910 30.989986 31.139610 +v 54.519211 27.507523 30.732908 +v 54.323425 27.472813 31.336838 +v 54.364616 30.966013 31.550964 +v 54.365128 30.939987 31.975449 +v 54.323933 27.446785 31.761326 +v 54.768723 27.525665 30.388943 +v 54.809917 31.018864 30.603065 +v 55.480682 27.545013 29.936293 +v 55.521873 31.038216 30.150417 +v 57.730923 30.926712 31.544512 +v 57.689728 27.433510 31.330389 +v 56.532982 27.335281 33.155418 +v 56.574177 30.828482 33.369545 +v 56.917576 27.341869 32.973953 +v 56.958771 30.835072 33.188076 +v 57.244938 27.354631 32.702774 +v 57.286133 30.847832 32.916893 +v 57.494453 27.372772 32.358807 +v 57.482563 30.711140 32.657219 +v 57.650547 27.395142 31.963852 +v 57.691742 30.888344 32.177975 +v 57.690231 27.407484 31.754875 +v 57.731426 30.900684 31.968998 +v 55.484539 27.347519 33.157459 +v 55.525734 30.840721 33.371586 +v 55.902386 27.337767 33.236183 +v 56.048359 30.679604 33.447617 +v 56.115440 27.335281 33.235748 +v 56.156635 30.828482 33.449875 +v 54.771538 27.383511 32.707481 +v 54.812733 30.876711 32.921604 +v 54.968224 30.865974 33.066856 +v 54.430199 27.420340 32.172325 +v 54.471390 30.913540 32.386452 +v 57.492516 27.472813 30.727158 +v 57.533710 30.966013 30.941284 +v 57.127823 31.000725 30.453102 +v 55.898224 27.545013 29.855965 +v 55.939415 31.038216 30.070087 +v 56.730991 27.337767 33.076775 +v 56.772186 30.830967 33.290901 +v 57.380516 27.363094 32.538616 +v 57.584949 27.383511 32.166229 +v 57.626144 30.876711 32.380348 +v 57.703369 27.420340 31.542622 +v 57.744564 30.913540 31.756746 +v 71.968338 26.315218 30.481468 +v 70.098694 26.579535 26.529116 +v 69.414413 26.607899 30.284462 +v 72.160309 26.581665 30.184135 +v 71.733040 27.032171 27.003265 +v 69.956200 26.819508 26.728064 +v 70.342125 26.580122 26.472696 +v 68.642342 26.707924 28.801178 +v 68.660568 26.732841 28.391180 +v 68.739578 26.754332 28.025385 +v 69.299118 26.615072 30.189610 +v 68.675522 26.691605 29.061028 +v 72.108589 26.319937 30.377516 +v 72.493721 26.602472 29.780579 +v 72.015846 27.016306 27.207657 +v 69.592812 26.812012 26.920242 +v 69.678917 26.574068 26.699045 +v 68.562599 26.497252 28.167009 +v 68.509338 26.474747 28.544397 +v 69.119263 26.628878 29.998981 +v 69.728638 26.850067 30.359697 +v 68.707001 26.409433 29.571854 +v 72.668144 26.934923 28.409842 +v 72.417603 26.334480 30.080812 +v 72.495529 26.339773 29.979467 +v 69.341316 26.564245 26.924273 +v 54.651581 26.609947 33.091145 +v 58.167965 26.443861 31.037786 +v 57.984772 26.707924 30.851517 +v 57.238449 26.315218 33.315254 +v 54.565544 26.801542 29.982037 +v 58.079407 26.371498 32.235325 +v 58.013012 26.474747 30.563715 +v 55.368805 26.579535 29.362902 +v 54.993431 26.591278 33.329979 +v 55.230995 26.581665 33.441048 +v 54.771683 26.602472 33.190002 +v 57.761147 26.739536 30.378838 +v 57.003143 27.032171 29.837051 +v 55.107254 26.576782 29.458143 +v 53.974915 26.521887 30.571533 +v 53.910694 26.512682 30.734039 +v 57.818161 26.607899 32.515388 +v 57.606628 26.754332 30.167179 +v 57.285950 27.016306 30.041443 +v 54.862923 26.812012 29.754026 +v 54.949017 26.574068 29.532829 +v 55.612232 26.580122 29.306482 +v 54.232159 26.780737 30.385586 +v 54.302399 26.549704 30.054762 +v 54.033253 26.528168 30.457861 +v 54.457920 26.362295 33.082184 +v 58.015217 26.362295 32.397823 +v 57.954395 26.482391 30.450287 +v 54.224499 26.544409 30.156107 +v 55.350819 26.319937 33.601429 +v 72.581818 31.931406 28.642597 +v 69.247734 32.009239 28.014282 +v 69.908287 33.045830 27.321976 +v 72.610809 31.917156 28.869511 +v 68.997543 31.931406 29.332151 +v 72.329315 31.973259 28.008385 +v 68.968552 31.945658 29.105236 +v 72.524551 31.945658 28.421124 +v 69.718849 31.853573 30.463123 +v 72.582649 31.889553 29.325228 +v 69.417747 33.029507 27.682684 +v 69.600281 33.037315 27.520166 +v 71.349800 33.037315 27.183588 +v 69.250061 31.889553 29.966362 +v 72.740997 32.896088 29.219879 +v 72.040512 31.835407 30.312843 +v 68.996719 31.973259 28.649521 +v 69.773560 32.035866 27.478678 +v 69.506042 33.033638 27.598263 +v 70.333771 32.044880 27.223888 +v 70.872749 32.809589 30.990463 +v 69.385010 31.876631 30.151194 +v 72.525909 31.876631 29.546936 +v 71.864098 31.828529 30.458990 +v 68.855392 32.956409 28.983366 +v 69.053452 31.986179 28.427811 +v 69.137558 31.998238 28.214966 +v 69.382187 32.019012 27.828947 +v 72.171112 32.998066 27.665890 +v 70.787460 32.043091 27.165773 +v 71.459435 32.027405 27.292416 +v 70.386681 32.817955 30.947479 +v 70.253113 32.821613 30.913517 +v 69.819046 32.838135 30.727482 +v 69.509567 32.855495 30.503813 +v 69.063927 32.896088 29.927288 +v 72.747513 32.902519 29.113739 +v 71.455765 32.809589 30.878304 +v 71.787888 32.815147 30.723747 +v 72.331635 31.853573 29.960466 +v 72.441803 31.864576 29.759783 +v 72.541077 32.855495 29.920601 +v 68.861908 32.962837 28.877224 +v 69.061829 33.003433 28.176502 +v 69.266235 33.020794 27.853973 +v 70.019012 33.047729 27.269726 +v 70.147141 33.049339 27.218803 +v 72.194366 31.986179 27.823555 +v 71.860519 32.009239 27.511625 +v 70.990250 33.045830 27.113823 +v 71.173492 32.035866 27.209354 +v 70.607948 33.050293 27.114550 +v 69.054817 31.917156 29.553627 +v 70.722923 31.820793 30.804764 +v 70.612663 32.813095 30.983280 +v 70.119934 31.835407 30.682331 +v 69.912468 32.833904 30.778522 +v 69.431808 32.860863 30.431215 +v 71.783859 33.020794 27.369621 +v 71.690437 33.025024 27.318584 +v 69.018494 32.902519 29.831139 +v 70.506989 32.815147 30.970173 +v 71.694626 32.813095 30.775129 +v 71.583900 32.811199 30.827377 +v 71.339340 32.808632 30.916300 +v 71.168411 32.808216 30.955994 +v 72.259796 31.848354 30.059460 +v 72.268860 32.833904 30.325191 +v 72.538986 32.962837 28.169817 +v 72.584412 32.956409 28.265965 +v 72.093338 33.003433 27.593292 +v 70.994957 32.808632 30.982553 +v 57.851929 31.931406 31.476381 +v 54.517845 32.009239 30.848066 +v 55.178398 33.045830 30.155760 +v 57.880920 31.917156 31.703297 +v 54.267658 31.931406 32.165936 +v 57.599419 31.973259 30.842171 +v 54.238655 31.945658 31.939022 +v 57.794659 31.945658 31.254906 +v 54.988960 31.853573 33.296906 +v 57.852760 31.889553 32.159012 +v 54.808964 32.027405 30.495689 +v 54.985382 32.034283 30.349545 +v 56.511497 32.034283 30.055944 +v 54.520180 31.889553 32.800144 +v 58.011108 32.896088 32.053665 +v 57.310623 31.835407 33.146629 +v 54.266842 31.973259 31.483303 +v 55.085125 33.043781 30.207142 +v 55.704605 33.050713 29.974895 +v 56.142860 32.809589 33.824249 +v 54.655117 31.876631 32.984978 +v 57.796021 31.876631 32.380722 +v 57.161301 32.817955 33.491821 +v 54.125465 32.956409 31.817158 +v 54.323566 31.986179 31.261597 +v 54.407684 31.998238 31.048748 +v 54.652302 32.019012 30.662731 +v 55.533676 33.050293 30.014589 +v 57.441216 32.998066 30.499674 +v 56.057571 32.043091 29.999559 +v 56.729546 32.027405 30.126202 +v 55.608093 31.828529 33.586372 +v 55.089157 32.838135 33.561268 +v 54.779667 32.855495 33.337601 +v 54.334042 32.896088 32.761070 +v 58.017624 32.902519 31.947525 +v 56.725872 32.809589 33.712090 +v 57.058002 32.815147 33.557533 +v 57.601746 31.853573 32.794250 +v 57.711914 31.864576 32.593567 +v 57.811188 32.855495 32.754387 +v 54.132027 32.962837 31.711008 +v 54.331932 33.003433 31.010290 +v 54.536339 33.020794 30.687756 +v 55.385986 32.043091 30.128761 +v 57.464470 31.986179 30.657339 +v 57.130630 32.009239 30.345409 +v 56.260357 33.045830 29.947609 +v 55.878059 33.050293 29.948336 +v 54.324921 31.917156 32.387413 +v 55.993080 31.820793 33.638538 +v 55.882774 32.813095 33.817062 +v 55.390045 31.835407 33.516117 +v 55.182579 32.833904 33.612305 +v 54.701931 32.860863 33.264996 +v 57.272736 32.821613 33.410721 +v 57.053970 33.020794 30.203407 +v 56.960548 33.025024 30.152367 +v 56.366035 33.043781 29.960716 +v 54.288567 32.902519 32.664932 +v 55.777092 32.815147 33.803955 +v 56.964729 32.813095 33.608917 +v 56.854012 32.811199 33.661163 +v 56.609451 32.808632 33.750084 +v 56.438522 32.808216 33.789780 +v 57.529907 31.848354 32.893242 +v 57.538971 32.833904 33.158978 +v 57.809090 32.962837 31.003601 +v 57.854523 32.956409 31.099751 +v 57.363449 33.003433 30.427076 +v 56.265068 32.808632 33.816338 +v 67.710693 39.093384 25.775009 +v 58.390137 29.744638 12.561989 +v 53.301613 30.837086 17.513102 +v 66.998322 30.350315 22.819202 +v 53.510498 29.193283 22.495529 +v 65.717789 30.823067 15.353117 +v 65.329773 35.080704 19.524605 +v 65.208099 35.579727 19.579735 +v 65.675636 35.415257 22.173010 +v 61.460754 30.122192 27.606140 +v 47.649055 27.454153 12.493173 +v 47.290409 27.790154 12.328449 +v 70.123520 33.096260 26.457876 +v 70.434082 33.071548 26.801268 +v 69.936821 33.076488 26.816385 +v 69.685043 33.065853 27.038284 +v 69.770439 33.080070 26.789906 +v 63.151642 33.009308 29.217632 +v 63.290779 32.984978 29.587803 +v 63.429760 32.932781 30.412621 +v 63.407700 32.940395 30.292667 +v 63.390350 32.948917 30.156967 +v 63.334511 32.970093 29.822248 +v 63.318298 32.978077 29.695122 +v 63.284714 32.991623 29.480576 +v 65.170296 32.997131 29.027990 +v 61.567287 33.059551 28.702791 +v 63.587120 33.089775 27.821175 +v 63.470627 33.093452 27.783619 +v 60.887775 33.080994 28.483789 +v 54.167400 33.082066 29.759163 +v 52.926620 33.098160 29.735325 +v 52.659462 33.091377 29.897301 +v 52.462818 33.080070 30.119602 +v 52.384384 33.071388 30.276371 +v 52.328743 33.060848 30.458994 +v 52.313683 33.046104 30.702461 +v 52.329174 33.038490 30.823681 +v 52.969357 33.039158 30.689613 +v 52.571171 33.087246 29.981724 +v 53.488445 33.076488 29.980782 +v 73.954010 33.014244 27.058990 +v 73.492531 32.996738 27.433302 +v 73.832451 33.033283 26.771727 +v 73.760345 33.040184 26.672985 +v 73.582001 33.052505 26.506313 +v 73.367722 33.062210 26.389236 +v 71.162155 33.053223 26.960196 +v 70.851105 33.067471 26.787560 +v 70.676483 33.070595 26.770233 +v 74.502411 32.833042 29.909569 +v 73.923241 32.854427 29.672142 +v 74.496109 32.814003 30.221432 +v 74.465790 32.807098 30.339878 +v 73.815895 32.821659 30.227333 +v 74.362015 32.794777 30.560827 +v 73.649445 32.806507 30.506533 +v 74.206474 32.785072 30.749050 +v 74.112228 32.781399 30.827145 +v 63.571854 32.884747 31.168949 +v 63.482590 32.914238 30.704979 +v 63.449841 32.926147 30.517002 +v 65.286980 32.919964 30.264404 +v 61.677921 32.908604 31.144112 +v 65.416847 32.890198 30.725039 +v 63.595421 32.876957 31.291462 +v 63.613636 32.870937 31.386137 +v 61.732666 32.866093 31.827078 +v 61.727100 32.859928 31.928732 +v 63.721375 32.849617 31.713259 +v 63.746559 32.843353 31.810556 +v 61.692570 32.837376 32.303265 +v 63.794128 32.829422 32.028717 +v 65.893768 32.825825 31.683489 +v 61.684250 32.823376 32.533310 +v 66.160912 32.812107 31.855865 +v 66.533264 32.776691 32.361996 +v 53.116489 32.810104 34.398037 +v 52.954250 32.833042 34.055077 +v 53.403961 32.854427 33.619709 +v 53.503826 32.840313 33.830807 +v 53.653728 32.825954 34.036137 +v 54.578373 32.788551 34.468437 +v 53.657028 32.781399 34.762383 +v 53.540535 32.785072 34.724831 +v 53.326263 32.794777 34.607758 +v 48.117363 32.414230 25.472364 +v 47.936600 37.451244 25.062084 +v 47.462337 33.349678 22.596886 +v 48.472832 34.769199 27.849392 +v 48.132996 37.451244 25.024300 +v 47.448547 35.891544 21.995876 +v 47.658737 33.349678 22.559101 +v 48.669228 34.769199 27.811611 +v 68.597794 32.460957 20.770002 +v 68.529022 34.956100 20.941839 +v 68.656647 37.451244 21.075893 +v 68.725418 34.956100 20.904057 +v 69.192871 34.769199 23.863203 +v 68.061569 35.143002 17.982693 +v 55.493916 28.714106 7.319708 +v 55.513721 36.246075 7.617717 +v 55.613857 36.785557 7.873100 +v 55.716690 36.180756 8.096821 +v 61.174526 35.372417 7.578281 +v 56.715851 36.162281 9.156989 +v 56.882771 35.167583 9.204084 +v 56.822067 30.019897 8.888545 +v 56.431080 30.478729 8.663978 +v 56.156376 36.533798 8.710601 +v 55.448944 35.688725 7.386063 +v 58.242477 32.209637 9.415129 +v 57.847332 33.890442 9.466166 +v 55.887363 36.691738 8.367769 +v 60.789288 36.162281 8.373327 +v 60.650536 35.064186 8.472656 +v 60.650768 30.852600 8.148406 +v 61.226772 28.741980 5.832928 +v 61.276680 36.221165 6.092371 +v 61.304726 36.245945 6.238131 +v 61.139194 35.320530 7.654732 +v 59.853283 33.890442 9.080255 +v 61.334484 36.246075 6.497900 +v 61.323769 36.180756 7.018112 +v 61.336254 36.785557 6.772207 +v 61.212914 36.040863 7.487852 +v 61.270916 36.696884 7.311611 +v 61.101208 36.471527 7.835536 +v 60.939262 35.652382 8.111008 +v 55.129189 35.673710 5.810829 +v 55.272560 36.103405 6.556059 +v 55.155918 35.824539 5.949753 +v 55.188896 35.935432 6.121159 +v 55.388840 36.221134 7.160454 +v 55.282745 35.532955 6.608987 +v 54.923420 34.600830 4.741237 +v 55.004421 35.057861 5.162272 +v 55.052929 35.978577 5.414425 +v 54.940838 30.423944 4.831768 +v 55.182667 30.252462 6.088789 +v 55.207291 28.823801 6.216789 +v 60.840225 34.880630 3.736892 +v 60.853653 32.655666 3.806683 +v 60.900909 31.887091 4.052315 +v 60.842358 35.727879 3.747977 +v 60.912659 36.041630 4.113379 +v 60.989826 36.306293 4.514506 +v 61.135990 36.051147 5.274265 +v 61.164520 36.103405 5.422544 +v 61.280792 36.221134 6.026940 +v 61.204659 29.431095 5.674609 +v 55.328812 29.431095 6.805025 +v 61.294086 36.786648 6.124968 +v 55.408882 36.221165 7.221238 +v 46.259369 31.056692 15.285244 +v 44.349480 28.218271 12.921448 +v 44.634125 27.525064 13.077703 +v 49.204826 26.501539 28.896095 +v 45.588505 27.542517 12.810178 +# 3660 vertices, 0 vertices normals + +f 90 1792 1 1782 +f 2 1784 83 1782 +f 1783 3 1784 2 +f 3 3624 1716 1784 +f 1785 1690 3655 5 +f 1786 73 1882 4 +f 1787 1667 1785 5 +f 4 1903 1668 1787 +f 1786 5 3635 6 +f 1759 1804 18 1788 +f 97 1808 23 1788 +f 69 1805 20 1789 +f 24 1809 25 1789 +f 7 1918 99 1790 +f 1763 3649 1764 1790 +f 1739 3634 22 1791 +f 8 1879 17 1791 +f 90 1919 89 1792 +f 9 3651 1 1792 +f 6 3635 16 1793 +f 70 1881 73 1793 +f 1750 1808 10 1794 +f 11 1932 1749 1794 +f 47 1809 12 1795 +f 13 2847 45 1795 +f 94 1923 93 1796 +f 1796 1720 3628 1723 +f 1797 1676 1798 94 +f 1798 14 1799 94 +f 15 1904 82 1800 +f 76 1801 1699 1800 +f 1801 1685 3611 1699 +f 89 1915 7 1802 +f 1764 3650 9 1802 +f 16 3632 1739 1803 +f 17 1880 70 1803 +f 19 1931 18 1804 +f 1759 3647 1761 1804 +f 1738 3638 20 1805 +f 69 1877 21 1805 +f 99 1931 19 1806 +f 1761 3648 1763 1806 +f 22 3639 1738 1807 +f 21 1878 8 1807 +f 1810 27 2494 26 +f 1811 612 1812 40 +f 510 2615 29 1813 +f 1814 28 1813 29 +f 1815 31 1814 29 +f 1815 616 2590 590 +f 31 1815 590 1816 +f 1816 30 2591 606 +f 31 1816 606 1817 +f 33 1818 32 1817 +f 1818 607 2592 593 +f 32 1818 593 1819 +f 1819 34 2593 36 +f 1820 35 1821 32 +f 1821 531 1822 32 +f 1822 608 1823 32 +f 1823 527 1829 32 +f 1824 526 1830 43 +f 586 2600 37 1825 +f 1825 40 1826 38 +f 614 2587 39 1827 +f 1827 40 2424 43 +f 1828 603 2599 614 +f 43 1833 585 1828 +f 1830 42 1831 43 +f 1831 41 1832 43 +f 1832 602 1833 43 +f 1833 601 2530 585 +f 1834 859 1840 44 +f 1835 753 1836 45 +f 1836 784 1837 45 +f 46 1875 47 1837 +f 1838 791 1846 859 +f 1839 781 1840 859 +f 1841 48 1839 859 +f 1842 805 1841 859 +f 1843 49 1842 859 +f 1844 760 1843 859 +f 1845 802 1844 859 +f 1846 801 1845 859 +f 1838 54 1847 50 +f 1848 51 1847 54 +f 1849 52 1848 54 +f 1850 795 1849 54 +f 1851 799 1850 54 +f 1852 53 1851 54 +f 1853 56 1854 55 +f 1854 1776 1855 55 +f 1856 57 1865 1776 +f 1857 828 1856 1776 +f 60 2805 61 1858 +f 1859 75 1894 60 +f 1860 823 1862 59 +f 1861 62 1859 60 +f 1862 850 1864 59 +f 1863 63 1861 60 +f 1865 857 1855 1776 +f 1866 58 1857 1776 +f 1867 64 1866 1776 +f 1868 768 1867 1776 +f 1869 65 1868 1776 +f 66 1869 1776 1864 +f 1870 140 1872 60 +f 1871 67 1863 60 +f 1872 132 1871 60 +f 59 1873 255 1858 +f 1874 493 2497 32 +f 1873 1773 1874 32 +f 1875 25 1809 47 +f 1875 46 2738 68 +f 1876 69 1789 25 +f 1876 68 2728 777 +f 1877 777 2757 780 +f 1878 780 2759 751 +f 1879 751 2734 750 +f 1880 750 2753 72 +f 1881 72 2752 71 +f 73 1881 71 1882 +f 1882 847 1883 4 +f 1884 82 1883 843 +f 1885 76 1884 74 +f 1886 817 1887 75 +f 1887 838 1888 75 +f 1888 813 1889 75 +f 1889 77 1890 75 +f 1890 78 1891 75 +f 1891 763 1892 75 +f 1892 810 1893 75 +f 1893 807 1894 75 +f 1885 79 1886 75 +f 1895 938 1901 1547 +f 1896 933 1895 1547 +f 1897 80 1899 81 +f 1898 867 1899 80 +f 1900 864 1902 945 +f 938 2966 945 1902 +f 864 3539 1578 1902 +f 82 1904 1670 1903 +f 93 1922 83 1905 +f 1905 1716 3626 1719 +f 1906 1720 1796 93 +f 121 1974 120 1907 +f 1907 1593 1909 84 +f 1908 123 1977 84 +f 1909 865 1908 84 +f 98 2722 742 1910 +f 1911 98 1931 99 +f 1912 85 1911 99 +f 1913 752 1912 99 +f 1914 86 1913 99 +f 1915 749 1920 7 +f 1916 756 1914 99 +f 1917 87 1916 99 +f 778 2733 88 1918 +f 1919 91 1915 89 +f 1920 778 1918 7 +f 1919 90 1921 773 +f 1921 83 1922 846 +f 92 2719 846 1922 +f 1923 841 2823 92 +f 1924 95 2825 841 +f 1924 94 1799 138 +f 1925 815 2798 95 +f 1925 138 1983 129 +f 1926 812 2820 815 +f 1926 129 1985 134 +f 1927 836 2794 812 +f 1927 134 1992 133 +f 1928 834 2795 836 +f 1928 133 1986 139 +f 1929 139 1997 125 +f 1929 96 2792 834 +f 11 1794 10 1910 +f 1930 98 1910 10 +f 97 1788 18 1930 +f 1931 98 1930 18 +f 1932 11 1910 742 +f 1933 804 1934 101 +f 1934 761 1935 101 +f 1935 748 1936 101 +f 1936 759 1937 101 +f 1937 800 1938 101 +f 100 1939 861 1938 +f 1939 789 1940 861 +f 1940 790 1941 861 +f 1941 102 1942 861 +f 1942 103 1943 861 +f 1943 794 1944 861 +f 1944 105 1945 861 +f 1945 104 1946 861 +f 1946 830 1947 861 +f 1947 106 1948 861 +f 1948 108 1949 861 +f 1949 107 1968 861 +f 1950 826 1967 115 +f 96 1929 125 1951 +f 126 1952 109 1951 +f 1953 808 1952 126 +f 1954 110 1953 126 +f 1955 111 1954 126 +f 1956 851 1955 126 +f 1957 821 1956 126 +f 1958 112 1959 848 +f 1959 629 1960 848 +f 126 2196 628 1958 +f 1961 818 1960 629 +f 1962 113 1963 766 +f 1964 746 1963 113 +f 1965 630 1966 114 +f 1969 116 1970 117 +f 1970 633 1971 117 +f 1971 118 1972 117 +f 1972 631 1975 117 +f 1973 119 1974 121 +f 1968 115 1969 117 +f 1976 122 1978 117 +f 1978 861 1968 117 +f 124 1977 123 1976 +f 1870 255 1979 125 +f 1979 126 1951 125 +f 1859 62 1980 127 +f 1981 128 1980 62 +f 1982 1693 1981 62 +f 1983 1702 1994 130 +f 1984 1711 1985 129 +f 1871 132 1986 133 +f 132 1872 139 1986 +f 1985 131 1987 134 +f 1987 1713 1988 134 +f 1988 135 1989 134 +f 1989 1701 1992 134 +f 1990 137 3613 136 +f 1861 63 1991 1691 +f 1991 136 1982 1691 +f 63 1992 1701 1991 +f 1992 63 1863 133 +f 1863 67 1871 133 +f 1799 1733 1993 138 +f 1993 1730 1994 138 +f 1994 1702 1983 138 +f 1801 76 1885 75 +f 1995 1685 1801 75 +f 75 1859 127 1996 +f 1872 140 1997 139 +f 140 1870 125 1997 +f 142 2034 1779 1998 +f 141 3512 149 1998 +f 1999 1756 2169 1548 +f 2000 143 1999 1548 +f 2001 1715 2000 1548 +f 2002 1725 2168 231 +f 2003 1672 2002 231 +f 2004 1727 2003 231 +f 2005 1728 2004 231 +f 2006 1705 2005 231 +f 2007 146 2008 1712 +f 2009 1663 2008 146 +f 2010 144 2009 146 +f 2011 145 2010 146 +f 2012 147 2011 146 +f 1692 3613 1695 2013 +f 2013 146 3503 1535 +f 2014 1692 2013 1535 +f 2015 1694 2014 1535 +f 2016 149 2017 1682 +f 2018 1666 2035 1769 +f 1535 3504 142 2016 +f 2019 1683 2017 149 +f 2020 1666 3602 148 +f 2020 149 3512 860 +f 156 2034 252 2021 +f 2022 156 2195 150 +f 151 3351 1345 2022 +f 2023 156 2022 1345 +f 1346 3353 1319 2023 +f 2024 156 2023 1319 +f 152 3344 1321 2024 +f 2025 1320 2026 156 +f 2026 153 2027 156 +f 2027 1348 2028 156 +f 2028 154 2029 156 +f 2029 155 2030 156 +f 2030 157 2033 156 +f 2031 1360 2032 156 +f 2032 1326 2126 156 +f 2033 1325 2031 156 +f 860 2843 1744 2035 +f 2036 1226 2037 166 +f 2037 158 2038 166 +f 2038 160 2039 166 +f 2039 159 2047 166 +f 2040 161 2041 162 +f 2041 1276 2042 162 +f 2042 1248 2043 162 +f 1249 3312 163 2043 +f 2044 162 2043 163 +f 1278 3311 164 2044 +f 2045 162 2044 164 +f 1247 3321 165 2045 +f 2046 162 2045 165 +f 2046 1274 3310 1246 +f 2048 162 2046 1246 +f 2049 1225 2137 866 +f 2050 1291 2049 866 +f 2051 167 2050 866 +f 2052 1289 2051 866 +f 2053 168 2052 866 +f 2054 1237 2053 866 +f 2055 169 2054 866 +f 2056 1229 2055 866 +f 2057 1261 2056 866 +f 2058 1262 2057 866 +f 2059 170 2058 866 +f 2060 1260 2059 866 +f 1258 3316 171 2061 +f 2061 866 2062 1301 +f 172 3327 1301 2062 +f 2062 866 2063 1300 +f 1236 3306 1300 2063 +f 2063 866 2064 1256 +f 1257 3315 1256 2064 +f 2064 866 2065 173 +f 2066 1287 2065 866 +f 2067 1285 2066 866 +f 2068 1284 2067 866 +f 2069 174 2068 866 +f 2070 175 2069 866 +f 2071 1253 2070 866 +f 2072 176 2071 866 +f 2073 1299 2072 866 +f 2074 1252 2073 866 +f 2075 177 2074 866 +f 2076 178 2075 866 +f 2077 1329 2078 179 +f 2079 200 2107 1374 +f 2080 180 2090 188 +f 2048 181 3314 1255 +f 2081 182 2082 162 +f 2082 1282 2083 162 +f 2084 1254 2085 1545 +f 2085 183 2086 1545 +f 2086 184 2087 1545 +f 2088 1306 2087 197 +f 2089 185 2088 186 +f 2090 187 3305 188 +f 2091 198 2104 1332 +f 2092 190 2105 199 +f 2093 191 2079 1374 +f 2094 203 2109 202 +f 2083 192 2084 1545 +f 2095 234 3511 1545 +f 2089 187 2090 1365 +f 2096 1303 2095 1545 +f 2097 193 2096 1545 +f 2098 194 2097 1545 +f 2099 1335 2098 1545 +f 2100 195 2099 1545 +f 2101 1307 2100 1545 +f 2102 1334 2101 1545 +f 2103 196 2102 1545 +f 180 3345 1365 2090 +f 2080 189 2091 1332 +f 2106 1361 2105 190 +f 2108 201 2107 200 +f 2077 866 2110 1304 +f 2111 213 2112 1363 +f 2113 204 2112 213 +f 2114 205 2113 213 +f 2115 206 2114 213 +f 2116 207 2115 213 +f 2117 208 2116 213 +f 2118 1373 2136 213 +f 2119 219 2135 213 +f 1375 3358 210 2120 +f 211 3338 217 2121 +f 2122 213 2132 214 +f 2123 212 2131 213 +f 2124 1322 2130 213 +f 2125 1328 2127 213 +f 2127 1358 2128 213 +f 2128 1359 2129 213 +f 2129 1350 2124 213 +f 2130 1353 2123 213 +f 2131 1354 2132 213 +f 2132 1355 3329 214 +f 2122 215 3339 1357 +f 2133 216 2121 213 +f 213 2121 217 2120 +f 2134 1305 2119 213 +f 2135 218 2118 213 +f 2136 209 2117 213 +f 2137 220 2138 866 +f 2139 221 2140 1548 +f 2140 1293 2141 1548 +f 2141 1239 2142 1548 +f 2142 222 2143 1548 +f 2143 1238 2144 1548 +f 2144 1266 2145 1548 +f 2145 1265 2146 1548 +f 2146 1263 2167 1548 +f 2147 1264 2148 230 +f 2148 1267 2149 230 +f 2149 1240 2150 230 +f 2150 223 2166 230 +f 2151 224 2152 166 +f 2152 225 2153 166 +f 2153 1233 2154 166 +f 2154 1268 2155 166 +f 2155 1235 2156 166 +f 2156 1234 2157 166 +f 2157 1273 2158 166 +f 2158 226 2159 166 +f 2159 227 2160 166 +f 2160 1245 2161 166 +f 1242 3304 1272 2161 +f 2162 166 2161 1272 +f 1241 3308 228 2162 +f 2163 166 2162 228 +f 229 3318 1270 2163 +f 2164 166 2163 1270 +f 1302 3328 1295 2164 +f 2165 1297 2036 166 +f 2167 230 3507 231 +f 1548 2167 231 2168 +f 2169 232 2846 1548 +f 143 3619 233 1999 +f 2095 1303 3330 235 +f 234 2095 235 2170 +f 2170 1309 3331 236 +f 234 2170 236 2171 +f 2171 1367 3356 1315 +f 234 2171 1315 2172 +f 2172 237 3340 238 +f 234 2172 238 2173 +f 2173 1368 3346 1337 +f 2174 240 2175 234 +f 2175 239 2176 234 +f 2176 241 2177 234 +f 2177 1317 2178 234 +f 2178 242 2179 234 +f 2179 243 2180 234 +f 2180 245 2181 234 +f 2181 244 2182 234 +f 2182 1310 2191 234 +f 2183 1311 2184 252 +f 2184 1343 2185 252 +f 2185 1342 2186 252 +f 2186 246 2187 252 +f 2187 1344 2188 252 +f 2188 247 2192 252 +f 248 3349 249 2189 +f 250 3348 253 2190 +f 251 3333 1369 2021 +f 2192 1312 3336 1340 +f 2193 1318 2189 252 +f 252 2189 249 2190 +f 2194 251 2021 252 +f 1347 3352 150 2195 +f 2195 156 2021 1369 +f 254 2616 628 2196 +f 2197 439 2419 254 +f 2198 1544 2197 254 +f 2196 126 1979 257 +f 2199 1534 2198 254 +f 2200 257 1979 255 +f 2201 255 1873 32 +f 2201 258 3504 256 +f 1544 3507 1539 2197 +f 2202 1536 3505 1537 +f 2203 1543 2205 453 +f 2204 258 2201 32 +f 2206 259 2207 43 +f 2207 1538 1829 43 +f 260 2227 272 2208 +f 2209 363 2319 372 +f 2210 262 2236 283 +f 2211 312 2269 264 +f 2212 338 2291 400 +f 263 2294 279 2212 +f 2213 271 2353 386 +f 2214 347 2312 365 +f 2215 349 2211 264 +f 2216 379 2326 378 +f 2217 289 2294 265 +f 2210 266 2349 267 +f 269 2230 268 2218 +f 398 2290 282 2218 +f 2219 275 2352 270 +f 2220 288 2219 270 +f 2221 356 2253 325 +f 353 2297 342 2215 +f 2222 273 2287 369 +f 344 2299 345 2223 +f 331 2223 330 2224 +f 332 2214 365 2225 +f 261 2209 372 2226 +f 332 2261 306 2214 +f 2216 334 2267 311 +f 2227 333 2323 272 +f 2228 267 2349 336 +f 2229 335 2290 399 +f 2228 337 2237 262 +f 2222 375 2293 277 +f 269 2232 339 2230 +f 274 2347 268 2230 +f 275 2219 324 2217 +f 2231 276 2232 269 +f 2233 685 2231 282 +f 2232 276 2663 671 +f 2234 284 2233 335 +f 2235 277 2292 339 +f 2236 285 2234 283 +f 2235 671 2641 650 +f 2237 646 2236 262 +f 2238 273 2222 277 +f 2239 684 2237 337 +f 2238 650 2639 278 +f 2240 286 2239 338 +f 2241 280 2287 273 +f 2241 278 2665 281 +f 2242 645 2240 279 +f 2243 654 2242 289 +f 2244 287 2243 324 +f 2245 290 2244 288 +f 2245 340 2246 681 +f 2246 292 2247 291 +f 727 2685 291 2247 +f 2247 292 2306 293 +f 2248 724 2710 727 +f 2248 293 2296 294 +f 2249 725 2708 724 +f 2249 294 2284 341 +f 2250 295 2691 725 +f 2250 341 2283 296 +f 2251 718 2706 295 +f 2251 296 2305 357 +f 2252 700 2689 718 +f 2252 357 2282 325 +f 2253 297 2689 700 +f 2254 298 2681 297 +f 2254 356 2281 299 +f 2255 299 2304 319 +f 2255 300 2714 298 +f 2256 331 2224 280 +f 2256 281 2643 677 +f 2257 344 2223 331 +f 2257 677 2667 302 +f 2258 301 2299 344 +f 2258 302 2629 643 +f 2259 303 2288 301 +f 2259 643 2629 304 +f 2260 332 2225 303 +f 2260 304 2661 305 +f 2261 305 2637 667 +f 2262 261 2226 306 +f 2262 667 2656 307 +f 2263 308 2209 261 +f 2263 307 2658 637 +f 308 2263 637 2264 +f 2264 636 2265 260 +f 2266 350 2265 309 +f 2267 334 2266 310 +f 2268 311 2267 705 +f 2269 312 2268 317 +f 2270 264 2269 318 +f 2271 353 2270 313 +f 2272 354 2271 711 +f 2273 343 2272 314 +f 2274 360 2273 315 +f 2275 323 2274 666 +f 2276 361 2275 316 +f 2277 732 2712 300 +f 2277 319 2295 321 +f 2278 735 2716 732 +f 2278 321 2303 320 +f 2279 716 2704 735 +f 2279 320 2302 322 +f 2280 713 2702 716 +f 2280 322 2301 351 +f 2276 713 2280 351 +f 2219 288 2244 324 +f 2281 356 2221 394 +f 358 2340 326 2282 +f 327 2339 390 2283 +f 2284 294 2296 389 +f 327 2283 341 2284 +f 323 2286 329 2285 +f 362 2330 329 2286 +f 2287 346 2314 369 +f 2288 366 2320 373 +f 2225 366 2288 303 +f 334 2216 378 2289 +f 2290 398 2348 399 +f 266 2210 283 2229 +f 2291 337 2228 336 +f 405 2355 274 2292 +f 2293 405 2292 277 +f 2294 289 2242 279 +f 2294 263 2351 265 +f 271 2213 340 2220 +f 2213 292 2246 340 +f 393 2342 392 2295 +f 326 2340 394 2221 +f 359 2334 389 2296 +f 354 2307 382 2297 +f 2285 328 2298 360 +f 2298 328 2328 384 +f 2299 373 2311 345 +f 2224 346 2287 280 +f 2226 347 2214 306 +f 2208 363 2209 308 +f 2211 349 2329 348 +f 312 2211 348 2300 +f 2289 333 2227 350 +f 2227 260 2265 350 +f 335 2233 282 2290 +f 397 2309 351 2301 +f 322 2302 352 2301 +f 355 2341 352 2302 +f 2303 355 2302 320 +f 393 2295 319 2304 +f 2305 296 2283 390 +f 359 2296 293 2306 +f 2306 292 2213 386 +f 2307 384 2332 382 +f 2307 354 2272 343 +f 2300 379 2216 311 +f 2303 321 2295 392 +f 395 2304 299 2281 +f 358 2282 357 2305 +f 2298 343 2273 360 +f 361 2276 351 2308 +f 396 2317 362 2308 +f 2309 396 2308 351 +f 2286 323 2275 361 +f 417 2369 411 2310 +f 363 2208 272 2310 +f 373 2320 374 2311 +f 364 2316 345 2311 +f 433 2366 406 2312 +f 406 2384 432 2313 +f 366 2225 365 2313 +f 346 2322 367 2314 +f 429 2315 369 2314 +f 368 2321 375 2315 +f 376 2322 330 2316 +f 370 2330 362 2317 +f 396 2356 371 2317 +f 412 2363 433 2318 +f 347 2226 372 2318 +f 411 2368 412 2319 +f 432 2383 374 2320 +f 2321 404 2293 375 +f 418 2370 417 2323 +f 377 2371 418 2324 +f 333 2289 378 2324 +f 383 2375 423 2325 +f 349 2215 342 2325 +f 420 2372 377 2326 +f 380 2328 328 2327 +f 329 2330 381 2327 +f 385 2332 384 2328 +f 423 2374 421 2329 +f 383 2325 342 2331 +f 382 2332 426 2331 +f 421 2373 420 2333 +f 379 2300 348 2333 +f 359 2306 386 2334 +f 2334 1541 2335 389 +f 2335 387 2338 389 +f 2336 388 2337 327 +f 2337 1536 2339 327 +f 327 2284 389 2338 +f 2339 1536 2202 358 +f 2202 453 2340 358 +f 453 2344 394 2340 +f 391 2345 352 2341 +f 2341 355 2303 392 +f 391 2341 392 2342 +f 2342 393 2304 395 +f 2343 391 2342 395 +f 453 2343 395 2344 +f 2309 397 2345 1531 +f 2345 397 2301 352 +f 391 3502 1531 2345 +f 2346 401 2349 266 +f 2347 455 2348 398 +f 2348 455 2346 266 +f 2349 401 2350 336 +f 400 2291 336 2350 +f 2350 401 2351 263 +f 2351 401 2352 275 +f 2352 401 2353 271 +f 2354 455 2347 274 +f 2355 402 2354 274 +f 403 2415 371 2356 +f 396 2309 1531 2356 +f 2357 402 2355 404 +f 2355 405 2293 404 +f 2358 651 2385 404 +f 2359 430 2384 406 +f 2360 674 2359 406 +f 2361 407 2360 406 +f 2362 678 2361 406 +f 2363 413 2367 433 +f 2364 408 2362 406 +f 2365 642 2364 406 +f 410 2637 409 2366 +f 2367 410 2366 433 +f 2368 668 2363 412 +f 2368 411 2369 414 +f 2369 417 2370 416 +f 415 2677 416 2370 +f 2371 419 2699 415 +f 2372 706 2695 419 +f 2373 649 2697 706 +f 2374 422 2635 649 +f 2375 424 2679 422 +f 2376 427 2701 424 +f 2376 383 2331 426 +f 2377 426 2332 385 +f 2377 425 2687 427 +f 2378 435 2390 439 +f 1533 2357 428 2379 +f 2379 404 2382 663 +f 2321 368 2380 430 +f 429 2314 367 2380 +f 2381 430 2380 367 +f 376 2316 364 2381 +f 2383 430 2381 364 +f 2385 434 2386 404 +f 2386 431 2382 404 +f 2387 664 2388 1533 +f 2388 647 2389 1533 +f 2389 661 2378 1533 +f 2390 436 2391 439 +f 2392 438 2422 439 +f 440 2393 437 2391 +f 655 2394 441 2393 +f 2394 659 2395 441 +f 2396 696 2397 632 +f 2395 658 2396 632 +f 2398 443 2399 442 +f 2400 702 2401 634 +f 2401 719 2414 634 +f 2402 720 2403 444 +f 2403 691 2404 444 +f 2404 445 2405 444 +f 2405 731 2406 444 +f 2397 728 2398 442 +f 2407 736 2408 371 +f 2408 712 2413 371 +f 2409 446 2683 425 +f 2409 385 2328 380 +f 2410 448 2693 446 +f 2410 380 2327 381 +f 2406 447 2407 371 +f 2411 449 2654 448 +f 2411 381 2330 370 +f 449 2411 370 2412 +f 371 2413 640 2412 +f 2415 444 2406 371 +f 2399 701 2400 634 +f 444 2416 450 2414 +f 2416 1573 2417 450 +f 2418 635 2417 1573 +f 2420 451 2419 439 +f 2421 627 2420 439 +f 2422 452 2421 439 +f 2423 1778 1901 1578 +f 43 2424 467 2425 +f 2424 475 2454 467 +f 2343 453 2416 444 +f 2205 43 2425 1578 +f 2426 1573 2416 453 +f 1542 2346 455 2427 +f 2427 1533 2428 454 +f 439 2197 1539 2428 +f 2429 1777 3657 456 +f 2430 1772 2429 456 +f 2431 492 2445 457 +f 2432 464 2442 1532 +f 2433 459 2438 462 +f 2434 1532 2435 463 +f 1532 2442 461 2435 +f 2436 687 2441 463 +f 2437 652 2433 463 +f 2438 501 2504 462 +f 2438 459 2640 676 +f 2439 676 2666 653 +f 2440 653 2644 679 +f 2440 511 2513 512 +f 2439 512 2503 501 +f 2441 672 2437 463 +f 686 2436 463 2435 +f 2443 458 2432 1532 +f 2444 465 2443 1532 +f 2446 657 2445 492 +f 2447 466 2446 492 +f 2448 680 2447 492 +f 2449 683 2448 492 +f 697 2449 492 2450 +f 467 2451 729 2450 +f 2452 726 2451 467 +f 2453 703 2452 467 +f 2454 721 2705 468 +f 477 2703 717 2455 +f 2456 491 2493 477 +f 2457 469 2459 505 +f 2458 470 2456 477 +f 2460 514 2458 477 +f 2461 734 2463 475 +f 2462 472 2460 477 +f 2463 692 2465 475 +f 2464 521 2462 477 +f 2465 474 2467 475 +f 2466 473 2464 477 +f 2467 722 2471 475 +f 2468 523 2466 477 +f 2469 1774 2472 1778 +f 505 2507 504 2455 +f 2470 476 2468 477 +f 2471 721 2454 475 +f 2423 467 2450 492 +f 2459 471 2461 475 +f 2473 478 2494 505 +f 2474 479 2505 511 +f 2474 679 2668 644 +f 2475 507 2499 479 +f 2475 644 2630 669 +f 2476 508 2511 507 +f 2476 669 2660 670 +f 2477 497 2502 508 +f 2477 670 2662 480 +f 2478 498 2501 497 +f 2478 480 2638 481 +f 2479 483 2509 498 +f 2479 481 2657 482 +f 2480 494 2510 483 +f 2480 482 2659 484 +f 494 2480 484 2481 +f 2481 485 2482 513 +f 2483 503 2482 486 +f 2484 515 2483 707 +f 2485 708 2486 491 +f 2486 487 2487 491 +f 2487 689 2488 491 +f 2488 488 2489 491 +f 2489 489 2490 491 +f 2490 695 2491 491 +f 2491 490 2492 491 +f 2492 641 2493 491 +f 2484 710 2485 491 +f 1813 28 1814 460 +f 2495 510 1813 460 +f 1532 2434 31 2496 +f 2431 1532 2496 32 +f 525 2522 495 2498 +f 494 2481 513 2498 +f 507 2511 621 2499 +f 605 2505 479 2499 +f 2500 491 2456 496 +f 2456 470 2517 496 +f 620 2589 617 2501 +f 617 2533 622 2502 +f 512 2513 499 2503 +f 500 2504 501 2503 +f 509 2512 462 2504 +f 502 2513 511 2505 +f 528 2524 597 2506 +f 503 2483 515 2506 +f 518 2518 504 2507 +f 505 2494 27 2507 +f 2468 476 2508 524 +f 506 2614 620 2509 +f 495 2613 506 2510 +f 622 2602 621 2511 +f 2512 510 2495 462 +f 2495 463 2433 462 +f 597 2523 525 2514 +f 513 2482 503 2514 +f 2515 609 2458 514 +f 2460 472 2462 519 +f 2516 528 2506 515 +f 2500 516 2516 515 +f 2470 504 2518 517 +f 2462 521 2519 519 +f 2464 473 2520 520 +f 2466 523 2521 522 +f 2522 550 2552 625 +f 2523 597 2524 552 +f 527 1823 569 1824 +f 34 1819 593 2525 +f 2526 583 1810 612 +f 1811 37 2600 604 +f 2527 577 2599 603 +f 1823 608 2604 569 +f 2528 600 2515 519 +f 2529 530 2574 529 +f 2530 584 2527 585 +f 42 1830 570 1831 +f 41 1831 570 2529 +f 2531 580 2580 587 +f 2532 586 1825 38 +f 33 2611 560 2534 +f 1821 35 2594 595 +f 2516 516 2595 599 +f 550 2522 525 2523 +f 532 2556 598 2535 +f 2536 524 2508 578 +f 575 2561 556 2536 +f 2537 533 2538 615 +f 2538 534 2609 615 +f 2539 542 2537 624 +f 2538 533 2664 673 +f 2540 543 2539 535 +f 2541 619 2590 534 +f 2542 675 2540 536 +f 2541 673 2626 688 +f 2543 537 2542 589 +f 2544 540 2603 619 +f 2545 538 2543 618 +f 2544 688 2676 665 +f 2546 546 2545 539 +f 2547 558 2591 540 +f 2547 665 2653 559 +f 2548 541 2546 545 +f 2549 544 2548 623 +f 2550 547 2549 548 +f 2551 549 2550 626 +f 2551 625 2552 638 +f 2552 550 2553 639 +f 551 2678 639 2553 +f 2553 550 2523 552 +f 2554 553 2700 551 +f 2554 552 2524 599 +f 2555 554 2696 553 +f 2555 599 2595 598 +f 2556 709 2698 554 +f 2557 555 2636 709 +f 2557 532 2605 610 +f 2558 690 2680 555 +f 2558 610 2605 600 +f 2559 690 2558 600 +f 2528 557 2560 698 +f 2560 699 2688 698 +f 2560 557 2596 556 +f 2561 694 2684 699 +f 2562 560 2611 558 +f 2562 559 2634 648 +f 2563 592 2534 560 +f 2563 648 2674 662 +f 2564 591 2592 592 +f 2564 662 2632 563 +f 2565 561 2525 591 +f 2565 563 2632 562 +f 2566 594 2593 561 +f 2566 562 2648 564 +f 2567 596 2594 594 +f 2567 564 2646 656 +f 2568 595 2594 596 +f 2568 656 2670 565 +f 2569 566 2604 595 +f 2569 565 2650 660 +f 566 2569 660 2570 +f 2570 682 2571 569 +f 2572 568 2571 567 +f 2573 570 2572 730 +f 2574 530 2573 571 +f 2575 529 2574 572 +f 2576 573 2575 723 +f 2577 584 2576 574 +f 2578 577 2577 693 +f 2579 613 2578 579 +f 2580 580 2579 733 +f 2581 587 2580 581 +f 2582 588 2581 582 +f 2583 704 2694 694 +f 2583 575 2536 578 +f 2584 576 2655 704 +f 2584 578 2606 611 +f 2585 715 2628 576 +f 2585 611 2607 583 +f 2586 715 2585 583 +f 2526 604 2582 714 +f 601 2598 573 2530 +f 41 2529 529 1832 +f 39 2531 587 1826 +f 2587 613 2531 39 +f 587 2581 588 2532 +f 2588 618 2543 589 +f 2589 620 2614 548 +f 2593 594 1820 36 +f 34 2525 561 2593 +f 1822 531 1821 595 +f 2517 609 2605 532 +f 2535 516 2500 496 +f 518 2606 578 2518 +f 2508 517 2518 578 +f 2521 524 2536 556 +f 2520 522 2596 557 +f 2519 520 2520 557 +f 526 2597 570 1830 +f 2597 568 2572 570 +f 2598 529 2575 573 +f 2598 601 1833 602 +f 586 2532 588 2600 +f 2588 502 2505 605 +f 509 2504 500 2601 +f 539 2610 621 2602 +f 2603 540 2591 30 +f 2534 592 2592 607 +f 35 1820 594 2594 +f 2604 566 2570 569 +f 608 1822 595 2604 +f 2605 609 2515 600 +f 2606 518 2507 27 +f 27 1810 583 2607 +f 526 1824 569 2597 +f 614 2599 577 2587 +f 535 2601 500 2608 +f 616 1815 29 2609 +f 623 2533 617 2589 +f 2603 590 2590 619 +f 606 2591 558 2611 +f 2608 499 2612 536 +f 2612 499 2513 502 +f 626 2614 506 2613 +f 618 2588 605 2610 +f 2602 622 2533 545 +f 2533 623 2548 545 +f 2612 589 2542 536 +f 615 2609 29 2615 +f 2512 509 2601 624 +f 2615 510 2512 624 +f 2601 535 2539 624 +f 2613 495 2522 625 +f 2614 626 2550 548 +f 1962 629 2420 627 +f 2421 452 1965 113 +f 1967 630 2422 438 +f 2392 437 1969 115 +f 112 1958 628 2616 +f 2419 451 1959 112 +f 1972 118 2620 442 +f 442 2399 634 2617 +f 437 2393 441 2618 +f 1970 116 2618 441 +f 441 2395 632 2619 +f 1971 633 2619 632 +f 632 2397 442 2620 +f 1974 119 2621 450 +f 2417 635 2622 120 +f 1973 631 2617 634 +f 634 2414 450 2621 +f 1593 1907 120 2622 +f 2418 1599 3548 1593 +f 414 2369 416 2623 +f 636 2264 637 2623 +f 638 2552 639 2624 +f 485 2481 484 2624 +f 434 2385 276 2625 +f 2386 434 2625 685 +f 2441 687 2626 673 +f 687 2436 688 2626 +f 316 2654 449 2627 +f 640 2413 713 2627 +f 641 2492 576 2628 +f 2493 641 2628 715 +f 2364 642 2629 302 +f 546 2660 669 2630 +f 644 2668 538 2630 +f 2390 435 2651 286 +f 2631 436 2390 286 +f 2632 465 2444 562 +f 2444 457 2648 562 +f 664 2387 285 2633 +f 2388 664 2633 646 +f 2442 464 2634 559 +f 464 2432 648 2634 +f 318 2269 317 2635 +f 2486 708 2698 709 +f 2367 413 2656 667 +f 2637 410 2367 667 +f 544 2549 547 2638 +f 674 2360 278 2639 +f 2359 674 2639 650 +f 543 2666 676 2640 +f 459 2642 542 2640 +f 430 2359 650 2641 +f 2358 430 2641 671 +f 652 2437 533 2642 +f 678 2362 677 2643 +f 2361 678 2643 281 +f 537 2668 679 2644 +f 653 2666 675 2644 +f 655 2393 440 2645 +f 654 2243 287 2645 +f 2446 466 2670 656 +f 2631 645 2647 440 +f 645 2242 654 2647 +f 2445 657 2646 564 +f 658 2395 659 2649 +f 290 2245 681 2649 +f 2448 683 2672 660 +f 661 2389 684 2651 +f 2443 465 2632 662 +f 663 2382 284 2652 +f 2387 663 2652 285 +f 665 2676 686 2653 +f 461 2442 559 2653 +f 666 2693 448 2654 +f 490 2491 704 2655 +f 2492 490 2655 576 +f 413 2363 668 2656 +f 547 2550 549 2657 +f 668 2368 414 2658 +f 549 2551 638 2659 +f 2365 409 2661 304 +f 2629 642 2365 304 +f 546 2546 541 2660 +f 2637 305 2661 409 +f 541 2548 544 2662 +f 651 2358 671 2663 +f 2385 651 2663 276 +f 2437 672 2664 533 +f 672 2441 673 2664 +f 407 2361 281 2665 +f 2360 407 2665 278 +f 408 2364 302 2667 +f 2362 408 2667 677 +f 2394 655 2645 287 +f 287 2244 290 2669 +f 2447 680 2650 565 +f 2396 658 2649 681 +f 2671 696 2396 681 +f 682 2570 660 2672 +f 2449 697 2686 682 +f 647 2388 646 2673 +f 2389 647 2673 684 +f 2432 458 2674 648 +f 458 2443 662 2674 +f 431 2386 685 2675 +f 2382 431 2675 284 +f 2436 686 2676 688 +f 309 2265 636 2677 +f 486 2482 485 2678 +f 313 2270 318 2679 +f 2487 487 2636 555 +f 2404 691 2681 298 +f 691 2403 297 2681 +f 692 2463 579 2682 +f 2465 692 2682 693 +f 314 2687 425 2683 +f 446 2693 315 2683 +f 489 2489 699 2684 +f 2490 489 2684 694 +f 2671 291 2685 728 +f 567 2571 682 2686 +f 697 2450 729 2686 +f 711 2701 427 2687 +f 488 2488 698 2688 +f 2489 488 2688 699 +f 2403 720 2689 297 +f 474 2465 693 2690 +f 2467 474 2690 574 +f 2400 701 2708 725 +f 572 2574 571 2692 +f 2453 468 2707 572 +f 695 2490 694 2694 +f 2491 695 2694 704 +f 705 2267 310 2695 +f 710 2484 707 2696 +f 317 2268 705 2697 +f 2485 710 2696 554 +f 310 2266 309 2699 +f 707 2483 486 2700 +f 313 2679 424 2701 +f 2559 698 2488 689 +f 2413 712 2702 713 +f 712 2408 716 2702 +f 2586 714 2703 477 +f 2408 736 2704 716 +f 736 2407 735 2704 +f 2703 714 2582 582 +f 2457 717 2703 582 +f 2402 719 2706 718 +f 2689 720 2402 718 +f 2705 721 2471 574 +f 2471 722 2467 574 +f 2401 702 2691 295 +f 723 2575 572 2707 +f 2705 723 2707 468 +f 701 2399 443 2708 +f 571 2573 730 2709 +f 2452 703 2692 571 +f 2398 728 2685 727 +f 730 2572 567 2711 +f 2451 726 2709 730 +f 732 2716 447 2712 +f 731 2405 300 2712 +f 2713 581 2580 733 +f 2461 471 2713 733 +f 2405 445 2714 300 +f 445 2404 298 2714 +f 734 2461 733 2715 +f 2463 734 2715 579 +f 2407 447 2716 735 +f 469 2457 582 2717 +f 2713 471 2459 469 +f 2718 773 1921 846 +f 2719 738 3624 737 +f 737 3621 772 2718 +f 2720 847 2721 739 +f 71 2755 775 2721 +f 2722 741 3644 740 +f 1933 742 2722 740 +f 1839 48 2723 782 +f 2724 745 1963 746 +f 1964 114 2836 1709 +f 2725 744 2724 746 +f 2726 1680 1867 768 +f 2727 1758 1916 87 +f 747 2757 777 2728 +f 68 2738 1736 2728 +f 2729 1754 3645 1753 +f 1939 100 2729 1753 +f 1846 791 2730 1743 +f 2731 758 1936 748 +f 1843 760 2732 1741 +f 1920 749 2749 1765 +f 2733 778 1920 1765 +f 779 3636 771 2734 +f 2735 1747 1912 752 +f 1836 753 2736 754 +f 2737 757 1914 756 +f 46 2765 1735 2738 +f 1942 102 2768 787 +f 2739 1757 1943 103 +f 2740 788 1847 51 +f 1848 52 2775 1746 +f 2741 1752 1937 759 +f 1844 802 2742 1742 +f 2743 762 1935 761 +f 1842 49 2744 1740 +f 811 2792 96 2745 +f 109 1952 809 2745 +f 2746 764 1892 763 +f 1891 78 2793 765 +f 2747 767 1961 766 +f 2748 1681 1868 65 +f 2749 749 1915 91 +f 769 2749 91 2750 +f 774 3620 770 2750 +f 769 3650 1765 2749 +f 2751 776 2752 1771 +f 2752 72 2753 1771 +f 771 3632 1771 2753 +f 91 1919 773 2754 +f 772 3621 774 2754 +f 2752 776 2755 71 +f 1917 88 2758 1762 +f 2756 1760 2727 87 +f 747 3633 1737 2757 +f 2733 1766 2758 88 +f 1737 3634 779 2759 +f 2760 1748 1911 85 +f 1835 44 2761 755 +f 2762 741 2722 98 +f 1840 781 2763 1734 +f 2764 783 1913 86 +f 784 1836 754 2765 +f 1944 794 2774 1707 +f 2766 785 2778 105 +f 2767 796 2777 795 +f 1850 799 2781 786 +f 1941 790 2770 1755 +f 2769 50 1847 788 +f 1940 789 1939 1753 +f 2771 1745 2730 791 +f 2769 792 2771 791 +f 2772 793 2774 794 +f 2773 1707 2774 793 +f 2776 1689 3653 1768 +f 1849 795 2777 1768 +f 2778 1714 1945 105 +f 2779 797 1946 104 +f 1851 53 2780 798 +f 2782 1754 2729 800 +f 2729 100 1938 800 +f 1845 801 2783 803 +f 2784 1751 1934 804 +f 1841 805 2785 743 +f 1954 111 2786 1704 +f 2787 60 1894 1678 +f 1894 807 2816 1678 +f 1952 808 2788 809 +f 2789 1732 2788 808 +f 2790 1677 2791 810 +f 2791 833 1893 810 +f 1731 2818 834 2792 +f 1890 77 2819 1687 +f 1675 2821 812 2794 +f 2794 836 2795 1710 +f 2795 835 3622 1710 +f 2796 1686 1889 813 +f 2797 1700 2796 813 +f 1888 838 2822 839 +f 814 2826 95 2798 +f 815 2820 1721 2798 +f 2799 840 1887 817 +f 1886 79 2827 816 +f 2800 1729 1960 818 +f 2801 820 3610 819 +f 1869 66 2801 819 +f 1957 848 2831 822 +f 2802 852 1956 821 +f 2803 824 1862 823 +f 1955 851 2804 825 +f 2804 1706 3617 825 +f 2787 806 3608 853 +f 1950 107 2809 827 +f 2806 1673 1966 826 +f 1857 58 2807 1696 +f 1949 108 2840 1724 +f 2808 1726 2809 107 +f 1856 828 2810 829 +f 1947 830 2813 831 +f 2811 832 1948 106 +f 1855 857 2812 1665 +f 1852 55 2814 1664 +f 1953 110 2815 1703 +f 2817 835 2795 834 +f 2818 837 2817 834 +f 2820 812 2821 1722 +f 1717 2829 92 2823 +f 841 2825 1718 2823 +f 842 3605 845 2824 +f 74 1884 843 2824 +f 2825 95 2826 844 +f 845 3605 1671 2828 +f 79 1885 74 2828 +f 2829 738 2719 92 +f 2830 842 2824 843 +f 2720 1669 2830 843 +f 2832 849 2831 848 +f 2833 1679 2834 850 +f 2834 820 2801 850 +f 2801 66 1864 850 +f 2804 851 1956 852 +f 2805 853 3609 854 +f 1860 61 2805 854 +f 2835 1674 2836 114 +f 1866 64 2837 1697 +f 2838 1698 2837 64 +f 2839 855 2840 108 +f 1865 57 2841 856 +f 2842 858 2841 57 +f 2843 860 2844 859 +f 863 3640 1744 2843 +f 860 1853 54 2844 +f 101 1938 861 2845 +f 1548 2846 862 2845 +f 2847 863 1834 45 +f 1932 101 2848 1749 +f 1900 81 1908 865 +f 123 1908 81 2849 +f 1899 867 1898 213 +f 2111 866 2849 81 +f 866 2138 122 2849 +f 2850 124 1976 117 +f 2851 1022 2850 117 +f 2852 1021 2851 117 +f 2853 868 2852 117 +f 2854 1108 2853 117 +f 2855 1059 2854 117 +f 2856 869 2855 117 +f 2857 1054 2856 117 +f 2858 870 2857 117 +f 2859 871 2858 117 +f 2860 1068 2859 117 +f 2861 872 2860 117 +f 2862 1063 2861 117 +f 2863 874 2864 873 +f 2865 1075 2866 874 +f 2866 875 2867 874 +f 2867 1039 2868 874 +f 2868 876 2869 874 +f 2869 877 2870 874 +f 2870 878 2871 874 +f 2871 1031 2872 874 +f 2872 879 2873 874 +f 2873 881 2874 874 +f 2874 880 2875 874 +f 2875 882 2876 874 +f 2876 1065 2864 874 +f 2850 1022 3183 1117 +f 2877 883 2878 124 +f 2878 884 2879 124 +f 2879 885 2880 124 +f 2880 1116 2881 124 +f 2881 886 2882 124 +f 2882 1114 2883 124 +f 2883 1045 2884 124 +f 2884 887 2885 124 +f 2885 1112 2886 124 +f 2886 888 2887 124 +f 2887 889 2893 124 +f 2888 1026 2889 891 +f 2889 894 2894 891 +f 2890 891 2891 890 +f 2892 1023 2891 891 +f 2894 893 2895 891 +f 2895 895 2896 891 +f 2896 1093 2897 891 +f 2897 896 2898 891 +f 2898 897 2899 891 +f 2899 898 2900 891 +f 2900 899 2901 891 +f 2901 1052 2902 891 +f 2902 892 2892 891 +f 2903 900 2904 904 +f 2890 874 2903 904 +f 905 2979 952 2904 +f 121 1907 84 2905 +f 902 2987 901 2905 +f 2906 902 2905 84 +f 2893 891 2908 903 +f 1977 124 2893 903 +f 904 2904 952 2907 +f 2907 946 2908 891 +f 2909 905 2904 900 +f 2910 953 2903 874 +f 2911 906 1975 121 +f 2863 117 1975 906 +f 2912 940 2913 907 +f 2914 1161 2913 940 +f 2915 1160 2914 940 +f 2916 1210 2915 940 +f 2917 908 2916 940 +f 2918 909 2917 940 +f 2919 910 2918 940 +f 2920 911 2919 940 +f 2921 1171 2920 940 +f 2922 912 2921 940 +f 2923 1132 2922 940 +f 2924 1213 2923 940 +f 2925 913 2924 940 +f 2926 933 2927 914 +f 2928 916 2929 933 +f 2929 917 2930 933 +f 2930 1139 2931 933 +f 2931 918 2932 933 +f 2932 1177 2933 933 +f 2933 919 2934 933 +f 2934 920 2935 933 +f 2935 1142 2936 933 +f 2936 921 2937 933 +f 2937 922 2938 933 +f 2938 915 2927 933 +f 2939 923 2940 927 +f 2940 924 2941 927 +f 2941 1192 2942 927 +f 2942 925 2943 927 +f 2943 926 2944 927 +f 2944 928 2945 927 +f 2945 1218 2946 927 +f 2946 1190 2947 927 +f 2947 1148 2948 927 +f 2948 1187 2949 927 +f 2949 929 2950 927 +f 2950 930 2951 927 +f 2951 931 2959 927 +f 2952 1201 2953 80 +f 2953 1200 2960 80 +f 1896 80 2954 932 +f 2955 1120 2954 80 +f 2956 1207 2955 80 +f 2957 934 2956 80 +f 2958 1130 2957 80 +f 2960 1197 2961 80 +f 2961 1198 2962 80 +f 2962 1156 2963 80 +f 2963 935 2964 80 +f 2964 1204 2958 80 +f 942 2970 944 2965 +f 936 3056 1000 2965 +f 937 3047 993 2966 +f 2967 937 2966 938 +f 2926 940 2969 939 +f 1895 933 2926 939 +f 942 2965 1000 2968 +f 2968 941 2969 940 +f 2912 927 2970 942 +f 2971 936 2965 944 +f 2972 943 2970 927 +f 2973 995 1897 945 +f 2959 80 1897 995 +f 2908 946 2974 947 +f 2975 958 2910 906 +f 2976 950 3028 981 +f 905 2909 958 2977 +f 2977 957 2983 950 +f 2976 949 2978 951 +f 2979 905 2977 950 +f 2979 951 2980 952 +f 2980 954 2981 952 +f 948 2974 946 2981 +f 2909 953 2910 958 +f 2982 962 2988 954 +f 2980 951 2978 955 +f 956 2985 960 2983 +f 956 2983 957 2984 +f 2975 959 2985 956 +f 2906 903 2908 947 +f 2911 901 2985 959 +f 2985 901 2987 960 +f 961 3015 974 2986 +f 2987 955 2986 960 +f 2982 955 2987 902 +f 2988 962 2982 902 +f 2989 963 2990 961 +f 2988 947 2974 954 +f 2974 948 2981 954 +f 2991 965 2990 963 +f 2992 964 2991 963 +f 2993 1092 2992 963 +f 2994 966 2993 963 +f 2995 968 2994 963 +f 2996 967 2995 963 +f 2997 1086 2996 963 +f 2998 1089 2997 963 +f 2999 969 2998 963 +f 3000 970 2999 963 +f 3001 1081 3000 963 +f 3002 1083 3001 963 +f 1098 3002 963 3003 +f 3003 971 3004 1099 +f 3005 1047 3004 971 +f 3006 1095 3005 971 +f 3007 1096 3006 971 +f 3008 1050 3007 971 +f 3009 972 3008 971 +f 3010 1102 3009 971 +f 3011 1100 3010 971 +f 3012 973 3011 971 +f 3013 1106 3012 971 +f 3014 1104 3013 971 +f 3015 975 3016 974 +f 3016 1053 3017 974 +f 3017 976 3018 974 +f 3018 1061 3019 974 +f 3019 1060 3020 974 +f 3020 977 3021 974 +f 3021 1056 3022 974 +f 3022 978 3023 974 +f 3023 979 3024 974 +f 3024 1037 3025 974 +f 3025 980 3026 974 +f 3026 1067 3027 974 +f 3027 1066 3028 974 +f 3029 1080 3030 981 +f 3030 1043 3031 981 +f 3031 982 3032 981 +f 3032 983 3033 981 +f 3033 1076 3034 981 +f 3034 985 3035 981 +f 3035 984 3036 981 +f 3036 1040 3037 981 +f 3037 1041 3038 981 +f 3038 1074 3039 981 +f 3039 986 3040 981 +f 3040 949 2976 981 +f 2989 955 2978 971 +f 950 2983 960 3041 +f 2972 995 3042 987 +f 3043 990 2969 941 +f 3044 988 3097 1018 +f 937 2967 990 3045 +f 3045 989 3051 988 +f 3044 1012 3046 991 +f 3047 937 3045 988 +f 3047 991 3048 993 +f 3048 994 3049 993 +f 2973 993 3049 992 +f 2967 939 2969 990 +f 3050 996 3057 994 +f 3048 991 3046 1020 +f 998 3054 997 3051 +f 998 3051 989 3052 +f 3043 999 3053 998 +f 2971 943 2972 987 +f 999 3043 941 3053 +f 1000 3054 998 3053 +f 1184 3084 1015 3055 +f 3054 1000 3056 1020 +f 3055 997 3054 1020 +f 3050 1020 3056 936 +f 3057 996 3050 936 +f 3058 1004 3059 1184 +f 3057 987 3042 994 +f 3042 992 3049 994 +f 3060 1185 3059 1004 +f 3061 1224 3060 1004 +f 3062 1193 3061 1004 +f 3063 1001 3062 1004 +f 3064 1002 3063 1004 +f 3065 1220 3064 1004 +f 3066 1003 3065 1004 +f 3067 1151 3066 1004 +f 3068 1188 3067 1004 +f 3069 1189 3068 1004 +f 3070 1005 3069 1004 +f 3071 1121 3070 1004 +f 1123 3071 1004 3072 +f 3072 1006 3073 1203 +f 3074 1153 3073 1006 +f 3075 1155 3074 1006 +f 3076 1007 3075 1006 +f 3077 1125 3076 1006 +f 3078 1008 3077 1006 +f 3079 1010 3078 1006 +f 3080 1009 3079 1006 +f 3081 1131 3080 1006 +f 3082 1011 3081 1006 +f 3083 1208 3082 1006 +f 3084 1162 3085 1015 +f 3085 1013 3086 1015 +f 3086 1165 3087 1015 +f 3087 1169 3088 1015 +f 3088 1211 3089 1015 +f 3089 1167 3090 1015 +f 3090 1166 3091 1015 +f 3091 1172 3092 1015 +f 3092 1137 3093 1015 +f 3093 1136 3094 1015 +f 3094 1216 3095 1015 +f 3095 1014 3096 1015 +f 3096 1170 3097 1015 +f 3098 1147 3099 1018 +f 3099 1016 3100 1018 +f 3100 1129 3101 1018 +f 3101 1144 3102 1018 +f 3102 1182 3103 1018 +f 3103 1178 3104 1018 +f 3104 1176 3105 1018 +f 3105 1141 3106 1018 +f 3106 1017 3107 1018 +f 3107 1173 3108 1018 +f 3108 1019 3109 1018 +f 3109 1012 3044 1018 +f 988 3051 997 3110 +f 3058 1020 3046 1006 +f 3015 961 3111 1387 +f 3112 1290 2851 1021 +f 3113 1436 3014 949 +f 2891 1023 3114 1024 +f 2888 889 3170 1025 +f 3115 1027 2889 1026 +f 3116 1082 3001 1083 +f 3002 1098 3189 1447 +f 2898 896 3139 1028 +f 3117 897 2898 1028 +f 3118 1049 3007 1050 +f 3008 972 3009 1030 +f 2871 878 2870 1298 +f 3119 1275 2872 1031 +f 3120 985 3034 1077 +f 3034 1076 3167 1077 +f 2874 881 3131 1042 +f 3121 1271 2875 880 +f 3122 1032 3032 982 +f 3031 1043 3134 1377 +f 2901 899 3123 1033 +f 3124 1034 3011 973 +f 2860 872 3151 1035 +f 3125 1036 2859 1068 +f 3126 1038 3025 1037 +f 3024 979 3156 1423 +f 2858 871 3155 1232 +f 3127 1269 2857 870 +f 3128 1069 3023 978 +f 3022 1056 3148 1431 +f 2868 1039 3129 1250 +f 3130 1419 3037 1040 +f 2873 879 3166 1296 +f 3132 1078 3033 983 +f 3133 1079 2876 882 +f 3030 1080 3169 1044 +f 2884 1045 3174 1259 +f 3135 1288 2885 887 +f 3136 1088 2997 1089 +f 2998 969 3173 1084 +f 2895 893 3186 1251 +f 3137 1046 2896 895 +f 3138 1097 3004 1047 +f 3005 1095 3185 1407 +f 2897 1093 3184 1094 +f 3140 1048 3006 1096 +f 2902 1052 3141 1051 +f 3142 1411 3012 1106 +f 3143 1231 2852 868 +f 3016 975 3144 1444 +f 3145 1107 2854 1059 +f 3018 976 3146 1430 +f 3147 1054 2857 1269 +f 3021 977 3020 1110 +f 3149 1058 2855 869 +f 3019 1061 3150 1445 +f 2861 1063 3198 1062 +f 3152 1064 3026 980 +f 2864 1065 3168 1244 +f 3153 1243 2862 873 +f 3154 1402 3029 1066 +f 3027 1067 3199 1111 +f 2866 1075 3157 1073 +f 3158 1071 3039 1074 +f 2869 876 3159 1277 +f 3160 1072 3036 984 +f 2870 877 3161 1298 +f 3120 1420 3385 1400 +f 3035 985 3120 1400 +f 2867 875 3162 1279 +f 3163 1397 3038 1041 +f 3164 1378 3040 986 +f 2865 890 3165 1070 +f 2887 888 3200 1283 +f 3171 1439 3000 1081 +f 3172 1286 2886 1112 +f 2999 970 3201 1113 +f 2883 1114 3202 1085 +f 3175 1087 2996 1086 +f 3176 1090 2881 1116 +f 2995 967 3177 1091 +f 3177 1415 3399 1091 +f 3178 1227 2879 884 +f 2993 966 3179 1389 +f 3180 1228 2878 883 +f 2992 1092 3181 1412 +f 2990 965 3182 1393 +f 1022 2851 1290 3183 +f 1292 3205 1117 3183 +f 2894 894 3188 1280 +f 3187 1099 3004 1097 +f 3187 1424 3189 1098 +f 2900 898 3190 1281 +f 3191 1425 3010 1100 +f 2899 897 3117 1101 +f 3117 1029 3313 1101 +f 3192 1030 3009 1102 +f 2892 892 3193 1103 +f 3194 1105 3013 1104 +f 3195 1294 2853 1108 +f 3017 1053 3196 1109 +f 3147 1055 3317 1057 +f 2856 1054 3147 1057 +f 3020 1060 3197 1110 +f 2882 886 2881 1090 +f 3177 967 2996 1087 +f 3203 1115 2880 885 +f 2994 968 3204 1417 +f 3205 1230 2877 1117 +f 2991 964 3206 1394 +f 3084 1184 3207 1529 +f 3208 1119 2913 1161 +f 3209 1451 3083 1012 +f 2954 1120 3210 1323 +f 2951 930 3268 1186 +f 3211 1371 2952 931 +f 3212 1526 3070 1121 +f 3071 1123 3285 1122 +f 2963 1156 3235 1124 +f 3213 935 2963 1124 +f 3214 1157 3076 1125 +f 3077 1008 3078 1206 +f 2934 919 2933 1370 +f 3215 1181 2935 920 +f 3216 1178 3103 1126 +f 3103 1182 3263 1126 +f 2937 921 3227 1127 +f 3217 1145 2938 922 +f 3218 1457 3101 1129 +f 3100 1016 3230 1128 +f 2957 1130 3219 1351 +f 3220 1468 3080 1131 +f 2922 1132 3247 1133 +f 3221 1134 2921 912 +f 3222 1504 3094 1136 +f 3093 1137 3252 1135 +f 2920 1171 3251 1138 +f 3223 1339 2919 911 +f 3224 1481 3092 1172 +f 3091 1166 3244 1517 +f 2931 1139 3225 1175 +f 3226 1498 3106 1141 +f 2936 1142 3262 1143 +f 3228 1479 3102 1144 +f 3229 1146 2927 915 +f 3099 1147 3265 1501 +f 2947 1190 3272 1314 +f 3231 1330 2948 1148 +f 3232 1149 3066 1151 +f 3067 1188 3271 1150 +f 2960 1200 3282 1349 +f 3233 1152 2961 1197 +f 3234 1507 3073 1153 +f 3074 1155 3281 1154 +f 2962 1198 3280 1313 +f 3236 1199 3075 1007 +f 2956 934 3237 1158 +f 3238 1485 3081 1011 +f 3239 1118 2914 1160 +f 3085 1162 3240 1163 +f 3241 1209 2916 908 +f 3087 1165 3242 1164 +f 3243 910 2919 1339 +f 3090 1167 3089 1503 +f 3245 1336 2917 909 +f 3088 1169 3246 1168 +f 2923 1213 3294 1214 +f 3248 1215 3095 1216 +f 2925 914 3264 1341 +f 3249 1212 2924 913 +f 3250 1183 3098 1170 +f 3096 1014 3295 1466 +f 2929 916 3253 1179 +f 3254 1473 3108 1173 +f 2932 918 3255 1174 +f 3256 1476 3105 1176 +f 2933 1177 3257 1370 +f 3216 1478 3456 1499 +f 3104 1178 3216 1499 +f 2930 917 3258 1140 +f 3259 1180 3107 1017 +f 3260 1450 3109 1019 +f 2928 932 3261 1324 +f 3059 1185 3266 1470 +f 3267 1366 2939 907 +f 2950 929 3296 1362 +f 3269 1217 3069 1005 +f 3270 1364 2949 1187 +f 3068 1189 3297 1523 +f 2946 1218 3298 1219 +f 3273 1191 3065 1003 +f 3274 1331 2944 926 +f 3064 1220 3275 1493 +f 3275 1221 3470 1493 +f 3276 1333 2942 1192 +f 3062 1001 3277 1196 +f 3278 1194 2941 924 +f 3061 1193 3279 1195 +f 2953 1201 3284 1372 +f 3283 1203 3073 1507 +f 3283 1202 3285 1123 +f 2958 1204 3286 1352 +f 3287 1483 3079 1009 +f 2964 935 3213 1205 +f 3213 1356 3329 1205 +f 3288 1206 3078 1010 +f 2955 1207 3289 1327 +f 3290 1159 3082 1208 +f 3291 1308 2915 1210 +f 3086 1013 3292 1515 +f 3243 1316 3347 1338 +f 2918 910 3243 1338 +f 3089 1211 3293 1503 +f 2945 928 2944 1331 +f 3275 1220 3065 1191 +f 3299 1222 2943 925 +f 3063 1002 3300 1492 +f 3301 1223 2940 923 +f 3060 1224 3302 1471 +f 2049 1291 2050 1290 +f 3112 1231 2137 1225 +f 1257 2064 173 3303 +f 2036 1297 2165 1296 +f 2076 179 3184 1046 +f 2058 170 3203 1227 +f 2055 1229 2056 1228 +f 3180 1230 2053 1237 +f 2054 169 2055 1228 +f 2140 221 3143 1294 +f 2139 220 2137 1231 +f 3155 1036 2155 1268 +f 2154 1233 2153 1232 +f 3125 1035 2157 1234 +f 2156 1235 2155 1036 +f 1244 3168 1272 3304 +f 3139 1094 2079 191 +f 2093 190 3323 1028 +f 1029 2091 189 3305 +f 2082 182 2081 1103 +f 3306 1259 3174 1300 +f 3205 1292 2052 168 +f 2143 222 2142 1107 +f 2142 1239 2141 1107 +f 1055 3147 1269 3307 +f 2150 1240 2149 1055 +f 2152 224 3127 1232 +f 2153 225 2152 1232 +f 1079 3133 228 3308 +f 3153 1244 3304 1242 +f 1242 2161 1245 3309 +f 2160 227 3198 1243 +f 1070 3165 1246 3310 +f 2041 161 2040 1298 +f 3161 1277 2042 1276 +f 3311 1279 3162 1247 +f 3162 1073 3321 1247 +f 3312 1249 2043 1248 +f 3312 1250 3129 163 +f 3129 1279 3320 163 +f 3186 1280 2073 1252 +f 2074 177 3137 1251 +f 2071 176 3188 1027 +f 3141 1033 2084 192 +f 3190 1101 3313 186 +f 3313 187 2089 186 +f 3314 1024 3114 1255 +f 3114 1103 2081 1255 +f 1259 3306 1256 3315 +f 3135 1259 3315 1257 +f 1258 2061 1301 3316 +f 1236 2063 1256 3306 +f 2069 175 3115 1025 +f 3176 1115 2059 1260 +f 2060 171 3316 1090 +f 3178 1228 2056 1261 +f 2057 1262 2058 1227 +f 2144 1238 3145 1058 +f 2147 1263 3317 1055 +f 1263 2146 1057 3317 +f 2145 1266 2144 1058 +f 2148 1264 2147 1055 +f 2149 1267 2148 1055 +f 2151 223 3307 1269 +f 3121 1042 3318 229 +f 1271 3121 229 3319 +f 3133 1271 3319 228 +f 3308 1241 2162 1272 +f 3168 1079 3308 1272 +f 3151 1062 2158 1273 +f 2159 226 2158 1062 +f 3310 1274 2046 165 +f 2039 160 2038 1298 +f 2037 1226 3166 1275 +f 3159 1250 3312 1248 +f 1279 3311 1278 3320 +f 3157 1070 3310 165 +f 2109 200 2079 1094 +f 2072 1299 2073 1280 +f 2086 183 2085 1033 +f 2085 1254 2084 1033 +f 3123 1281 3322 184 +f 3322 197 2087 184 +f 1281 3190 186 3322 +f 1029 3117 1028 3323 +f 2092 198 2091 1029 +f 3193 1051 3324 1282 +f 1051 3141 192 3324 +f 3165 1024 3314 1246 +f 3314 181 2048 1246 +f 2067 1284 2068 1283 +f 2068 174 3170 1283 +f 2066 1285 3200 1286 +f 2065 1287 3172 1288 +f 2070 1253 2071 1027 +f 2051 1289 2052 1292 +f 2050 167 3325 1290 +f 167 2051 1292 3325 +f 2141 1293 3195 1107 +f 2146 1265 3149 1057 +f 2165 1295 3328 1296 +f 2040 159 2039 1298 +f 2038 158 3119 1298 +f 2075 178 2076 1046 +f 1085 3202 172 3326 +f 3174 1085 3326 1300 +f 1090 3316 1301 3327 +f 3202 1090 3327 172 +f 1042 3131 1302 3318 +f 3131 1296 3328 1302 +f 2078 202 2109 1094 +f 1355 3354 1205 3329 +f 1356 3339 214 3329 +f 3208 1118 3331 235 +f 1309 2170 235 3331 +f 1329 2077 1304 3332 +f 1143 3262 1347 3333 +f 2134 210 3280 1152 +f 1306 2088 185 3334 +f 2101 1334 2102 1194 +f 3278 1223 2099 195 +f 2100 1307 2101 1194 +f 1308 3291 1367 3335 +f 3239 1308 3335 236 +f 3331 1118 3239 236 +f 3251 1134 2183 1310 +f 2182 244 2181 1138 +f 3221 1133 2185 1343 +f 2184 1311 2183 1134 +f 1341 3264 1340 3336 +f 3235 1313 3338 211 +f 3337 1124 3235 211 +f 3339 215 2122 214 +f 2127 1328 2125 1327 +f 2107 201 2108 1314 +f 3301 1366 2098 1335 +f 3340 1209 3241 238 +f 237 2172 1315 3340 +f 1316 3243 1339 3341 +f 2178 1317 2177 1316 +f 2180 243 3223 1138 +f 2181 245 2180 1138 +f 1146 3229 248 3342 +f 2030 155 3253 1324 +f 152 2024 1319 3343 +f 3255 1175 3344 152 +f 2025 1321 3344 1175 +f 3225 1140 2026 1320 +f 2027 153 2026 1140 +f 2117 209 3284 1371 +f 2130 1322 2124 1351 +f 3261 1323 2031 1325 +f 2125 1326 3210 1327 +f 2094 1329 3231 1314 +f 2104 199 2105 1331 +f 2108 203 2094 1314 +f 2115 207 3211 1186 +f 180 2080 1332 3345 +f 3276 1194 2102 196 +f 2103 1306 3334 1333 +f 3346 1336 3245 1337 +f 2175 240 3347 1316 +f 240 2174 1338 3347 +f 2174 1337 3245 1338 +f 2176 239 2175 1316 +f 2177 241 2176 1316 +f 2179 242 3341 1339 +f 3348 250 2190 249 +f 3217 1127 3348 249 +f 1145 3217 249 3349 +f 3229 1145 3349 248 +f 2193 1340 3264 1146 +f 3247 1214 2186 1342 +f 2188 1344 3294 1212 +f 3249 1341 3336 1312 +f 1312 2192 247 3350 +f 2187 246 2186 1214 +f 2033 157 2030 1324 +f 1370 3353 1346 3351 +f 1181 3215 150 3352 +f 3262 1181 3352 1347 +f 3257 1174 3343 1319 +f 2028 1348 2027 1140 +f 3258 1179 2029 154 +f 3233 1349 2135 219 +f 2119 1305 2134 1152 +f 3282 1372 2118 218 +f 2124 1350 2129 1351 +f 3219 1352 2123 1353 +f 3286 1205 3354 1354 +f 2131 212 2123 1352 +f 1356 2133 1357 3339 +f 3337 216 2133 1356 +f 3289 1158 2128 1358 +f 3237 1351 2129 1359 +f 2032 1360 2031 1323 +f 2105 1361 3298 1331 +f 2113 205 2114 1362 +f 2114 206 3268 1362 +f 2112 204 3296 1364 +f 2110 1363 3270 1330 +f 2116 208 2117 1371 +f 3274 1222 3355 1365 +f 3345 1331 3274 1365 +f 3299 1333 3334 185 +f 185 2089 1365 3355 +f 2097 194 2098 1366 +f 2096 193 3267 1119 +f 1209 3340 1315 3356 +f 3291 1209 3356 1367 +f 3346 1368 2173 238 +f 3241 1336 3346 238 +f 1370 3351 151 3357 +f 3215 1370 3357 150 +f 1313 3358 1375 3338 +f 2136 1373 2118 1372 +f 2106 1374 3272 1219 +f 1127 3227 253 3348 +f 2194 253 3227 1143 +f 3111 1393 3359 1376 +f 3359 1604 3543 1376 +f 3360 1556 3519 1565 +f 3361 1553 3122 1377 +f 3134 1044 3404 1552 +f 3362 1605 3552 1603 +f 3363 1399 3563 1379 +f 3113 1378 3363 1379 +f 3364 1589 3545 1592 +f 3365 1380 3522 1381 +f 3366 1382 3544 1588 +f 3367 1383 3572 1626 +f 3368 1384 3554 1606 +f 1385 3167 1078 3369 +f 1390 3132 1032 3370 +f 1600 3409 1423 3371 +f 3366 1387 3111 1376 +f 3372 1621 3561 1388 +f 3362 1394 3206 1413 +f 3181 1389 3373 1607 +f 3374 1426 3559 1610 +f 3375 1419 3130 1557 +f 3375 1561 3524 1567 +f 3376 1032 3122 1553 +f 3377 1554 3369 1078 +f 3376 1391 3516 1386 +f 3378 1402 3154 1549 +f 3154 1111 3410 1549 +f 1446 3189 1424 3379 +f 3124 1411 3372 1388 +f 3380 1034 3124 1388 +f 3359 1393 3182 1603 +f 3182 1394 3362 1603 +f 3381 1417 3204 1414 +f 3382 1084 3397 1396 +f 3365 1071 3158 1398 +f 3383 1398 3158 1397 +f 3384 1563 3520 1557 +f 3130 1072 3384 1557 +f 3363 1378 3164 1381 +f 3164 1071 3365 1381 +f 3384 1072 3160 1568 +f 3385 1564 3527 1568 +f 3160 1400 3385 1568 +f 3360 1420 3403 1401 +f 3386 1077 3167 1385 +f 3169 1402 3378 1550 +f 3378 1403 3515 1550 +f 3387 1404 3197 1445 +f 3150 1430 3406 1594 +f 3388 1064 3152 1601 +f 3156 1069 3389 1405 +f 3389 1598 3549 1405 +f 1406 3420 1407 3390 +f 1632 3390 1407 3391 +f 3118 1030 3392 1408 +f 3380 1622 3568 1623 +f 3192 1425 3416 1409 +f 3393 1438 3367 1030 +f 3372 1411 3394 1620 +f 3142 1105 3419 1410 +f 3206 1412 3395 1413 +f 1612 3422 1439 3396 +f 3173 1113 3421 1611 +f 3398 1609 3557 1608 +f 3204 1091 3399 1414 +f 3399 1416 3556 1414 +f 3400 1416 3399 1415 +f 3179 1417 3381 1418 +f 3381 1395 3555 1418 +f 3368 1389 3179 1418 +f 3401 1558 3163 1419 +f 3402 1564 3385 1420 +f 3386 1421 3403 1420 +f 1551 3404 1044 3405 +f 3406 1430 3146 1592 +f 3146 1109 3364 1592 +f 3407 1433 3547 1596 +f 3388 1602 3551 1422 +f 3152 1038 3408 1601 +f 3199 1064 3388 1422 +f 3411 1616 3562 1618 +f 3411 1436 3113 1379 +f 3412 1635 3577 1392 +f 3412 1424 3187 1097 +f 3413 1635 3412 1097 +f 3414 1048 3140 1627 +f 3140 1049 3415 1627 +f 3191 1034 3380 1623 +f 3374 1088 3136 1428 +f 3136 1084 3382 1428 +f 3382 1427 3560 1428 +f 3398 1415 3425 1429 +f 3406 1591 3546 1594 +f 3364 1109 3427 1590 +f 3148 1110 3417 1432 +f 3407 1110 3429 1595 +f 3389 1069 3128 1597 +f 3128 1431 3418 1597 +f 3126 1423 3409 1434 +f 3409 1435 3550 1434 +f 3194 1436 3411 1618 +f 3138 1407 3420 1437 +f 3185 1048 3414 1630 +f 3414 1629 3575 1630 +f 3201 1439 3422 1440 +f 3171 1082 3423 1441 +f 3424 1613 3423 1082 +f 3116 1447 3430 1442 +f 1443 3558 1429 3425 +f 1415 3177 1087 3425 +f 3426 1443 3425 1087 +f 3175 1088 3374 1610 +f 3196 1444 3428 1587 +f 3144 1387 3366 1588 +f 3207 1470 3431 1566 +f 3431 1559 3521 1566 +f 3432 1580 3535 1448 +f 3433 1584 3218 1128 +f 3230 1501 3475 1575 +f 3434 1617 3562 1615 +f 3435 1449 3580 1639 +f 3209 1450 3435 1639 +f 3436 1562 3525 1452 +f 3437 1472 3538 1453 +f 3438 1560 3523 1454 +f 3439 1645 3587 1644 +f 3440 1455 3561 1460 +f 3441 1581 3541 1582 +f 3442 1583 3534 1456 +f 3443 1135 3252 1458 +f 3438 1529 3207 1566 +f 3444 1459 3579 1638 +f 3434 1471 3302 1619 +f 3279 1196 3445 1488 +f 3446 1633 3574 1628 +f 3447 1498 3226 1461 +f 3447 1576 3537 1462 +f 3448 1463 3442 1457 +f 3441 1479 3228 1456 +f 3228 1457 3442 1456 +f 3449 1464 3458 1183 +f 3250 1466 3479 1465 +f 1467 3285 1202 3450 +f 3220 1485 3444 1638 +f 3451 1468 3220 1638 +f 3431 1470 3266 1615 +f 3266 1471 3434 1615 +f 3452 1492 3300 1624 +f 1634 3486 1150 3453 +f 3437 1473 3254 1577 +f 3454 1577 3254 1180 +f 3455 1474 3540 1461 +f 3226 1476 3455 1461 +f 3435 1450 3260 1453 +f 3260 1473 3437 1453 +f 3455 1476 3256 1475 +f 3456 1579 3536 1475 +f 3256 1499 3456 1475 +f 3432 1478 3474 1477 +f 3457 1126 3263 1582 +f 3263 1479 3441 1582 +f 3265 1183 3458 1586 +f 3459 1530 3293 1168 +f 3246 1164 3476 1513 +f 3460 1215 3248 1574 +f 3252 1481 3461 1458 +f 3461 1571 3529 1458 +f 3462 1643 3585 1642 +f 3462 1154 3281 1521 +f 3214 1206 3463 1482 +f 3451 1469 3583 1646 +f 3288 1483 3485 1511 +f 3464 1484 3439 1206 +f 3444 1485 3465 1486 +f 3238 1159 3490 1487 +f 3302 1195 3466 1619 +f 1636 3493 1217 3467 +f 3468 1490 3453 1150 +f 3271 1523 3492 1524 +f 3469 1512 3571 1491 +f 3300 1493 3470 1624 +f 3470 1494 3570 1624 +f 3471 1494 3470 1221 +f 3277 1492 3452 1495 +f 3452 1496 3568 1495 +f 3440 1196 3277 1495 +f 3472 1497 3259 1498 +f 3473 1579 3456 1478 +f 3457 1500 3474 1478 +f 3475 1585 3533 1575 +f 3475 1501 3265 1586 +f 3476 1164 3242 1452 +f 3242 1515 3436 1452 +f 3477 1555 3528 1570 +f 3460 1480 3532 1505 +f 3248 1504 3478 1574 +f 3443 1572 3530 1520 +f 3295 1215 3460 1505 +f 3480 1506 3581 1640 +f 3480 1451 3209 1639 +f 3481 1508 3584 1641 +f 3481 1202 3283 1507 +f 3482 1508 3481 1507 +f 3483 1199 3236 1510 +f 3236 1157 3484 1510 +f 3287 1468 3451 1646 +f 3446 1149 3232 1631 +f 3232 1150 3486 1631 +f 3469 1221 3496 1625 +f 3476 1502 3526 1513 +f 3436 1515 3498 1514 +f 3244 1503 3487 1518 +f 3477 1503 3500 1569 +f 3461 1481 3224 1516 +f 3224 1517 3488 1516 +f 3222 1135 3489 1519 +f 3290 1451 3480 1640 +f 3234 1154 3491 1509 +f 3281 1199 3483 1521 +f 3483 1522 3586 1521 +f 3297 1217 3493 1525 +f 3269 1526 3494 1489 +f 3495 1637 3494 1526 +f 3212 1122 3501 1527 +f 1614 3573 1625 3496 +f 1221 3275 1191 3496 +f 3497 1614 3496 1191 +f 3273 1149 3446 1628 +f 3292 1163 3499 1528 +f 3240 1529 3438 1454 +f 2473 475 1812 26 +f 403 2356 1531 3502 +f 2415 403 3502 391 +f 1814 31 2434 460 +f 2354 402 2357 1533 +f 2199 257 3503 146 +f 2007 231 2198 1534 +f 2200 256 3504 1535 +f 2337 388 2336 1540 +f 1536 2337 1540 3505 +f 3505 166 2047 1537 +f 2047 162 2203 1537 +f 2204 1538 3506 142 +f 1538 2207 252 3506 +f 3507 230 2166 1539 +f 3508 1540 3509 454 +f 3509 1542 2427 454 +f 2166 166 3508 1539 +f 3509 1540 3510 386 +f 1540 2335 1541 3510 +f 2353 401 2346 1542 +f 2206 1543 3511 234 +f 2191 252 2207 259 +f 3507 1544 2198 231 +f 2336 387 2335 1540 +f 3511 1543 2203 162 +f 3512 1546 3513 860 +f 3513 56 1853 860 +f 1898 1547 2126 213 +f 1978 122 2138 1548 +f 3514 1553 3361 1599 +f 3410 1422 3551 1599 +f 1403 3378 1549 3515 +f 3515 1599 3405 1550 +f 3404 1551 3405 1599 +f 3361 1552 3404 1599 +f 3514 1573 3516 1391 +f 3370 1386 3516 1573 +f 3377 1390 3370 1573 +f 3369 1554 3377 1573 +f 1421 3386 1385 3517 +f 3403 1421 3517 1573 +f 1556 3360 1401 3518 +f 3518 1573 3528 1555 +f 3520 1452 3525 1557 +f 3383 1558 3499 1454 +f 1559 3563 1399 3521 +f 3522 1566 3521 1381 +f 1560 3522 1380 3523 +f 3401 1567 3498 1528 +f 3524 1514 3498 1567 +f 1562 3524 1561 3525 +f 3526 1502 3520 1563 +f 3519 1555 3477 1569 +f 3523 1398 3383 1454 +f 3526 1568 3527 1513 +f 3459 1513 3527 1564 +f 3500 1530 3459 1564 +f 3402 1565 3519 1569 +f 3487 1570 3528 1573 +f 3488 1518 3487 1573 +f 1571 3461 1516 3529 +f 3529 1573 3530 1458 +f 1572 3443 1458 3530 +f 3530 1573 3489 1520 +f 3478 1519 3489 1573 +f 1480 3460 1574 3531 +f 2426 1578 3532 1480 +f 1578 3479 1505 3532 +f 3533 1578 3433 1575 +f 3474 1500 3541 1578 +f 3535 1578 3473 1448 +f 3537 1578 3472 1462 +f 3538 1578 3539 1453 +f 3539 1449 3435 1453 +f 3538 1472 3437 1577 +f 3454 1497 3472 1578 +f 3537 1576 3447 1461 +f 1578 3537 1461 3540 +f 1578 3540 1474 3536 +f 3535 1580 3432 1477 +f 1578 3541 1581 3534 +f 1578 3534 1583 3542 +f 3448 1584 3433 1578 +f 3533 1585 3475 1586 +f 3458 1464 3449 1578 +f 3449 1465 3479 1578 +f 1909 1593 3543 1604 +f 3543 1593 3544 1382 +f 3428 1588 3544 1593 +f 3427 1587 3428 1593 +f 1589 3364 1590 3545 +f 3545 1593 3546 1592 +f 1591 3406 1592 3546 +f 3546 1593 3387 1594 +f 3429 1404 3387 1593 +f 1433 3407 1595 3547 +f 3547 1593 3417 1596 +f 3418 1432 3417 1593 +f 1598 3389 1597 3548 +f 1599 3371 1405 3549 +f 3550 1435 3409 1600 +f 1599 3408 1434 3550 +f 3551 1602 3388 1601 +f 865 1909 1604 3552 +f 865 3552 1605 3553 +f 3395 1607 3373 865 +f 3373 1606 3554 865 +f 3554 1384 3368 1418 +f 865 3554 1418 3555 +f 3555 1395 3381 1414 +f 865 3555 1414 3556 +f 3400 1608 3557 865 +f 3557 1609 3398 1429 +f 865 3557 1429 3558 +f 3426 1610 3559 865 +f 3559 1426 3374 1428 +f 865 3559 1428 3560 +f 3560 1427 3382 1396 +f 3397 1611 3421 865 +f 3421 1440 3422 865 +f 3422 1612 3396 865 +f 3396 1441 3423 865 +f 3423 1613 3424 865 +f 3424 1442 3430 865 +f 3430 1446 3379 865 +f 3379 1392 3578 865 +f 3415 1408 3573 1614 +f 3561 1455 3567 1388 +f 3562 1617 3565 1618 +f 3563 1559 3431 1615 +f 1615 3562 1616 3564 +f 3419 1618 3565 1619 +f 3466 1488 3394 1410 +f 3445 1460 3566 1620 +f 1460 3561 1621 3566 +f 1495 3568 1622 3567 +f 1496 3569 1623 3568 +f 3416 1623 3569 1624 +f 1409 3416 1624 3570 +f 3393 1409 3570 1494 +f 3471 1491 3571 1438 +f 3571 1383 3367 1438 +f 3572 1512 3469 1625 +f 3392 1626 3572 1625 +f 1408 3392 1625 3573 +f 3497 1628 3574 1627 +f 3574 1629 3414 1627 +f 1633 3391 1630 3575 +f 1632 3391 1633 3576 +f 3486 1634 3390 1632 +f 3420 1406 3390 1634 +f 3453 1490 3468 1437 +f 3468 1524 3413 1437 +f 1524 3492 1392 3577 +f 3492 1525 3493 1392 +f 3493 1636 3578 1392 +f 3467 1489 3494 864 +f 3494 1637 3495 864 +f 3495 1527 3501 864 +f 3484 1482 3463 864 +f 864 3581 1506 3580 +f 864 3490 1640 3581 +f 3465 1487 3490 864 +f 864 3579 1459 3582 +f 864 3583 1469 3579 +f 864 3485 1646 3583 +f 3578 1636 3467 864 +f 3501 1467 3450 864 +f 3450 1641 3584 864 +f 3482 1509 3491 864 +f 3491 1642 3585 864 +f 3585 1643 3462 1521 +f 864 3585 1521 3586 +f 3586 1522 3483 1510 +f 3463 1644 3587 864 +f 3587 1645 3439 1484 +f 3464 1511 3485 864 +f 1780 3594 1647 3588 +f 1648 3591 1654 3588 +f 3589 1650 3593 1649 +f 3590 1780 3591 1651 +f 3592 1653 3589 1651 +f 1653 3592 1781 3593 +f 3594 1781 3592 1652 +f 3595 1648 3588 1647 +f 3595 1652 3592 1651 +f 1656 3601 1662 3596 +f 1660 3600 1655 3596 +f 1657 3598 1661 3597 +f 1656 3596 1655 3597 +f 1657 3600 1658 3598 +f 1659 3601 1661 3598 +f 1660 3596 1662 3599 +f 1659 3598 1658 3599 +f 1660 3599 1658 3600 +f 1657 3597 1655 3600 +f 1656 3597 1661 3601 +f 1659 3599 1662 3601 +f 1988 1713 2008 1663 +f 2009 144 1989 135 +f 2780 1664 3602 1666 +f 2814 1665 2812 148 +f 2812 856 2841 148 +f 2841 858 2019 148 +f 1667 1787 1668 3603 +f 1669 2720 739 3603 +f 1669 3603 1668 3604 +f 2830 1669 3604 1670 +f 1670 1904 1671 3605 +f 2002 1672 2809 1726 +f 2806 827 2809 1672 +f 2835 1673 2806 1672 +f 2821 1675 3606 1676 +f 1675 2794 1710 3606 +f 1797 1723 3627 1722 +f 1980 128 3607 1684 +f 1677 2790 1684 3607 +f 2791 1677 3607 128 +f 2816 833 2791 128 +f 806 2787 1678 3608 +f 3608 128 3609 853 +f 3609 1694 2803 854 +f 2833 824 2803 1694 +f 2015 1682 2834 1679 +f 2838 1680 2726 1682 +f 2726 1681 2748 1682 +f 2748 819 3610 1682 +f 2017 1683 2837 1698 +f 1700 2797 1699 3611 +f 1995 1684 2796 1700 +f 2819 1686 2796 1684 +f 2793 1687 2819 1684 +f 2746 765 2793 1684 +f 2790 764 2746 1684 +f 2776 796 2767 1688 +f 2767 786 2018 1688 +f 2781 798 2780 1666 +f 2751 1770 3655 1690 +f 2755 776 2751 1690 +f 1785 1667 3603 739 +f 739 2721 775 3612 +f 1692 2014 1693 3614 +f 2014 1694 1981 1693 +f 2012 1695 3613 137 +f 2842 829 2810 1683 +f 2810 1696 2807 1683 +f 2807 1697 2837 1683 +f 1904 15 3615 1671 +f 2827 1671 3615 1699 +f 2799 816 2827 1699 +f 2822 840 2799 1699 +f 2797 839 2822 1699 +f 2011 147 1990 1701 +f 2010 145 2011 1701 +f 1994 1730 3616 130 +f 2789 1703 2815 130 +f 2815 1704 2786 130 +f 2786 825 3617 130 +f 2005 1705 2831 849 +f 2802 822 2831 1705 +f 1705 3617 1706 3618 +f 2773 1767 3652 233 +f 2766 1707 2773 233 +f 785 2766 233 3619 +f 2778 785 3619 143 +f 1708 3620 774 3621 +f 1783 1708 3621 737 +f 2003 1727 2836 1674 +f 2725 1709 2836 1727 +f 1798 1676 3606 1710 +f 14 1798 1710 3622 +f 2817 837 3629 14 +f 1984 130 3617 1705 +f 2006 1712 3623 1711 +f 1987 131 1985 1711 +f 1713 1987 1711 3623 +f 2779 1714 2778 143 +f 2000 1715 2813 797 +f 2811 831 2813 1715 +f 2839 832 2811 1715 +f 3 1783 737 3624 +f 3624 738 3626 1716 +f 2829 1717 3625 1719 +f 1718 2825 844 3625 +f 2826 814 3627 1723 +f 1906 1719 3625 844 +f 1721 2820 1722 3627 +f 2001 1725 2840 855 +f 2808 1724 2840 1725 +f 2004 1728 2724 744 +f 2747 745 2724 1728 +f 2800 767 2747 1728 +f 2832 1729 2800 1728 +f 1733 1799 14 3629 +f 2818 1731 3630 1730 +f 811 2745 809 3630 +f 2788 1732 3616 1730 +f 1993 1733 3629 837 +f 2763 782 2847 13 +f 2761 1734 3639 22 +f 2736 755 2761 22 +f 1735 2765 754 3631 +f 3631 22 3633 1736 +f 747 2728 1736 3633 +f 3633 22 3634 1737 +f 3635 1771 3632 16 +f 3637 1734 2763 13 +f 12 1809 24 3637 +f 3638 1734 3637 24 +f 2723 743 2785 863 +f 2785 1740 2744 863 +f 2744 1741 2732 863 +f 2732 1742 2742 863 +f 2742 803 2783 863 +f 2783 1743 2730 863 +f 2730 1745 3640 863 +f 1745 2771 1744 3640 +f 2771 792 3641 1744 +f 2740 1746 3642 1744 +f 2775 1768 3653 1769 +f 1769 2035 1744 3642 +f 2848 862 2762 1748 +f 2760 1747 2735 1749 +f 2735 783 2764 1749 +f 3643 1750 1794 1749 +f 757 2737 23 3643 +f 2846 232 2770 1753 +f 862 2784 740 3644 +f 2743 1751 2784 862 +f 2731 762 2743 862 +f 2741 758 2731 862 +f 2782 1752 2741 862 +f 2768 1755 2770 232 +f 2739 787 2768 232 +f 2169 1756 2772 1757 +f 2764 757 3643 1749 +f 2737 1758 3646 23 +f 1758 2727 1759 3646 +f 2727 1760 3647 1759 +f 1760 2756 1761 3647 +f 2756 1762 3648 1761 +f 1762 2758 1763 3648 +f 2758 1766 3649 1763 +f 3649 1766 2733 1765 +f 1764 3649 1765 3650 +f 769 3651 9 3650 +f 769 3654 2 3651 +f 1767 2773 793 3652 +f 1756 1999 233 3652 +f 1688 2018 1769 3653 +f 1708 1783 2 3654 +f 3620 1708 3654 769 +f 1770 2751 1771 3655 +f 3656 1772 2430 1773 +f 2430 1774 1874 1773 +f 1777 2429 1776 3657 +f 1854 56 3513 1775 +f 3513 1546 3660 1775 +f 1775 3658 456 3657 +f 3656 59 1864 1776 +f 2429 1772 3656 1776 +f 3658 141 1998 1779 +f 2472 456 3658 1779 +f 1547 1901 1778 3659 +f 1779 2034 156 3659 +f 141 3658 1775 3660 +f 2469 492 2497 493 +f 3590 1649 3593 1781 +f 1782 1 3651 2 +f 1782 83 1921 90 +f 1784 1716 1905 83 +f 1785 739 3612 1690 +f 1786 4 1787 5 +f 1786 6 1793 73 +f 1788 23 3646 1759 +f 1789 20 3638 24 +f 1790 99 1806 1763 +f 1790 1764 1802 7 +f 1791 22 1807 8 +f 1791 17 1803 1739 +f 1792 89 1802 9 +f 1793 16 1803 70 +f 1795 12 3637 13 +f 1795 45 1837 47 +f 1796 1723 1797 94 +f 1797 1722 2821 1676 +f 1800 82 1884 76 +f 1800 1699 3615 15 +f 1804 1761 1806 19 +f 1805 21 1807 1738 +f 1808 97 1930 10 +f 1808 1750 3643 23 +f 1810 26 1812 612 +f 40 1825 37 1811 +f 1811 604 2526 612 +f 475 2424 40 1812 +f 1816 590 2603 30 +f 1817 606 2611 33 +f 1817 32 2496 31 +f 1818 33 2534 607 +f 1819 36 1820 32 +f 1824 43 1829 527 +f 1826 40 1827 39 +f 1826 587 2532 38 +f 1827 43 1828 614 +f 1828 585 2527 603 +f 1538 2204 32 1829 +f 1832 529 2598 602 +f 1834 44 1835 45 +f 863 2843 859 1834 +f 755 2736 753 1835 +f 1837 784 2765 46 +f 859 2844 54 1838 +f 50 2769 791 1838 +f 782 2763 781 1839 +f 1734 2761 44 1840 +f 743 2723 48 1841 +f 1740 2785 805 1842 +f 1741 2744 49 1843 +f 1742 2732 760 1844 +f 803 2742 802 1845 +f 1743 2783 801 1846 +f 1848 1746 2740 51 +f 1768 2775 52 1849 +f 1850 786 2767 795 +f 798 2781 799 1851 +f 1852 54 1853 55 +f 1664 2780 53 1852 +f 1854 1775 3657 1776 +f 1665 2814 55 1855 +f 829 2842 57 1856 +f 1696 2810 828 1857 +f 1858 61 1860 59 +f 1858 255 1870 60 +f 854 2803 823 1860 +f 1691 1982 62 1861 +f 824 2833 850 1862 +f 856 2812 857 1865 +f 1697 2807 58 1866 +f 1680 2838 64 1867 +f 1681 2726 768 1868 +f 819 2748 65 1869 +f 1873 59 3656 1773 +f 1874 1774 2469 493 +f 1875 68 1876 25 +f 1876 777 1877 69 +f 1877 780 1878 21 +f 1878 751 1879 8 +f 1879 750 1880 17 +f 1880 72 1881 70 +f 1882 71 2721 847 +f 1883 847 2720 843 +f 82 1903 4 1883 +f 1886 816 2799 817 +f 1887 840 2822 838 +f 1888 839 2797 813 +f 1889 1686 2819 77 +f 1890 1687 2793 78 +f 1891 765 2746 763 +f 1892 764 2790 810 +f 1893 833 2816 807 +f 939 2967 938 1895 +f 1896 1547 1898 80 +f 932 2928 933 1896 +f 1897 81 1900 945 +f 1899 213 2111 81 +f 865 3578 864 1900 +f 938 1902 1578 1901 +f 1903 1670 3604 1668 +f 1905 1719 1906 93 +f 1906 844 3628 1720 +f 1748 2762 98 1911 +f 1747 2760 85 1912 +f 783 2735 752 1913 +f 757 2764 86 1914 +f 1758 2737 756 1916 +f 1917 99 1918 88 +f 1917 1762 2756 87 +f 1922 93 1923 92 +f 1923 94 1924 841 +f 1924 138 1925 95 +f 1925 129 1926 815 +f 1926 134 1927 812 +f 1927 133 1928 836 +f 1928 139 1929 834 +f 742 1933 101 1932 +f 740 2784 804 1933 +f 1751 2743 761 1934 +f 762 2731 748 1935 +f 758 2741 759 1936 +f 1752 2782 800 1937 +f 1940 1753 2770 790 +f 1941 1755 2768 102 +f 1942 787 2739 103 +f 1943 1757 2772 794 +f 1944 1707 2766 105 +f 1714 2779 104 1945 +f 1946 797 2813 830 +f 1947 831 2811 106 +f 1948 832 2839 108 +f 1949 1724 2808 107 +f 1950 115 1968 107 +f 1950 827 2806 826 +f 1951 109 2745 96 +f 1703 2789 808 1953 +f 1704 2815 110 1954 +f 825 2786 111 1955 +f 126 1958 848 1957 +f 1957 822 2802 821 +f 1959 451 2420 629 +f 1960 1729 2832 848 +f 629 1962 766 1961 +f 1961 767 2800 818 +f 1962 627 2421 113 +f 1963 745 2747 766 +f 113 1965 114 1964 +f 1964 1709 2725 746 +f 1965 452 2422 630 +f 630 1967 826 1966 +f 1966 1673 2835 114 +f 1967 438 2392 115 +f 1969 437 2618 116 +f 1970 441 2619 633 +f 1971 632 2620 118 +f 1972 442 2617 631 +f 1973 121 1975 631 +f 1973 634 2621 119 +f 1974 450 2417 120 +f 1976 123 2849 122 +f 903 2906 84 1977 +f 1548 2845 861 1978 +f 1684 1996 127 1980 +f 1694 3609 128 1981 +f 1982 136 3614 1693 +f 1983 130 1984 129 +f 1984 1705 2006 1711 +f 1988 1663 2009 135 +f 1989 144 2010 1701 +f 1990 136 1991 1701 +f 147 2012 137 1990 +f 1993 837 2818 1730 +f 75 1996 1684 1995 +f 1700 3611 1685 1995 +f 1998 149 2016 142 +f 797 2779 143 2000 +f 2001 1548 2168 1725 +f 855 2839 1715 2001 +f 1726 2808 1725 2002 +f 1674 2835 1672 2003 +f 744 2725 1727 2004 +f 849 2832 1728 2005 +f 2006 231 2007 1712 +f 2007 1534 2199 146 +f 2008 1713 3623 1712 +f 146 2013 1695 2012 +f 2015 1535 2016 1682 +f 2015 1679 2833 1694 +f 2017 1698 2838 1682 +f 2018 786 2781 1666 +f 149 2020 148 2019 +f 2019 858 2842 1683 +f 2020 860 2035 1666 +f 150 3357 151 2022 +f 1345 3351 1346 2023 +f 2024 1321 2025 156 +f 2025 1175 3225 1320 +f 2028 1140 3258 154 +f 2029 1179 3253 155 +f 2032 1323 3210 1326 +f 1324 3261 1325 2033 +f 2034 142 3506 252 +f 1296 3166 1226 2036 +f 1275 3119 158 2037 +f 2040 162 2047 159 +f 2041 1298 3161 1276 +f 2042 1277 3159 1248 +f 163 3320 1278 2044 +f 164 3311 1247 2045 +f 2048 1255 2081 162 +f 2049 1290 3112 1225 +f 2053 1230 3205 168 +f 2054 1228 3180 1237 +f 2057 1227 3178 1261 +f 2059 1115 3203 170 +f 2060 866 2061 171 +f 2060 1090 3176 1260 +f 1300 3326 172 2062 +f 1288 3303 173 2065 +f 1286 3172 1287 2066 +f 2067 1283 3200 1285 +f 2069 1025 3170 174 +f 2070 1027 3115 175 +f 2072 1280 3188 176 +f 2074 1251 3186 1252 +f 2075 1046 3137 177 +f 2076 866 2077 179 +f 1329 2094 202 2078 +f 2078 1094 3184 179 +f 188 3305 189 2080 +f 1103 3193 1282 2082 +f 2083 1282 3324 192 +f 1545 3511 162 2083 +f 1033 3123 184 2086 +f 1306 2103 1545 2087 +f 197 3322 186 2088 +f 2092 199 2104 198 +f 2092 1029 3323 190 +f 1374 2106 190 2093 +f 2093 1028 3139 191 +f 1119 3330 1303 2096 +f 2097 1366 3267 193 +f 2099 1223 3301 1335 +f 2100 1194 3278 195 +f 2103 1333 3276 196 +f 2104 1331 3345 1332 +f 1219 3298 1361 2106 +f 2107 1314 3272 1374 +f 2108 200 2109 203 +f 2110 866 2111 1363 +f 1330 3332 1304 2110 +f 1364 3270 1363 2112 +f 2113 1362 3296 204 +f 2115 1186 3268 206 +f 2116 1371 3211 207 +f 2119 1152 3233 219 +f 2120 210 2134 213 +f 2120 217 3338 1375 +f 2121 216 3337 211 +f 2122 1357 2133 213 +f 2125 213 2126 1326 +f 1547 3659 156 2126 +f 1327 3289 1358 2127 +f 1158 3237 1359 2128 +f 1351 3219 1353 2130 +f 1352 3286 1354 2131 +f 2132 1354 3354 1355 +f 2135 1349 3282 218 +f 2136 1372 3284 209 +f 220 2139 1548 2138 +f 2139 1231 3143 221 +f 1294 3195 1293 2140 +f 2143 1107 3145 1238 +f 2145 1058 3149 1265 +f 2147 230 2167 1263 +f 2150 1055 3307 223 +f 2151 166 2166 223 +f 2151 1269 3127 224 +f 2154 1232 3155 1268 +f 2156 1036 3125 1234 +f 2157 1035 3151 1273 +f 2159 1062 3198 227 +f 2160 1243 3309 1245 +f 228 3319 229 2163 +f 1270 3318 1302 2164 +f 2164 1295 2165 166 +f 1757 2739 232 2169 +f 2171 236 3335 1367 +f 2173 1337 2174 234 +f 2178 1316 3341 242 +f 2179 1339 3223 243 +f 2182 1138 3251 1310 +f 2183 252 2191 1310 +f 2184 1134 3221 1343 +f 2185 1133 3247 1342 +f 2187 1214 3294 1344 +f 2188 1212 3350 247 +f 2189 1318 3342 248 +f 2190 253 2194 252 +f 259 2206 234 2191 +f 2192 1340 2193 252 +f 1146 3342 1318 2193 +f 1143 3333 251 2194 +f 1369 3333 1347 2195 +f 2196 257 2199 254 +f 2200 255 2201 256 +f 2200 1535 3503 257 +f 2202 1537 2203 453 +f 142 3504 258 2204 +f 2205 1543 2206 43 +f 1578 2426 453 2205 +f 308 2264 260 2208 +f 267 2228 262 2210 +f 2212 400 2350 263 +f 2212 279 2240 338 +f 2215 264 2270 353 +f 2217 265 2351 275 +f 2217 324 2243 289 +f 2218 268 2347 398 +f 2218 282 2231 269 +f 2220 270 2352 271 +f 2220 340 2245 288 +f 2221 325 2282 326 +f 2222 369 2315 375 +f 2223 345 2316 330 +f 2224 330 2322 346 +f 2229 399 2348 266 +f 2229 283 2234 335 +f 2230 339 2292 274 +f 685 2625 276 2231 +f 2232 671 2235 339 +f 284 2675 685 2233 +f 285 2652 284 2234 +f 2235 650 2238 277 +f 646 2633 285 2236 +f 684 2673 646 2237 +f 2238 278 2241 273 +f 337 2291 338 2239 +f 286 2651 684 2239 +f 645 2631 286 2240 +f 2241 281 2256 280 +f 291 2671 681 2246 +f 2247 293 2248 727 +f 2248 294 2249 724 +f 2249 341 2250 725 +f 2250 296 2251 295 +f 2251 357 2252 718 +f 2252 325 2253 700 +f 2253 356 2254 297 +f 2254 299 2255 298 +f 2255 319 2277 300 +f 2256 677 2257 331 +f 2257 302 2258 344 +f 2258 643 2259 301 +f 2259 304 2260 303 +f 2260 305 2261 332 +f 2261 667 2262 306 +f 2262 307 2263 261 +f 334 2289 350 2266 +f 312 2300 311 2268 +f 313 2701 711 2271 +f 354 2297 353 2271 +f 711 2687 314 2272 +f 314 2683 315 2273 +f 315 2693 666 2274 +f 323 2285 360 2274 +f 666 2654 316 2275 +f 316 2627 713 2276 +f 2277 321 2278 732 +f 2278 320 2279 735 +f 2279 322 2280 716 +f 394 2344 395 2281 +f 2285 329 2327 328 +f 361 2308 362 2286 +f 2288 373 2299 301 +f 2297 382 2331 342 +f 2298 384 2307 343 +f 390 2339 358 2305 +f 2310 411 2319 363 +f 2310 272 2323 417 +f 2311 374 2383 364 +f 2312 406 2313 365 +f 2312 347 2318 433 +f 2313 432 2320 366 +f 2315 429 2380 368 +f 2317 371 2412 370 +f 2318 372 2319 412 +f 2321 430 2358 404 +f 2322 376 2381 367 +f 2323 333 2324 418 +f 2324 378 2326 377 +f 2325 423 2329 349 +f 2326 379 2333 420 +f 2329 421 2333 348 +f 2334 386 3510 1541 +f 2336 327 2338 387 +f 444 2415 391 2343 +f 2353 1542 3509 386 +f 1533 2427 455 2354 +f 404 2379 428 2357 +f 2365 406 2366 409 +f 2370 418 2371 415 +f 2371 377 2372 419 +f 2372 420 2373 706 +f 2373 421 2374 649 +f 2374 423 2375 422 +f 2375 383 2376 424 +f 2376 426 2377 427 +f 2377 385 2409 425 +f 439 2428 1533 2378 +f 2378 661 2651 435 +f 2379 663 2387 1533 +f 2383 432 2384 430 +f 2391 436 2631 440 +f 2391 437 2392 439 +f 2394 287 2669 659 +f 2397 696 2671 728 +f 2398 727 2710 443 +f 2400 725 2691 702 +f 2401 295 2706 719 +f 2402 444 2414 719 +f 731 2712 447 2406 +f 2409 380 2410 446 +f 2410 381 2411 448 +f 2412 640 2627 449 +f 1573 3514 1599 2418 +f 2418 1593 2622 635 +f 2419 112 2616 254 +f 1578 2425 467 2423 +f 2423 492 2469 1778 +f 1480 3531 1573 2426 +f 2428 1539 3508 454 +f 2430 456 2472 1774 +f 2431 457 2444 1532 +f 2431 32 2497 492 +f 2433 652 2642 459 +f 463 2495 460 2434 +f 2435 461 2653 686 +f 2438 676 2439 501 +f 2439 653 2440 512 +f 2440 679 2474 511 +f 2445 564 2648 457 +f 2446 656 2646 657 +f 2447 565 2670 466 +f 2448 660 2650 680 +f 2449 682 2672 683 +f 2451 730 2711 729 +f 2452 571 2709 726 +f 467 2454 468 2453 +f 2453 572 2692 703 +f 2455 717 2457 505 +f 2455 504 2470 477 +f 2458 609 2517 470 +f 475 2473 505 2459 +f 2460 519 2515 514 +f 520 2519 521 2464 +f 522 2520 473 2466 +f 524 2521 523 2468 +f 517 2508 476 2470 +f 2472 1779 3659 1778 +f 26 2494 478 2473 +f 2474 644 2475 479 +f 2475 669 2476 507 +f 2476 670 2477 508 +f 2477 480 2478 497 +f 2478 481 2479 498 +f 2479 482 2480 483 +f 491 2500 515 2484 +f 2485 554 2698 708 +f 2486 709 2636 487 +f 2487 555 2680 689 +f 715 2586 477 2493 +f 2498 495 2510 494 +f 2498 513 2514 525 +f 2499 621 2610 605 +f 2501 617 2502 497 +f 2501 498 2509 620 +f 2502 622 2511 508 +f 2503 499 2608 500 +f 2506 597 2514 503 +f 2509 483 2510 506 +f 599 2524 528 2516 +f 532 2535 496 2517 +f 557 2528 519 2519 +f 556 2596 522 2521 +f 2525 593 2592 591 +f 714 2586 583 2526 +f 2527 584 2577 577 +f 698 2559 600 2528 +f 2529 570 2573 530 +f 2530 573 2576 584 +f 2531 613 2579 580 +f 2535 598 2595 516 +f 615 2615 624 2537 +f 542 2642 533 2537 +f 2538 673 2541 534 +f 543 2640 542 2539 +f 535 2608 536 2540 +f 675 2666 543 2540 +f 2541 688 2544 619 +f 537 2644 675 2542 +f 538 2668 537 2543 +f 2544 665 2547 540 +f 618 2610 539 2545 +f 546 2630 538 2545 +f 539 2602 545 2546 +f 2547 559 2562 558 +f 623 2589 548 2549 +f 626 2613 625 2551 +f 2553 552 2554 551 +f 2554 599 2555 553 +f 2555 598 2556 554 +f 2556 532 2557 709 +f 2557 610 2558 555 +f 689 2680 690 2559 +f 2560 556 2561 699 +f 2561 575 2583 694 +f 2562 648 2563 560 +f 2563 662 2564 592 +f 2564 563 2565 591 +f 2565 562 2566 561 +f 2566 564 2567 594 +f 2567 656 2568 596 +f 2568 565 2569 595 +f 568 2597 569 2571 +f 723 2705 574 2576 +f 574 2690 693 2577 +f 693 2682 579 2578 +f 613 2587 577 2578 +f 579 2715 733 2579 +f 581 2717 582 2581 +f 604 2600 588 2582 +f 2583 578 2584 704 +f 2584 611 2585 576 +f 589 2612 502 2588 +f 2590 616 2609 534 +f 2606 27 2607 611 +f 2623 416 2677 636 +f 2623 637 2658 414 +f 2624 639 2678 485 +f 2624 484 2659 638 +f 2635 422 2679 318 +f 2635 317 2697 649 +f 2638 547 2657 481 +f 2638 480 2662 544 +f 2645 440 2647 654 +f 2649 659 2669 290 +f 2656 668 2658 307 +f 2657 549 2659 482 +f 2660 541 2662 670 +f 2677 415 2699 309 +f 2678 551 2700 486 +f 2686 729 2711 567 +f 2695 706 2697 705 +f 2695 310 2699 419 +f 2696 707 2700 553 +f 2708 443 2710 724 +f 2713 469 2717 581 +f 2718 846 2719 737 +f 2718 772 2754 773 +f 2723 863 2847 782 +f 2734 771 2753 750 +f 2734 751 2759 779 +f 2736 22 3631 754 +f 2738 1735 3631 1736 +f 1744 3641 788 2740 +f 2750 91 2754 774 +f 2750 770 3620 769 +f 2755 1690 3612 775 +f 2757 1737 2759 780 +f 1749 2848 1748 2760 +f 862 3644 741 2762 +f 788 3641 792 2769 +f 1756 3652 793 2772 +f 2775 1769 3642 1746 +f 2776 1768 2777 796 +f 1688 3653 1689 2776 +f 2782 862 3645 1754 +f 2787 853 2805 60 +f 2788 1730 3630 809 +f 130 3616 1732 2789 +f 2792 811 3630 1731 +f 2798 1721 3627 814 +f 1705 3618 852 2802 +f 852 3618 1706 2804 +f 2814 148 3602 1664 +f 2816 128 3608 1678 +f 2817 14 3622 835 +f 2823 1718 3625 1717 +f 2824 845 2828 74 +f 2826 1723 3628 844 +f 2827 79 2828 1671 +f 2829 1719 3626 738 +f 1670 3605 842 2830 +f 2834 1682 3610 820 +f 2845 862 2848 101 +f 2846 1753 3645 862 +f 2850 1117 2877 124 +f 1231 3112 1021 2852 +f 1294 3143 868 2853 +f 1107 3195 1108 2854 +f 1058 3145 1059 2855 +f 1057 3149 869 2856 +f 2858 1232 3127 870 +f 2859 1036 3155 871 +f 2860 1035 3125 1068 +f 2861 1062 3151 872 +f 2862 117 2863 873 +f 2862 1243 3198 1063 +f 906 2910 874 2863 +f 2864 1244 3153 873 +f 874 2890 890 2865 +f 1070 3157 1075 2865 +f 1073 3162 875 2866 +f 1279 3129 1039 2867 +f 1250 3159 876 2868 +f 1277 3161 877 2869 +f 2871 1298 3119 1031 +f 2872 1275 3166 879 +f 2873 1296 3131 881 +f 2874 1042 3121 880 +f 2875 1271 3133 882 +f 2876 1079 3168 1065 +f 1230 3180 883 2877 +f 1228 3178 884 2878 +f 1227 3203 885 2879 +f 1115 3176 1116 2880 +f 2882 1090 3202 1114 +f 2883 1085 3174 1045 +f 2884 1259 3135 887 +f 2885 1288 3172 1112 +f 2886 1286 3200 888 +f 2887 1283 3170 889 +f 2888 891 2893 889 +f 2888 1025 3115 1026 +f 2889 1027 3188 894 +f 904 2907 891 2890 +f 1024 3165 890 2891 +f 1103 3114 1023 2892 +f 2894 1280 3186 893 +f 2895 1251 3137 895 +f 2896 1046 3184 1093 +f 2897 1094 3139 896 +f 1101 3190 898 2899 +f 1281 3123 899 2900 +f 1033 3141 1052 2901 +f 1051 3193 892 2902 +f 953 2909 900 2903 +f 2905 901 2911 121 +f 947 2988 902 2906 +f 2907 952 2981 946 +f 959 2975 906 2911 +f 907 2939 927 2912 +f 942 2968 940 2912 +f 1119 3267 907 2913 +f 1118 3208 1161 2914 +f 1308 3239 1160 2915 +f 1209 3291 1210 2916 +f 1336 3241 908 2917 +f 1338 3245 909 2918 +f 2920 1138 3223 911 +f 2921 1134 3251 1171 +f 2922 1133 3221 912 +f 2923 1214 3247 1132 +f 2924 1212 3294 1213 +f 2925 940 2926 914 +f 2925 1341 3249 913 +f 2927 1146 3264 914 +f 1324 3253 916 2928 +f 1179 3258 917 2929 +f 1140 3225 1139 2930 +f 1175 3255 918 2931 +f 1174 3257 1177 2932 +f 2934 1370 3215 920 +f 2935 1181 3262 1142 +f 2936 1143 3227 921 +f 2937 1127 3217 922 +f 2938 1145 3229 915 +f 1366 3301 923 2939 +f 1223 3278 924 2940 +f 1194 3276 1192 2941 +f 1333 3299 925 2942 +f 1222 3274 926 2943 +f 2945 1331 3298 1218 +f 2946 1219 3272 1190 +f 2947 1314 3231 1148 +f 2948 1330 3270 1187 +f 2949 1364 3296 929 +f 2950 1362 3268 930 +f 2951 1186 3211 931 +f 2952 80 2959 931 +f 2952 1371 3284 1201 +f 2953 1372 3282 1200 +f 1323 3261 932 2954 +f 1327 3210 1120 2955 +f 1158 3289 1207 2956 +f 1351 3237 934 2957 +f 1352 3219 1130 2958 +f 995 2972 927 2959 +f 2960 1349 3233 1197 +f 2961 1152 3280 1198 +f 2962 1313 3235 1156 +f 1205 3286 1204 2964 +f 2966 993 2973 945 +f 2968 1000 3053 941 +f 943 2971 944 2970 +f 987 3057 936 2971 +f 992 3042 995 2973 +f 2975 956 2984 958 +f 2976 951 2979 950 +f 2977 958 2984 957 +f 949 3014 971 2978 +f 2980 955 2982 954 +f 2986 974 3041 960 +f 955 2989 961 2986 +f 971 3003 963 2989 +f 1393 3111 961 2990 +f 1394 3182 965 2991 +f 1412 3206 964 2992 +f 1389 3181 1092 2993 +f 1417 3179 966 2994 +f 1091 3204 968 2995 +f 2997 1088 3175 1086 +f 2998 1084 3136 1089 +f 2999 1113 3173 969 +f 3000 1439 3201 970 +f 3001 1082 3171 1081 +f 3002 1447 3116 1083 +f 1099 3187 1098 3003 +f 3005 1407 3138 1047 +f 3006 1048 3185 1095 +f 3007 1049 3140 1096 +f 3008 1030 3118 1050 +f 1425 3192 1102 3010 +f 1034 3191 1100 3011 +f 1411 3124 973 3012 +f 1105 3142 1106 3013 +f 1436 3194 1104 3014 +f 1387 3144 975 3015 +f 1444 3196 1053 3016 +f 1109 3146 976 3017 +f 1430 3150 1061 3018 +f 1445 3197 1060 3019 +f 3021 1110 3148 1056 +f 3022 1431 3128 978 +f 3023 1069 3156 979 +f 3024 1423 3126 1037 +f 3025 1038 3152 980 +f 3026 1064 3199 1067 +f 3027 1111 3154 1066 +f 3028 1066 3029 981 +f 3028 950 3041 974 +f 3029 1402 3169 1080 +f 3030 1044 3134 1043 +f 3031 1377 3122 982 +f 3032 1032 3132 983 +f 3033 1078 3167 1076 +f 1400 3160 984 3035 +f 1072 3130 1040 3036 +f 1419 3163 1041 3037 +f 1397 3158 1074 3038 +f 1071 3164 986 3039 +f 1378 3113 949 3040 +f 3043 998 3052 990 +f 3044 991 3047 988 +f 3045 990 3052 989 +f 1012 3083 1006 3046 +f 3048 1020 3050 994 +f 3055 1015 3110 997 +f 1020 3058 1184 3055 +f 1006 3072 1004 3058 +f 1470 3207 1184 3059 +f 1471 3266 1185 3060 +f 1195 3302 1224 3061 +f 1196 3279 1193 3062 +f 1492 3277 1001 3063 +f 1493 3300 1002 3064 +f 3066 1149 3273 1003 +f 3067 1150 3232 1151 +f 3068 1523 3271 1188 +f 3069 1217 3297 1189 +f 3070 1526 3269 1005 +f 3071 1122 3212 1121 +f 1203 3283 1123 3072 +f 3074 1154 3234 1153 +f 3075 1199 3281 1155 +f 3076 1157 3236 1007 +f 3077 1206 3214 1125 +f 1483 3288 1010 3079 +f 1468 3287 1009 3080 +f 1485 3220 1131 3081 +f 1159 3238 1011 3082 +f 1451 3290 1208 3083 +f 1529 3240 1162 3084 +f 1163 3292 1013 3085 +f 1515 3242 1165 3086 +f 1164 3246 1169 3087 +f 1168 3293 1211 3088 +f 3090 1503 3244 1166 +f 3091 1517 3224 1172 +f 3092 1481 3252 1137 +f 3093 1135 3222 1136 +f 3094 1504 3248 1216 +f 3095 1215 3295 1014 +f 3096 1466 3250 1170 +f 3097 1170 3098 1018 +f 3097 988 3110 1015 +f 3098 1183 3265 1147 +f 3099 1501 3230 1016 +f 3100 1128 3218 1129 +f 3101 1457 3228 1144 +f 3102 1479 3263 1182 +f 1499 3256 1176 3104 +f 1476 3226 1141 3105 +f 1498 3259 1017 3106 +f 1180 3254 1173 3107 +f 1473 3260 1019 3108 +f 1450 3209 1012 3109 +f 3116 1442 3424 1082 +f 1408 3415 1049 3118 +f 1077 3386 1420 3120 +f 1434 3408 1038 3126 +f 3132 1390 3377 1078 +f 3134 1552 3361 1377 +f 1257 3303 1288 3135 +f 1437 3413 1097 3138 +f 3142 1410 3394 1411 +f 3144 1588 3428 1444 +f 1432 3418 1431 3148 +f 3150 1594 3387 1445 +f 3153 1242 3309 1243 +f 1405 3371 1423 3156 +f 3157 165 3321 1073 +f 1558 3383 1397 3163 +f 1550 3405 1044 3169 +f 1441 3396 1439 3171 +f 3173 1611 3397 1084 +f 3175 1610 3426 1087 +f 1607 3395 1412 3181 +f 3183 1290 3325 1292 +f 1630 3391 1407 3185 +f 3189 1446 3430 1447 +f 3191 1623 3416 1425 +f 3192 1409 3393 1030 +f 3194 1618 3419 1105 +f 3196 1587 3427 1109 +f 3197 1404 3429 1110 +f 3199 1422 3410 1111 +f 3201 1440 3421 1113 +f 3208 235 3330 1119 +f 3212 1527 3495 1526 +f 1124 3337 1356 3213 +f 1482 3484 1157 3214 +f 1126 3457 1478 3216 +f 3218 1584 3448 1457 +f 1519 3478 1504 3222 +f 3230 1575 3433 1128 +f 1329 3332 1330 3231 +f 1509 3482 1507 3234 +f 3238 1487 3465 1485 +f 3240 1454 3499 1163 +f 1518 3488 1517 3244 +f 3246 1513 3459 1168 +f 3249 1312 3350 1212 +f 3250 1465 3449 1183 +f 3255 152 3343 1174 +f 3257 1319 3353 1370 +f 1497 3454 1180 3259 +f 1489 3467 1217 3269 +f 3271 1524 3468 1150 +f 3273 1628 3497 1191 +f 1488 3466 1195 3279 +f 3280 210 3358 1313 +f 3285 1467 3501 1122 +f 3287 1646 3485 1483 +f 3288 1511 3464 1206 +f 3290 1640 3490 1159 +f 3292 1528 3498 1515 +f 3293 1530 3500 1503 +f 3295 1505 3479 1466 +f 3297 1525 3492 1523 +f 3299 185 3355 1222 +f 3305 187 3313 1029 +f 1603 3552 1604 3359 +f 1565 3402 1420 3360 +f 3362 1413 3553 1605 +f 1381 3521 1399 3363 +f 3365 1398 3523 1380 +f 3366 1376 3543 1382 +f 1626 3392 1030 3367 +f 1606 3373 1389 3368 +f 3369 1573 3517 1385 +f 3370 1032 3376 1386 +f 1599 3550 1600 3371 +f 3372 1620 3566 1621 +f 3375 1557 3525 1561 +f 1567 3401 1419 3375 +f 1553 3514 1391 3376 +f 3379 1424 3412 1392 +f 1388 3567 1622 3380 +f 3384 1568 3526 1563 +f 1494 3471 1438 3393 +f 1488 3445 1620 3394 +f 865 3553 1413 3395 +f 865 3560 1396 3397 +f 1608 3400 1415 3398 +f 865 3556 1416 3400 +f 3401 1528 3499 1558 +f 1569 3500 1564 3402 +f 3403 1573 3518 1401 +f 1596 3417 1110 3407 +f 1599 3551 1601 3408 +f 3410 1599 3515 1549 +f 3411 1379 3564 1616 +f 1524 3577 1635 3413 +f 3415 1614 3497 1627 +f 3418 1593 3548 1597 +f 1619 3466 1410 3419 +f 1634 3453 1437 3420 +f 865 3558 1443 3426 +f 3427 1593 3545 1590 +f 3429 1593 3547 1595 +f 1448 3473 1478 3432 +f 3434 1619 3565 1617 +f 3436 1514 3524 1562 +f 3438 1566 3522 1560 +f 1644 3463 1206 3439 +f 1460 3445 1196 3440 +f 3440 1495 3567 1455 +f 1456 3534 1581 3441 +f 3442 1463 3542 1583 +f 1520 3489 1135 3443 +f 3444 1486 3582 1459 +f 1631 3576 1633 3446 +f 1462 3472 1498 3447 +f 1578 3542 1463 3448 +f 3450 1202 3481 1641 +f 1638 3579 1469 3451 +f 3452 1624 3569 1496 +f 1578 3538 1577 3454 +f 3455 1475 3536 1474 +f 1582 3541 1500 3457 +f 1578 3533 1586 3458 +f 1642 3491 1154 3462 +f 864 3587 1484 3464 +f 864 3582 1486 3465 +f 1491 3471 1221 3469 +f 1578 3536 1579 3473 +f 3474 1578 3535 1477 +f 1452 3520 1502 3476 +f 1570 3487 1503 3477 +f 3478 1573 3531 1574 +f 3480 1639 3580 1506 +f 864 3584 1508 3482 +f 3484 864 3586 1510 +f 1632 3576 1631 3486 +f 3488 1573 3529 1516 +f 3505 1540 3508 166 +f 3512 141 3660 1546 +f 3518 1555 3519 1556 +f 3539 864 3580 1449 +f 3548 1599 3549 1598 +f 3563 1615 3564 1379 +f 3571 1512 3572 1383 +f 3574 1633 3575 1629 +f 3588 1654 3591 1780 +f 3589 1649 3590 1651 +f 1653 3593 1650 3589 +f 3590 1781 3594 1780 +f 3591 1648 3595 1651 +f 1652 3595 1647 3594 +f 3613 1692 3614 136 +f 3632 771 3636 1739 +f 3634 1739 3636 779 +f 3635 5 3655 1771 +f 3638 1738 3639 1734 +# 3672 faces, 0 coords texture + +# End of File diff --git a/tests/example/example_tests_main.cpp b/tests/example/example_tests_main.cpp index 32e8d02b76..426d1ffef7 100644 --- a/tests/example/example_tests_main.cpp +++ b/tests/example/example_tests_main.cpp @@ -1,4 +1,3 @@ -#define CATCH_CONFIG_MAIN #include TEST_CASE("Is example succesful", "[example]") { diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index e34a40f346..d8dac3c10b 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -1,4 +1,5 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) + add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp test_3mf.cpp @@ -10,9 +11,20 @@ add_executable(${_TEST_NAME}_tests test_placeholder_parser.cpp test_polygon.cpp test_stl.cpp + test_meshsimplify.cpp + test_meshboolean.cpp ) + +if (TARGET OpenVDB::openvdb) + target_sources(${_TEST_NAME}_tests PRIVATE test_hollowing.cpp) +endif() + target_link_libraries(${_TEST_NAME}_tests test_common libslic3r) set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") +if (WIN32) + prusaslicer_copy_dlls(${_TEST_NAME}_tests) +endif() + # catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${CATCH_EXTRA_ARGS}) diff --git a/tests/libslic3r/libslic3r_tests.cpp b/tests/libslic3r/libslic3r_tests.cpp index caf5b3b9ac..f4dcab42a0 100644 --- a/tests/libslic3r/libslic3r_tests.cpp +++ b/tests/libslic3r/libslic3r_tests.cpp @@ -1,6 +1,6 @@ #include -#include "libslic3r/libslic3r.h" +#include "libslic3r/Utils.hpp" namespace { @@ -11,4 +11,31 @@ TEST_CASE("sort_remove_duplicates", "[utils]") { REQUIRE(data_src == data_dst); } +TEST_CASE("string_printf", "[utils]") { + SECTION("Empty format with empty data should return empty string") { + std::string outs = Slic3r::string_printf(""); + REQUIRE(outs.empty()); + } + + SECTION("String output length should be the same as input") { + std::string outs = Slic3r::string_printf("1234"); + REQUIRE(outs.size() == 4); + } + + SECTION("String format should be interpreted as with sprintf") { + std::string outs = Slic3r::string_printf("%d %f %s", 10, 11.4, " This is a string"); + char buffer[1024]; + + sprintf(buffer, "%d %f %s", 10, 11.4, " This is a string"); + + REQUIRE(outs.compare(buffer) == 0); + } + + SECTION("String format should survive large input data") { + std::string input(2048, 'A'); + std::string outs = Slic3r::string_printf("%s", input.c_str()); + REQUIRE(outs.compare(input) == 0); + } +} + } diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp index 6b60182b53..fb41ef93b5 100644 --- a/tests/libslic3r/test_3mf.cpp +++ b/tests/libslic3r/test_3mf.cpp @@ -51,11 +51,7 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") { WHEN("model is saved+loaded to/from 3mf file") { // save the model to 3mf file std::string test_file = std::string(TEST_DATA_DIR) + "/test_3mf/prusa.3mf"; -#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF store_3mf(test_file.c_str(), &src_model, nullptr, false); -#else - store_3mf(test_file.c_str(), &src_model, nullptr); -#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF // load back the model from the 3mf file Model dst_model; diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp new file mode 100644 index 0000000000..65b87c2a23 --- /dev/null +++ b/tests/libslic3r/test_hollowing.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include +#include "libslic3r/SLA/Hollowing.hpp" +#include +#include "libslic3r/Format/OBJ.hpp" + +#include + +#include + +#if defined(WIN32) || defined(_WIN32) +#define PATH_SEPARATOR R"(\)" +#else +#define PATH_SEPARATOR R"(/)" +#endif + +static Slic3r::TriangleMesh load_model(const std::string &obj_filename) +{ + Slic3r::TriangleMesh mesh; + auto fpath = TEST_DATA_DIR PATH_SEPARATOR + obj_filename; + Slic3r::load_obj(fpath.c_str(), &mesh); + return mesh; +} + + +TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") +{ + Slic3r::TriangleMesh in_mesh = load_model("20mm_cube.obj"); + Benchmark bench; + bench.start(); + + std::unique_ptr out_mesh_ptr = + Slic3r::sla::generate_interior(in_mesh); + + bench.stop(); + + std::cout << "Elapsed processing time: " << bench.getElapsedSec() << std::endl; + + if (out_mesh_ptr) in_mesh.merge(*out_mesh_ptr); + in_mesh.require_shared_vertices(); + in_mesh.WriteOBJFile("merged_out.obj"); +} + diff --git a/tests/libslic3r/test_meshboolean.cpp b/tests/libslic3r/test_meshboolean.cpp new file mode 100644 index 0000000000..97d03ac238 --- /dev/null +++ b/tests/libslic3r/test_meshboolean.cpp @@ -0,0 +1,26 @@ +#include +#include + +#include +#include +#include + +using namespace Slic3r; + +TEST_CASE("CGAL and TriangleMesh conversions", "[MeshBoolean]") { + TriangleMesh sphere = make_sphere(1.); + + auto cgalmesh_ptr = MeshBoolean::cgal::triangle_mesh_to_cgal(sphere); + + REQUIRE(cgalmesh_ptr); + REQUIRE(! MeshBoolean::cgal::does_self_intersect(*cgalmesh_ptr)); + + TriangleMesh M = MeshBoolean::cgal::cgal_to_triangle_mesh(*cgalmesh_ptr); + + REQUIRE(M.its.vertices.size() == sphere.its.vertices.size()); + REQUIRE(M.its.indices.size() == sphere.its.indices.size()); + + REQUIRE(M.volume() == Approx(sphere.volume())); + + REQUIRE(! MeshBoolean::cgal::does_self_intersect(M)); +} diff --git a/tests/libslic3r/test_meshsimplify.cpp b/tests/libslic3r/test_meshsimplify.cpp new file mode 100644 index 0000000000..d21c3a8924 --- /dev/null +++ b/tests/libslic3r/test_meshsimplify.cpp @@ -0,0 +1,11 @@ +#include +#include + +//#include + +//TEST_CASE("Mesh simplification", "[mesh_simplify]") { +// Simplify::load_obj(TEST_DATA_DIR PATH_SEPARATOR "zaba.obj"); +// Simplify::simplify_mesh_lossless(); +// Simplify::write_obj("zaba_simplified.obj"); +//} + diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 4d8217c167..e632dc7057 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -14,7 +14,15 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { { "nozzle_diameter", "0.6;0.6;0.6;0.6" }, { "temperature", "357;359;363;378" } }); - parser.apply_config(config); + // To test the "first_layer_extrusion_width" over "first_layer_heigth" over "layer_height" chain. + config.option("first_layer_height")->value = 150.; + config.option("first_layer_height")->percent = true; + // To let the PlaceholderParser throw when referencing first_layer_speed if it is set to percent, as the PlaceholderParser does not know + // a percent to what. + config.option("first_layer_speed")->value = 50.; + config.option("first_layer_speed")->percent = true; + + parser.apply_config(config); parser.set("foo", 0); parser.set("bar", 2); parser.set("num_extruders", 4); @@ -41,6 +49,19 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("math: int(13.4)") { REQUIRE(parser.process("{int(13.4)}") == "13"); } SECTION("math: int(-13.4)") { REQUIRE(parser.process("{int(-13.4)}") == "-13"); } + // Test the "coFloatOrPercent" and "xxx_extrusion_width" substitutions. + // first_layer_extrusion_width ratio_over first_layer_heigth ratio_over layer_height + SECTION("perimeter_extrusion_width") { REQUIRE(std::stod(parser.process("{perimeter_extrusion_width}")) == Approx(0.67500001192092896)); } + SECTION("first_layer_extrusion_width") { REQUIRE(std::stod(parser.process("{first_layer_extrusion_width}")) == Approx(0.9)); } + SECTION("support_material_xy_spacing") { REQUIRE(std::stod(parser.process("{support_material_xy_spacing}")) == Approx(0.3375)); } + // external_perimeter_speed over perimeter_speed + SECTION("external_perimeter_speed") { REQUIRE(std::stod(parser.process("{external_perimeter_speed}")) == Approx(30.)); } + // infill_overlap over perimeter_extrusion_width + SECTION("infill_overlap") { REQUIRE(std::stod(parser.process("{infill_overlap}")) == Approx(0.16875)); } + // If first_layer_speed is set to percent, then it is applied over respective extrusion types by overriding their respective speeds. + // The PlaceholderParser has no way to know which extrusion type the caller has in mind, therefore it throws. + SECTION("first_layer_speed") { REQUIRE_THROWS(parser.process("{first_layer_speed}")); } + // Test the boolean expression parser. auto boolean_expression = [&parser](const std::string& templ) { return parser.evaluate_boolean_expression(templ, parser.config()); }; diff --git a/tests/sla_print/CMakeLists.txt b/tests/sla_print/CMakeLists.txt index e8921ba486..9d47f3ae4d 100644 --- a/tests/sla_print/CMakeLists.txt +++ b/tests/sla_print/CMakeLists.txt @@ -1,5 +1,8 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) -add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp) +add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp + sla_print_tests.cpp + sla_test_utils.hpp sla_test_utils.cpp + sla_raycast_tests.cpp) target_link_libraries(${_TEST_NAME}_tests test_common libslic3r) set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index 229eb42676..10f5742d34 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -1,376 +1,17 @@ -#include - #include #include #include -// Debug -#include +#include "sla_test_utils.hpp" -#include "libslic3r/libslic3r.h" -#include "libslic3r/Format/OBJ.hpp" -#include "libslic3r/SLAPrint.hpp" -#include "libslic3r/TriangleMesh.hpp" -#include "libslic3r/SLA/SLAPad.hpp" -#include "libslic3r/SLA/SLASupportTreeBuilder.hpp" -#include "libslic3r/SLA/SLASupportTreeBuildsteps.hpp" -#include "libslic3r/SLA/SLAAutoSupports.hpp" -#include "libslic3r/SLA/SLARaster.hpp" -#include "libslic3r/SLA/ConcaveHull.hpp" -#include "libslic3r/MTUtils.hpp" +namespace { -#include "libslic3r/SVG.hpp" -#include "libslic3r/Format/OBJ.hpp" - -#if defined(WIN32) || defined(_WIN32) -#define PATH_SEPARATOR R"(\)" -#else -#define PATH_SEPARATOR R"(/)" -#endif - -namespace { -using namespace Slic3r; - -TriangleMesh load_model(const std::string &obj_filename) -{ - TriangleMesh mesh; - auto fpath = TEST_DATA_DIR PATH_SEPARATOR + obj_filename; - load_obj(fpath.c_str(), &mesh); - return mesh; -} - -enum e_validity { - ASSUME_NO_EMPTY = 1, - ASSUME_MANIFOLD = 2, - ASSUME_NO_REPAIR = 4 -}; - -void check_validity(const TriangleMesh &input_mesh, - int flags = ASSUME_NO_EMPTY | ASSUME_MANIFOLD | - ASSUME_NO_REPAIR) -{ - TriangleMesh mesh{input_mesh}; - - if (flags & ASSUME_NO_EMPTY) { - REQUIRE_FALSE(mesh.empty()); - } else if (mesh.empty()) - return; // If it can be empty and it is, there is nothing left to do. - - REQUIRE(stl_validate(&mesh.stl)); - - bool do_update_shared_vertices = false; - mesh.repair(do_update_shared_vertices); - - if (flags & ASSUME_NO_REPAIR) { - REQUIRE_FALSE(mesh.needed_repair()); - } - - if (flags & ASSUME_MANIFOLD) { - mesh.require_shared_vertices(); - if (!mesh.is_manifold()) mesh.WriteOBJFile("non_manifold.obj"); - REQUIRE(mesh.is_manifold()); - } -} - -struct PadByproducts -{ - ExPolygons model_contours; - ExPolygons support_contours; - TriangleMesh mesh; -}; - -void _test_concave_hull(const Polygons &hull, const ExPolygons &polys) -{ - REQUIRE(polys.size() >=hull.size()); - - double polys_area = 0; - for (const ExPolygon &p : polys) polys_area += p.area(); - - double cchull_area = 0; - for (const Slic3r::Polygon &p : hull) cchull_area += p.area(); - - REQUIRE(cchull_area >= Approx(polys_area)); - - size_t cchull_holes = 0; - for (const Slic3r::Polygon &p : hull) - cchull_holes += p.is_clockwise() ? 1 : 0; - - REQUIRE(cchull_holes == 0); - - Polygons intr = diff(to_polygons(polys), hull); - REQUIRE(intr.empty()); -} - -void test_concave_hull(const ExPolygons &polys) { - sla::PadConfig pcfg; - - Slic3r::sla::ConcaveHull cchull{polys, pcfg.max_merge_dist_mm, []{}}; - - _test_concave_hull(cchull.polygons(), polys); - - coord_t delta = scaled(pcfg.brim_size_mm + pcfg.wing_distance()); - ExPolygons wafflex = sla::offset_waffle_style_ex(cchull, delta); - Polygons waffl = sla::offset_waffle_style(cchull, delta); - - _test_concave_hull(to_polygons(wafflex), polys); - _test_concave_hull(waffl, polys); -} - -void test_pad(const std::string & obj_filename, - const sla::PadConfig &padcfg, - PadByproducts & out) -{ - REQUIRE(padcfg.validate().empty()); - - TriangleMesh mesh = load_model(obj_filename); - - REQUIRE_FALSE(mesh.empty()); - - // Create pad skeleton only from the model - Slic3r::sla::pad_blueprint(mesh, out.model_contours); - - test_concave_hull(out.model_contours); - - REQUIRE_FALSE(out.model_contours.empty()); - - // Create the pad geometry for the model contours only - Slic3r::sla::create_pad({}, out.model_contours, out.mesh, padcfg); - - check_validity(out.mesh); - - auto bb = out.mesh.bounding_box(); - REQUIRE(bb.max.z() - bb.min.z() == Approx(padcfg.full_height())); -} - -void test_pad(const std::string & obj_filename, - const sla::PadConfig &padcfg = {}) -{ - PadByproducts byproducts; - test_pad(obj_filename, padcfg, byproducts); -} - -struct SupportByproducts -{ - std::string obj_fname; - std::vector slicegrid; - std::vector model_slices; - sla::SupportTreeBuilder supporttree; - TriangleMesh input_mesh; -}; - -const constexpr float CLOSING_RADIUS = 0.005f; - -void check_support_tree_integrity(const sla::SupportTreeBuilder &stree, - const sla::SupportConfig &cfg) -{ - double gnd = stree.ground_level; - double H1 = cfg.max_solo_pillar_height_mm; - double H2 = cfg.max_dual_pillar_height_mm; - - for (const sla::Head &head : stree.heads()) { - REQUIRE((!head.is_valid() || head.pillar_id != sla::ID_UNSET || - head.bridge_id != sla::ID_UNSET)); - } - - for (const sla::Pillar &pillar : stree.pillars()) { - if (std::abs(pillar.endpoint().z() - gnd) < EPSILON) { - double h = pillar.height; - - if (h > H1) REQUIRE(pillar.links >= 1); - else if(h > H2) { REQUIRE(pillar.links >= 2); } - } - - REQUIRE(pillar.links <= cfg.pillar_cascade_neighbors); - REQUIRE(pillar.bridges <= cfg.max_bridges_on_pillar); - } - - double max_bridgelen = 0.; - auto chck_bridge = [&cfg](const sla::Bridge &bridge, double &max_brlen) { - Vec3d n = bridge.endp - bridge.startp; - double d = sla::distance(n); - max_brlen = std::max(d, max_brlen); - - double z = n.z(); - double polar = std::acos(z / d); - double slope = -polar + PI / 2.; - REQUIRE(std::abs(slope) >= cfg.bridge_slope - EPSILON); - }; - - for (auto &bridge : stree.bridges()) chck_bridge(bridge, max_bridgelen); - REQUIRE(max_bridgelen <= cfg.max_bridge_length_mm); - - max_bridgelen = 0; - for (auto &bridge : stree.crossbridges()) chck_bridge(bridge, max_bridgelen); - - double md = cfg.max_pillar_link_distance_mm / std::cos(-cfg.bridge_slope); - REQUIRE(max_bridgelen <= md); -} - -void test_supports(const std::string & obj_filename, - const sla::SupportConfig &supportcfg, - SupportByproducts & out) -{ - using namespace Slic3r; - TriangleMesh mesh = load_model(obj_filename); - - REQUIRE_FALSE(mesh.empty()); - - TriangleMeshSlicer slicer{&mesh}; - - auto bb = mesh.bounding_box(); - double zmin = bb.min.z(); - double zmax = bb.max.z(); - double gnd = zmin - supportcfg.object_elevation_mm; - auto layer_h = 0.05f; - - out.slicegrid = grid(float(gnd), float(zmax), layer_h); - slicer.slice(out.slicegrid , CLOSING_RADIUS, &out.model_slices, []{}); - - // Create the special index-triangle mesh with spatial indexing which - // is the input of the support point and support mesh generators - sla::EigenMesh3D emesh{mesh}; - - // Create the support point generator - sla::SLAAutoSupports::Config autogencfg; - autogencfg.head_diameter = float(2 * supportcfg.head_front_radius_mm); - sla::SLAAutoSupports point_gen{emesh, out.model_slices, out.slicegrid, - autogencfg, [] {}, [](int) {}}; - - // Get the calculated support points. - std::vector support_points = point_gen.output(); - - int validityflags = ASSUME_NO_REPAIR; - - // If there is no elevation, support points shall be removed from the - // bottom of the object. - if (std::abs(supportcfg.object_elevation_mm) < EPSILON) { - sla::remove_bottom_points(support_points, zmin, - supportcfg.base_height_mm); - } else { - // Should be support points at least on the bottom of the model - REQUIRE_FALSE(support_points.empty()); - - // Also the support mesh should not be empty. - validityflags |= ASSUME_NO_EMPTY; - } - - // Generate the actual support tree - sla::SupportTreeBuilder treebuilder; - treebuilder.build(sla::SupportableMesh{emesh, support_points, supportcfg}); - - check_support_tree_integrity(treebuilder, supportcfg); - - const TriangleMesh &output_mesh = treebuilder.retrieve_mesh(); - - check_validity(output_mesh, validityflags); - - // Quick check if the dimensions and placement of supports are correct - auto obb = output_mesh.bounding_box(); - - double allowed_zmin = zmin - supportcfg.object_elevation_mm; - - if (std::abs(supportcfg.object_elevation_mm) < EPSILON) - allowed_zmin = zmin - 2 * supportcfg.head_back_radius_mm; - - REQUIRE(obb.min.z() >= allowed_zmin); - REQUIRE(obb.max.z() <= zmax); - - // Move out the support tree into the byproducts, we can examine it further - // in various tests. - out.obj_fname = std::move(obj_filename); - out.supporttree = std::move(treebuilder); - out.input_mesh = std::move(mesh); -} - -void test_supports(const std::string & obj_filename, - const sla::SupportConfig &supportcfg = {}) -{ - SupportByproducts byproducts; - test_supports(obj_filename, supportcfg, byproducts); -} - -void export_failed_case(const std::vector &support_slices, - const SupportByproducts &byproducts) -{ - for (size_t n = 0; n < support_slices.size(); ++n) { - const ExPolygons &sup_slice = support_slices[n]; - const ExPolygons &mod_slice = byproducts.model_slices[n]; - Polygons intersections = intersection(sup_slice, mod_slice); - - std::stringstream ss; - if (!intersections.empty()) { - ss << byproducts.obj_fname << std::setprecision(4) << n << ".svg"; - SVG svg(ss.str()); - svg.draw(sup_slice, "green"); - svg.draw(mod_slice, "blue"); - svg.draw(intersections, "red"); - svg.Close(); - } - } - - TriangleMesh m; - byproducts.supporttree.retrieve_full_mesh(m); - m.merge(byproducts.input_mesh); - m.repair(); - m.require_shared_vertices(); - m.WriteOBJFile(byproducts.obj_fname.c_str()); -} - -void test_support_model_collision( - const std::string & obj_filename, - const sla::SupportConfig &input_supportcfg = {}) -{ - SupportByproducts byproducts; - - sla::SupportConfig supportcfg = input_supportcfg; - - // Set head penetration to a small negative value which should ensure that - // the supports will not touch the model body. - supportcfg.head_penetration_mm = -0.15; - - // TODO: currently, the tailheads penetrating into the model body do not - // respect the penetration parameter properly. No issues were reported so - // far but we should definitely fix this. - supportcfg.ground_facing_only = true; - - test_supports(obj_filename, supportcfg, byproducts); - - // Slice the support mesh given the slice grid of the model. - std::vector support_slices = - byproducts.supporttree.slice(byproducts.slicegrid, CLOSING_RADIUS); - - // The slices originate from the same slice grid so the numbers must match - - bool support_mesh_is_empty = - byproducts.supporttree.retrieve_mesh(sla::MeshType::Pad).empty() && - byproducts.supporttree.retrieve_mesh(sla::MeshType::Support).empty(); - - if (support_mesh_is_empty) - REQUIRE(support_slices.empty()); - else - REQUIRE(support_slices.size() == byproducts.model_slices.size()); - - bool notouch = true; - for (size_t n = 0; notouch && n < support_slices.size(); ++n) { - const ExPolygons &sup_slice = support_slices[n]; - const ExPolygons &mod_slice = byproducts.model_slices[n]; - - Polygons intersections = intersection(sup_slice, mod_slice); - - notouch = notouch && intersections.empty(); - } - - if (!notouch) export_failed_case(support_slices, byproducts); - - REQUIRE(notouch); -} - -const char * const BELOW_PAD_TEST_OBJECTS[] = { +const char *const BELOW_PAD_TEST_OBJECTS[] = { "20mm_cube.obj", "V.obj", }; -const char * const AROUND_PAD_TEST_OBJECTS[] = { +const char *const AROUND_PAD_TEST_OBJECTS[] = { "20mm_cube.obj", "V.obj", "frog_legs.obj", @@ -385,58 +26,6 @@ const char *const SUPPORT_TEST_MODELS[] = { } // namespace -// Test pair hash for 'nums' random number pairs. -template void test_pairhash() -{ - const constexpr size_t nums = 1000; - I A[nums] = {0}, B[nums] = {0}; - std::unordered_set CH; - std::unordered_map> ints; - - std::random_device rd; - std::mt19937 gen(rd()); - - const I Ibits = int(sizeof(I) * CHAR_BIT); - const II IIbits = int(sizeof(II) * CHAR_BIT); - - int bits = IIbits / 2 < Ibits ? Ibits / 2 : Ibits; - if (std::is_signed::value) bits -= 1; - const I Imin = 0; - const I Imax = I(std::pow(2., bits) - 1); - - std::uniform_int_distribution dis(Imin, Imax); - - for (size_t i = 0; i < nums;) { - I a = dis(gen); - if (CH.find(a) == CH.end()) { CH.insert(a); A[i] = a; ++i; } - } - - for (size_t i = 0; i < nums;) { - I b = dis(gen); - if (CH.find(b) == CH.end()) { CH.insert(b); B[i] = b; ++i; } - } - - for (size_t i = 0; i < nums; ++i) { - I a = A[i], b = B[i]; - - REQUIRE(a != b); - - II hash_ab = sla::pairhash(a, b); - II hash_ba = sla::pairhash(b, a); - REQUIRE(hash_ab == hash_ba); - - auto it = ints.find(hash_ab); - - if (it != ints.end()) { - REQUIRE(( - (it->second.first == a && it->second.second == b) || - (it->second.first == b && it->second.second == a) - )); - } else - ints[hash_ab] = std::make_pair(a, b); - } -} - TEST_CASE("Pillar pairhash should be unique", "[SLASupportGeneration]") { test_pairhash(); test_pairhash(); @@ -444,74 +33,123 @@ TEST_CASE("Pillar pairhash should be unique", "[SLASupportGeneration]") { test_pairhash(); } +TEST_CASE("Support point generator should be deterministic if seeded", + "[SLASupportGeneration], [SLAPointGen]") { + TriangleMesh mesh = load_model("A_upsidedown.obj"); + + sla::EigenMesh3D emesh{mesh}; + + sla::SupportConfig supportcfg; + sla::SupportPointGenerator::Config autogencfg; + autogencfg.head_diameter = float(2 * supportcfg.head_front_radius_mm); + sla::SupportPointGenerator point_gen{emesh, autogencfg, [] {}, [](int) {}}; + + TriangleMeshSlicer slicer{&mesh}; + + auto bb = mesh.bounding_box(); + double zmin = bb.min.z(); + double zmax = bb.max.z(); + double gnd = zmin - supportcfg.object_elevation_mm; + auto layer_h = 0.05f; + + auto slicegrid = grid(float(gnd), float(zmax), layer_h); + std::vector slices; + slicer.slice(slicegrid, SlicingMode::Regular, CLOSING_RADIUS, &slices, []{}); + + point_gen.seed(0); + point_gen.execute(slices, slicegrid); + + auto get_chksum = [](const std::vector &pts){ + long long chksum = 0; + for (auto &pt : pts) { + auto p = scaled(pt.pos); + chksum += p.x() + p.y() + p.z(); + } + + return chksum; + }; + + long long checksum = get_chksum(point_gen.output()); + size_t ptnum = point_gen.output().size(); + REQUIRE(point_gen.output().size() > 0); + + for (int i = 0; i < 20; ++i) { + point_gen.output().clear(); + point_gen.seed(0); + point_gen.execute(slices, slicegrid); + REQUIRE(point_gen.output().size() == ptnum); + REQUIRE(checksum == get_chksum(point_gen.output())); + } +} + TEST_CASE("Flat pad geometry is valid", "[SLASupportGeneration]") { sla::PadConfig padcfg; - + // Disable wings padcfg.wall_height_mm = .0; - + for (auto &fname : BELOW_PAD_TEST_OBJECTS) test_pad(fname, padcfg); } TEST_CASE("WingedPadGeometryIsValid", "[SLASupportGeneration]") { sla::PadConfig padcfg; - + // Add some wings to the pad to test the cavity padcfg.wall_height_mm = 1.; - + for (auto &fname : BELOW_PAD_TEST_OBJECTS) test_pad(fname, padcfg); } TEST_CASE("FlatPadAroundObjectIsValid", "[SLASupportGeneration]") { sla::PadConfig padcfg; - + // Add some wings to the pad to test the cavity padcfg.wall_height_mm = 0.; // padcfg.embed_object.stick_stride_mm = 0.; padcfg.embed_object.enabled = true; padcfg.embed_object.everywhere = true; - + for (auto &fname : AROUND_PAD_TEST_OBJECTS) test_pad(fname, padcfg); } TEST_CASE("WingedPadAroundObjectIsValid", "[SLASupportGeneration]") { sla::PadConfig padcfg; - + // Add some wings to the pad to test the cavity padcfg.wall_height_mm = 1.; padcfg.embed_object.enabled = true; padcfg.embed_object.everywhere = true; - + for (auto &fname : AROUND_PAD_TEST_OBJECTS) test_pad(fname, padcfg); } TEST_CASE("ElevatedSupportGeometryIsValid", "[SLASupportGeneration]") { sla::SupportConfig supportcfg; supportcfg.object_elevation_mm = 5.; - + for (auto fname : SUPPORT_TEST_MODELS) test_supports(fname); } TEST_CASE("FloorSupportGeometryIsValid", "[SLASupportGeneration]") { sla::SupportConfig supportcfg; supportcfg.object_elevation_mm = 0; - + for (auto &fname: SUPPORT_TEST_MODELS) test_supports(fname, supportcfg); } TEST_CASE("ElevatedSupportsDoNotPierceModel", "[SLASupportGeneration]") { - + sla::SupportConfig supportcfg; - + for (auto fname : SUPPORT_TEST_MODELS) test_support_model_collision(fname, supportcfg); } TEST_CASE("FloorSupportsDoNotPierceModel", "[SLASupportGeneration]") { - + sla::SupportConfig supportcfg; supportcfg.object_elevation_mm = 0; - + for (auto fname : SUPPORT_TEST_MODELS) test_support_model_collision(fname, supportcfg); } @@ -525,7 +163,7 @@ TEST_CASE("InitializedRasterShouldBeNONEmpty", "[SLARasterOutput]") { // Default Prusa SL1 display parameters sla::Raster::Resolution res{2560, 1440}; sla::Raster::PixelDim pixdim{120. / res.width_px, 68. / res.height_px}; - + sla::Raster raster; raster.reset(res, pixdim); REQUIRE_FALSE(raster.empty()); @@ -535,75 +173,12 @@ TEST_CASE("InitializedRasterShouldBeNONEmpty", "[SLARasterOutput]") { REQUIRE(raster.pixel_dimensions().h_mm == Approx(pixdim.h_mm)); } -using TPixel = uint8_t; -static constexpr const TPixel FullWhite = 255; -static constexpr const TPixel FullBlack = 0; - -template constexpr int arraysize(const A (&)[N]) { return N; } - -static void check_raster_transformations(sla::Raster::Orientation o, - sla::Raster::TMirroring mirroring) -{ - double disp_w = 120., disp_h = 68.; - sla::Raster::Resolution res{2560, 1440}; - sla::Raster::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px}; - - auto bb = BoundingBox({0, 0}, {scaled(disp_w), scaled(disp_h)}); - sla::Raster::Trafo trafo{o, mirroring}; - trafo.origin_x = bb.center().x(); - trafo.origin_y = bb.center().y(); - - sla::Raster raster{res, pixdim, trafo}; - - // create box of size 32x32 pixels (not 1x1 to avoid antialiasing errors) - coord_t pw = 32 * coord_t(std::ceil(scaled(pixdim.w_mm))); - coord_t ph = 32 * coord_t(std::ceil(scaled(pixdim.h_mm))); - ExPolygon box; - box.contour.points = {{-pw, -ph}, {pw, -ph}, {pw, ph}, {-pw, ph}}; - - double tr_x = scaled(20.), tr_y = tr_x; - - box.translate(tr_x, tr_y); - ExPolygon expected_box = box; - - // Now calculate the position of the translated box according to output - // trafo. - if (o == sla::Raster::Orientation::roPortrait) expected_box.rotate(PI / 2.); - - if (mirroring[X]) - for (auto &p : expected_box.contour.points) p.x() = -p.x(); - - if (mirroring[Y]) - for (auto &p : expected_box.contour.points) p.y() = -p.y(); - - raster.draw(box); - - Point expected_coords = expected_box.contour.bounding_box().center(); - double rx = unscaled(expected_coords.x() + bb.center().x()) / pixdim.w_mm; - double ry = unscaled(expected_coords.y() + bb.center().y()) / pixdim.h_mm; - auto w = size_t(std::floor(rx)); - auto h = res.height_px - size_t(std::floor(ry)); - - REQUIRE((w < res.width_px && h < res.height_px)); - - auto px = raster.read_pixel(w, h); - - if (px != FullWhite) { - sla::PNGImage img; - std::fstream outf("out.png", std::ios::out); - - outf << img.serialize(raster); - } - - REQUIRE(px == FullWhite); -} - TEST_CASE("MirroringShouldBeCorrect", "[SLARasterOutput]") { sla::Raster::TMirroring mirrorings[] = {sla::Raster::NoMirror, sla::Raster::MirrorX, sla::Raster::MirrorY, sla::Raster::MirrorXY}; - + sla::Raster::Orientation orientations[] = {sla::Raster::roLandscape, sla::Raster::roPortrait}; for (auto orientation : orientations) @@ -611,81 +186,43 @@ TEST_CASE("MirroringShouldBeCorrect", "[SLARasterOutput]") { check_raster_transformations(orientation, mirror); } -static ExPolygon square_with_hole(double v) -{ - ExPolygon poly; - coord_t V = scaled(v / 2.); - - poly.contour.points = {{-V, -V}, {V, -V}, {V, V}, {-V, V}}; - poly.holes.emplace_back(); - V = V / 2; - poly.holes.front().points = {{-V, V}, {V, V}, {V, -V}, {-V, -V}}; - return poly; -} - -static double pixel_area(TPixel px, const sla::Raster::PixelDim &pxdim) -{ - return (pxdim.h_mm * pxdim.w_mm) * px * 1. / (FullWhite - FullBlack); -} - -static double raster_white_area(const sla::Raster &raster) -{ - if (raster.empty()) return std::nan(""); - - auto res = raster.resolution(); - double a = 0; - - for (size_t x = 0; x < res.width_px; ++x) - for (size_t y = 0; y < res.height_px; ++y) { - auto px = raster.read_pixel(x, y); - a += pixel_area(px, raster.pixel_dimensions()); - } - - return a; -} - -static double predict_error(const ExPolygon &p, const sla::Raster::PixelDim &pd) -{ - auto lines = p.lines(); - double pix_err = pixel_area(FullWhite, pd) / 2.; - - // Worst case is when a line is parallel to the shorter axis of one pixel, - // when the line will be composed of the max number of pixels - double pix_l = std::min(pd.h_mm, pd.w_mm); - - double error = 0.; - for (auto &l : lines) - error += (unscaled(l.length()) / pix_l) * pix_err; - - return error; -} TEST_CASE("RasterizedPolygonAreaShouldMatch", "[SLARasterOutput]") { double disp_w = 120., disp_h = 68.; sla::Raster::Resolution res{2560, 1440}; sla::Raster::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px}; - + sla::Raster raster{res, pixdim}; auto bb = BoundingBox({0, 0}, {scaled(disp_w), scaled(disp_h)}); - + ExPolygon poly = square_with_hole(10.); poly.translate(bb.center().x(), bb.center().y()); raster.draw(poly); - + double a = poly.area() / (scaled(1.) * scaled(1.)); double ra = raster_white_area(raster); double diff = std::abs(a - ra); - + REQUIRE(diff <= predict_error(poly, pixdim)); - + raster.clear(); poly = square_with_hole(60.); poly.translate(bb.center().x(), bb.center().y()); raster.draw(poly); - + a = poly.area() / (scaled(1.) * scaled(1.)); ra = raster_white_area(raster); diff = std::abs(a - ra); - + REQUIRE(diff <= predict_error(poly, pixdim)); } + +TEST_CASE("Triangle mesh conversions should be correct", "[SLAConversions]") +{ + sla::Contour3D cntr; + + { + std::fstream infile{"extruder_idler_quads.obj", std::ios::in}; + cntr.from_obj(infile); + } +} diff --git a/tests/sla_print/sla_print_tests_main.cpp b/tests/sla_print/sla_print_tests_main.cpp new file mode 100644 index 0000000000..b2aa80259d --- /dev/null +++ b/tests/sla_print/sla_print_tests_main.cpp @@ -0,0 +1 @@ +#include diff --git a/tests/sla_print/sla_raycast_tests.cpp b/tests/sla_print/sla_raycast_tests.cpp new file mode 100644 index 0000000000..74c7994723 --- /dev/null +++ b/tests/sla_print/sla_raycast_tests.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include +#include + +#include "sla_test_utils.hpp" + +using namespace Slic3r; + +// First do a simple test of the hole raycaster. +TEST_CASE("Raycaster - find intersections of a line and cylinder") +{ + sla::DrainHole hole{Vec3f(0,0,0), Vec3f(0,0,1), 5, 10}; + std::array, 2> out; + Vec3f s; + Vec3f dir; + + // Start inside the hole and cast perpendicular to its axis. + s = {-1.f, 0, 5.f}; + dir = {1.f, 0, 0}; + hole.get_intersections(s, dir, out); + REQUIRE(out[0].first == Approx(-4.f)); + REQUIRE(out[1].first == Approx(6.f)); + + // Start outside and cast parallel to axis. + s = {0, 0, -1.f}; + dir = {0, 0, 1.f}; + hole.get_intersections(s, dir, out); + REQUIRE(std::abs(out[0].first - 1.f) < 0.001f); + REQUIRE(std::abs(out[1].first - 11.f) < 0.001f); + + // Start outside and cast so that entry is in base and exit on the cylinder + s = {0, -1.f, -1.f}; + dir = {0, 1.f, 1.f}; + dir.normalize(); + hole.get_intersections(s, dir, out); + REQUIRE(std::abs(out[0].first - std::sqrt(2.f)) < 0.001f); + REQUIRE(std::abs(out[1].first - std::sqrt(72.f)) < 0.001f); +} + + +// Create a simple scene with a 20mm cube and a big hole in the front wall +// with 5mm radius. Then shoot rays from interesting positions and see where +// they land. +TEST_CASE("Raycaster with loaded drillholes", "[sla_raycast]") +{ + // Load the cube and make it hollow. + TriangleMesh cube = load_model("20mm_cube.obj"); + sla::HollowingConfig hcfg; + std::unique_ptr cube_inside = sla::generate_interior(cube, hcfg); + REQUIRE(cube_inside); + + // Helper bb + auto boxbb = cube.bounding_box(); + + // Create the big 10mm long drainhole in the front wall. + Vec3f center = boxbb.center().cast(); + Vec3f p = {center.x(), 0., center.z()}; + Vec3f normal = {0.f, 1.f, 0.f}; + float radius = 5.f; + float hole_length = 10.; + sla::DrainHoles holes = { sla::DrainHole{p, normal, radius, hole_length} }; + + cube.merge(*cube_inside); + cube.require_shared_vertices(); + + sla::EigenMesh3D emesh{cube}; + emesh.load_holes(holes); + + Vec3d s = center.cast(); + // Fire from center, should hit the interior wall + auto hit = emesh.query_ray_hit(s, {0, 1., 0.}); + REQUIRE(hit.distance() == Approx(boxbb.size().x() / 2 - hcfg.min_thickness)); + + // Fire upward from hole center, hit distance equals the radius (hits the + // side of the hole cut. + s.y() = hcfg.min_thickness / 2; + hit = emesh.query_ray_hit(s, {0, 0., 1.}); + REQUIRE(hit.distance() == Approx(radius)); + + // Fire from outside, hit the back side of the cube interior + s.y() = -1.; + hit = emesh.query_ray_hit(s, {0, 1., 0.}); + REQUIRE(hit.distance() == Approx(boxbb.max.y() - hcfg.min_thickness - s.y())); + + // Fire downwards from above the hole cylinder. Has to go through the cyl. + // as it was not there. + s = center.cast(); + s.z() = boxbb.max.z() - hcfg.min_thickness - 1.; + hit = emesh.query_ray_hit(s, {0, 0., -1.}); + REQUIRE(hit.distance() == Approx(s.z() - boxbb.min.z() - hcfg.min_thickness)); + + // Check for support tree correctness + test_support_model_collision("20mm_cube.obj", {}, hcfg, holes); +} diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp new file mode 100644 index 0000000000..a844b2eaed --- /dev/null +++ b/tests/sla_print/sla_test_utils.cpp @@ -0,0 +1,394 @@ +#include "sla_test_utils.hpp" + +void test_support_model_collision(const std::string &obj_filename, + const sla::SupportConfig &input_supportcfg, + const sla::HollowingConfig &hollowingcfg, + const sla::DrainHoles &drainholes) +{ + SupportByproducts byproducts; + + sla::SupportConfig supportcfg = input_supportcfg; + + // Set head penetration to a small negative value which should ensure that + // the supports will not touch the model body. + supportcfg.head_penetration_mm = -0.15; + + test_supports(obj_filename, supportcfg, hollowingcfg, drainholes, byproducts); + + // Slice the support mesh given the slice grid of the model. + std::vector support_slices = + byproducts.supporttree.slice(byproducts.slicegrid, CLOSING_RADIUS); + + // The slices originate from the same slice grid so the numbers must match + + bool support_mesh_is_empty = + byproducts.supporttree.retrieve_mesh(sla::MeshType::Pad).empty() && + byproducts.supporttree.retrieve_mesh(sla::MeshType::Support).empty(); + + if (support_mesh_is_empty) + REQUIRE(support_slices.empty()); + else + REQUIRE(support_slices.size() == byproducts.model_slices.size()); + + bool notouch = true; + for (size_t n = 0; notouch && n < support_slices.size(); ++n) { + const ExPolygons &sup_slice = support_slices[n]; + const ExPolygons &mod_slice = byproducts.model_slices[n]; + + Polygons intersections = intersection(sup_slice, mod_slice); + + notouch = notouch && intersections.empty(); + } + + /*if (!notouch) */export_failed_case(support_slices, byproducts); + + REQUIRE(notouch); +} + +void export_failed_case(const std::vector &support_slices, const SupportByproducts &byproducts) +{ + for (size_t n = 0; n < support_slices.size(); ++n) { + const ExPolygons &sup_slice = support_slices[n]; + const ExPolygons &mod_slice = byproducts.model_slices[n]; + Polygons intersections = intersection(sup_slice, mod_slice); + + std::stringstream ss; + if (!intersections.empty()) { + ss << byproducts.obj_fname << std::setprecision(4) << n << ".svg"; + SVG svg(ss.str()); + svg.draw(sup_slice, "green"); + svg.draw(mod_slice, "blue"); + svg.draw(intersections, "red"); + svg.Close(); + } + } + + TriangleMesh m; + byproducts.supporttree.retrieve_full_mesh(m); + m.merge(byproducts.input_mesh); + m.repair(); + m.require_shared_vertices(); + m.WriteOBJFile(byproducts.obj_fname.c_str()); +} + +void test_supports(const std::string &obj_filename, + const sla::SupportConfig &supportcfg, + const sla::HollowingConfig &hollowingcfg, + const sla::DrainHoles &drainholes, + SupportByproducts &out) +{ + using namespace Slic3r; + TriangleMesh mesh = load_model(obj_filename); + + REQUIRE_FALSE(mesh.empty()); + + if (hollowingcfg.enabled) { + auto inside = sla::generate_interior(mesh, hollowingcfg); + REQUIRE(inside); + mesh.merge(*inside); + mesh.require_shared_vertices(); + } + + TriangleMeshSlicer slicer{&mesh}; + + auto bb = mesh.bounding_box(); + double zmin = bb.min.z(); + double zmax = bb.max.z(); + double gnd = zmin - supportcfg.object_elevation_mm; + auto layer_h = 0.05f; + + out.slicegrid = grid(float(gnd), float(zmax), layer_h); + slicer.slice(out.slicegrid, SlicingMode::Regular, CLOSING_RADIUS, &out.model_slices, []{}); + sla::cut_drainholes(out.model_slices, out.slicegrid, CLOSING_RADIUS, drainholes, []{}); + + // Create the special index-triangle mesh with spatial indexing which + // is the input of the support point and support mesh generators + sla::EigenMesh3D emesh{mesh}; + if (hollowingcfg.enabled) + emesh.load_holes(drainholes); + + // Create the support point generator + sla::SupportPointGenerator::Config autogencfg; + autogencfg.head_diameter = float(2 * supportcfg.head_front_radius_mm); + sla::SupportPointGenerator point_gen{emesh, autogencfg, [] {}, [](int) {}}; + + point_gen.seed(0); // Make the test repeatable + point_gen.execute(out.model_slices, out.slicegrid); + + // Get the calculated support points. + std::vector support_points = point_gen.output(); + + int validityflags = ASSUME_NO_REPAIR; + + // If there is no elevation, support points shall be removed from the + // bottom of the object. + if (std::abs(supportcfg.object_elevation_mm) < EPSILON) { + sla::remove_bottom_points(support_points, zmin, + supportcfg.base_height_mm); + } else { + // Should be support points at least on the bottom of the model + REQUIRE_FALSE(support_points.empty()); + + // Also the support mesh should not be empty. + validityflags |= ASSUME_NO_EMPTY; + } + + // Generate the actual support tree + sla::SupportTreeBuilder treebuilder; + treebuilder.build(sla::SupportableMesh{emesh, support_points, supportcfg}); + + check_support_tree_integrity(treebuilder, supportcfg); + + const TriangleMesh &output_mesh = treebuilder.retrieve_mesh(); + + check_validity(output_mesh, validityflags); + + // Quick check if the dimensions and placement of supports are correct + auto obb = output_mesh.bounding_box(); + + double allowed_zmin = zmin - supportcfg.object_elevation_mm; + + if (std::abs(supportcfg.object_elevation_mm) < EPSILON) + allowed_zmin = zmin - 2 * supportcfg.head_back_radius_mm; + + REQUIRE(obb.min.z() >= allowed_zmin); + REQUIRE(obb.max.z() <= zmax); + + // Move out the support tree into the byproducts, we can examine it further + // in various tests. + out.obj_fname = std::move(obj_filename); + out.supporttree = std::move(treebuilder); + out.input_mesh = std::move(mesh); +} + +void check_support_tree_integrity(const sla::SupportTreeBuilder &stree, + const sla::SupportConfig &cfg) +{ + double gnd = stree.ground_level; + double H1 = cfg.max_solo_pillar_height_mm; + double H2 = cfg.max_dual_pillar_height_mm; + + for (const sla::Head &head : stree.heads()) { + REQUIRE((!head.is_valid() || head.pillar_id != sla::ID_UNSET || + head.bridge_id != sla::ID_UNSET)); + } + + for (const sla::Pillar &pillar : stree.pillars()) { + if (std::abs(pillar.endpoint().z() - gnd) < EPSILON) { + double h = pillar.height; + + if (h > H1) REQUIRE(pillar.links >= 1); + else if(h > H2) { REQUIRE(pillar.links >= 2); } + } + + REQUIRE(pillar.links <= cfg.pillar_cascade_neighbors); + REQUIRE(pillar.bridges <= cfg.max_bridges_on_pillar); + } + + double max_bridgelen = 0.; + auto chck_bridge = [&cfg](const sla::Bridge &bridge, double &max_brlen) { + Vec3d n = bridge.endp - bridge.startp; + double d = sla::distance(n); + max_brlen = std::max(d, max_brlen); + + double z = n.z(); + double polar = std::acos(z / d); + double slope = -polar + PI / 2.; + REQUIRE(std::abs(slope) >= cfg.bridge_slope - EPSILON); + }; + + for (auto &bridge : stree.bridges()) chck_bridge(bridge, max_bridgelen); + REQUIRE(max_bridgelen <= cfg.max_bridge_length_mm); + + max_bridgelen = 0; + for (auto &bridge : stree.crossbridges()) chck_bridge(bridge, max_bridgelen); + + double md = cfg.max_pillar_link_distance_mm / std::cos(-cfg.bridge_slope); + REQUIRE(max_bridgelen <= md); +} + +void test_pad(const std::string &obj_filename, const sla::PadConfig &padcfg, PadByproducts &out) +{ + REQUIRE(padcfg.validate().empty()); + + TriangleMesh mesh = load_model(obj_filename); + + REQUIRE_FALSE(mesh.empty()); + + // Create pad skeleton only from the model + Slic3r::sla::pad_blueprint(mesh, out.model_contours); + + test_concave_hull(out.model_contours); + + REQUIRE_FALSE(out.model_contours.empty()); + + // Create the pad geometry for the model contours only + Slic3r::sla::create_pad({}, out.model_contours, out.mesh, padcfg); + + check_validity(out.mesh); + + auto bb = out.mesh.bounding_box(); + REQUIRE(bb.max.z() - bb.min.z() == Approx(padcfg.full_height())); +} + +static void _test_concave_hull(const Polygons &hull, const ExPolygons &polys) +{ + REQUIRE(polys.size() >=hull.size()); + + double polys_area = 0; + for (const ExPolygon &p : polys) polys_area += p.area(); + + double cchull_area = 0; + for (const Slic3r::Polygon &p : hull) cchull_area += p.area(); + + REQUIRE(cchull_area >= Approx(polys_area)); + + size_t cchull_holes = 0; + for (const Slic3r::Polygon &p : hull) + cchull_holes += p.is_clockwise() ? 1 : 0; + + REQUIRE(cchull_holes == 0); + + Polygons intr = diff(to_polygons(polys), hull); + REQUIRE(intr.empty()); +} + +void test_concave_hull(const ExPolygons &polys) { + sla::PadConfig pcfg; + + Slic3r::sla::ConcaveHull cchull{polys, pcfg.max_merge_dist_mm, []{}}; + + _test_concave_hull(cchull.polygons(), polys); + + coord_t delta = scaled(pcfg.brim_size_mm + pcfg.wing_distance()); + ExPolygons wafflex = sla::offset_waffle_style_ex(cchull, delta); + Polygons waffl = sla::offset_waffle_style(cchull, delta); + + _test_concave_hull(to_polygons(wafflex), polys); + _test_concave_hull(waffl, polys); +} + +void check_validity(const TriangleMesh &input_mesh, int flags) +{ + TriangleMesh mesh{input_mesh}; + + if (flags & ASSUME_NO_EMPTY) { + REQUIRE_FALSE(mesh.empty()); + } else if (mesh.empty()) + return; // If it can be empty and it is, there is nothing left to do. + + REQUIRE(stl_validate(&mesh.stl)); + + bool do_update_shared_vertices = false; + mesh.repair(do_update_shared_vertices); + + if (flags & ASSUME_NO_REPAIR) { + REQUIRE_FALSE(mesh.needed_repair()); + } + + if (flags & ASSUME_MANIFOLD) { + mesh.require_shared_vertices(); + if (!mesh.is_manifold()) mesh.WriteOBJFile("non_manifold.obj"); + REQUIRE(mesh.is_manifold()); + } +} + +void check_raster_transformations(sla::Raster::Orientation o, sla::Raster::TMirroring mirroring) +{ + double disp_w = 120., disp_h = 68.; + sla::Raster::Resolution res{2560, 1440}; + sla::Raster::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px}; + + auto bb = BoundingBox({0, 0}, {scaled(disp_w), scaled(disp_h)}); + sla::Raster::Trafo trafo{o, mirroring}; + trafo.origin_x = bb.center().x(); + trafo.origin_y = bb.center().y(); + + sla::Raster raster{res, pixdim, trafo}; + + // create box of size 32x32 pixels (not 1x1 to avoid antialiasing errors) + coord_t pw = 32 * coord_t(std::ceil(scaled(pixdim.w_mm))); + coord_t ph = 32 * coord_t(std::ceil(scaled(pixdim.h_mm))); + ExPolygon box; + box.contour.points = {{-pw, -ph}, {pw, -ph}, {pw, ph}, {-pw, ph}}; + + double tr_x = scaled(20.), tr_y = tr_x; + + box.translate(tr_x, tr_y); + ExPolygon expected_box = box; + + // Now calculate the position of the translated box according to output + // trafo. + if (o == sla::Raster::Orientation::roPortrait) expected_box.rotate(PI / 2.); + + if (mirroring[X]) + for (auto &p : expected_box.contour.points) p.x() = -p.x(); + + if (mirroring[Y]) + for (auto &p : expected_box.contour.points) p.y() = -p.y(); + + raster.draw(box); + + Point expected_coords = expected_box.contour.bounding_box().center(); + double rx = unscaled(expected_coords.x() + bb.center().x()) / pixdim.w_mm; + double ry = unscaled(expected_coords.y() + bb.center().y()) / pixdim.h_mm; + auto w = size_t(std::floor(rx)); + auto h = res.height_px - size_t(std::floor(ry)); + + REQUIRE((w < res.width_px && h < res.height_px)); + + auto px = raster.read_pixel(w, h); + + if (px != FullWhite) { + sla::PNGImage img; + std::fstream outf("out.png", std::ios::out); + + outf << img.serialize(raster); + } + + REQUIRE(px == FullWhite); +} + +ExPolygon square_with_hole(double v) +{ + ExPolygon poly; + coord_t V = scaled(v / 2.); + + poly.contour.points = {{-V, -V}, {V, -V}, {V, V}, {-V, V}}; + poly.holes.emplace_back(); + V = V / 2; + poly.holes.front().points = {{-V, V}, {V, V}, {V, -V}, {-V, -V}}; + return poly; +} + +double raster_white_area(const sla::Raster &raster) +{ + if (raster.empty()) return std::nan(""); + + auto res = raster.resolution(); + double a = 0; + + for (size_t x = 0; x < res.width_px; ++x) + for (size_t y = 0; y < res.height_px; ++y) { + auto px = raster.read_pixel(x, y); + a += pixel_area(px, raster.pixel_dimensions()); + } + + return a; +} + +double predict_error(const ExPolygon &p, const sla::Raster::PixelDim &pd) +{ + auto lines = p.lines(); + double pix_err = pixel_area(FullWhite, pd) / 2.; + + // Worst case is when a line is parallel to the shorter axis of one pixel, + // when the line will be composed of the max number of pixels + double pix_l = std::min(pd.h_mm, pd.w_mm); + + double error = 0.; + for (auto &l : lines) + error += (unscaled(l.length()) / pix_l) * pix_err; + + return error; +} diff --git a/tests/sla_print/sla_test_utils.hpp b/tests/sla_print/sla_test_utils.hpp new file mode 100644 index 0000000000..f3727bd394 --- /dev/null +++ b/tests/sla_print/sla_test_utils.hpp @@ -0,0 +1,187 @@ +#ifndef SLA_TEST_UTILS_HPP +#define SLA_TEST_UTILS_HPP + +#include +#include + +// Debug +#include +#include + +#include "libslic3r/libslic3r.h" +#include "libslic3r/Format/OBJ.hpp" +#include "libslic3r/SLAPrint.hpp" +#include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/SLA/Pad.hpp" +#include "libslic3r/SLA/SupportTreeBuilder.hpp" +#include "libslic3r/SLA/SupportTreeBuildsteps.hpp" +#include "libslic3r/SLA/SupportPointGenerator.hpp" +#include "libslic3r/SLA/Raster.hpp" +#include "libslic3r/SLA/ConcaveHull.hpp" +#include "libslic3r/MTUtils.hpp" + +#include "libslic3r/SVG.hpp" +#include "libslic3r/Format/OBJ.hpp" + +using namespace Slic3r; + +enum e_validity { + ASSUME_NO_EMPTY = 1, + ASSUME_MANIFOLD = 2, + ASSUME_NO_REPAIR = 4 +}; + +void check_validity(const TriangleMesh &input_mesh, + int flags = ASSUME_NO_EMPTY | ASSUME_MANIFOLD | + ASSUME_NO_REPAIR); + +struct PadByproducts +{ + ExPolygons model_contours; + ExPolygons support_contours; + TriangleMesh mesh; +}; + +void test_concave_hull(const ExPolygons &polys); + +void test_pad(const std::string & obj_filename, + const sla::PadConfig &padcfg, + PadByproducts & out); + +inline void test_pad(const std::string & obj_filename, + const sla::PadConfig &padcfg = {}) +{ + PadByproducts byproducts; + test_pad(obj_filename, padcfg, byproducts); +} + +struct SupportByproducts +{ + std::string obj_fname; + std::vector slicegrid; + std::vector model_slices; + sla::SupportTreeBuilder supporttree; + TriangleMesh input_mesh; +}; + +const constexpr float CLOSING_RADIUS = 0.005f; + +void check_support_tree_integrity(const sla::SupportTreeBuilder &stree, + const sla::SupportConfig &cfg); + +void test_supports(const std::string &obj_filename, + const sla::SupportConfig &supportcfg, + const sla::HollowingConfig &hollowingcfg, + const sla::DrainHoles &drainholes, + SupportByproducts &out); + +inline void test_supports(const std::string &obj_filename, + const sla::SupportConfig &supportcfg, + SupportByproducts &out) +{ + sla::HollowingConfig hcfg; + hcfg.enabled = false; + test_supports(obj_filename, supportcfg, hcfg, {}, out); +} + +inline void test_supports(const std::string &obj_filename, + const sla::SupportConfig &supportcfg = {}) +{ + SupportByproducts byproducts; + test_supports(obj_filename, supportcfg, byproducts); +} + +void export_failed_case(const std::vector &support_slices, + const SupportByproducts &byproducts); + + +void test_support_model_collision( + const std::string &obj_filename, + const sla::SupportConfig &input_supportcfg, + const sla::HollowingConfig &hollowingcfg, + const sla::DrainHoles &drainholes); + +inline void test_support_model_collision( + const std::string &obj_filename, + const sla::SupportConfig &input_supportcfg = {}) +{ + sla::HollowingConfig hcfg; + hcfg.enabled = false; + test_support_model_collision(obj_filename, input_supportcfg, hcfg, {}); +} + +// Test pair hash for 'nums' random number pairs. +template void test_pairhash() +{ + const constexpr size_t nums = 1000; + I A[nums] = {0}, B[nums] = {0}; + std::unordered_set CH; + std::unordered_map> ints; + + std::random_device rd; + std::mt19937 gen(rd()); + + const I Ibits = int(sizeof(I) * CHAR_BIT); + const II IIbits = int(sizeof(II) * CHAR_BIT); + + int bits = IIbits / 2 < Ibits ? Ibits / 2 : Ibits; + if (std::is_signed::value) bits -= 1; + const I Imin = 0; + const I Imax = I(std::pow(2., bits) - 1); + + std::uniform_int_distribution dis(Imin, Imax); + + for (size_t i = 0; i < nums;) { + I a = dis(gen); + if (CH.find(a) == CH.end()) { CH.insert(a); A[i] = a; ++i; } + } + + for (size_t i = 0; i < nums;) { + I b = dis(gen); + if (CH.find(b) == CH.end()) { CH.insert(b); B[i] = b; ++i; } + } + + for (size_t i = 0; i < nums; ++i) { + I a = A[i], b = B[i]; + + REQUIRE(a != b); + + II hash_ab = sla::pairhash(a, b); + II hash_ba = sla::pairhash(b, a); + REQUIRE(hash_ab == hash_ba); + + auto it = ints.find(hash_ab); + + if (it != ints.end()) { + REQUIRE(( + (it->second.first == a && it->second.second == b) || + (it->second.first == b && it->second.second == a) + )); + } else + ints[hash_ab] = std::make_pair(a, b); + } +} + +// SLA Raster test utils: + +using TPixel = uint8_t; +static constexpr const TPixel FullWhite = 255; +static constexpr const TPixel FullBlack = 0; + +template constexpr int arraysize(const A (&)[N]) { return N; } + +void check_raster_transformations(sla::Raster::Orientation o, + sla::Raster::TMirroring mirroring); + +ExPolygon square_with_hole(double v); + +inline double pixel_area(TPixel px, const sla::Raster::PixelDim &pxdim) +{ + return (pxdim.h_mm * pxdim.w_mm) * px * 1. / (FullWhite - FullBlack); +} + +double raster_white_area(const sla::Raster &raster); + +double predict_error(const ExPolygon &p, const sla::Raster::PixelDim &pd); + +#endif // SLA_TEST_UTILS_HPP diff --git a/tests/test_utils.hpp b/tests/test_utils.hpp new file mode 100644 index 0000000000..b129cc79f1 --- /dev/null +++ b/tests/test_utils.hpp @@ -0,0 +1,21 @@ +#ifndef SLIC3R_TEST_UTILS +#define SLIC3R_TEST_UTILS + +#include +#include + +#if defined(WIN32) || defined(_WIN32) +#define PATH_SEPARATOR R"(\)" +#else +#define PATH_SEPARATOR R"(/)" +#endif + +inline Slic3r::TriangleMesh load_model(const std::string &obj_filename) +{ + Slic3r::TriangleMesh mesh; + auto fpath = TEST_DATA_DIR PATH_SEPARATOR + obj_filename; + Slic3r::load_obj(fpath.c_str(), &mesh); + return mesh; +} + +#endif // SLIC3R_TEST_UTILS diff --git a/version.inc b/version.inc index dbd97b6554..997d44ab28 100644 --- a/version.inc +++ b/version.inc @@ -3,7 +3,7 @@ set(SLIC3R_APP_NAME "PrusaSlicer") set(SLIC3R_APP_KEY "PrusaSlicer") -set(SLIC3R_VERSION "2.2.0-alpha2") +set(SLIC3R_VERSION "2.2.0-alpha4") set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN") set(SLIC3R_RC_VERSION "2,2,0,0") set(SLIC3R_RC_VERSION_DOTS "2.2.0.0") diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 74c0f4b0ce..0952513ca3 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -48,12 +48,8 @@ _constant() Ref model_object(); Ref config() %code%{ RETVAL = &THIS->config(); %}; - Points copies(); Clone bounding_box(); - Points _shifted_copies() - %code%{ RETVAL = THIS->copies(); %}; - size_t layer_count(); Ref get_layer(int idx); @@ -152,9 +148,6 @@ _constant() croak("Configuration is not valid: %s\n", err.c_str()); RETVAL = 1; %}; - Clone bounding_box(); - Clone total_bounding_box(); - Clone size() %code%{ RETVAL = THIS->bounding_box().size(); %}; void set_callback_event(int evt) %code%{ %}; diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index f3153665cb..230f8b2a54 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -182,7 +182,7 @@ TriangleMesh::slice(z) std::vector layers; TriangleMeshSlicer mslicer(THIS); - mslicer.slice(z_f, 0.049f, &layers, [](){}); + mslicer.slice(z_f, SlicingMode::Regular, 0.049f, &layers, [](){}); AV* layers_av = newAV(); size_t len = layers.size();